Qt编写全能播放组件(支持ffmpeg2/3/4/5/6/Qt4/5/6)

news2024/11/16 5:45:57

一、前言

从代码层面以及自由度来说,用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最牛逼的就是编解码和滤镜。在兼容各个版本的这条路上,大致整理了以下几条:

  1. 以前AVStream带了解码器参数,stream->codec,现在去掉了,对应放在stream->codecpar中。
  2. 拷贝上下文参数以前是avcodec_copy_context,现在对应avcodec_parameters_copy。
  3. 参数拷贝以前是avcodec_copy_context,现在对应avcodec_parameters_from_context/avcodec_parameters_to_context。
  4. 编码以前是avcodec_encode_video2/avcodec_encode_audio2,现在对应avcodec_send_frame后avcodec_receive_packet(视音频步骤一样)。
  5. 解码以前是avcodec_decode_video2/avcodec_decode_audio4,现在对应avcodec_send_packet后avcodec_receive_frame(视音频步骤一样)。
  6. 打印解码编码器名称以前是av_codec_next,现在对应av_codec_iterate。
  7. 获取和设置旋转角度以前是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。

二、效果图

在这里插入图片描述

三、体验地址

  1. 国内站点:https://gitee.com/feiyangqingyun
  2. 国际站点:https://github.com/feiyangqingyun
  3. 个人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 体验地址: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;
}

