2023-04-12:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息。
2023-04-12:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息。
答案2023-04-12:
主要的过程包括:
- 打开输入视频文件并查找视频流信息。
- 根据视频流类型打开解码器,并设置解码器参数。
- 循环读取视频帧数据。
- 对每一帧数据进行解码并提取其中的运动矢量信息。
- 输出每个运动矢量的相关参数:帧号、来源、块大小、源位置、目标位置、标志等。
具体的过程实现在 main0 函数中,其中调用了 decode_packet 和 open_codec_context 函数来完成解码和上下文打开的过程。最终输出结果通过 fmt.Printf 函数打印到控制台上。
整个程序的主函数为 main,其中设置了 FFmpeg 库的路径和创建了一个 out 目录用于存放输出结果。
代码见moonfdd/ffmpeg-go库。
命令如下:
go run ./examples/internalexamples/extract_mvs/main.go ./resources/big_buck_bunny.mp4
golang代码如下:
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:使用 Go 重写 FFmpeg 的 extract_mvs.c 工具程序,提取视频中的运动矢量信息。的更多相关文章
- FFMPEG,将字幕“烧进”MP4视频中
原文地址:http://blog.csdn.net/ufocode/article/details/75475539 由于mp4容器,不像MKV等容器有自己的字幕流. MKV这种容器的视频格式中,会带 ...
- ffmpeg使用C语言sdk实现抽取视频中的视频数据
主要使用函数 特征码:Start code 解码的一些视频参数,分辨率和帧率:SPS/PPS ffmpeg获取SPS/PPS:codec->extradata 实例 #include <s ...
- java 时间格式化(2016.04.12 12:32:55)
输入的时间格式如:2016.04.12 12:32:55所示: 想要获取一定格式的日期,时间的方法 String startString = "2016.04.25 12:25:44&quo ...
- Ubuntu 13.04/12.10安装Oracle 11gR2图文教程(转)
Ubuntu 13.04/12.10安装Oracle 11gR2图文教程 原文标题:How to Install Oracle 11G R2 Enterprise Edition Database U ...
- Ubuntu LTS 系统学习使用体会和实用工具软件汇总 6.04 8.04 10.04 12.04 14.04 16.04
Ubuntu LTS 系统学习体会和工具软件汇总 6.04 8.04 10.04 12.04 14.04 16.04 ubuntu入门必备pdf:http://download.csdn.net/de ...
- FFmpeg简易播放器的实现-音视频同步
本文为作者原创,转载请注明出处:https://www.cnblogs.com/leisure_chn/p/10284653.html 基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文 ...
- FFmpeg 向视频中添加文字
原文地址:http://www.cnblogs.com/wanggang123/p/6707985.html FFmpeg支持添加文字功能,具体如何将文字叠加到视频中的每一张图片,FFmpeg调用了文 ...
- 解惑:在Ubuntu18.04.2的idea上运行Scala支持的spark程序遇到的问题
解惑:在Ubuntu18.04.2的idea上运行Scala支持的spark程序遇到的问题 一.前言 最近在做一点小的实验,用到了Scala,spark这些东西,于是在Linux平台上来完成,结果一个 ...
- 最简单的基于FFmpeg的移动端例子:IOS 视频解码器-保存
===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...
- 最简单的基于FFmpeg的移动端例子:IOS 视频转码器
===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...
随机推荐
- 记录Nginx配置
1 # Proxy to the Airsonic server location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_hea ...
- P3128 [USACO15DEC]Max Flow P(树上倍增和树链剖分)
思路1(树上倍增$ + $树上差分) 每次都修改一条从\(u\)到\(v\),不就是树上差分的专门操作吗?? 直接用倍增求\(LCA\),每次\(d[u]++,d[v]++,d[LCA(u,v)]-- ...
- spring事务不生效8种原因
1.数据库引擎不支持事务 这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB. 根据 MySQL 的官方文档: ...
- Spring全家桶
常用框架的知识点汇总,如图: 一.5个常用的spring框架 ▌1.spring framework 也就是我们经常说的spring框架,包括了ioc依赖注入,Context上下文.bean管理.sp ...
- 基于LAMP搭建WordPress博客
1.安装Apache. 1)执行如下命令,安装Apache服务及其扩展包. yum -y install httpd mod_ssl mod_perl mod_auth_mysql 2)执行如下命令, ...
- RPC通信原理概述
RPC通信原理概述 1.RPC概述 1.什么是RPC RPC(Remote Procedure Call Protocol)远程过程调用协议.它是一种通过网络从远程计算机程序上请求服务,而不需要了解底 ...
- 续集来了!我让 GPT-4 用 Laf 三分钟写了个完整的待办事项 App
书接前文,上篇文章我们教大家如何三分钟时间用 Laf 实现一个自己的 ChatGPT. 一觉醒来,GPT-4 已经发布了! GPT-4 实现了真正的多模态,可以把纸笔画的原型直接写出网页代码.读论文时 ...
- 【LeetCode动态规划#05】背包问题的理论分析(基于代码随想录的个人理解,多图)
背包问题 问题描述 背包问题是一系列问题的统称,具体包括:01背包.完全背包.多重背包.分组背包等(仅需掌握前两种,后面的为竞赛级题目) 下面来研究01背包 实际上即使是最经典的01背包,也不会直接出 ...
- 【Visual Leak Detector】配置项 ForceIncludeModulesmd
说明 使用 VLD 内存泄漏检测工具辅助开发时整理的学习笔记.本篇介绍 VLD 配置文件中配置项 ForceIncludeModules 的使用方法. 同系列文章目录可见 <内存泄漏检测工具&g ...
- vue 之 computed方法自带缓存踩坑1
使用场景:ant-vue 穿梭框使用 页面使用computed方法处理组织结构数据,退出页面时,对加载数据做了set null 操作,再次进入页面时,穿梭框只显示数据,无法做左右穿梭功能. 原因:co ...