当前位置: 首页 > news >正文

Qt+FFmpeg开发视频播放器笔记(三):音视频流解析封装

音频解析

音频解码是指将压缩的音频数据转换为可以再生的PCM(脉冲编码调制)数据的过程。

FFmpeg音频解码的基本步骤如下:  

  1. 初始化FFmpeg解码器(4.0版本后可省略):
    调用av_register_all()初始化编解码器。

    调用avcodec_register_all()注册所有编解码器。

  2. 打开输入的音频流:

    使用avformat_open_input()函数来读取和打开音频文件。                                                        
    使用avformat_find_stream_info()函数获取流信息。

  3. 查找音频流:
    检索音频流的索AVMEDIA_TYPE_AUDIO。                                                                         
    使用av_find_best_stream()找到第一个音频流并记下它的index。
  4.  打开对应的解码器:

    查找音频流对应的解解码器avcodec_find_decoder()。
    使用avcodec_open2()函数来打开解码器。

  5.  读取音频包解码:

    遍历音频数据,读取音频包(AVPacket)。
    使用av_read_frame()来读取。
    检查包是否属于所需的音频流。

  6. 将音频包送入解码器:

    使用avcodec_send_packet()将包送入解码器准备解码。

  7. 从解码器读取解码后的音频帧:

    使用avcodec_receive_frame()获取解码后的帧(AVFrame)。
    继续从解码器获取所有解码后的帧直到返回EAGAIN或错误。

  8. 转换音频格式 (可选):

    如果需要,将音频数据转换成不同的格式或采样率,可以使用’libswresample’或者’libavresample’。

  9. 后处理 (可选):

    对解码的音频进行必要的后处理,比如音量调整、混音等。

  10.  清理和资源释放: 

    关闭解码器。
    关闭音频文件。
    释放所有使用过的AVFrame和AVPacket。
    释放编解码上下文等。

视频解析  

视频解码的流程目的是将压缩的视频数据流转换成解码后的原始视频帧(通常是YUV或RGB格式)。

 

FFmpeg视频解码的基本步骤如下:  

  1. 初始化FFmpeg解码器(4.0版本后可省略):
    调用av_register_all()初始化编解码器。

    调用avcodec_register_all()注册所有编解码器。

  2. 打开输入的视频流:

    使用avformat_open_input()函数来读取和打开音频文件。                                                        
    使用avformat_find_stream_info()函数获取流信息。

  3. 查找视频流:
     检索视频流的索AVMEDIA_TYPE_VIDEO。                                                                         
    使用av_find_best_stream()找到第一个视频流并记下它的index。
  4.  打开对应的解码器:

    查找视频流对应的解解码器avcodec_find_decoder()。
    使用avcodec_open2()函数来打开解码器。

  5.  读取视频流包解码:

    通过av_read_frame()从媒体文件中读取视频数据(AVPacket)。
    考虑只处理我们之前记下的视频流索引对应的包。

  6. 发送数据到解码器:

    使用avcodec_send_packet()将数据包送入解码器准备解码。

  7. 从解码器读取解码后的视频帧:

    使用avcodec_receive_frame()从解码器中获取解码后的视频帧(AVFrame)。
    需要循环重复此过程以获取所有解码后的帧。

  8. 视频帧处理 (可选):

    将解码的视频帧转换成需要的格式或进行处理,可以使用libswscale来进行格式转换或调整尺寸。

  9. 帧率控制  (可选):

    根据视频的PTS(Presentation Time Stamp)来处理帧率,确保视频按正确的速率播放。

  10.  清理和资源释放: 

    释放已分配的AVCodecContext和AVFormatContext。
    释放使用过的AVFrame和AVPacket。
    关闭视频流和网络库(如果初始化了)。

 视频流解析代码

decoder.h

