kaiyun全站网页版登录 如何使用FFmpeg精确剪辑视频

发布于:25-07-17 播放次数:

1.问题描述1.1 背景

先前我们针对ffmpeg进行了二次开发,实现了视频处理的常规功能,并通过ffmpeg命令行进行辅助处理。在此基础之上,我们又构建了一个转码接入与调度系统,该系统对外提供相关服务。此功能需具备以下特性:须能迅速从选定视频中截取特定时间段内的片段,且需满足两个条件:一是操作速度要快,不能像进行视频转码那样耗时;二是剪辑需精确,允许用户自行设定起始秒数和结束秒数。

1.2 难点

用ffmpeg很容易从一个长视频剪辑出一段小视频。使用ffmpeg命令,输入文件为input.mp4,指定从00:10:03秒开始,持续时长为00:03:00,采用copy方式进行视频和音频编码,最终输出文件保存为output.mp4,从而实现从原视频中提取并剪辑出一段时长为3分钟的视频内容。使用参数-vcodec copy和-acodec copy可以实现对视频原始音视频流的直接复制,从而避免编解码过程。尽管这种方法操作简便,却存在一个严重的不足:视频画面在起初会出现短暂的停滞(而声音保持流畅),需等待数秒后画面才能恢复正常滚动。以下视频片段可作为这一问题的示例。

该文章明确指出,相关内容不得擅自进行修改,且必须严格遵守相关规定。

2.原因分析

探究其根本原因,我们发现视频剪辑的起始点恰好位于GOP(Group of Pictures)的中间,而非首个I帧。对于稍有视频编码知识的人来说,I帧、B帧和P帧的概念并不陌生。简言之,I帧代表一幅完整的图像,P帧则是基于I帧进行差分编码,而B帧则是根据前后的I帧、P帧和B帧进行差分编码。也就是说,I帧包含了全部信息,而P帧与B帧则不具备这一特性,因此,一旦缺少I帧,P帧与B帧便无法进行有效的解码。一般来说,在GOP(组帧)中,首帧是I帧,紧随其后的是一系列的P帧与B帧。有些GOP的时长可以达到10秒钟。下图中展示了一个实际视频的I帧、B帧与P帧的信息,其中红色代表I帧,从中我们可以观察到两个I帧之间的间隔相当大(实际上相隔了10秒)。

ffmpeg视频裁剪精确到秒_mpeg2和mpeg4的区别_ffmpeg视频剪辑I帧问题解决方案

根据前面的分析,我们可以推断,剪辑的起始点很可能并不位于I帧。缺少I帧将会导致后续的P帧和B帧无法正确解码,进而造成画面停滞。此前的分析均假设了视频内容未经编解码的直接复制。若我们先将视频内容解码为单独的图像,再对满足特定时间条件的图像进行编码,那么剪辑的精确度将大大提高。然而,这样的操作过程耗时非常长;它需要消耗大量的CPU资源来完成编解码任务。

相关学习资料推荐,点击下方链接免费报名,先码住不迷路~】

音视频免费学习地址:

专注于FFmpeg、WebRTC、RTMP、NDK以及Android平台上的音视频流媒体技术的高级开发研究。

免费提供音视频学习资源、知名企业面试题目、技术讲解视频以及学习路径规划,资料范畴涵盖C/C++、Linux、FFmpeg、webRTC、rtmp、hls、rtsp、ffplay、srs等多种技术,如有需求,请点击788280672加入群聊,即可免费获取。

mpeg2和mpeg4的区别_ffmpeg视频剪辑I帧问题解决方案_ffmpeg视频裁剪精确到秒

3.解决方案

针对这一问题kaiyun全站网页版登录,我们仍有一套可行的方案:首先,对满足时间条件的首个GOP进行编码处理,随后将后续的GOP内容直接复制至目标视频。首先,由于第一个GOP的帧是经过重新编码的,因此会重新分配I帧,确保视频能够顺利播放;其次,后续的GOP内容系直接复制,对CPU的消耗极低,大大提升了性能。具体过程可参考下图展示。

ffmpeg视频裁剪精确到秒_ffmpeg视频剪辑I帧问题解决方案_mpeg2和mpeg4的区别

当然这里面还是有一些坑的,下面开始填坑。

3.1 拼接