五、功能特点

  1. 支持各种音视频文件、本地摄像头设备,各种视频流网络流。
  2. 支持开始播放、暂停播放、继续播放、停止播放、设置播放进度、倍速播放。
  3. 可设置音量、静音切换、抓拍图片、录像存储。
  4. 自动提取专辑信息比如标题、艺术家、专辑、专辑封面,自动显示专辑封面。
  5. 完美支持音视频同步和倍速播放。
  6. 解码策略支持速度优先、质量优先、均衡处理、最快速度。
  7. 支持手机视频旋转角度显示,比如一般手机拍摄的视频是旋转了90度的,解码显示的时候需要重新旋转90度才是正的。
  8. 自动转换yuv420格式,比如本地摄像头是yuyv422格式,有些视频文件是xx格式,统一将非yuv420格式转换,然后再进行处理。
  9. 支持硬解码dxva2、d3d11va等,性能极高尤其是大分辨率比如4K视频。
  10. 视频响应极低延迟0.2s左右,极速响应打开视频流0.5s左右,专门做了优化处理。
  11. 硬解码和GPU绘制组合,极低CPU占用,比海康大华等客户端更优。
  12. 支持视频流中的各种音频格式,AAC、PCM、G.726、G.711A、G.711Mu、G.711ulaw、G.711alaw、MP2L2等都支持,推荐选择AAC兼容性跨平台性最好。
  13. 视频存储支持yuv、h264、mp4多种格式,音频存储支持pcm、wav、aac多种格式。默认视频mp4格式、音频aac格式。
  14. 支持分开存储音频视频文件,也支持合并到一个mp4文件,默认策略是无论何种音视频文件格式存储,最终都转成mp4及aac格式,然后合并成音视频一起的mp4文件。
  15. 支持本地摄像头实时视频显示带音频输入输出,音视频录制合并到一个mp4文件。
  16. 支持H264/H265编码(现在越来越多的监控摄像头是H265视频流格式)生成视频文件,内部自动识别切换编码格式。
  17. 自动识别视频流动态分辨率改动,重新打开视频流。
  18. 支持用户信息中包含特殊字符(比如用户信息中包含+#@等字符)的视频流播放,内置解析转义处理。
  19. 纯qt+ffmpeg解码,非sdl等第三方绘制播放依赖,gpu绘制采用qopenglwidget,音频播放采用qaudiooutput。
  20. 同时支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5、ffmpeg6以及后续版本,全部做了兼容处理。如果需要支持xp需要选用ffmpeg3或ffmpeg2。
  21. 支持滤镜,源头带各种水印及图形效果,可以将OSD标签信息和各种图形信息写入到MP4文件。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/637414.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【探索 Kubernetes|集群搭建篇 系列 6】从 0 到 1,轻松搭建完整的 Kubernetes 集群

前言 大家好&#xff0c;我是秋意零。 前面一篇中&#xff0c;我们介绍了 kubeadm 的工作流程。那么今天我们就实际操作一下&#xff0c;探索如何快速、高效地从 0 开始搭建一个完整的 Kubernetes 集群&#xff0c;让你轻松驾驭容器化技术的力量&#xff01;&#xff01; &am…

*问题 F: 2026 模拟测试2(三数之和)

题目描述 有3个整数a1&#xff0c;a2&#xff0c;a3。已知0 < a1, a2, a3 < n&#xff0c;而且a1 a2是2的倍数&#xff0c;a2 a3是3的倍数&#xff0c; a1 a2 a3是5的倍数。你的任务是找到一组a1&#xff0c;a2&#xff0c;a3&#xff0c;使得a1 a2 a3最大。 输入 …

【JavaWeb】IDEA专业版和社区版创建Servlet项目

文章目录 1. 什么是Servlet2. 创建项目3. 引入依赖3.1 在pom.xml中引入依赖3.2 下载jar包引入依赖 4. 创建目录5. 编写代码验证6.总结 1. 什么是Servlet Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app.&#xf…

短视频账号矩阵系统源码--并发量问题怎么开发?

目录&#xff1a; 1.视频存储后端并发量如何实现 2.短视频矩阵系统开发的语言 3.需要对接的技术开放平台开发者对接的api接口 4.功能模型技术创建 文章目录 短视频账号矩阵系统后端如何处理并发量冲突问题前言一、视频存储后端并发量如何实现二、短视频矩阵系统开发语言三、…

RestTemplate发送HTTP请求

一、前言 之前对发送http请求&#xff0c;使用过okhttp&#xff0c;还有httpclient等&#xff0c;这次就直接使用springboot的RestTemplate了。 二、不同的请求方式 我这里只针对POST请求做说明&#xff1a; 下面针对post请求做三种说明&#xff1a; 1、调用的接口参数用Re…

我们来谈谈https

"这一封信只是得到它要回答问题&#xff0c;那个答案早已点燃在心里" 一、 http明文传输 紧接上文这仍然是一款拙劣的http服务器&#xff0c;我们此时在用户数输入栏输入数据信息并提交表单。我们先来认识认识使用到的两个工具软件。 1.PostMan postman是一款支持h…

阿里巴巴首次公开4份【并发编程全彩小册】:模型 + 原理 + 应用 + 模式, 四管齐下

相信大家都是知道的&#xff0c;阿里可以说是程序员的“必修地”每一个程序员都渴望去阿里看看&#xff0c;学习进步一下&#xff0c;但是有时候偏偏局限于自己的技术不到位&#xff01; 但是没关系&#xff0c;就算进不来了阿里&#xff0c;但是可以学习他们的技术呀&#xf…

CONT: Contrastive Neural Text Generation

CONT: Contrastive Neural Text Generation 首先&#xff0c;CONT使用自己的预测中的负面例子&#xff08;3.1&#xff09;来构建B集。&#xff08;原因解释&#xff1a;Kalkstein等人[18]指出&#xff0c;使用不同的对比性样本有助于提高模型的泛化能力。因此&#xff0c;我们…

短视频云端批量混剪实操指南

本文为阿里云智能媒体服务IMS「智能内容创作」实践指南第一期&#xff0c;讲述围绕新媒体广告营销场景&#xff0c;通过“去重策略”全自动批量混剪短视频&#xff0c;助力更高效、更快速地创作优质短视频内容。 欧叔&#xff5c;作者 5G时代&#xff0c;越来越多的企业把短视…

步进电机 力矩选型参考

(* 要提升1KG 的物体&#xff0c;以0 .3 米/秒的速度的提升&#xff0c;步进电机如何选型&#xff1f; 如果不是克服重力则FM*A 力越大加速度越大 *) Clear["Global"] qua 0.2;(* 输入 quality 1kg *) Tao qua*9.8; mps 0.2;(* 输入 0.3 M/S *) dia 0.01;(* 1cm…

多对一(association)、一对多(collection)

1、多对一的处理 多对一的理解&#xff1a; 多个学生对应一个老师如果对于学生这边&#xff0c;就是多对一的现象&#xff0c;即从学生这边关联一个老师&#xff01; 1.1、数据库的设计 CREATE TABLE teacher ( id INT(10) NOT NULL, name VARCHAR(30) DEFAULT NULL, PRIMAR…

Apple Vision Pro -开始使用visionOS

Apple Vision Pro -开始使用visionOS visionOS将熟悉的框架和全新的概念结合在一起&#xff0c;因此您可以为空间计算构建一个全新的应用程序世界。为了帮助您开始您的旅程&#xff0c;我们将介绍一系列会议&#xff0c;涵盖空间计算的构建块以及为该平台设计应用程序和游戏。…

软件开发模型

一、 概述   软件开发模型(Software Development Model)是指软件开发全部过程、活动和任务的结构框架。软件开发包括需求、设计、编码、测试和维护 阶段。   软件开发模型能清晰、直观地表达软件开发全过程&#xff0c;明确规定了要完成的主要活动和任务&#xff0c;用来作…

rider发布项目报错:[MSB4057] 该项目中不存在目标“WebPublish”。

情景再现 Rider升级到2022.3版本及以上时&#xff0c;发布项目显示&#xff1a; [MSB4057] 该项目中不存在目标“WebPublish”。 或者显示 Error:Current toolset does not support publishing for non-Core ASP.NET projects 原因 这是因为Rider MSBuild版本不一致导致。 解…

基于javaweb(springboot+mybatis)宠物医院预约管理系统设计和实现以及论文报告

基于javaweb(springbootmybatis)宠物医院预约管理系统设计和实现以及论文报告 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言…

参会记录 | 北京智源大会-基础模型前沿技术论坛

2023年6月10日上午&#xff0c;线上参与了北京智源大会&#xff0c;主要观看了2023北京智源大会-基础模型前沿技术论坛&#xff0c;以下是论坛中比较感兴趣的点和思考。 本次论坛的演讲嘉宾如下&#xff1a; 上述嘉宾有来自业界的刘胤晗老师和周彦祺老师&#xff0c;也有来自…

【06】STM32·HAL库开发-STM系统框架 | STM32寻址范围 | STM32存储器映射 | STM32寄存器映射

目录 1.STM32系统框架&#xff08;了解&#xff09;1.1Cortex M内核 & 芯片1.2F1系统架构1.2.1F1系统框架简图1.2.2F1系统框架总图 1.3F4系统架构1.3.1F4系统框架简图&#xff08;F407为例&#xff09;1.3.2F4系统框架总图 1.4F7系统架构1.4.1多重AHB总线矩阵1.4.2F7系统结…

如何防止U盘复制电脑文件?

在我们的工作中&#xff0c;经常需要将重要的文件存在电脑中&#xff0c;我们必须要妥善的保护这些重要数据&#xff0c;那么该如何防止U盘复制电脑中的文件呢&#xff1f; 要想防止U盘复制电脑文件&#xff0c;可从三个方面入手&#xff0c;分别是&#xff1a; 禁止电脑使用U…

吴恩达471机器学习入门课程1第2周——线性回归(单变量)

文章目录 Linear Regression1 导包2 - 问题陈述3 - 数据集可视化数据 4 - 线性回归复习5 - 计算代价代价函数模型预测实现 6 - 梯度下降批量梯度下降法学习参数 Linear Regression 您将使用一个变量实现线性回归&#xff0c;以预测餐厅特许经营的利润。 1 导包 首先&#xf…

【电路】电路与电子技术基础 课堂笔记 第12章 集成逻辑门电路

12.4 正逻辑与负逻辑 正负逻辑转换规则 正负逻辑符号