[音视频学习笔记]七、自制音视频播放器Part2 - VS + Qt +FFmpeg 写一个简单的视频播放器

前言

话不多说,重走霄骅登神路

前一篇文章
[音视频学习笔记]六、自制音视频播放器Part1 -新版本ffmpeg,Qt +VS2022,都什么年代了还在写传统播放器?

本文相关代码仓库:
MediaPlay-FFmpeg - Public

转载雷神的两个流程图,挺实用的,不过现在接口有一些小修改,可能有点不一样了

在这里插入图片描述

流程

  1. 首先,定义了一些变量,包括用于存储视频帧的缓冲区 buf,标记是否为视频流的 isVideo,以及一些用于处理视频的结构体和指针。

  2. 创建并初始化 AVFormatContext 结构体 ptr_formatCtx,并打开视频文件以获取音视频流数据信息。

  3. 使用 avformat_find_stream_info 函数获取音视频流信息,并通过循环找到视频流的索引 streamIndex。

  4. 检查是否成功找到视频流,判断 ptr_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO 如果没有找到则退出。

  5. 分配并初始化 AVCodecContext 结构体 ptr_avctx,并根据流的参数找到AVCodecParameters,将其放在ptr_formatCtx->streams[streamIndex]->codecpar。找到对应视频流的解码器avcodec_find_decoder,并检查。

  6. 使用 avcodec_open2 函数打开解码器,初始化ptr_avctx,准备解码视频帧。

  7. 分配并初始化 AVPacket 结构体 ptr_avpkg,以及视频帧的空间 ptr_avframe和 ptr_avframeRGB。

  8. 为图像数据存储空间 buf 分配内存,并根据视频的宽高调整窗口大小。

  9. 初始化 SwsContext 结构体 ptr_swsCtx,用于图像的缩放和格式转换。

  10. 进入循环,不断读取视频数据帧,并解码、处理、显示。

  11. 在循环中,通过 av_read_frame 读取视频帧数据,然后使用 avcodec_send_packet将数据发送给解码器进行解码,使用avcodec_receive_frame解码视频数据,并将解码后的图像数据经过格式转换后显示在界面上。

  12. 循环中还包含了处理视频播放状态的逻辑,包括播放、暂停和结束播放等情况。

  13. 最后,释放相关资源,包括 SwsContext 结构体、图像帧内存和解码器等。

需要注意的点

  1. 播放延时
    我们在播放的时候实际上是需要提供延时的,但是又不能直接QThread::msleep(10),因为画面的渲染在主线程上进行,而主线程是严格禁止msleep(10)的否则容易崩溃,所以采用以下方式:
//延时, 不能直接sleep延时,UI主线程不能直接被阻塞,不然会有问题的
void delay(int ms)
{QTime stopTime;stopTime.start();//qDebug()<<"start:"<<QTime::currentTime().toString("HH:mm:ss.zzz");while(stopTime.elapsed() < ms)//stopTime.elapsed()返回从start开始到现在的毫秒数{QCoreApplication::processEvents();}//qDebug()<<"stop :"<<QTime::currentTime().toString("HH:mm:ss.zzz");
}
  1. 播放速度
    播放速度实际上就是通过影响上述的delay来实现的,在播放的过程中通过对应的值影响延时,从而修改播放速度。

项目效果:
在这里插入图片描述

核心代码

