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

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

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

相关文章

Idea springboot项目热部署

使用 spring-boot-devtools spring-boot-devtools 是 Spring Boot 提供的开发工具模块&#xff0c;它可以自动检测到代码的变化并重启应用&#xff0c;实现热部署。 配置步骤&#xff1a; 添加依赖&#xff1a; 在项目的 pom.xml 中加入 spring-boot-devtools 依赖&#xff1…

Redis(主从复制、哨兵模式、集群)概述及部署测试

目录 一、Redis 主从复制 1.1、Redis 主从复制概念 1.2、主从复制的作用 1.3、主从复制流程 1.4、搭建Redis 主从复制 二、Redis 哨兵模式 2.1、Redis 哨兵模式概念 2.2、哨兵模式原理 2.3、哨兵模式的作用 2.4、哨兵模式的结构 2.5、故障转移机制 2.6、主节点的选…

Node.js 多版本安装与切换指南

一.使用nvm的方法 1. 卸载nodejs 如果你的电脑有安装nodejs&#xff0c;需要先卸载掉&#xff1b;若没有请直接下一步。 2. 前往官网下载nvm nvm&#xff1a;一个nodejs版本管理工具&#xff01; 官网地址&#xff1a;nvm文档手册 - nvm是一个nodejs版本管理工具 - nvm中文…

MySQL详解:数据类型、约束

MySQL 1. 数据类型1.1 数值类型1.1.1 bit 位类型1.1.2 整数数据类型1.1.3 小数类型floatdecimal 1.2 字符类型1.2.1 char1.2.2 varchar 可变长字符串1.2.3 日期和时间类型datedatetimetimestamp 1.2.4 enum1.2.5 set集合查询函数 find_in_set 2. 表的约束2.1 NULL 空属性2.2 默…

基于鸿蒙API10的RTSP播放器(七:亮度调节功能测试)

目标&#xff1a; 当我的手指在设备左方进行上下移动的时候&#xff0c;可以进行屏幕亮度的调节&#xff0c;在调节的同时&#xff0c;有实时的调节进度条显示 步骤&#xff1a; 界面逻辑&#xff1a;使用Stack() 组件&#xff0c;完成音量图标和进度条的组合显示&#xff0c…

Linux echo,printf 命令

参考资料 【Linux】ハイフンをいっぱい出したかっただけなのに【printfコマンド】 目录 一. echo命令1.1 -n 选项1.2 -e 选项1.3 配合扩展实现批量换行输出1.3.1 xargs -n 11.3.2 tr \n1.3.3 xargs printf "%s\n"1.4 ANSI转义序列1.5 彩色文本输出 二. printf 命令…

C# System.BadImageFormatException问题及解决

C# System.BadImageFormatException问题 出现System.BadImageFormatException 异常有两种情况&#xff1a;程序目标平台不一致&引用dll文件的系统平台不一致。 异常参考 BadImageFormatException 程序目标平台不一致&#xff1a; 项目>属性>生成&#xff1a;x86 …

【吊打面试官系列-Redis面试题】使用过 Redis 做异步队列么,你是怎么用的?

大家好&#xff0c;我是锋哥。今天分享关于【使用过 Redis 做异步队列么&#xff0c;你是怎么用的&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 使用过 Redis 做异步队列么&#xff0c;你是怎么用的&#xff1f; 一般使用 list 结构作为队列&#xff0c;rpus…

【Redis】redis5种数据类型(list)

目录 基本介绍 命令 LPUSH LPUSHX RPUSH RPUSHX LRANGE LPOP RPOP LINDEX LINSERT LLEN LREM LTRIM LSET 阻塞版本的命令 BLPOP 内部编码 基本介绍 list相当于c的双端队列deque 区分获取和删除的区别 lindex能获取到元素的值lrem也能返回被删除元素的值 命…

828华为云征文 | 华为云FlexusX实例下的Kafka集群部署实践与性能优化

前言 华为云FlexusX实例&#xff0c;以创新的柔性算力技术&#xff0c;为Kafka集群部署带来前所未有的性能飞跃。其灵活的CPU与内存配比&#xff0c;结合智能调度与加速技术&#xff0c;让Kafka在高并发场景下依然游刃有余。在828华为云企业上云节期间&#xff0c;FlexusX实例携…

