Qt+FFmpeg开发视频播放器笔记(三):音视频流解析封装
音频解析
音频解码是指将压缩的音频数据转换为可以再生的PCM(脉冲编码调制)数据的过程。

FFmpeg音频解码的基本步骤如下:
- 初始化FFmpeg解码器(4.0版本后可省略):
调用av_register_all()初始化编解码器。调用avcodec_register_all()注册所有编解码器。
- 打开输入的音频流:
使用avformat_open_input()函数来读取和打开音频文件。
使用avformat_find_stream_info()函数获取流信息。 - 查找音频流:
检索音频流的索AVMEDIA_TYPE_AUDIO。
使用av_find_best_stream()找到第一个音频流并记下它的index。 - 打开对应的解码器:
查找音频流对应的解解码器avcodec_find_decoder()。
使用avcodec_open2()函数来打开解码器。 - 读取音频包解码:
遍历音频数据,读取音频包(AVPacket)。
使用av_read_frame()来读取。
检查包是否属于所需的音频流。 - 将音频包送入解码器:
使用avcodec_send_packet()将包送入解码器准备解码。
- 从解码器读取解码后的音频帧:
使用avcodec_receive_frame()获取解码后的帧(AVFrame)。
继续从解码器获取所有解码后的帧直到返回EAGAIN或错误。 - 转换音频格式 (可选):
如果需要,将音频数据转换成不同的格式或采样率,可以使用’libswresample’或者’libavresample’。
- 后处理 (可选):
对解码的音频进行必要的后处理,比如音量调整、混音等。
- 清理和资源释放:
关闭解码器。
关闭音频文件。
释放所有使用过的AVFrame和AVPacket。
释放编解码上下文等。
视频解析
视频解码的流程目的是将压缩的视频数据流转换成解码后的原始视频帧(通常是YUV或RGB格式)。
FFmpeg视频解码的基本步骤如下:
- 初始化FFmpeg解码器(4.0版本后可省略):
调用av_register_all()初始化编解码器。调用avcodec_register_all()注册所有编解码器。
- 打开输入的视频流:
使用avformat_open_input()函数来读取和打开音频文件。
使用avformat_find_stream_info()函数获取流信息。 - 查找视频流:
检索视频流的索AVMEDIA_TYPE_VIDEO。
使用av_find_best_stream()找到第一个视频流并记下它的index。 - 打开对应的解码器:
查找视频流对应的解解码器avcodec_find_decoder()。
使用avcodec_open2()函数来打开解码器。 - 读取视频流包解码:
通过av_read_frame()从媒体文件中读取视频数据(AVPacket)。
考虑只处理我们之前记下的视频流索引对应的包。 - 发送数据到解码器:
使用avcodec_send_packet()将数据包送入解码器准备解码。
- 从解码器读取解码后的视频帧:
使用avcodec_receive_frame()从解码器中获取解码后的视频帧(AVFrame)。
需要循环重复此过程以获取所有解码后的帧。 - 视频帧处理 (可选):
将解码的视频帧转换成需要的格式或进行处理,可以使用libswscale来进行格式转换或调整尺寸。
- 帧率控制 (可选):
根据视频的PTS(Presentation Time Stamp)来处理帧率,确保视频按正确的速率播放。
- 清理和资源释放:
释放已分配的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()注册所有编解码器。 打开输入的音频流:…...
从黎巴嫩电子通信设备爆炸看如何防范网络电子袭击
引言: 在当今数字化时代,电子通信设备已成为我们日常生活中不可或缺的一部分。然而,近期黎巴嫩发生的电子设备爆炸事件提醒我们,这些设备也可能成为危险的武器。本文将深入探讨电子袭击的原理、防范措施,以及网络智能…...
【Verilog学习日常】—牛客网刷题—Verilog快速入门—VL16
使用8线-3线优先编码器Ⅰ实现16线-4线优先编码器 描述 ②请使用2片该优先编码器Ⅰ及必要的逻辑电路实现16线-4线优先编码器。优先编码器Ⅰ的真值表和代码已给出。 可将优先编码器Ⅰ的代码添加到本题答案中,并例化。 优先编码器Ⅰ的代码如下: module…...
12 - TCPServer实验
在上一章节中,我们学习了TCPClient通信测试的相关知识。接下来,本章节将以此为基础,构建一个基础性的TCPServer连接机制,该机制将利用之前所建立的WIFI网络连接。为方便演示,我们将借助网络调试助手工具进行数据的发送…...
Explain执行计划
Explain执行计划 explain可以帮助开发人员分析SQL问题,explain用于显示MySQL如何使用SQL执行计划,可以帮助开发人员写出更优化的查询语句。使用方法就是在查询语句前加上explain关键字。 执行添加上explain关键字的语句可以看到一个列表: 其…...
ARM/Linux嵌入式面经(三六):中科曙光
文章目录 1.AD转换,怎么在项目中运用2.项目中的通信网络介绍一下通信网络介绍1. 通信网络类型2. 通信网络特点3. 应用场景4. 关键技术5. 项目中的具体应用和实现方式模拟面试官追问3.socketSocket介绍深度拓展与追问深度拓展可能的追问4.进程间通信方式进程间通信方式介绍总结…...
Python和C++气候模型算法模型气候学模拟和统计学数据可视化及指标评估
🎯要点 贝叶斯推理气候模型辐射对流及干湿能量平衡模型时间空间气象变化预测模型评估统计指标气象预测数据变换天气和气象变化长短期影响预估降低气候信息尺度评估算法气象行为模拟:碳循环、辐射强迫和温度响应温室气体排放碳循环温室诱导气候变化评估气…...
鸿蒙开发城市联动选择弹框
鸿蒙开发城市联动选择弹框 城市联动选择弹框不容易,在Android那边也是不容易。选择某个省份时,城市要对得上,切换得及时 一、思路: 关键用Provide和Consume互相监听对方的变化 二、效果图: 三、视频效果࿱…...
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三天入门大模型,我领先你好几个版本了
大模型时代下,nlp初学者需要怎么入门? 入门姿势简单粗暴:打一些必要的基础就跑步进入Transformera 大模型时代,传统的算法,像分词、词性标注,被替代得非常厉害,在入门阶段没必要花费太多精力在传统算法上面。 数学和…...
专题六_模拟_算法详细总结
目录 模拟算法 1.模拟算法流程(一定要在草稿纸上演算一遍流程) 2.把流程转换成代码 1. 替换所有的问号(easy) 解析: 1.暴力: 2.优化:(找规律) 总结: …...
ArrayList的扩容机制
ArrayList的扩容机制 ArrayList中的成员变量:1.不带参数的构造方法 让elementDate 引用指向 DEFAULTCAPACITY_EMPTY_ELEMENTDATA所指向的对象 > 当我们调用 不带参数的构造方法的时候 第一次进行add元素的时候,会为底层的数组 进行内存的分配&…...
一、编译原理(引论)
目录 【一】、引论 一、编译器 1、编译器 2、编译器与解释器 3、编译器结构 【一】、引论 一、编译器 1、编译器 (1)编译器:将人类易懂的 高级语言 翻译成 硬件可执行的目标机器语言 (2) 高级语言 ⚫ 直接面…...
【Javascript修炼篇】JS中的函数式编程
介绍: 函数式编程(FP)是一种编程范式,这意味着一种基于一些原则来思考软件构建的方法,比如 纯函数、不可变性、一等与高阶函数、函数组合、闭包、声明式编程、递归、引用透明性、柯里化 和 部分应用。 当这些原则有效…...
spring cxf 常用注解
在Spring框架中,特别是当与Apache CXF(一个流行的SOAP和RESTful Web服务框架)结合使用时,我们会遇到一系列的注解。以下是一些在Spring和CXF中常用的注解: Spring相关注解: Component:用于定义一…...
python | x-y 网格切片
写在前面 通常, 我们处理的毕竟完善的nc产品,一般呈现未timexlatxlon的维度,且lon和lat都是规则的网格,我们可以方便的使用xarray.sel()选择合适的区域进行切片。但是,部分nc产品比如卫星轨道或者模式输出的数据&…...
【C#】vs2022 .net8
Visual Studio 2022 IDE - 适用于软件开发人员的编程工具 (microsoft.com) 更新就会出现...
【华为杯】第二十一届中国研究生数学建模竞赛
“华为杯”第二十一届中国研究生数学建模竞赛即将开始,梦想科研社给大家整理一些比赛信息,在正式开赛后,我们也会持续分享一些课题的分析以及代码,有需要的可以联系我们获取资料信息哦 一、时间节点 1.加密赛题开始下载时间&…...
首次开机android.intent.action.BOOT_COMPLETED开机广播发送慢的问题
1. 背景 做过android开发的同学相信一定做个这种逻辑:app接收BOOT_COMPLETED开机广播,自启动,或者收到广播做一些事情。目前在我们的项目上遇到首次开机,BOOT_COMPLETED开机广播发送慢的问题。接下来分享记录下如何定位这类问题。 2. 分析过…...
通信工程学习:什么是OLT光线路终端
OLT:光线路终端 OLT(Optical Line Terminal,光线路终端)是光纤通信系统中的核心局端设备,特别是在无源光网络(Passive Optical Network, PON)架构中扮演着至关重要的角色。以下是关于OLT光线路终…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
CSS | transition 和 transform的用处和区别
省流总结: transform用于变换/变形,transition是动画控制器 transform 用来对元素进行变形,常见的操作如下,它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
Xela矩阵三轴触觉传感器的工作原理解析与应用场景
Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知,帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量,能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度,还为机器人、医疗设备和制造业的智…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
