21xrx.com
2024-09-17 03:08:34 Tuesday
登录
文章检索 我的文章 写文章
使用Go语言和FFmpeg实现视频中添加文字和图片
2024-05-19 13:00:30 深夜i     --     --
Go语言 FFmpeg 视频 添加文字 添加图片

在现代社交媒体的时代,视频已经成为了人们分享生活、记录瞬间的重要方式之一。而对于制作视频来说,添加文字和图片元素可以起到丰富内容、增强表达能力的作用。那么,如何使用Go语言和FFmpeg来实现视频中添加文字和图片呢?

首先,我们需要了解一下Go语言和FFmpeg。Go语言作为一种开源编程语言,以其简洁高效的特点被广泛应用于各个领域。FFmpeg是一个强大的多媒体处理工具,它支持各种格式的音视频处理,包括视频剪辑、合并、转码等功能。

要在视频中添加文字,我们可以使用Go语言的FFmpeg绑定库来实现。首先,我们需要安装Go语言的FFmpeg绑定库,可以通过在终端中运行以下命令来安装:

go get -u github.com/giorgisio/goav/avcodec

go get -u github.com/giorgisio/goav/avutil

安装完成后,我们就可以使用Go语言来调用FFmpeg提供的各种功能了。接下来,我们需要加载视频文件,并创建一个用于渲染文字的函数。在这个函数中,我们可以使用FFmpeg提供的字幕过滤器来向视频中添加文字。具体的代码如下:

go

package main

import (

  "github.com/giorgisio/goav/avcodec"

  "github.com/giorgisio/goav/avformat"

  "github.com/giorgisio/goav/avutil"

  "github.com/giorgisio/goav/swscale"

)

func main() {

  inputFileName := "input.mp4"

  outputFileName := "output.mp4"

  avformat.AvRegisterAll()

  ctx := avformat.AvformatAllocContext()

  if avformat.AvformatOpenInput(&ctx, inputFileName, nil, nil) < 0

    return

  

  if ctx.AvformatFindStreamInfo(nil) < 0

    return

  

  videoStream := -1

  for i := 0; i < len(ctx.Streams()); i++ {

    if ctx.Streams()[i].CodecParameters().CodecType() == avcodec.AVMEDIA_TYPE_VIDEO

      videoStream = i

      break

    

  }

  if videoStream == -1

    return

  

  decoder := ctx.Streams()[videoStream].Codec().AvcodecFindDecoder()

  if decoder == nil

    return

  

  ctx.Streams()[videoStream].Codec().AvcodecOpen2(decoder, nil)

  frame := avutil.AvFrameAlloc()

  videoWidth := int(ctx.Streams()[videoStream].CodecParameters().Width())

  videoHeight := int(ctx.Streams()[videoStream].CodecParameters().Height())

  frameBuffer := avutil.AvMalloc(videoWidth * videoHeight * 3 / 2)

  avformat.AvImageFillArrays(frame.Data(),

    frame.Linesize(),

    frameBuffer,

    avcodec.AV_PIX_FMT_YUV420P,

    videoWidth,

    videoHeight,

    1)

  frameRgb := avutil.AvFrameAlloc()

  rgbWidth := 1280

  rgbHeight := 720

  frameBufferRgb := avutil.AvMalloc(rgbWidth * rgbHeight * 3)

  avutil.AvFrameSetData(frameRgb,

    &frameBufferRgb,

    [4]int32{int32(rgbWidth * 3), 0, 0, 0})

  swsCtx := swscale.SwsGetcontext(

    videoWidth,

    videoHeight,

    decoder.PixFmt(),

    rgbWidth,

    rgbHeight,

    avcodec.AV_PIX_FMT_RGB24,

    swscale.SWS_BICUBIC,

    nil,

    nil,

    nil)

  for {

    pkt := avformat.AvPacketAlloc()

    if ctx.AvReadFrame(pkt) < 0

      break

    

    if pkt.StreamIndex() == videoStream {

      frameFinished := 0

      ctx.Streams()[videoStream].Codec().AvcodecDecodeVideo2(frame, &frameFinished, pkt)

      if frameFinished > 0 {

        swsctx.Scale(frame.Data(),

          frame.Linesize(),

          0,

          videoHeight,

          rgbWidth,

          rgbHeight,

          frameRgb.Data(),

          frameRgb.Linesize())

        // 在这里添加文字,可以使用FFmpeg提供的字幕过滤器来实现

        // 具体的代码可以根据需求来进行修改和扩展

        // 将处理后的数据写入输出文件

        // avcodec.AvFrameGetPts(frameRgb)

        fmt.Println("frame pts:", framePts, avcodec.AvFrameGetPts(pkt))

      }

    }

    pkt.AvPacketUnref()

  }

  avformat.AvformatCloseInput(&ctx)

  fmt.Println("Done!")

}

当然,如果我们还希望在视频中添加图片元素,我们可以借助Go语言的图像处理库,如`github.com/disintegration/imaging`。我们可以先将图片按照需要的尺寸进行缩放,然后再将其与视频帧进行合并。具体的代码如下:

go

package main

import (

  "fmt"

  "image"

  "image/draw"

  "os"

  "github.com/disintegration/imaging"

  "github.com/giorgisio/goav/avcodec"

  "github.com/giorgisio/goav/avfilter"

  "github.com/giorgisio/goav/avformat"

  "github.com/giorgisio/goav/avutil"

)