#include "../include/MediaPlay_Core.h"MediaPlay_Core::MediaPlay_Core()
{}MediaPlay_Core::~MediaPlay_Core()
{}void MediaPlay_Core::register_func(func_frame func)
{this->callback = func;
}int MediaPlay_Core::playVideo(const char* videopath)
{unsigned char* buf;bool blnVideo = false;int ret, gotPicture;unsigned int streamIndex = 0;const AVCodec* ptr_codec = nullptr;AVPacket* ptr_avpkg = nullptr;AVCodecContext* ptr_avctx = nullptr;AVFrame* ptr_avframe, * ptr_avframeRGB = nullptr;AVFormatContext* ptr_formatCtx = nullptr;struct SwsContext* ptr_swsCtx = nullptr;this->videoUrl = QString::fromLocal8Bit(videopath);int width = 0;int height = 0;//创建AVFormatContextptr_formatCtx = avformat_alloc_context();//初始化ptr_formatCtx if (avformat_open_input(&ptr_formatCtx, videopath, nullptr, nullptr) != 0) {qDebug() << "AVFormat open input Error";return -1;}//获取音频流数据信息if (avformat_find_stream_info(ptr_formatCtx, nullptr) < 0) {avformat_close_input(&ptr_formatCtx);qDebug() << "AVFormat find stream info Error";return -2;}//找到视频流的索引for (int i = 0; i < ptr_formatCtx->nb_streams; ++i) {if (ptr_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {streamIndex = i;blnVideo = true;break;}}//没有找到视频流则直接退出if (!blnVideo) {avformat_close_input(&ptr_formatCtx);qDebug() << "nv_streams error";return -3;}//获取视频流编码ptr_avctx = avcodec_alloc_context3(nullptr);//查找编码器avcodec_parameters_to_context(ptr_avctx, ptr_formatCtx->streams[streamIndex]->codecpar);ptr_codec = avcodec_find_decoder(ptr_avctx->codec_id);if (!ptr_codec) {avcodec_close(ptr_avctx);avformat_close_input(&ptr_formatCtx);qDebug() << "Can't find decoder";return -4;}//初始化ptr_avctxif (avcodec_open2(ptr_avctx, ptr_codec, nullptr) < 0) {avcodec_close(ptr_avctx);avformat_close_input(&ptr_formatCtx);qDebug() << "avcodec_open2 err.";return -5;}//初始化ptr_avpkgptr_avpkg = (AVPacket*)av_malloc(sizeof(AVPacket));//初始化数据帧空间ptr_avframe = av_frame_alloc();ptr_avframeRGB = av_frame_alloc();//创建图像数据存储buf//av_image_get_buffer_size一帧大小buf = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB32, ptr_avctx->width, ptr_avctx->height, 1));av_image_fill_arrays(ptr_avframeRGB->data, ptr_avframeRGB->linesize, buf, AV_PIX_FMT_RGB32, ptr_avctx->width, ptr_avctx->height, 1);width = ptr_avctx->width;height = ptr_avctx->height;//初始化ptr_swsCtxptr_swsCtx = sws_getContext(ptr_avctx->width, ptr_avctx->height, ptr_avctx->pix_fmt, ptr_avctx->width, ptr_avctx->height, AV_PIX_FMT_RGB32, SWS_BICUBIC, nullptr, nullptr, nullptr);//尝试循环读取视频数据while (true) {if (this->state_playVideo == PlayState::Video_Playing) { // 正在播放if (av_read_frame(ptr_formatCtx, ptr_avpkg) >= 0) {	//读取一帧未解码的数据//如果是视频数据if (ptr_avpkg->stream_index == (int)streamIndex) {// 将数据发送给解码器进行解码ret = avcodec_send_packet(ptr_avctx, ptr_avpkg);if (ret < 0) {qDebug() << "发送数据包到解码器时发生错误: "<<ret;continue; // 处理错误情况并继续读取下一帧数据}//解码视频数据ret = avcodec_receive_frame(ptr_avctx, ptr_avframe);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){av_packet_unref(ptr_avpkg); // 释放解码后的帧数据continue; // 没有可解码的帧数据或者已经解码完毕,继续读取下一帧}if (ret < 0) {qDebug() << "Decode error";continue;}// 处理解码后的图像帧sws_scale(ptr_swsCtx, (const unsigned char* const*)ptr_avframe->data, ptr_avframe->linesize, 0, ptr_avctx->height, ptr_avframeRGB->data, ptr_avframeRGB->linesize);if(this->callback != nullptr)this->callback((uchar*)ptr_avframeRGB->data[0], width, height);av_frame_unref(ptr_avframe);}}else {break;}}else if (this->state_playVideo == PlayState::Video_Finish)//播放结束{break;}else//暂停{delay(300);}}//释放资源sws_freeContext(ptr_swsCtx);av_frame_free(&ptr_avframeRGB);av_frame_free(&ptr_avframe);avcodec_close(ptr_avctx);avformat_close_input(&ptr_formatCtx);this->state_playVideo = PlayState::Video_Finish;qDebug() << "play finish!";return 0;
}void MediaPlay_Core::VideoPlayControl(bool blnPlay)
{if (blnPlay) {if (this->state_playVideo != PlayState::Video_Playing) {this->state_playVideo = PlayState::Video_Playing;playVideo(this->videoUrl.toLocal8Bit().data());}else {this->state_playVideo = PlayState::Video_Playing;}}else {if (this->state_playVideo == PlayState::Video_Playing) {this->state_playVideo = PlayState::Video_Pause;}}
}void MediaPlay_Core::VideoPlayEnd()
{//停止播放视频this->state_playVideo = PlayState::Video_Finish;
}void MediaPlay_Core::delay(int msc)
{QTime stopTime;stopTime.start();while (stopTime.elapsed() < msc)//stopTime.elapsed()返回从start开始到现在的毫秒数{QCoreApplication::processEvents();}
}

