网站首页 > 资源文章 正文
1.问题描述
1.1 背景
之前基于ffmpeg做二次开发,完成常见的视频处理功能,并用ffmpeg命令行做兜底。在此基础上,还做一个转码接入和调度系统对外提供服务。有个功能需要是这样的:快速从指定的视频中裁剪某一时间范围的子视频, 两个要求:1. 要快,不能像转码一样耗时;2.要精确,剪辑的时候能指定从哪一秒开始,到哪一秒结束。
1.2 难点
用ffmpeg很容易从一个长视频剪辑出一段小视频。比如命令ffmpeg -i input.mp4 -ss 00:10:03 -t 00:03:00 -vcodec copy -acodec copy output.mp4就是从input.mp4的第10分钟03秒开始剪辑出一个3分钟的视频并且保存为output.mp4文件。参数-vcodec copy -acodec copy就是直接拷贝原始视频的音视频流,不进行编解码。虽然上面的方法很方便,但有一个致命的缺陷:画面在一开始会卡住(但声音一直是正常的),几秒后画面才正常滚动。下面视频是一个例子。
https://zhuanlan.zhihu.com/p/423444166
2.原因分析
究其原因,剪辑的开始时间落在视频GOP的中间位置而不是第一个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秒)。
从上面分析可知:剪辑的开始时间很大可能不是落在I帧,由于缺少I帧会使得后面的P和B帧无法解码导致画面卡住。上面的分析都是基于不编解码的直接拷贝视频内容的,如果考虑先解码成一张张的图像,然后再对符合时间要求的图像编码,那么剪辑时间可以做到非常精准。但这样做的就是耗时过长:需求花费大量的CPU完成编解码操作。
相关学习资料推荐,点击下方链接免费报名,先码住不迷路~】
音视频免费学习地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发
【免费分享】音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击788280672加群免费领取~
3.解决方案
解决的办法还是有的:对前面第一个符合时间要求的GOP编解码,而之后的GOP内容则直接拷贝到目标视频。一来,第一个GOP的帧由于是重新编码所以会重新分配I帧从而能播放,二来,之后的GOP内容是直接拷贝的所以基本不消耗CPU,性能杠杆的。如下图所示:
当然这里面还是有一些坑的,下面开始填坑。
3.1 拼接
源视频可能会惊讶:我凭本事编的码,为什么你直接拷贝就能解码?一般来说解码依赖于SPS和PPS,而源视频与目标视频的SPS和PPS会有所不同,因此直接拷贝是不能正确解码的。对于mp4文件,SPS和PPS一般是放到文件头。一个文件只能有一个文件头,也就不能存放两个不同的SPS和PPS。为了能正确解码目标视频必须得有源视频的SPS和PPS。不能放文件头的话,那能放哪里?能不能放到拷贝的帧的前面呢?如何放?一筹莫展、无处下手,直到有一天突然想起之前为了填一个坑,追踪到h264_mp4toannexb的实现,它的作用就是将SPS和PPS拷贝到帧(准确来说应该是AVPacket)的前面。来!温习一下h264_mp4toannexb的具体实现:在所有AVPacket前面增加0x000001或者0x00000001,在I帧的前面插入SPS和PPS。也就是通过h264_mp4toexannb就能把解码所需的SPS和PPS正确插入到视频中。h264_mp4toannexb使用起来也比较简单,代码如下:
AVBSFContext* initBSF(const std::string &filter_name, const AVCodecParameters *codec_par, AVRational tb)
{
const AVBitStreamFilter *filter = av_bsf_get_by_name(m_filter_name.c_str());
?
AVBSFContext *bsf_ctx = nullptr;
av_bsf_alloc(filter, &bsf_ctx);
?
avcodec_parameters_copy(bsf_ctx->par_in, codec_par);
bsf_ctx->time_base_in = tb;
?
av_bsf_init(bsf_ctx);
return bsf_ctx;
}
?
AVPacket* feedPacket(AVBSFContext *bsf_ctx, AVPacket &packet)
{
av_bsf_send_packet(bsf_ctx, packet);
?
AVPacket *dst_packet = av_packet_alloc();
av_bsf_receive_packet(bsf_ctx, dst_packet);
?
return dst_packet;
}
?
void test()
{
AVBSFContext *bsf_ctx = initBSF("h264_mp4toannexb", video_stream->codecpar, video_stream->time_base);
AVPacket *packet = readVideoPacket();
AVPacket *dst_packet = feedPacket(bsf_ctx, packet);
}
注意:编解码第一个GOP和原始视频后续GOP拼接时的时间戳要小心处理,不然视频播放时可能会出现抖动现象。
3.2 花屏
以为就完了吗?没有!!你会发现有些视频会在最后一秒出现花屏。。。。
出现花屏的原因其实也不难猜到:最后一帧是B帧。由于不是所有剪辑的视频最后一帧都是B帧,所以花屏也不是必现的。知道是B帧引起的,那解决方案也就明确了:保证最后一帧是P帧。即使时间上稍微超一点(音频流也应该跟着视频流稍微超一下时间)。不过呢,由于不能直接从AVPacket判断一个帧是否为P帧,所以最后一个GOP也得解码(无需编码)。记录超出时间范围后的第一个P帧的pts,后面拷贝GOP的时候,拷贝到这个pts就可以停止了。
4.总结
起初觉得问题很难解决,毕竟ffmpeg命令行都裁剪出来的都有问题。而万变不离其宗,从问题的原因出发,一步步寻找解决方案,并将一路上碰到的问题逐一击破。记住,明白原理才能解决问题。
原文 https://zhuanlan.zhihu.com/p/423444166
- 上一篇: FFmpeg4.2.2 交叉编译
- 下一篇: msys2编译FFmpeg全网最详细步骤
猜你喜欢
- 2025-01-09 原来FFmpeg这么有意思 (二)
- 2025-01-09 音视频开发7. ffmpeg 几个重要结构体
- 2025-01-09 FFmpeg简介及使用入门
- 2025-01-09 超详细的手把手下载安装FFmpeg整个过程,你学会了吗?
- 2025-01-09 mPEG-DOPE 甲氧基聚乙二醇二油酰基磷脂酰乙醇胺
- 2025-01-09 音视频开发-FFmpeg详解
- 2025-01-09 ffmpeg、ffprobe、ffplay 常用参数及命令行示例说明
- 2025-01-09 音视频开发-ffmpeg指令(2)
- 2025-01-09 FFmpeg/WebRTC/RTMP 音视频流媒体高级开发知识点总结
- 2025-01-09 msys2编译FFmpeg全网最详细步骤
你 发表评论:
欢迎- 05-1430个在线地图瓦片URL分享
- 05-1425个在线地图瓦片URL分享
- 05-14PixelStyle for Mac(mac照片编辑器)
- 05-14一篇文章带你了解CSS3 3D 转换知识
- 05-14我们的世界是假的?马斯克:我们生活在高文明模拟的矩阵游戏中
- 05-14【视觉AI的基石】斯坦福大学笔记!带你吃透卷积神经网络 (CNN)
- 05-14潮流 | 通过AAPE的新价目,无虑入荷未来主义服饰
- 05-14身份证查询服务API:准确识别身份证信息
- 最近发表
- 标签列表
-
- 电脑显示器花屏 (79)
- 403 forbidden (65)
- linux怎么查看系统版本 (54)
- 补码运算 (63)
- 缓存服务器 (61)
- 定时重启 (59)
- plsql developer (73)
- 对话框打开时命令无法执行 (61)
- excel数据透视表 (72)
- oracle认证 (56)
- 网页不能复制 (84)
- photoshop外挂滤镜 (58)
- 网页无法复制粘贴 (55)
- vmware workstation 7 1 3 (78)
- jdk 64位下载 (65)
- phpstudy 2013 (66)
- 卡通形象生成 (55)
- psd模板免费下载 (67)
- shift (58)
- localhost打不开 (58)
- 检测代理服务器设置 (55)
- frequency (66)
- indesign教程 (55)
- 运行命令大全 (61)
- ping exe (64)
本文暂时没有评论,来添加一个吧(●'◡'●)