【Qt】ffmpeg照片提取、视频播放▲

目录

一、图像的成像原理:

RGB成像原理:

YUV成像原理:

二、多线程

三、ffmpeg解码(照片提取)

1.准备工作

(1)在工程文件夹里面新建三个文件夹

(2)在main函数中加入这两个

(3)在要解码的多线程子线程中加入

(4)在.pro文件工程里面加入

2.注册主键

3.打开视频文件

4.获取视频信息

5.判断是否有视频流

6.查找编码器

7.打开编码器

8.读取一帧压缩数据

9.读取一帧压缩数据,解码一帧数据

四、视频播放

1.paintEvent界面重绘事件

2.普通类如何使用信号与槽

3.在子线程中发送图片信号

4.在UI主线程槽函数接收


一、图像的成像原理:

RGB 和 YUV 是两种常见的颜色空间模型

  • RGB成像原理:

RGB 即红(Red)、绿(Green)、蓝(Blue),是一种基于三原色原理的颜色空间,在显示设备(如电脑显示屏、手机屏幕等)中,每个像素点都由红、绿、蓝三个子像素组成。通过控制这三个子像素的发光强度,就可以混合出各种不同的颜色

  • YUV成像原理:

YUV 是一种将亮度和色度分离的颜色空间。其中,Y 表示亮度,它反映了图像的明亮程度;U 和 V 表示色度,用于描述颜色的色调和饱和度,它们携带了图像的颜色信息

二、多线程

因为耗时逻辑就会造成UI卡顿,卡死或则白屏,所以不能在槽函数里面做太耗时操作(文件、解码都不行),所以使用了多线程,在UI主线程中,不执行耗时逻辑,耗时逻辑放在子线程去完成

三、ffmpeg解码(照片提取)

1.准备工作

(1)在工程文件夹里面新建三个文件夹

(2)在main函数中加入这两个

(3)在要解码的多线程子线程中加入

(4)在.pro文件工程里面加入

2.初始化文件相关操作

(1)注册主键

注册所有可用的编解码器、格式和协议等组件,这是使用 FFmpeg 进行编解码操作的第一步

av_register_all();

(2)打开视频文件

为 AVFormatContext 分配内存,然后打开指定的视频文件。AVFormatContext 用于存储输入视频文件的格式信息

    AVFormatContext *formatContext;

    //开空间

    formatContext=avformat_alloc_context();

    //打开输入视频文件

    int res=avformat_open_input(&formatContext,"../video/Warcraft3_End.avi",nullptr,nullptr);

    if(res<0)

    {

        qDebug()<<"open avformat_open_input fail";

    }

    else {

        qDebug()<<"success";

    }

(3)获取视频信息

读取文件的流信息,填充 AVFormatContext 中的流信息,包括视频流、音频流等

 res=avformat_find_stream_info(formatContext,nullptr);

    if(res<0)

    {

        qDebug()<<"avformat_find_stream_info fail";

    }

    else {

        qDebug()<<"avformat_find_stream_info success";

    }

(4)判断是否有视频流

遍历所有流,找到视频流的索引。

 int video_input=-1;

    //nb_streams:输入视频的AVStream个数

    for(int i=0;i<formatContext->nb_streams;i++)

    {

        /*

         * streams :输入视频的AVStream []数组

         *   有0或者1:0表示视频,1表示音频

         * codec:编码器

         * codec_type:编码的类型:

         * (1)AVMEDIA_TYPE_VIDEO:表示视频类型。

           (2)AVMEDIA_TYPE_AUDIO:表示音频类型。

           (3)AVMEDIA_TYPE_SUBTITLE:表示字幕类型。

        */

        if(formatContext->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)

        {

            video_input=i;

            break;

        }

    }

    if(video_input==-1)

    {

        qDebug()<<"video not find";

    }

    else {

        qDebug()<<formatContext->streams[video_input]->nb_frames;

        qDebug()<<"video_input"<<video_input;

    }

(5)查找编码器

获取视频流的编解码器上下文 AVCodecContext,然后根据编解码器 ID 查找对应的解码器

 AVCodecContext* codecContext=formatContext->streams[video_input]->codec;

    //通过文件信息中编码id找到解码器

    // 每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体。

    AVCodec* decoder=avcodec_find_decoder(codecContext->codec_id);

    if(decoder==nullptr)

    {

        qDebug()<<"avcodec_find_decoder fail";

    }

    else {

        qDebug()<<"avcodec_find_decoder success";

    }

(6)打开编码器

