ffmpeg 将拆分的数据合成一帧_新手学习FFmpeg - 调用API完成两个视频的任意合并-后台/架构/数据库-敏捷大拇指-一个敢保留真话的IT精英社区...

本文介绍了如何使用FFmpeg在视频A的任意位置插入视频B,通过修改PTS实现变序合并。详细讲解了判断插入时间点、视频处理完毕和从断点处重新读取Frame的处理方法,提供了伪代码实现。
摘要由CSDN通过智能技术生成

快来登录

363dbc0c403b997e84527ea2a54a2180.png

获取优质的科技资讯内容

收藏热门的IT网络技术干货

拷贝下载Swift Demo源代码

订阅梳理好了的知识点专辑

本次尝试在视频A中的任意位置插入视频B.

在上一篇中,我们通过调整PTS可以实现视频的加减速。这只是对同一个视频的调转,本次我们尝试对多个视频进行合并处理。

Concat如何运行

ffmpeg提供了一个concat滤镜来合并多个视频,例如:要合并视频Video A和Video B,通过调用

ffmpeg -i va.mp4 -i vb.mp4 -filter_complex "[0][1]concat[out]" -map '[out]' -y output.mp4

concat支持多个Input Source,上面的命令只合并了两个视频,通过生成concat流程图可以看到一些细节:

echo "movie=va.mp4[0];movie=vb.mp4[1];[0][1]concat,nullsink" | graph2dot -o graph.tmp

dot -Tpng graph.tmp -o graph.png

6f982ab36dfdedc86590150d44a904e0.gif

7baaadd442be58b27cf3ca3b622c7c31.jpg (34.4 KB, 下载次数: 0)

2019-9-23 09:32 上传

这是concat典型用法,循环读取输入源,然后通过修改pts完成合并。

concat是顺序修改,如果需要在video A中某个时间点插入video B,那么concat就无法完成了。 顺序合并是通过修改PTS实现,那么变序合并也可以通过修改PTS来实现,下面借助concat的逻辑来看看如何实现变序合并。

变序合并

为了方便说明问题,我们来看一下顺序和变序不同点到底在哪里。

问题分析

我们仍然假设需要合并的两个视频分别是Video A和Video B, 需要将Video B插入在Video A中。AF表示Video A的帧, BF表示Video B的帧。

顺序合并

+---------------------------------------------------------------------------------------------------------------+

|       AF1    AF2    AF3     AF4     AF5    AF6     AF7    BF1    BF2     BF3    BF4     BF5    BF6            |

|       |--------------|--------------|--------------|--------------|--------------|--------------|--->         |

|Time   0              10             20             30            40              50            60             |

|PTS    0      100     200     250    300     350    400    500    600     650    700    750     800            |

+---------------------------------------------------------------------------------------------------------------+

顺序合并就是读取Video B的帧,然后将pts以Video A结束时的PTS为基准进行修改。

变序合并

+---------------------------------------------------------------------------------------------------------------+

|       AF1    AF2    AF3     AF4     BF1    BF2     BF3    BF4     BF5    BF6    AF5    AF6     AF7            |

|       |--------------|--------------|--------------|--------------|--------------|--------------|--->         |

|Time   0              10             20             30            40              50            60             |

|PTS    0      100     200     250    300     350    400    500    600     650    700    750     800            |

+---------------------------------------------------------------------------------------------------------------+

变序合并时先读取Video A的帧,当达到规定的PTS时,开始读取Video B的帧,然后以A截断时的PTS为基准重新计算PTS。当Video B所有的帧都处理完毕之后,在从截断处开始重新处理Video A的帧。

从上面两个图来看,问题好像不是很难解决。 只要达到截断的条件,就去处理另外一个视频,等待视频处理完毕之后。再返回来处理被截断的视频。

但在实现的道路上有如下三个问题需要解决:

如何判断到达插入时间点

如何判断视频处理完毕

如何从断点处重新读取Frame

下面就需要逐个问题解决了。

如何判断到达插入时间点

因为我们是需要在视频A中插入视频B,所以需要首先找到插入点。 而根据时间来判断插入点无疑是最简单的一种形式,计算时间就可以依靠前几篇中介绍的PTS知识了。

当从视频源中读取到每帧后,我们通过帧的PTS和Time-Base根据pts * av_q2d(time_base)转换成播放时间。 这样第一个问题就顺利解决。