.

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python_BeautifulSoup爬取汽车评论数据

爬取的网站&#xff1a; 完整代码在文章末尾 https://koubei.16888.com/57233/0-0-0-2 使用方法&#xff1a; from bs4 import BeautifulSoup 拿到html后使用find_all()拿到文本数据&#xff0c;下图可见&#xff0c;数据标签为&#xff1a; content_text soup.find_all…

手机网页关键词视频爬虫采集软件可导出视频分享链接|视频无水印批量下载工具

全新音视频批量下载工具&#xff0c;为您解放视频管理烦恼&#xff01; 现如今&#xff0c;音上涌现出大量精彩的视频内容&#xff0c;但是要想高效地获取、管理和分享这些视频却是一件颇具挑战的事情。针对这一难题&#xff0c;我们自主研发了全新的音视频批量下载工具&#x…

基于javaSpringboot+mybatis+layui的装修验收管理系统设计和实现

基于javaSpringbootmybatislayui的装修验收管理系统设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留…

Day41:WEB攻防-ASP应用HTTP.SYS短文件文件解析Access注入数据库泄漏

目录 ASP-默认安装-MDB数据库泄漏下载 ASP-中间件-CVE&短文件&解析&写权限 HTTP.SYS&#xff08;CVE-2015-1635&#xff09;主要用作蓝屏破坏&#xff0c;跟权限不挂钩 IIS短文件(iis全版本都可能有这个问题) IIS文件解析 IIS写权限 ASP-SQL注入-SQLMAP使用…

WPF 立体Border

WPF 立体Border &#xff0c;用来划分各个功能区块 在资源文件中&#xff0c;添加如下样式代码&#xff1a; <Style x:Key"BaseBorder" TargetType"Border"><Setter Property"Background" Value"White" /><Setter Prop…

Mac nvm install failed python: not found

报错 $>./configure --prefix/Users/xxx/.nvm/versions/node/v12.22.12 < ./configure: line 3: exec: python: not found nvm: install v12.22.12 failed!解决方法 到 App 文件夹&#xff0c;并且打开 cd /System/Applications/Utilities/ open .记得改完 Rosetta 之…

(done) 机器学习中的方差 variance 和 偏差 bias 怎么理解?

来源&#xff1a;https://blog.csdn.net/weixin_41479678/article/details/116230631 情况1属于&#xff1a;低 bias&#xff0c;高 variance (和 human performance 相近&#xff0c;但和 验证集dev set 相远) 通常意味着模型训练轮数太多 情况2属于&#xff1a;高 bias&#…

【Django框架学习笔记】超详细的Python后端开发Django框架学习笔记

十二&#xff0c;Django框架 可以以下链接获取Django框架学习笔记,md文档和pdf文档 Django框架超详细的学习笔记&#xff0c;点击我获取 12.1 命令行操作 # 创建django项目 django-admin startproject aini# 启动项目 cd /mysite python3 manage.py runserver## 创建应用 …

GraphPad Prism 10:一站式数据分析解决方案

GraphPad Prism 10是一款功能强大的数据分析和可视化软件&#xff0c;广泛应用于生命科学研究、医学、生物、化学等多个领域。以下是对其详细功能的介绍&#xff1a; 首先&#xff0c;GraphPad Prism 10具有出色的数据可视化功能。它支持各种类型的图表和图形&#xff0c;包括…

MySQL 搭建双主复制服务 并 通过 HAProxy 负载均衡