使用找到的解码器打开编解码器上下文,准备进行解码操作

 res=avcodec_open2(codecContext,decoder,nullptr);

    if(res!=0)

    {

        qDebug()<<"avcodec_open2 fail";

    }

    else {

        qDebug()<<"avcodec_open2 success";

    }

3.初始化解码所需的数据结构

(1)初始化AVPacket:

    //AVPacket 是 FFmpeg 中用于存储压缩数据的结构体

    AVPacket* pkt;

    pkt=(AVPacket*)malloc(sizeof (AVPacket));

    int size=codecContext->width * codecContext->height;

    res=av_new_packet(pkt,size);

    if(res!=0)

    {

        qDebug()<<"av_new_packet fail";

    }

    else {

        qDebug()<<"av_new_packet success";

    }

(2)初始化AVFrame 用于 RGB 数据

    //********AVFrame RGB准备工作**********

    //AVFrame 用于存储解码后的原始数据

    AVFrame* pictureRGB;

    AVFrame* picture;

    picture=av_frame_alloc();

    pictureRGB=av_frame_alloc();

    //解码画面信息按原图信息设置

    pictureRGB->width=codecContext->width;

    pictureRGB->height=codecContext->height;

    pictureRGB->format=codecContext->pix_fmt;//帧画面信息

    //设置缓冲区

    int imgByteRGB=avpicture_get_size(AV_PIX_FMT_RGB32,codecContext->width,codecContext->height);

    uint8_t *bufferRGB=(uint8_t*)av_malloc(imgByteRGB* sizeof (uint8_t));

    //填充缓冲区

    avpicture_fill((AVPicture*)pictureRGB,bufferRGB,AV_PIX_FMT_RGB32,codecContext->width,codecContext->height);

    //制定一个图像规则

    //转换前的视频宽高到转换后的视频宽高

    SwsContext* SwsContextRGB=sws_getContext(

                        codecContext->width,codecContext->height,codecContext->pix_fmt,

                        codecContext->width,codecContext->height,AV_PIX_FMT_RGB32,

                                             SWS_BICUBIC,nullptr,nullptr,nullptr);

    //********AVFrame准备工作完成**********

(3)初始化AVFrame 用于 YUV 数据

    this->pictureYUV=av_frame_alloc();

    this->pictureYUV->width=codecContext->width;

    this->pictureYUV->height=codecContext->height;

    this->pictureYUV->format=codecContext->pix_fmt;//帧画面信息

    this->imgByteYUV=avpicture_get_size(AV_PIX_FMT_YUV420P,

                                        codecContext->width,codecContext->height);

    this->bufferYUV=(uint8_t*)av_malloc(imgByteYUV* sizeof (uint8_t));

    avpicture_fill((AVPicture*)pictureYUV,bufferYUV,AV_PIX_FMT_YUV420P,

                                                codecContext->width,codecContext->height);

    SwsContextYUV=sws_getContext(

                         codecContext->width,codecContext->height,codecContext->pix_fmt,

                         codecContext->width,codecContext->height,AV_PIX_FMT_YUV420P,

                         SWS_BICUBIC,nullptr,nullptr,nullptr);

4.读取和解码视频帧

int x=0;

    //从输入文件读取一帧压缩数据,操作正常返回0

    while(av_read_frame(formatContext,pkt)==0)

    {

        //判断压缩数据是否为图像压缩数据

        if(pkt->stream_index==video_input)

        {

            //参数默认配置

            int get_picture_ptr=-1;

          /*解码一帧压缩:

          *  codecContext:AVCodecContext 包含了编解码器的上下文信息

          *  picture:AVFrame 用于存储解码后的视频帧数据

       *  get_picture_ptr:一个指向整数的指针,用于指示是否成功解码出一帧完整的视频图像

          * 如果解码成功并得到了一个完整的视频帧,该指针所指向的变量会被设置为非零值

          *  如果没有得到完整的帧,该变量会被设置为零

          *  pkt:AVPacket 用于存储压缩的视频数据包

          */

            avcodec_decode_video2(codecContext,picture,&get_picture_ptr,pkt);

            if(get_picture_ptr!=0)

            {

                //删除无效数据,转RGB存储

                //picture:原图    pictureRGB:转换后的图

                sws_scale(SwsContextRGB,picture->data,picture->linesize,0,picture->height,pictureRGB->data,pictureRGB->linesize);

                this->img=QImage((uchar *)bufferRGB,codecContext->width,codecContext->height,QImage::Format_RGB32);

                this->img.save(QString("../fileout/%1.jpg").arg(x));//保存进文件夹

//                emit sendImage(this->img);

//                msleep(40);//设置发送间隔时间

                x++;

            }

        }

        av_packet_unref(pkt);

    }

    //关闭解码器

    avcodec_close(codecContext);

    //释放关闭文件

    avformat_close_input(&formatContext);

