前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2023-04-12:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息。

2023-04-12:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息。

作者头像
福大大架构师每日一题
发布2023-06-08 16:13:03
2270
发布2023-06-08 16:13:03
举报
文章被收录于专栏:福大大架构师每日一题

2023-04-12:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息。

答案2023-04-12:

主要的过程包括:

1. 打开输入视频文件并查找视频流信息。

2. 根据视频流类型打开解码器,并设置解码器参数。

3. 循环读取视频帧数据。

4. 对每一帧数据进行解码并提取其中的运动矢量信息。

5. 输出每个运动矢量的相关参数:帧号、来源、块大小、源位置、目标位置、标志等。

具体的过程实现在 main0 函数中,其中调用了 decode_packet 和 open_codec_context 函数来完成解码和上下文打开的过程。最终输出结果通过 fmt.Printf 函数打印到控制台上。

整个程序的主函数为 main,其中设置了 FFmpeg 库的路径和创建了一个 out 目录用于存放输出结果。

代码见moonfdd/ffmpeg-go库。

命令如下:

代码语言:javascript
复制
go run ./examples/internalexamples/extract_mvs/main.go ./resources/big_buck_bunny.mp4

golang代码如下:

代码语言:javascript
复制
package main

import (
  "fmt"
  "os"
  "unsafe"

  "github.com/moonfdd/ffmpeg-go/ffcommon"
  "github.com/moonfdd/ffmpeg-go/libavcodec"
  "github.com/moonfdd/ffmpeg-go/libavformat"
  "github.com/moonfdd/ffmpeg-go/libavutil"
)

func main0() (ret ffcommon.FInt) {
  var pkt libavformat.AVPacket

  if len(os.Args) != 2 {
    fmt.Printf("Usage: %s <input video>\n", os.Args[0])
    os.Exit(1)
  }
  src_filename = os.Args[1]

  if libavformat.AvformatOpenInput(&fmt_ctx, src_filename, nil, nil) < 0 {
    fmt.Printf("Could not open source file %s\n", src_filename)
    os.Exit(1)
  }

  if fmt_ctx.AvformatFindStreamInfo(nil) < 0 {
    fmt.Printf("Could not find stream information\n")
    os.Exit(1)
  }

  open_codec_context(fmt_ctx, libavutil.AVMEDIA_TYPE_VIDEO)

  fmt_ctx.AvDumpFormat(0, src_filename, 0)
  for {
    if video_stream == nil {
      fmt.Printf("Could not find video stream in the input, aborting\n")
      ret = 1
      break
    }

    frame = libavutil.AvFrameAlloc()
    if frame == nil {
      fmt.Printf("Could not allocate frame\n")
      ret = -libavutil.ENOMEM
      break
    }

    fmt.Printf("framenum,source,blockw,blockh,srcx,srcy,dstx,dsty,flags\n")

    /* read frames from the file */
    for fmt_ctx.AvReadFrame(&pkt) >= 0 {
      if pkt.StreamIndex == uint32(video_stream_idx) {
        ret = decode_packet(&pkt)
      }
      pkt.AvPacketUnref()
      if ret < 0 {
        break
      }
    }

    /* flush cached frames */
    decode_packet(nil)
    break
  }
  // end:
  libavcodec.AvcodecFreeContext(&video_dec_ctx)
  libavformat.AvformatCloseInput(&fmt_ctx)
  libavutil.AvFrameFree(&frame)
  if ret < 0 {
    return 1
  } else {
    return 0
  }
}

var fmt_ctx *libavformat.AVFormatContext
var video_dec_ctx *libavcodec.AVCodecContext
var video_stream *libavformat.AVStream
var src_filename string

var video_stream_idx ffcommon.FInt = -1
var frame *libavutil.AVFrame
var video_frame_count ffcommon.FInt

