首页 话题 小组 问答 好文 用户 我的社区 域名交易 唠叨

[分享]使用Go语言和FFmpeg实现视频中添加文字和图片

发布于 2025-03-27 20:16:25
0
46

在现代社交媒体的时代,视频已经成为了人们分享生活、记录瞬间的重要方式之一。而对于制作视频来说,添加文字和图片元素可以起到丰富内容、增强表达能力的作用。那么,如何使用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,我们可以轻松地实现视频中添加文字和图片的功能。无论是为了更好地传达信息,还是为了增加视频的趣味性,这样的功能都能帮助我们达到目标。随着技术的进步和应用的拓展,我们相信,视频编辑的方式还会越来越丰富多样,为人们带来更多的乐趣和创造力的发挥空间。

评论
一个月内的热帖推荐
凯特网
Lv.1普通用户

261

帖子

13

小组

1225

积分

站长交流