四、视频播放

1.paintEvent界面重绘事件

Qt UI窗口存在事件循环机制,在QWidget类中,子类需要实现这个paintEvent事件,在窗口产生显示/切换/最大化/最小化自动调用,不需要自己调用,而update函数可以手动触发paintEvent重绘事件

2.普通类如何使用信号与槽

普通类在debug文件夹中不会生成moc_开头的文件,而信号与槽的核心机制为moc

第一步:删除工程项目下debug文件夹里的全部东西

3.在子线程中发送图片信号

4.在UI主线程槽函数接收

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

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

相关文章

⭐算法OJ⭐跳跃游戏【BFS+滑动窗口】(C++实现)Jump Game 系列 III,VII

⭐算法OJ⭐跳跃游戏【贪心算法】&#xff08;C实现&#xff09;Jump Game 系列 I,II 这篇文章介绍 跳跃游戏 的第三题和第七题&#xff0c;两道题目有异曲同工之妙&#xff0c;都运用了BFS广度优先搜索算法实现&#xff0c;难度相比于前两题较高&#xff0c;而且不同于更常见的…

【QGIS二次开发】地图显示与交互-01

1. 系统界面设计 设计的系统界面如下&#xff0c;很好还原了QGIS、ArcGIS等软件的系统界面&#xff0c;充分利用了QT中顶部工具栏、菜单栏、底部状态栏&#xff0c;实现了图层管理器、鹰眼图、工具箱三个工具面板。 菜单栏、工具栏、工具箱集成了系统中实现的全部功能&#x…

Skynet入门(一)

概念 skynet 是一个为网络游戏服务器设计的轻量框架。但它本身并没有任何为网络游戏业务而特别设计的部分&#xff0c;所以尽可以把它用于其它领域。 设计初衷 如何充分利用它们并行运作数千个相互独立的业务。 模块设计建议 在 skynet 中&#xff0c;用服务 (service) 这…

threejs:用着色器给模型添加光带扫描效果

第一步&#xff1a;给模型添加光带 首先创建一个立方体&#xff0c;不进行任何缩放平移操作&#xff0c;也不要set position。 基础代码如下&#xff1a; 在顶点着色器代码里varying vec3 vPosition;vPosition position;获得threejs自动计算的顶点坐标插值&#xff08;也就…

【时序预测】在线学习:算法选择(从线性模型到深度学习解析)

——如何为动态时序预测匹配最佳增量学习策略&#xff1f; 引言&#xff1a;在线学习的核心价值与挑战 在动态时序预测场景中&#xff08;如实时交通预测、能源消耗监控&#xff09;&#xff0c;数据以流式&#xff08;Streaming&#xff09;形式持续生成&#xff0c;且潜在的…

Spring Boot如何利用Twilio Verify 发送验证码短信?

Twilio提供了一个名为 Twilio Verify 的服务&#xff0c;专门用于处理验证码的发送和验证。这是一个更为简化和安全的解决方案&#xff0c;适合需要用户身份验证的应用。 使用Twilio Verify服务的步骤 以下是如何在Spring Boot中集成Twilio Verify服务的步骤&#xff1a; 1.…

【Linux操作系统】VM17虚拟机安装Ubuntu22.04,图文详细记录

1.双击桌面的 VMware Workstation17 Player&#xff0c;点击“创建新虚拟机”&#xff0c;如下图所示。 2.选择“稍后安装操作系统”&#xff0c;点击“下一步”。如下图所示。 3.客户机操作系统选择“Linux”&#xff0c;版本选择“ Ubuntu 64位”&#xff0c;然后点击“下一…

软件工程---净室软件工程

净室软件工程是一种软件开发方法&#xff0c;旨在通过形式化的数据和严格的测试来提高软件的可靠性和减少缺陷的数量。它的核心思想是在软件开发过程中最小化或消除软件缺陷&#xff0c;从而提高软件的质量和可靠性。这种方法强调在软件生命周期的早期阶段使用形式化方法进行规…

迷你世界脚本区域接口:Area

区域接口&#xff1a;Area 彼得兔 更新时间: 2023-12-18 11:35:14 具体函数名及描述如下: 序号 函数名 函数描述 1 createAreaRect(...) 创建矩形区域 2 createAreaRectByRange(...) 创建矩形区域(通过范围) 3 destroyArea(...) 销毁区域 4 getAre…

C# 牵手DeepSeek:打造本地AI超能力