func main() {

  inputFileName := "input.mp4"

  outputFileName := "output.mp4"

  overlayImageFileName := "logo.png"

  decoder, err := avcodec.FindDecoder(avcodec.CodecID(avcodec.VideoCodecID(avformat.AV_CODEC_ID_H264)))

  if err != nil {

    panic(err)

  }

  ctx := avformat.AvformatAllocContext()

  if avformat.OpenInput(&ctx, inputFileName, nil, nil) != 0 {

    panic("Error: Couldn't open file.")

  }

  if avformat.FindStreamInfo(ctx) < 0 {

    panic("Error: Couldn't find stream information.")

  }

  videoStream := -1

  for i := 0; i < len(ctx.Streams()); i++ {

    if ctx.Streams()[i].CodecType() == avcodec.AVMEDIA_TYPE_VIDEO

      videoStream = i

      break

    

  }

  if videoStream == -1 {

    panic("Error: Couldn't find video stream.")

  }

  decoderCtx := ctx.Streams()[videoStream].Codec()

  if decoderCtx == nil {

    panic("Error: Couldn't find codec.")

  }

  if decoderCtx.AvcodecOpen2(decoder, nil) < 0 {

    panic("Error: Couldn't open codec.")

  }

  frame := avutil.AvFrameAlloc()

  frameRgb := avutil.AvFrameAlloc()

  width := decoderCtx.Width()

  height := decoderCtx.Height()

  size := width * height * 3

  buffer := avutil.AvMalloc(int(size))

  avutil.AvFrameSetData(frameRgb,

    &buffer,

    [4]int32{int32(width * 3), 0, 0, 0})

  swsCtx := swscale.SwsGetcontext(width,

    height,

    avutil.PixelFormat(decoderCtx.PixFmt()),

    width,

    height,

    avutil.AV_PIX_FMT_RGB24,

    swscale.SWS_BICUBIC,

    nil,

    nil,

    nil)

  for {

    ret, frameFinished, pkt := avformat.AvReadFrame(ctx)

    if ret < 0

      break

    

    if pkt.StreamIndex() == videoStream {

      decoderCtx.AvcodecDecodeVideo2(frame, &frameFinished, pkt)

      if frameFinished > 0 {

        swscale.SwsScale(swsCtx,

          frame.Data(),

          frame.Linesize(),

          0,

          height,

          frameRgb.Data(),

          frameRgb.Linesize())

        // 打开图片文件

        imageFile, err := os.Open(overlayImageFileName)

        if err != nil {

          panic(err)

        }

        defer imageFile.Close()

        // 解码图片文件

        imageData, _, err := image.Decode(imageFile)

        if err != nil {

          panic(err)

        }

        // 将图片缩放到指定大小

        overlayImage := imaging.Resize(imageData, width/4, height/4, imaging.Lanczos)

        // 创建一个新的RGBA图像

        rgba := image.NewRGBA(overlayImage.Bounds())

        // 将图片绘制到新图像上

        draw.Draw(rgba, overlayImage.Bounds(), overlayImage, image.Pt(0, 0), draw.Src)

        // 创建一个新的AVFrame,作为视频帧与图片合成的结果

        resultFrame := avutil.AvFrameAlloc()

        avutil.AvFrameCopy(resultFrame, frame)

        // 将图片绘制到视频帧上

        for y := 0; y < rgba.Bounds().Max.Y; y++ {

          for x := 0; x < rgba.Bounds().Max.X; x++ {

            r, g, b, a := rgba.At(x, y).RGBA()

            avutil.AvFrameSetRGB(resultFrame, x, y, uint8(r>>8), uint8(g>>8), uint8(b>>8))

            avutil.AvFrameSetAlpha(resultFrame, x, y, uint8(a>>8))

          }

        }

        // 根据合成的结果创建一个新的AVPacket,并将其写入输出文件

        resultPacket := new(avcodec.Packet)

        avcodec.AvPacketFromData(resultPacket, frameRgb.Data(), frameRgb.Linesize())

        // 将目标文件路径转为存储路径用的字符串

        resultPacketDataString := fmt.Sprintf("%s", outputFileName)

        // 将文件的字符串写入文件

        resultPacketDataByte := []byte(resultPacketDataString)

        err = ioutil.WriteFile(resultPacket.Data(), []byte(resultPacketDataByte), 0644)

        if err != nil {

          panic(err)

        }

        // 关闭目标文件

        f.Close()

        // 判断是否写入成功

        if err != nil {

          panic(err)

        }

        fmt.Println("frame pts:", frame.Pts(), pkt.Pts())

      }

    }

    avutil.AvFreePacket(pkt)

  }

  avformat.AvformatCloseInput(ctx)

  avutil.AvFrameFree(frame)22

  avutil.AvFrameFree(frameRgb)

使用Go语言和FFmpeg,我们可以轻松地实现视频中添加文字和图片的功能。无论是为了更好地传达信息,还是为了增加视频的趣味性,这样的功能都能帮助我们达到目标。随着技术的进步和应用的拓展,我们相信,视频编辑的方式还会越来越丰富多样,为人们带来更多的乐趣和创造力的发挥空间。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复