源视频或许会感到疑惑:我凭借自己的能力编写的编码,为何你只需直接复制便能够成功解码?通常情况下,解码过程需要依赖SPS和PPS,而源视频与目标视频的SPS和PPS往往存在差异,这就导致了直接复制无法实现正确的解码。以mp4文件为例,SPS和PPS通常被放置在文件的开头部分。由于一个文件只能包含一个文件头,因此无法容纳两个不同的SPS和PPS。要准确解析目标视频,务必获取源视频中的序列参数集(SPS)和图像参数集(PPS)。若无法放置文件头部,那么这些信息该置于何处?是否可以将其置于复制帧的前端?如何实现这一操作?一度陷入困境,无从下手。然而,某日灵光一闪,回想起之前为了填补一个缺陷,追踪到了h264_mp4toannexb的实现过程,该过程的功能便是将SPS和PPS复制至帧(确切地说是AVPacket)的前端。快!回顾一下h264_mp4toannexb的实现细节:需在每一个AVPacket前添加0x000001或0x00000001云开·全站体育app登录,并在I帧前嵌入SPS与PPS。换言之,借助h264_mp4toexannb,能够准确地将解码所需之SPS和PPS插入至视频之中。使用h264_mp4toannexb工具相对简便,其代码呈现如下:

初始化AVBSFContext结构体,参数包括:过滤名称字符串、编解码器参数指针、时间基准有理数。
{
获取指定名称的AV比特流过滤器指针,该指针通过av_bsf_get_by_name函数实现,参数为m_filter_name字符串的c_str()转换。
​
bsf_ctx指针指向一个AVBSFContext类型的对象,当前该对象值为空。
对filter进行分配,并将bsf_ctx作为参数传递给av_bsf_alloc函数。
​
bsf_ctx->par_in接收了codec_par的参数复制;该操作通过avcodec_parameters_copy函数实现。
    bsf_ctx->time_base_in = tb;
​
    av_bsf_init(bsf_ctx);
    return bsf_ctx;
}
​
创建AVPacket指针,通过AVBSFContext和AVPacket引用进行初始化,函数名为feedPacket。
{
bsf_ctx对象调用send_packet函数,将packet作为参数传入执行。
​
分配了一个AVPacket类型的变量,命名为dst_packet。
执行av_bsf_receive_packet函数,传入bsf_ctx和dst_packet作为参数。
​
    return dst_packet;
}
​
void test()
{
AVBSFContext指针bsf_ctx是通过调用initBSF函数创建的,该函数的参数包括字符串"h264_mp4toannexb"、video_stream结构体中的codecpar成员以及time_base成员。
获取视频数据包后,AVPacket类型的变量packet被赋值。
AVPacket *dst_packet; 它由feedPacket函数生成,该函数接收bsf_ctx和packet作为参数。
}

在处理将编解码后的第一个GOP与原始视频中的后续GOP进行拼接的过程中,必须对时间戳进行谨慎操作,否则在视频播放时很可能会出现画面抖动的情况。

3.2 花屏

这难道就结束了?绝不!你还会发现某些视频在即将结束的最后一秒突然出现画面花屏的现象。。。。

ffmpeg视频剪辑I帧问题解决方案_mpeg2和mpeg4的区别_ffmpeg视频裁剪精确到秒

花屏现象的产生原因并不复杂,可以推测出来:问题出在最后一帧,它是B帧。但并非所有视频的最后一帧都是B帧,因此花屏现象并非必然发生。一旦确认是B帧导致的,解决方法也就显而易见了:确保最后一帧是P帧。即便在时间上有所超时(同时音频流也应与视频流同步超时)。然而,鉴于无法仅凭AVPacket来判定一个帧是否为P帧,因此即便是最后一个GOP,也必须进行解码(无需进行编码处理)。在记录了超出时间范围的首个P帧的pts之后,后续在复制GOP时,只需将这个pts复制过去,便可以停止复制。

4.总结

最初觉得这个问题解决起来颇为棘手,因为即便是使用ffmpeg命令行裁剪,结果仍然存在问题。然而,无论问题如何变化kaiyun.ccm,其核心始终不变。于是,我从问题的根源着手,逐步探索解决方案,并且将遇到的问题一个接一个地克服。切记,只有理解了原理,才能真正解决问题。

原文

https://zhuanlan.zhihu.com/p/423444166