一、引言 在人工智能飞速发展的当下&#xff0c;大语言模型如 DeepSeek 正掀起新一轮的技术变革浪潮&#xff0c;为自然语言处理领域带来了诸多创新应用。随着数据隐私和安全意识的提升&#xff0c;以及对模型部署灵活性的追求&#xff0c;本地部署 DeepSeek 成为众多开发者和…

Linux--基础命令3

大家好&#xff0c;今天我们继续学习Linux的基础命令 mv命令 mv命令是move的缩写&#xff0c;可以用来移动文件或者将文件改名 move(rename) files&#xff0c;经常⽤来备份⽂件或者目录 语法: mv [ 选项 ] 源⽂件或目录 目标⽂件或目录 mv src[文件、目录] dst[路径、文…

【每日八股】计算机网络篇(三):IP

目录 DNS 查询服务器的基本流程DNS 采用 TCP 还是 UDP&#xff0c;为什么&#xff1f;默认使用 UDP 的原因需要使用 TCP 的场景&#xff1f;总结 DNS 劫持是什么&#xff1f;解决办法&#xff1f;浏览器输入一个 URL 到显示器显示的过程&#xff1f;URL 解析TCP 连接HTTP 请求页…

探究DeepSeek R1与OpenAI模型文本相似度背后的秘密

摘要 一项由Copyleaks进行的新研究显示&#xff0c;DeepSeek R1生成的文本在风格上与OpenAI模型的相似度高达74.2%。这一发现引发了对DeepSeek训练数据来源和独特性的质疑。Copyleaks作为专业检测文本抄袭和AI生成内容的平台&#xff0c;其研究结果具有重要参考价值。此相似度揭…

【实战 ES】实战 Elasticsearch:快速上手与深度实践-2.2.3案例:电商订单日志每秒10万条写入优化

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 Elasticsearch批量写入性能调优实战&#xff1a;2.2.3 案例&#xff1a;电商订单日志每秒10万条写入优化1. 原始架构与瓶颈分析1.1 初始集群配置1.2 性能瓶颈定位 2. 全链路…

统计Excel列中某值出现的次数

统计Excel列中某值出现的次数&#xff1a; 1、COUNTIF 函数用于计算满足特定条件的单元格数量。假设要统计 A 列中值为 “苹果” 出现的次数&#xff0c;在其他单元格中输入公式&#xff1a;COUNTIF(A:A,“苹果”)。其中&#xff0c;A:A表示要统计的范围是 A 列&#xff0c;&q…

nnUNet报错

nnUNet报错处理 Traceback (most recent call last):File "/opt/conda/envs/nnunet/lib/python3.11/threading.py", line 1045, in _bootstrap_innerself.run()File "/opt/conda/envs/nnunet/lib/python3.11/threading.py", line 982, in runself._target…

Compose Multiplatform+Kotlin Multiplatfrom 第四弹跨平台

文章目录 引言功能效果开发准备依赖使用gradle依赖库MVIFlow设计富文本显示 总结 引言 Compose Multiplatformkotlin Multiplatfrom 今天已经到compose v1.7.3&#xff0c;从界面UI框架上实战开发看&#xff0c;很多api都去掉实验性注解&#xff0c;表示稳定使用了&#xff01;…

(十一)基于vue3+mapbox-GL实现模拟高德实时导航轨迹播放

要在 Vue 3 项目中结合 Mapbox GL 实现类似高德地图的实时导航轨迹功能,您可以按照以下步骤进行: 安装依赖: 首先,安装 mapbox-gl 和 @turf/turf 这两个必要的库: npm install mapbox-gl @turf/turf引入 Mapbox GL: 在组件中引入 mapbox-gl 并初始化地图实例: <templ…

智慧园区大数据云平台建设总体方案,平台方案架构-智慧园区大数据平台(320页原件Word)

第一章 项目建设背景及现状 1.1. 项目建设背景 1.2. 项目建设必要性 1.3. 项目建设目标 1.4. 建设原则 第二章 园区创新发展趋势 2.1园区经济向生态型转变 2.2 园区企业向高新型转变 2.3园区管理向城市化转变 第三章 工业园区大数据存在的问题 3.1信息化配套设施及服…

NeurIPS24 Oral!多模态融合+目标检测全新里程碑!

最近发现多模态融合目标检测实在太热了&#xff01;顶会频出&#xff01;像是NeurIPS24 Oral上端到端算法E2E-MFD&#xff1b;ECCV24上性能提升30.8&#xff05;的FRN&#xff1b;TPAMI24上推理效率狂飙270&#xff05;倍的FSF…… 主要在于&#xff1a;一方面&#xff0c;其能…