网站首页 > 技术教程 正文
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
猜你喜欢
- 2024-11-06 Qt音视频开发9-ffmpeg录像存储(nba比赛录像回放)
- 2024-11-06 音视频开源基础 - ffmpeg命令(ffmpeg音频处理)
- 2024-11-06 ffmpeg常用命令行集锦(ffmpeg 命令大全)
- 2024-11-06 音视频开发7. ffmpeg 几个重要结构体
- 2024-11-06 超详细的手把手下载安装FFmpeg整个过程,你学会了吗?
- 2024-11-06 FFmpeg硬解码(ffmpeg硬解码和直接使用cuda的区别)
- 2024-11-06 mPEG-Lys(MAL)-DBCO,甲氧基PEG赖氨酸马来酰亚胺二苯并环辛炔
- 2024-11-06 音视频流媒体高级开发(FFmpeg6.0/WebRTC/RTMP/RTSP/编码解码)
- 2024-11-06 FFmpeg在windows的安装、合并、切片、.m4s、.m3u8处理
- 2024-11-06 完美解决Linux环境编译ffmpeg库(linux编译环境配置)
你 发表评论:
欢迎- 最近发表
-
- Win11学院:如何在Windows 11上使用WSL安装Ubuntu
- linux移植(Linux移植freemodbus)
- 独家解读:Win10预览版9879为何无法识别硬盘
- 基于Linux系统的本地Yum源搭建与配置(ISO方式、RPM方式)
- Docker镜像瘦身(docker 减小镜像大小)
- 在linux上安装ollama(linux安装locale)
- 渗透测试系统Kali推出Docker镜像(kali linux渗透测试技术详解pdf)
- Linux环境中部署Harbor私有镜像仓库
- linux之间传文件命令之Rsync傻瓜式教程
- 解决ollama在linux中安装或升级时,通过国内镜像缩短安装时长
- 标签列表
-
- 下划线是什么 (87)
- 精美网站 (58)
- qq登录界面 (90)
- nginx 命令 (82)
- nginx .http (73)
- nginx lua (70)
- nginx 重定向 (68)
- Nginx超时 (65)
- nginx 监控 (57)
- odbc (59)
- rar密码破解工具 (62)
- annotation (71)
- 红黑树 (57)
- 智力题 (62)
- php空间申请 (61)
- 按键精灵 注册码 (69)
- 软件测试报告 (59)
- ntcreatefile (64)
- 闪动文字 (56)
- guid (66)
- abap (63)
- mpeg 2 (65)
- column (63)
- dreamweaver教程 (57)
- excel行列转换 (56)
本文暂时没有评论,来添加一个吧(●'◡'●)