Qt编写全能播放组件(支持ffmpeg2/3/4/5/6/Qt4/5/6)
cac55 2024-10-11 10:51 24 浏览 0 评论
一、前言
从代码层面以及自由度来说,用ffmpeg来写全能播放组件是最佳方案(跨平台最好最多、编解码能力最强),尽管已经有优秀的vlc/mpv等方案可以直接用,但是vlc/mpv对标主要是播放器应用层面,其他层面比如视频监控行业领域就比较鸡肋,所以还是从底层一点一滴做解码编码会让自己更熟练。关于网上很多ffmpeg的示例,尤其是播放的示例,数不胜数,比较适合用来入门学习,问题是随着ffmpeg官方不断的迭代更新,很多代码都不可用,因为api变了,尤其是最近5年迭代的特别快,从2017年开始直接猛飚版本,现在直接干到了ffmpeg6版本,一般在安排取消或者改动某些api接口前几个版本,都会打上对应的标记,既有新方法,也兼容旧的api,一般会放在下一个大版本将旧的api接口移除,以便减轻历史包袱,在核心功能编解码这块,一直是兼容的,不会说新版本不兼容以前旧版本的一些编解码格式。
编写这个全能播放组件,面对用户各种各样的需求,当然需要从ffmpeg2兼容到ffmpeg6以及后续的版本,现在用的最多的还是ffmpeg4版本,目测三五年后会陆续切换到ffmpeg5/ffmpeg6,主要是支持的格式多了,尤其是某些新标准的编解码的效率更高。在ffmpeg提供的头文件接口中,并没有提供ffmpeg的大版本号,只提供了字符串版本,所以需要通过子库的主版本号来定义一个ffmpeg的版本号,比如编解码库LIBAVCODEC_VERSION_MAJOR,56=ffmpeg2/57=ffmpeg3/58=ffmpeg4/59=ffmpeg5/60=ffmpeg6,这个编解码库就是ffmpeg的核心,看家的本领都在里面,个人觉得ffmpeg最牛逼的就是编解码和滤镜。在兼容各个版本的这条路上,大致整理了以下几条:
- 以前AVStream带了解码器参数,stream->codec,现在去掉了,对应放在stream->codecpar中。
- 拷贝上下文参数以前是avcodec_copy_context,现在对应avcodec_parameters_copy。
- 参数拷贝以前是avcodec_copy_context,现在对应avcodec_parameters_from_context/avcodec_parameters_to_context。
- 编码以前是avcodec_encode_video2/avcodec_encode_audio2,现在对应avcodec_send_frame后avcodec_receive_packet(视音频步骤一样)。
- 解码以前是avcodec_decode_video2/avcodec_decode_audio4,现在对应avcodec_send_packet后avcodec_receive_frame(视音频步骤一样)。
- 打印解码编码器名称以前是av_codec_next,现在对应av_codec_iterate。
- 获取和设置旋转角度以前是av_dict_get(stream->metadata, "rotate", NULL, 0)/av_dict_set(&stream->metadata, "rotate", "90", 0),现在对应av_stream_get_side_data后av_display_rotation_get/av_stream_new_side_data后av_display_rotation_set。
二、效果图
三、体验地址
- 国内站点:https://gitee.com/feiyangqingyun
- 国际站点:https://github.com/feiyangqingyun
- 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 体验地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取码:01jf 文件名:bin_video_demo。
四、相关代码
//通过avcode版本定义对应主版本
#if (LIBAVCODEC_VERSION_MAJOR == 56)
#define FFMPEG_VERSION_MAJOR 2
#elif (LIBAVCODEC_VERSION_MAJOR == 57)
#define FFMPEG_VERSION_MAJOR 3
#elif (LIBAVCODEC_VERSION_MAJOR == 58)
#define FFMPEG_VERSION_MAJOR 4
#elif (LIBAVCODEC_VERSION_MAJOR == 59)
#define FFMPEG_VERSION_MAJOR 5
#elif (LIBAVCODEC_VERSION_MAJOR == 60)
#define FFMPEG_VERSION_MAJOR 6
#endif
int FFmpegHelper::getRotate(AVStream *stream)
{
int rotate = 0;
//测试发现ffmpeg2不支持旋转滤镜
#if (FFMPEG_VERSION_MAJOR < 3)
return rotate;
#endif
#if (FFMPEG_VERSION_MAJOR < 5)
AVDictionaryEntry *tag = NULL;
tag = av_dict_get(stream->metadata, "rotate", NULL, 0);
if (tag) {
rotate = atoi(tag->value);
}
#else
//从ffplay源码中找到的方法
double theta = 0;
quint8 *displaymatrix = av_stream_get_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, NULL);
if (displaymatrix) {
theta = -av_display_rotation_get((qint32 *) displaymatrix);
theta -= 360 * floor(theta / 360 + 0.9 / 360);
rotate = theta;
}
#endif
return rotate;
}
void FFmpegHelper::setRotate(AVStream *stream, int rotate)
{
#if (FFMPEG_VERSION_MAJOR < 5)
av_dict_set(&stream->metadata, "rotate", QString::number(rotate).toUtf8().constData(), 0);
#else
quint8 *sidedata = av_stream_new_side_data(stream, AV_PKT_DATA_DISPLAYMATRIX, sizeof(qint32) * 9);
if (sidedata) {
av_display_rotation_set((qint32 *)sidedata, rotate);
}
#endif
}
void FFmpegHelper::getStreamInfo(AVStream *stream, int &id, int &width, int &height, qint64 &bitrate, int &sampleRate, int &channelCount, int &profile)
{
#if (FFMPEG_VERSION_MAJOR < 3)
id = stream->codec->codec_id;
width = stream->codec->width;
height = stream->codec->height;
bitrate = stream->codec->bit_rate;
sampleRate = stream->codec->sample_rate;
channelCount = stream->codec->channels;
profile = stream->codec->profile;
#else
id = stream->codecpar->codec_id;
width = stream->codecpar->width;
height = stream->codecpar->height;
bitrate = stream->codecpar->bit_rate;
sampleRate = stream->codecpar->sample_rate;
channelCount = stream->codecpar->channels;
profile = stream->codecpar->profile;
#endif
}
int FFmpegHelper::copyContext(AVStream *streamIn, AVStream *streamOut)
{
int result = -1;
//设置 codec_tag = 0 这个很关键(不加保存的数据可能不正确)
#if (FFMPEG_VERSION_MAJOR < 3)
result = avcodec_copy_context(streamOut->codec, streamIn->codec);
streamOut->codec->codec_tag = 0;
#else
result = avcodec_parameters_copy(streamOut->codecpar, streamIn->codecpar);
streamOut->codecpar->codec_tag = 0;
#endif
return result;
}
int FFmpegHelper::copyContext(AVCodecContext *avctx, AVStream *stream, bool from)
{
int result = -1;
#if (FFMPEG_VERSION_MAJOR < 3)
if (from) {
result = avcodec_copy_context(stream->codec, avctx);
} else {
result = avcodec_copy_context(avctx, stream->codec);
}
#else
if (from) {
result = avcodec_parameters_from_context(stream->codecpar, avctx);
} else {
result = avcodec_parameters_to_context(avctx, stream->codecpar);
}
#endif
return result;
}
int FFmpegHelper::decode(FFmpegThread *thread, AVCodecContext *avctx, AVPacket *packet, AVFrame *frame, bool video)
{
int result = -1;
#ifdef videoffmpeg
QString flag = video ? "视频解码" : "音频解码";
#if (FFMPEG_VERSION_MAJOR < 3)
if (video) {
avcodec_decode_video2(avctx, frame, &result, packet);
if (result < 0) {
thread->debug(result, flag, "avcodec_decode_video2");
return result;
}
} else {
avcodec_decode_audio4(avctx, frame, &result, packet);
if (result < 0) {
thread->debug(result, flag, "avcodec_decode_audio4");
return result;
}
}
goto end;
#else
result = avcodec_send_packet(avctx, packet);
if (result < 0 && (result != AVERROR(EAGAIN)) && (result != AVERROR_EOF)) {
//if (result < 0) {
thread->debug(result, flag, "avcodec_send_packet");
return result;
}
while (result >= 0) {
result = avcodec_receive_frame(avctx, frame);
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
break;
} else if (result < 0) {
thread->debug(result, flag, "avcodec_receive_frame");
break;
}
goto end;
}
#endif
return result;
end:
//调用线程处理解码后的数据
if (video) {
thread->decodeVideo2(packet);
} else {
thread->decodeAudio2(packet);
}
#endif
return result;
}
int FFmpegHelper::encode(FFmpegSave *thread, AVCodecContext *avctx, AVPacket *packet, AVFrame *frame, bool video)
{
int result = -1;
#ifdef videosave
QString flag = video ? "视频编码" : "音频编码";
#if (FFMPEG_VERSION_MAJOR < 3)
if (video) {
avcodec_encode_video2(avctx, packet, frame, &result);
if (result < 0) {
thread->debug(result, flag, "avcodec_encode_video2");
return result;
}
} else {
avcodec_encode_audio2(avctx, packet, frame, &result);
if (result < 0) {
thread->debug(result, flag, "avcodec_encode_audio2");
return result;
}
}
goto end;
#else
result = avcodec_send_frame(avctx, frame);
if (result < 0) {
thread->debug(result, flag, "avcodec_send_frame");
return result;
}
while (result >= 0) {
result = avcodec_receive_packet(avctx, packet);
if (result == AVERROR(EAGAIN) || result == AVERROR_EOF) {
break;
} else if (result < 0) {
thread->debug(result, flag, "avcodec_receive_packet");
break;
}
goto end;
}
#endif
return result;
end:
thread->writePacket2(packet, video);
#endif
return result;
}
五、功能特点
- 支持各种音视频文件、本地摄像头设备,各种视频流网络流。
- 支持开始播放、暂停播放、继续播放、停止播放、设置播放进度、倍速播放。
- 可设置音量、静音切换、抓拍图片、录像存储。
- 自动提取专辑信息比如标题、艺术家、专辑、专辑封面,自动显示专辑封面。
- 完美支持音视频同步和倍速播放。
- 解码策略支持速度优先、质量优先、均衡处理、最快速度。
- 支持手机视频旋转角度显示,比如一般手机拍摄的视频是旋转了90度的,解码显示的时候需要重新旋转90度才是正的。
- 自动转换yuv420格式,比如本地摄像头是yuyv422格式,有些视频文件是xx格式,统一将非yuv420格式转换,然后再进行处理。
- 支持硬解码dxva2、d3d11va等,性能极高尤其是大分辨率比如4K视频。
- 视频响应极低延迟0.2s左右,极速响应打开视频流0.5s左右,专门做了优化处理。
- 硬解码和GPU绘制组合,极低CPU占用,比海康大华等客户端更优。
- 支持视频流中的各种音频格式,AAC、PCM、G.726、G.711A、G.711Mu、G.711ulaw、G.711alaw、MP2L2等都支持,推荐选择AAC兼容性跨平台性最好。
- 视频存储支持yuv、h264、mp4多种格式,音频存储支持pcm、wav、aac多种格式。默认视频mp4格式、音频aac格式。
- 支持分开存储音频视频文件,也支持合并到一个mp4文件,默认策略是无论何种音视频文件格式存储,最终都转成mp4及aac格式,然后合并成音视频一起的mp4文件。
- 支持本地摄像头实时视频显示带音频输入输出,音视频录制合并到一个mp4文件。
- 支持H264/H265编码(现在越来越多的监控摄像头是H265视频流格式)生成视频文件,内部自动识别切换编码格式。
- 自动识别视频流动态分辨率改动,重新打开视频流。
- 支持用户信息中包含特殊字符(比如用户信息中包含+#@等字符)的视频流播放,内置解析转义处理。
- 纯qt+ffmpeg解码,非sdl等第三方绘制播放依赖,gpu绘制采用qopenglwidget,音频播放采用qaudiooutput。
- 同时支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5、ffmpeg6以及后续版本,全部做了兼容处理。如果需要支持xp需要选用ffmpeg3或ffmpeg2。
- 支持滤镜,源头带各种水印及图形效果,可以将OSD标签信息和各种图形信息写入到MP4文件。
相关推荐
- 无力吐槽的自动续费(你被自动续费困扰过吗?)
-
今天因为工作需要,需要在百度文库上下载一篇文章。没办法,确实需要也有必要,只能老老实实的按要求买了个VIP。过去在百度文库上有过类似经历,当时为了写论文买了一个月的VIP,后面也没有太注意,直到第二个...
- 百度文库推出“文源计划”创作者可一键认领文档
-
11月7日,百度文库发布了旨在保护创作者权益的“文源计划”。所谓“文源计划”,即为每一篇文档找到源头,让创作者享受更多的权益。据百度文库总经理李小婉介绍,文源计划分为三部分,分别是版权认证、版权扶持和...
- 有开放大学学号的同学,百度文库高校版可以用了。
-
还在网上找百度文库的下载方式,只要从身边的朋友在读开放大学的,那他(她)的学号就可以登陆到国家开放大学图书馆,还使用百度文库高校版来下载。与百度文库稍有不同,但足够使用了。现转国图链接如下:htt...
- 搜索资源方法推荐(搜索资源的方法)
-
今天msgbox就要教大家如何又快又准的搜到各类资源,第一点,排除干扰百度搜索出来啊经常前排展示它的产品以及百度文库,如何去除呢?很简单,后面输入空格减号百度文库,比如你搜高等数学百度文库很多,只要后...
- 一行代码搞定百度文库VIP功能(2021百度文库vip账号密码共享)
-
百度文库作为大家常用查资料找文档的平台,大多数文档我们都可以直接在百度文库找到,然而百度文库也有让人头痛的时候。好不容易找到一篇合适的文档,当你准备复制的时候他却提示你需要开通VIP才能复制~~~下载...
- 百度文库文档批量上传工具用户说明书
-
百度文库文档批量上传工具用户说明书1、软件主要功能1、批量上传文档到百度文库,支持上传到收费、VIP专享、优享以及共享。2、支持自动分类和自动获取标签3、支持多用户切换,一个账户传满可以切换到...
- 百度文库现在都看不到文档是否上传成功,要凉了吗?
-
打开知识店铺,百度文库文档里显示都是下载这一按键,上传的文档也看不到是否成功?咋情况,要取消了吗?没通过审核的也不让你删除,是几个意思,想通吃吗?现在百度上传文档也很费劲,有时弄了半天的资料上传审核过...
- 微信推广引流108式:利用百度文库长期分享软文引流
-
百度文库相对于百度知道、百度百科来说,操作上没那么多条条框框,规则上也相对好把握些。做一条百度知道所花费的精力一般都会比做一条百度文库的要多些,老马个人操作下来觉得百度文库更好把握。但见仁见智吧,今天...
- 职场“避雷”指南 百度文库推出标准化劳动合同范本
-
轰轰烈烈的毕业季结束了,众多应届生在经过了“职场海选”后,已正式成为职场生力军的一员。这一阶段,除了熟悉业务,签订劳动合同、了解职场福利也迅速被提上日程。而随着国人法律意识的增强,百度文库内《劳动合同...
- 《百度文库》:素材精选宝库(百度文库官网首页)
-
《百度文库》:独特功能助力选择高质量素材在当今信息爆炸的时代,如何高效地获取并利用有价值的素材成为了许多人面临的挑战。而《百度文库》作为百度公司推出的一款在线文档分享平台,凭借其丰富的资源、强大的功能...
- 深度整合和开放AI能力 百度文库和网盘推出内容操作系统「沧舟OS」
-
【TechWeb】4月25日消息,Create2025百度AI开发者大会上,百度文库和百度网盘推出全球首个内容操作系统——沧舟OS。基于沧舟OS,百度文库APP全新上线「GenFlow超能搭子」...
- 女子发现大二作业被百度文库要求付费下载,律师:平台侵权,应赔偿
-
近日,28岁的黎女士在百度百科搜索家乡的小地名时,发现了自己在大二完成的课题作业。她继续搜索,发现多个平台收录了该文,比如豆丁网和文档之家等,有的还设置了付费或积分下载。2月15日,九派新闻记者以用户...
- 2016杀入百度文库的新捷径,只有少数人才知道的喔
-
百度的产品在SEO优化中的分量真不用多说,其实很多人都像我一样一直在找捷径。但是我经常发现很多人都是在用死方法。比如发贴吧发帖而不知道去申请一个吧主,知道自问自答而不知道去申请一个合作资格。口碑和贴吧...
- 百度文库付费文档搜索方法(百度文库付费文档搜索方法有哪些)
-
一直以来,百度文库中无论是个人中心还是个人主页,都没有像淘宝一样的店内搜索功能,连最近新开的知识店铺也没有设计店内搜索功能,这无论是对上传用户还是下载用户都不方便,上传用户想要搜索自己的文档无法办到...
- 供读者免费使用!泰达图书馆机构版百度文库新年上新啦
-
在泰达图书馆读者使用百度文库数字资源不需要VIP,免-费-用!惊不惊喜?快来了解一下吧……新年伊始,为满足区域企业、高校、科研院所以及居民群众在教学、科研及学习过程中,对各类文献资源的需求,泰达图书馆...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- 如何绘制折线图 (52)
- javaabstract (48)
- 新浪微博头像 (53)
- grub4dos (66)
- s扫描器 (51)
- httpfile dll (48)
- ps实例教程 (55)
- taskmgr (51)
- s spline (61)
- vnc远程控制 (47)
- 数据丢失 (47)
- wbem (57)
- flac文件 (72)
- 网页制作基础教程 (53)
- 镜像文件刻录 (61)
- ug5 0软件免费下载 (78)
- debian下载 (53)
- ubuntu10 04 (60)
- web qq登录 (59)
- 笔记本变成无线路由 (52)
- flash player 11 4 (50)
- 右键菜单清理 (78)
- cuteftp 注册码 (57)
- ospf协议 (53)
- ms17 010 下载 (60)