当找到插入点后,我们需要暂存当前的位置,等待插入结束后,需要从断点处重新加载帧。

如何判断视频处理完毕

执行插入本质就是读取视频B的数据帧,然后修改PTS值。但我们需要得知视频B已经处理完毕,这样才能返回到视频A的断点处继续处理。 所以如何获取到视频处理完毕就是第二个问题。

如果抛开ffmpeg来说,处理视频本质也是一个IO流(从视频文件中读取的IO流),当判断到IO流结束时(通过seek来判断EOF)时就是视频处理完毕的时候。 但ffmpeg将这一层屏蔽掉了,也就是在filter中是无法直接获取到IO流状态的。

ffmpeg在屏蔽的同时,也提供了一种判断方式。filter在处理完每一帧之后,需要确认下一帧的状态(有下一帧/无下一帧),所以如果ffmpeg在读取到下一帧时返回了无下一帧,那就表示当前视频处理完毕。

通过ff_inlink_acknowledge_status(AVFilterLink *link, int *rstatus, int64_t *rpts)来获取下一帧的状态,当返回的ret>0表示没有下一帧,这个时候就可以通过判断当前处理状态来决定是否关闭输出流。

if 当前处理视频B

切换到视频A的断点

else 当前处理视频A

关闭所有的输入流

关闭输出流

如何从断点处重新读取Frame

这是最后一个待解决的问题了,当视频B的数据都处理完之后,就需要从视频A的断点处重新读取数据帧。上面说到对视频流的读取,本质就是对一个文件的IO流处理,而在IO时都会有一个指针来表示当前位置。

而ff_inlink_acknowledge_status有两个作用,一方面获取下一帧,另一方面是确认当前帧处理结束。 换言之,当调用ff_inlink_acknowledge_status之后,ffmpeg会将IO流的指针向后移动到下一帧的起始位置,如果移动失败,则表示没有下一帧了。 如果移动成功,那么下次ff_inlink_consume_frame读取帧时,就从这个位置开始读取。

因此如何从断点处重新读取Frame其实不是问题,只要断点处的帧被确认处理结束了,ffmpeg会自动的移到下一帧位置。当我们将输入源切换到视频A时,就自动从断点处开始读取帧了。

伪代码实现

通过下面的伪代码简要描述上述的过程:

通过ff_outlink_get_status判断输出流状态

if 输出流已关闭

退出

for {

通过ff_inlink_consume_frame 获取下一帧

通过frame->pts * av_q2d(time_base)计算时间

if 时间达到插入点

修改当前状态, 进入暂存状态。

通过push_frame处理每一帧

}

通过ff_inlink_acknowledge_status确认帧状态

if 当前是暂存状态

切换到视频B

if 没有下一帧

if 当前是视频B && 当前是暂存状态

关闭视频B

切换回视频A

if 当前是视频A && 当前是暂存状态

关闭视频A

关闭输出流

大致就是这个处理流程, 完整代码可以参考iconcat里面的代码。

游客,本帖隐藏的内容需要积分高于 10240000 才可浏览,您当前积分为 0

登录才可查看全文

LOGIN

好好学习Swift,天天想上Swifthumb

登录

SIGN UP

敏捷大拇指,一个敢保留真话的IT精英社区

注册

ce1e11e01c5c3077c61193b75b4c3ac0.png

ce1e11e01c5c3077c61193b75b4c3ac0.png

cda3b664839baeafd4a683dc219365d9.png

LOGIN

登录

cda3b664839baeafd4a683dc219365d9.png

SIGN UP

注册

都看到这里了,就把这篇资料推荐给您的好朋友吧,让他们也感受一下。

回帖是一种美德,也是对楼主发帖的尊重和支持。您的赞赏是我前进的方向。

*声明:敏捷大拇指是全球最大的Swift开发者社区、苹果粉丝家园、智能移动门户,所载内容仅限于传递更多最新信息,并不意味赞同其观点或证实其描述;内容仅供参考,并非绝对正确的建议。本站不对上述信息的真实性、合法性、完整性做出保证;转载请注明来源并加上本站链接,敏捷大拇指将保留所有法律权益。如有疑问或建议,邮件至marketing@swifthumb.com。

*联系:微信公众平台:“swifthumb” / 腾讯微博:@swifthumb / 新浪微博:@swifthumb / 官方QQ一群:343549891(满) / 官方QQ二群:245285613 ,需要报上用户名才会被同意进群,请先注册敏捷大拇指。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值