手机玩机常识____展讯芯片刷机平台ResearchDownload的一些基本常识与问题解决

展讯ResearchDownload工具 展讯芯片的刷机工具--ResearchDownload下载工具"是一款专为用户设计的高效、便捷的下载管理软件&#xff0c;它能够帮助用户快速、稳定地从互联网上获取各种文件。这款工具以其强大的功能和良好的用户体验&#xff0c;在众多展讯芯片下载工具中脱…

git-describe获取不到新创建的标签

一、问题描述 1、新建的分支 2、git-describe 失败 二、查询资料 &#xff08;1&#xff09;git-describe - 根据可用的ref给对象一个人类可读的名称 &#xff08;2&#xff09;该命令查找可从提交访问的最新标记。如果标记指向提交&#xff0c;则仅显示标记。否则&#xf…

S-Procedure的基本形式及使用

理论 Lemma 1. ( S- Procedure[ 34] ) : Define the quadratic func- \textbf{Lemma 1. ( S- Procedure[ 34] ) : Define the quadratic func- } Lemma 1. ( S- Procedure[ 34] ) : Define the quadratic func- tions w.r.t. x ∈ C M 1 \mathbf{x}\in\mathbb{C}^M\times1 x…

el-input设置type=‘number‘和v-model.number的区别

el-input设置typenumber’与设置.number修饰符的区别 1. 设置type‘number’ 使用el-input时想收集数字类型的数据&#xff0c;我们首先会想到typenumber&#xff0c;设置完type为number时会限制我们输入的内容只能为数字&#xff0c;不能为字符/汉字等非数字类型的数值&…

Leetcode面试经典150题-148.排序链表

题目比较简单&#xff0c;使用链表的归并排序 解法都在代码里&#xff0c;不懂就留言或者私信 合并链表部分没怎么加注释&#xff0c;时间实在是不充裕&#xff0c;看不懂的看一下这篇专门讲解合并链表的 Leetcode面试经典150题-21.合并两个有序链表-CSDN博客 /*** Definit…

Brave编译指南2024 Windows篇:安装Visual Studio 2022(二)

1.引言 在编译Brave浏览器之前&#xff0c;安装和配置合适的开发工具是至关重要的一步。Visual Studio 2022是编译Brave浏览器所需的重要开发环境&#xff0c;它提供了一整套工具和服务&#xff0c;以支持多种编程语言和技术。作为一款功能强大的集成开发环境&#xff08;IDE&…

【vue-media-upload】一个好用的上传图片的组件,注意事项

一、问题 media 的saved 数组中的图片使用的是location 相对路径&#xff0c;但是我的业务需要直接根据图片链接展示图片&#xff0c;而且用的也不是location 相关源代码 <div v-for"(image, index) in savedMedia" :key"index" class"mu-image-…

测试ASP.NET Core的WebApi项目调用WebService

虚拟机中部署的匿名访问的WebService&#xff0c;支持简单的加减乘除操作。本文记录在WebApi中调用该WebService的方式。   VS2022创建WebApi项目&#xff0c;然后在解决方案资源管理器的Connected Services节点点右键&#xff0c;选择管理连接的服务菜单。 点击下图圈红处…

Anolis OS 8.8 CentOS8离线安装mysql-8.0.9

下载mysql安装包&#xff1a; mysql下载地址 在Linux系统中&#xff0c;mysql的安装包除了要区分系统和cpu架构之外&#xff0c;还区分安装方式&#xff0c;下载不同的包&#xff0c;安装方式也完全不一样&#xff0c;安装完成后的效果也完全不一样。 我之前下载的包按照官方…

QT Layout布局,隐藏其中的某些部件后,不影响原来的布局

最近在工作时&#xff0c;被要求&#xff0c;需要将布局中的某些部件隐藏后&#xff0c;但不能影响原来的布局。 现在记录解决方案&#xff01; 一、水平布局&#xff08;垂直布局一样&#xff09; ui中的布局 效果&#xff1a; 按钮可以任意隐藏&#xff0c;都不影响其中布…