func decode_packet(pkt *libavcodec.AVPacket) ffcommon.FInt {
  ret := video_dec_ctx.AvcodecSendPacket(pkt)
  if ret < 0 {
    fmt.Printf("Error while sending a packet to the decoder: %s\n", libavutil.AvErr2str(ret))
    return ret
  }

  for ret >= 0 {
    ret = video_dec_ctx.AvcodecReceiveFrame(frame)
    if ret == -libavutil.EAGAIN || ret == libavutil.AVERROR_EOF {
      break
    } else if ret < 0 {
      fmt.Printf("Error while receiving a frame from the decoder: %s\n", libavutil.AvErr2str(ret))
      return ret
    }

    if ret >= 0 {
      var i ffcommon.FInt
      var sd *libavutil.AVFrameSideData

      video_frame_count++
      sd = frame.AvFrameGetSideData(libavutil.AV_FRAME_DATA_MOTION_VECTORS)
      if sd != nil {
        //const AVMotionVector
        // mvs := (*libavutil.AVMotionVector)(unsafe.Pointer(sd.Data))
        var a [2]libavutil.AVMotionVector
        len0 := uintptr(unsafe.Pointer(&a[1])) - uintptr(unsafe.Pointer(&a[0]))
        for i = 0; i < sd.Size/int32(len0); i++ {
          mv := (*libavutil.AVMotionVector)(unsafe.Pointer(uintptr(unsafe.Pointer(sd.Data)) + len0*uintptr(i)))
          fmt.Printf("%d,%2d,%2d,%2d,%4d,%4d,%4d,%4d,0x%d\n",
            video_frame_count, mv.Source,
            mv.W, mv.H, mv.SrcX, mv.SrcY,
            mv.DstX, mv.DstY, mv.Flags)
        }
      }
      frame.AvFrameUnref()
    }
  }

  return 0
}

func open_codec_context(fmt_ctx *libavformat.AVFormatContext, type0 libavutil.AVMediaType) ffcommon.FInt {
  var ret ffcommon.FInt
  var st *libavformat.AVStream
  var dec_ctx *libavcodec.AVCodecContext
  var dec *libavcodec.AVCodec
  var opts *libavutil.AVDictionary

  ret = fmt_ctx.AvFindBestStream(type0, -1, -1, &dec, 0)
  if ret < 0 {
    fmt.Printf("Could not find %s stream in input file '%s'\n",
      libavutil.AvGetMediaTypeString(type0), src_filename)
    return ret
  } else {
    stream_idx := ret
    st = fmt_ctx.GetStream(uint32(stream_idx))

    dec_ctx = dec.AvcodecAllocContext3()
    if dec_ctx == nil {
      fmt.Printf("Failed to allocate codec\n")
      return -libavutil.EINVAL
    }

    ret = dec_ctx.AvcodecParametersToContext(st.Codecpar)
    if ret < 0 {
      fmt.Printf("Failed to copy codec parameters to codec context\n")
      return ret
    }

    /* Init the video decoder */
    libavutil.AvDictSet(&opts, "flags2", "+export_mvs", 0)
    ret = dec_ctx.AvcodecOpen2(dec, &opts)
    if ret < 0 {
      fmt.Printf("Failed to open %s codec\n",
        libavutil.AvGetMediaTypeString(type0))
      return ret
    }

    video_stream_idx = stream_idx
    video_stream = fmt_ctx.GetStream(uint32(video_stream_idx))
    video_dec_ctx = dec_ctx
  }

  return 0
}

func main() {
  os.Setenv("Path", os.Getenv("Path")+";./lib")
  ffcommon.SetAvutilPath("./lib/avutil-56.dll")
  ffcommon.SetAvcodecPath("./lib/avcodec-58.dll")
  ffcommon.SetAvdevicePath("./lib/avdevice-58.dll")
  ffcommon.SetAvfilterPath("./lib/avfilter-7.dll")
  ffcommon.SetAvformatPath("./lib/avformat-58.dll")
  ffcommon.SetAvpostprocPath("./lib/postproc-55.dll")
  ffcommon.SetAvswresamplePath("./lib/swresample-3.dll")
  ffcommon.SetAvswscalePath("./lib/swscale-5.dll")

  genDir := "./out"
  _, err := os.Stat(genDir)
  if err != nil {
    if os.IsNotExist(err) {
      os.Mkdir(genDir, 0777) //  Everyone can read write and execute
    }
  }

  main0()
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-04-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 福大大架构师每日一题 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档