一、MySQL 搭建双主复制高可用服务 在数据库管理中&#xff0c;数据的备份和同步是至关重要的环节&#xff0c;而双主复制&#xff08;Dual Master Replication&#xff09;作为一种高可用性和数据同步的解决方案&#xff0c;通过让两个数据库实例同时充当主服务器和从服务器&…

python的OA公文发文管理系统flask-django-php-nodejs

采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自下而上”的思想&#xff0c;在OA公文发文管理系统实现了用户、公文分类、公文信息、待办提醒等的功能性。系统根据现有的管理模块进行开发和扩展&a…

两台电脑简单的通信过程详解(经过两个路由器,不同网段)

一、eNSP拓扑图 二、配置4台电脑的IP地址、子网掩码、网关地址。 三、配置路由器 1.AR1-接口对应IP <Huawei>sys #进入系统视图 [Huawei]int g0/0/0 #进入0/0/0接口 [Huawei-GigabitEthernet0/0/0]ip address 192.168.0.1 24 #配置ip和掩码 [Huawei-GigabitEthernet0…

智慧城市的发展趋势与挑战:未来展望

随着信息技术的飞速发展&#xff0c;智慧城市已成为现代城市发展的重要方向。智慧城市通过集成应用先进的信息通信技术&#xff0c;实现城市管理、服务、运行的智能化&#xff0c;为城市的可持续发展注入了新的活力。然而&#xff0c;在智慧城市的发展过程中&#xff0c;也面临…

LabVIEW焓差试验室流量计现场自动校准系统

LabVIEW焓差试验室流量计现场自动校准系统 在现代工业和科研领域&#xff0c;流量计的准确性对于保证生产过程的质量和效率非常重要。开发了一种基于LabVIEW的焓差试验室流量计现场自动校准系统&#xff0c;通过提高流量计校准的准确性和效率。 在空调器空气焓值法能效测量装…

iOS图片占内存大小与什么有关?

1. 问&#xff1a;一张图片所占内存大小跟什么有关&#xff1f; 图片所占内存大小&#xff0c;与图片的宽高有关 我们平时看到的png、jpg、webp这些图片格式&#xff0c;其实都是图片压缩格式。通过对应的算法来优化了大小以节省网络传输与本地保存所需的资源。 但是当我们加…

Avalonia(11.0.2)+.NET6 打包运行到银河麒麟V10桌面系统

操作系统配置 项目结构 .net版本 这次我们是在银河麒麟V10系统上打包运行Avalonia(11.0.2)+.NET6.0的程序 开始打包 准备Linux下的桌面快捷方式以及图标 调整AvaloniaApplication2.Desktop.csproj的配置项,重点看下图红色线圈出来的部分,里面涉及到了LinuxPath的设置。完整的配…

JS08-DOM节点完整版

DOM节点 查找节点 父节点 <div class="father"><div class="son">儿子</div></div><script>let son = document.querySelector(.son)console.log(son.parentNode);son.parentNode.style.display = none</script>通过…

基于Arduino IDE 野火ESP8266模块 MODBUS RTU开发

一、工程创建 1.新建工程&#xff0c;工程另存为modbusRtu。 2.官网搜索modbus 相关库 https://www.arduino.cc/reference/en/libraries/或者在Arduino IDE中库管理中搜索选择modbus库 安装完如下 选择更多信息&#xff0c;会跳到库的代码示例&#xff0c;可查看如何使用该…

2001-2023年中国各省市级是否属于“开通高铁”城市匹配数据

2001-2023年中国各省市级是否属于“开通高铁”城市匹配数据 1、时间&#xff1a;2001-2023年 2、范围&#xff1a;300个地级市&#xff08;包括直辖市&#xff09; 3、来源&#xff1a;历年中国铁道出版社出版的《全国铁路旅客列车时刻表》 4、用途&#xff1a;高铁开通可作…

【python】python3基础

文章目录 一、安装pycharm 二、输入输出输出 print()文件输出&#xff1a;格式化输出&#xff1a; 输入input注释 三、编码规范四、变量保留字变量 五、数据类型数字类型整数浮点数复数 字符串类型布尔类型序列结构序列属性列表list &#xff0c;有序多维列表列表推导式 元组tu…