#ifndef DECODER_H
#define DECODER_H#include <QThread>
#include <QImage>extern "C"
{
//#include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
#include "libavutil/pixfmt.h"
#include "libavutil/opt.h"
#include "libavcodec/avfft.h"
#include "libavutil/imgutils.h"
}#include "audiodecoder.h"class Decoder : public QThread
{Q_OBJECTpublic:enum PlayState {STOP,PAUSE,PLAYING,FINISH};explicit Decoder();~Decoder();double getCurrentTime();void seekProgress(qint64 pos);int getVolume();void setVolume(int volume);private:void run();void clearData();void setPlayState(Decoder::PlayState state);void displayVideo(QImage image);static int videoThread(void *arg);double synchronize(AVFrame *frame, double pts);bool isRealtime(AVFormatContext *pFormatCtx);int initFilter();int fileType;int videoIndex;int audioIndex;int subtitleIndex;QString currentFile;QString currentType;qint64 timeTotal;AVPacket seekPacket;qint64 seekPos;double seekTime;PlayState playState;bool isStop;bool gotStop;bool isPause;bool isSeek;bool isReadFinished;bool isDecodeFinished;AVFormatContext *pFormatCtx;AVCodecContext *pCodecCtx;          // video codec contextAvPacketQueue videoQueue;AvPacketQueue subtitleQueue;AVStream *videoStream;double videoClk;    // video frame timestampAudioDecoder *audioDecoder;AVFilterGraph   *filterGraph;AVFilterContext *filterSinkCxt;AVFilterContext *filterSrcCxt;public slots:void decoderFile(QString file, QString type);void stopVideo();void pauseVideo();void audioFinished();signals:void readFinished();void gotVideo(QImage image);void gotVideoTime(qint64 time);void playStateChanged(Decoder::PlayState state);};#endif // DECODER_H

decoder.cpp 

#include <QDebug>#include "decoder.h"Decoder::Decoder() :timeTotal(0),playState(STOP),isStop(false),isPause(false),isSeek(false),isReadFinished(false),audioDecoder(new AudioDecoder),filterGraph(NULL)
{av_init_packet(&seekPacket);seekPacket.data = (uint8_t *)"FLUSH";connect(audioDecoder, SIGNAL(playFinished()), this, SLOT(audioFinished()));connect(this, SIGNAL(readFinished()), audioDecoder, SLOT(readFileFinished()));
}Decoder::~Decoder()
{}void Decoder::displayVideo(QImage image)
{emit gotVideo(image);
}void Decoder::clearData()
{videoIndex = -1,audioIndex = -1,subtitleIndex = -1,timeTotal = 0;isStop  = false;isPause = false;isSeek  = false;isReadFinished      = false;isDecodeFinished    = false;videoQueue.empty();audioDecoder->emptyAudioData();videoClk = 0;
}void Decoder::setPlayState(Decoder::PlayState state)
{
//    qDebug() << "Set state: " << state;emit playStateChanged(state);playState = state;
}bool Decoder::isRealtime(AVFormatContext *pFormatCtx)
{if (!strcmp(pFormatCtx->iformat->name, "rtp")|| !strcmp(pFormatCtx->iformat->name, "rtsp")|| !strcmp(pFormatCtx->iformat->name, "sdp")) {return true;}// if(pFormatCtx->pb && (!strncmp(pFormatCtx->filename, "rtp:", 4)//     || !strncmp(pFormatCtx->filename, "udp:", 4)//     )) {//     return true;// }return false;
}int Decoder::initFilter()
{int ret;AVFilterInOut *out = avfilter_inout_alloc();AVFilterInOut *in = avfilter_inout_alloc();/* output format */enum AVPixelFormat pixFmts[] = {AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE};/* free last graph */if (filterGraph) {avfilter_graph_free(&filterGraph);}filterGraph = avfilter_graph_alloc();/* just add filter ouptut format rgb32,* use for function avfilter_graph_parse_ptr()*/QString filter("pp=hb/vb/dr/al");QString args = QString("video_size=%1x%2:pix_fmt=%3:time_base=%4/%5:pixel_aspect=%6/%7").arg(pCodecCtx->width).arg(pCodecCtx->height).arg(pCodecCtx->pix_fmt).arg(videoStream->time_base.num).arg(videoStream->time_base.den).arg(pCodecCtx->sample_aspect_ratio.num).arg(pCodecCtx->sample_aspect_ratio.den);/* create source filter */ret = avfilter_graph_create_filter(&filterSrcCxt, avfilter_get_by_name("buffer"), "in", args.toLocal8Bit().data(), NULL, filterGraph);if (ret < 0) {qDebug() << "avfilter graph create filter failed, ret:" << ret;avfilter_graph_free(&filterGraph);goto out;}/* create sink filter */ret = avfilter_graph_create_filter(&filterSinkCxt, avfilter_get_by_name("buffersink"), "out", NULL, NULL, filterGraph);if (ret < 0) {qDebug() << "avfilter graph create filter failed, ret:" << ret;avfilter_graph_free(&filterGraph);goto out;}/* set sink filter ouput format */ret = av_opt_set_int_list(filterSinkCxt, "pix_fmts", pixFmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);if (ret < 0) {qDebug() << "av opt set int list failed, ret:" << ret;avfilter_graph_free(&filterGraph);goto out;}out->name       = av_strdup("in");out->filter_ctx = filterSrcCxt;out->pad_idx    = 0;out->next       = NULL;in->name       = av_strdup("out");in->filter_ctx = filterSinkCxt;in->pad_idx    = 0;in->next       = NULL;if (filter.isEmpty() || filter.isNull()) {/* if no filter to add, just link source & sink */ret = avfilter_link(filterSrcCxt, 0, filterSinkCxt, 0);if (ret < 0) {qDebug() << "avfilter link failed, ret:" << ret;avfilter_graph_free(&filterGraph);goto out;}} else {/* add filter to graph */ret = avfilter_graph_parse_ptr(filterGraph, filter.toLatin1().data(), &in, &out, NULL);if (ret < 0) {qDebug() << "avfilter graph parse ptr failed, ret:" << ret;avfilter_graph_free(&filterGraph);goto out;}}/* check validity and configure all the links and formats in the graph */if ((ret = avfilter_graph_config(filterGraph, NULL)) < 0) {qDebug() << "avfilter graph config failed, ret:" << ret;avfilter_graph_free(&filterGraph);}out:avfilter_inout_free(&out);avfilter_inout_free(&in);return ret;
}void Decoder::decoderFile(QString file, QString type)
{
//    qDebug() << "Current state:" << playState;qDebug() << "File name:" << file << ", type:" << type;if (playState != STOP) {isStop = true;while (playState != STOP) {SDL_Delay(10);}SDL_Delay(100);}clearData();SDL_Delay(100);currentFile = file;currentType = type;this->start();
}void Decoder::audioFinished()
{isStop = true;if (currentType == "music") {SDL_Delay(100);emit playStateChanged(Decoder::FINISH);}
}void Decoder::stopVideo()
{if (playState == STOP) {setPlayState(Decoder::STOP);return;}gotStop = true;isStop  = true;audioDecoder->stopAudio();if (currentType == "video") {/* wait for decoding & reading stop */while (!isReadFinished || !isDecodeFinished) {SDL_Delay(10);}} else {while (!isReadFinished) {SDL_Delay(10);}}
}void Decoder::pauseVideo()
{if (playState == STOP) {return;}isPause = !isPause;audioDecoder->pauseAudio(isPause);if (isPause) {av_read_pause(pFormatCtx);setPlayState(PAUSE);} else {av_read_play(pFormatCtx);setPlayState(PLAYING);}
}int Decoder::getVolume()
{return audioDecoder->getVolume();
}void Decoder::setVolume(int volume)
{audioDecoder->setVolume(volume);
}double Decoder::getCurrentTime()
{if (audioIndex >= 0) {return audioDecoder->getAudioClock();}return 0;
}void Decoder::seekProgress(qint64 pos)
{if (!isSeek) {seekPos = pos;isSeek = true;}
}double Decoder::synchronize(AVFrame *frame, double pts)
{double delay;if (pts != 0) {videoClk = pts; // Get pts,then set video clock to it} else {pts = vi

相关文章:

Qt+FFmpeg开发视频播放器笔记(三):音视频流解析封装

音频解析 音频解码是指将压缩的音频数据转换为可以再生的PCM(脉冲编码调制)数据的过程。 FFmpeg音频解码的基本步骤如下: 初始化FFmpeg解码器(4.0版本后可省略): 调用av_register_all()初始化编解码器。 调用avcodec_register_all()注册所有编解码器。 打开输入的音频流:…...

从黎巴嫩电子通信设备爆炸看如何防范网络电子袭击

引言&#xff1a; 在当今数字化时代&#xff0c;电子通信设备已成为我们日常生活中不可或缺的一部分。然而&#xff0c;近期黎巴嫩发生的电子设备爆炸事件提醒我们&#xff0c;这些设备也可能成为危险的武器。本文将深入探讨电子袭击的原理、防范措施&#xff0c;以及网络智能…...

【Verilog学习日常】—牛客网刷题—Verilog快速入门—VL16

使用8线-3线优先编码器Ⅰ实现16线-4线优先编码器 描述 ②请使用2片该优先编码器Ⅰ及必要的逻辑电路实现16线-4线优先编码器。优先编码器Ⅰ的真值表和代码已给出。 可将优先编码器Ⅰ的代码添加到本题答案中&#xff0c;并例化。 优先编码器Ⅰ的代码如下&#xff1a; module…...

12 - TCPServer实验

在上一章节中&#xff0c;我们学习了TCPClient通信测试的相关知识。接下来&#xff0c;本章节将以此为基础&#xff0c;构建一个基础性的TCPServer连接机制&#xff0c;该机制将利用之前所建立的WIFI网络连接。为方便演示&#xff0c;我们将借助网络调试助手工具进行数据的发送…...

Explain执行计划

Explain执行计划 explain可以帮助开发人员分析SQL问题&#xff0c;explain用于显示MySQL如何使用SQL执行计划&#xff0c;可以帮助开发人员写出更优化的查询语句。使用方法就是在查询语句前加上explain关键字。 执行添加上explain关键字的语句可以看到一个列表&#xff1a; 其…...

ARM/Linux嵌入式面经(三六):中科曙光

文章目录 1.AD转换,怎么在项目中运用2.项目中的通信网络介绍一下通信网络介绍1. 通信网络类型2. 通信网络特点3. 应用场景4. 关键技术5. 项目中的具体应用和实现方式模拟面试官追问3.socketSocket介绍深度拓展与追问深度拓展可能的追问4.进程间通信方式进程间通信方式介绍总结…...

Python和C++气候模型算法模型气候学模拟和统计学数据可视化及指标评估

&#x1f3af;要点 贝叶斯推理气候模型辐射对流及干湿能量平衡模型时间空间气象变化预测模型评估统计指标气象预测数据变换天气和气象变化长短期影响预估降低气候信息尺度评估算法气象行为模拟&#xff1a;碳循环、辐射强迫和温度响应温室气体排放碳循环温室诱导气候变化评估气…...

鸿蒙开发城市联动选择弹框

鸿蒙开发城市联动选择弹框 城市联动选择弹框不容易&#xff0c;在Android那边也是不容易。选择某个省份时&#xff0c;城市要对得上&#xff0c;切换得及时 一、思路&#xff1a; 关键用Provide和Consume互相监听对方的变化 二、效果图&#xff1a; 三、视频效果&#xff1…...

css 控制虚线刻度尺寸

文章目录 css效果 css <div style"width: 100%; height: 1px;background-image: linear-gradient(to right, #545454 0%, #545454 80%, transparent 5%);background-size: 15px 10px;background-repeat: repeat-x; margin: 0 auto;"></div>效果...

NLP三天入门大模型,我领先你好几个版本了

大模型时代下&#xff0c;nlp初学者需要怎么入门? 入门姿势简单粗暴:打一些必要的基础就跑步进入Transformera 大模型时代&#xff0c;传统的算法&#xff0c;像分词、词性标注&#xff0c;被替代得非常厉害&#xff0c;在入门阶段没必要花费太多精力在传统算法上面。 数学和…...

专题六_模拟_算法详细总结

目录 模拟算法 1.模拟算法流程&#xff08;一定要在草稿纸上演算一遍流程&#xff09; 2.把流程转换成代码 1. 替换所有的问号&#xff08;easy&#xff09; 解析&#xff1a; 1.暴力&#xff1a; 2.优化&#xff1a;&#xff08;找规律&#xff09; 总结&#xff1a; …...

ArrayList的扩容机制

ArrayList的扩容机制 ArrayList中的成员变量&#xff1a;1.不带参数的构造方法 让elementDate 引用指向 DEFAULTCAPACITY_EMPTY_ELEMENTDATA所指向的对象 > 当我们调用 不带参数的构造方法的时候 第一次进行add元素的时候&#xff0c;会为底层的数组 进行内存的分配&…...

一、编译原理(引论)

目录 【一】、引论 一、编译器 1、编译器 2、编译器与解释器 3、编译器结构 【一】、引论 一、编译器 1、编译器 &#xff08;1&#xff09;编译器&#xff1a;将人类易懂的 高级语言 翻译成 硬件可执行的目标机器语言 &#xff08;2&#xff09; 高级语言 ⚫ 直接面…...

【Javascript修炼篇】JS中的函数式编程

介绍&#xff1a; 函数式编程&#xff08;FP&#xff09;是一种编程范式&#xff0c;这意味着一种基于一些原则来思考软件构建的方法&#xff0c;比如 纯函数、不可变性、一等与高阶函数、函数组合、闭包、声明式编程、递归、引用透明性、柯里化 和 部分应用。 当这些原则有效…...

spring cxf 常用注解

在Spring框架中&#xff0c;特别是当与Apache CXF&#xff08;一个流行的SOAP和RESTful Web服务框架&#xff09;结合使用时&#xff0c;我们会遇到一系列的注解。以下是一些在Spring和CXF中常用的注解&#xff1a; Spring相关注解&#xff1a; Component&#xff1a;用于定义一…...

python | x-y 网格切片

写在前面 通常&#xff0c; 我们处理的毕竟完善的nc产品&#xff0c;一般呈现未timexlatxlon的维度&#xff0c;且lon和lat都是规则的网格&#xff0c;我们可以方便的使用xarray.sel()选择合适的区域进行切片。但是&#xff0c;部分nc产品比如卫星轨道或者模式输出的数据&…...

【C#】vs2022 .net8

Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (microsoft.com) 更新就会出现...

【华为杯】第二十一届中国研究生数学建模竞赛

“华为杯”第二十一届中国研究生数学建模竞赛即将开始&#xff0c;梦想科研社给大家整理一些比赛信息&#xff0c;在正式开赛后&#xff0c;我们也会持续分享一些课题的分析以及代码&#xff0c;有需要的可以联系我们获取资料信息哦 一、时间节点 1.加密赛题开始下载时间&…...

首次开机android.intent.action.BOOT_COMPLETED开机广播发送慢的问题

1. 背景 做过android开发的同学相信一定做个这种逻辑:app接收BOOT_COMPLETED开机广播&#xff0c;自启动&#xff0c;或者收到广播做一些事情。目前在我们的项目上遇到首次开机&#xff0c;BOOT_COMPLETED开机广播发送慢的问题。接下来分享记录下如何定位这类问题。 2. 分析过…...

通信工程学习:什么是OLT光线路终端

OLT&#xff1a;光线路终端 OLT&#xff08;Optical Line Terminal&#xff0c;光线路终端&#xff09;是光纤通信系统中的核心局端设备&#xff0c;特别是在无源光网络&#xff08;Passive Optical Network, PON&#xff09;架构中扮演着至关重要的角色。以下是关于OLT光线路终…...

Unity的Button组件进行扩展

废话不多说&#xff0c;在Untiy中&#xff0c;如果想要对Button等组件进行扩展的话&#xff0c;那么不仅仅只需要将新增的属性设置为public或者增加SerializeField字段就行了的&#xff0c;同时需要对Inspector的GUI面板进行修改&#xff0c;以下直接展示代码&#xff1a; usi…...

前端vue-插值表达式和v-html的区别

创建vue实例的时候&#xff0c;可以有两种形式。 1.let appnew Vue({}) 2 const appnew Vue({}) 3 el是挂载点&#xff0c;是上面div的id值 4 data中的值可以展示在上面div中 5 v-html标签里面如果有内容&#xff0c;则我们的新内容会把标签里面的内容覆盖掉...

【开发心得】筑梦上海:项目风云录(4)

不知不觉已经写到了第4篇&#xff0c;天下大事&#xff0c;必作于细。 其实项目管理也是如此&#xff0c;成功都在细节之处。自从博士离开以后&#xff0c;项目逐步开始进入了正常轨道。来来回回的30多人&#xff0c;也不能一一列举的记流水账。 目录 会海和MSN 小娇往事 …...

el-table使用el-switch选择器没效果

出现问题的代码: 0表示启用&#xff0c;1表示禁用&#xff0c;发现页面根本没有效果&#xff0c;百思不得其解&#xff0c;查阅资料&#xff0c;恍然大悟。 <el-table :data"userList" stripe border style"width: 100%" height"500"><…...

libserailport交叉编译适配说明

1&#xff1a;libserialport简介 github路径 自己的gitee路径 libserialport 是一个跨平台的串口通信库&#xff0c;由 sigrok 项目开发。它简洁、易用&#xff0c;并且支持多种操作系统。 libserialport 支持阻塞和非阻塞模式&#xff0c;可以根据你的需求选择适当的模式。阻…...

C语言中的一些小知识(二)

一、"%"运算符两侧只能是整数 在C语言中&#xff0c;% 运算符称为模运算符或取余运算符&#xff0c;它用于计算两个整数相除后的余数。当使用 % 运算符时&#xff0c;操作数必须是整数类型&#xff08;包括 char、int、long 等&#xff09;。 语法 remainder div…...

使用 Go 语言实现简单聊天系统

在互联网时代&#xff0c;聊天系统是常见的应用场景之一。无论是即时通讯、在线客服还是多人游戏中的消息系统&#xff0c;聊天功能的实现都是必不可少的。本文将使用 Go 语言&#xff0c;结合 WebSocket 来构建一个简单的多人聊天室系统。 一、项目结构 首先&#xff0c;我们…...

用友U8二次开发工具KK-FULL-*****-EFWeb使用方法

1、安装: 下一步&#xff0c;下一步即可。弹出黑框不要关闭&#xff0c;让其自动执行并关闭。 2、服务配置&#xff1a; 输入服务器IP地址&#xff0c;选择U8数据源&#xff0c;输入U8用户名及账号&#xff0c;U8登录日期勾选系统日期。测试参数有效性&#xff0c;提示测试通过…...

【经验帖】脏读和不可重复读的概念及影响

脏读和不可重复读是数据库事务并发执行时可能出现的两种数据一致性问题&#xff0c;它们对数据的一致性和完整性有着显著的影响。以下是脏读和不可重复读的具体影响&#xff1a; 脏读的影响 脏读发生在一个事务读取了另一个事务未提交的数据时。由于这些数据尚未被提交&#x…...

MTK zephyr平台:USB升级、枚举流程

一、USB升级流程 通过代码及log分析,当前平台升级过程在PL阶段进行 USB download相关代码 mtk/modules/hal/boot/preloader/platform/flashc/ mtk/modules/hal/boot/preloader/platform/board_name/flash/ mtk/modules/hal/boot/preloader/platform/board_name/src/drive…...