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.mzph.cn/pingmian/54417.shtml

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

相关文章

pthread_cond_signal 和pthread_cond_wait

0、pthread_join()函数作用&#xff1a; pthread_join() 函数会一直阻塞调用它的线程&#xff0c;直至目标线程执行结束&#xff08;接收到目标线程的返回值&#xff09;&#xff0c;阻塞状态才会解除。如果 pthread_join() 函数成功等到了目标线程执行结束&#xff08;成功获取…

运行 xxxxApplication 时出错。命令行过长。 通过 JAR 清单或通过类路径文件缩短命令行,然后重新运行。

一、问题描述 运行 xxxxApplication 时出错。命令行过长。 通过 JAR 清单或通过类路径文件缩短命令行&#xff0c;然后重新运行。 二、问题分析 在idea中&#xff0c;运行一个springboot项目&#xff0c;在使用大量的库和依赖的时候&#xff0c;会出现报错“命令行过长”&…

Java | Leetcode Java题解之第406题根据身高重建队列

题目&#xff1a; 题解&#xff1a; class Solution {public int[][] reconstructQueue(int[][] people) {Arrays.sort(people, new Comparator<int[]>() {public int compare(int[] person1, int[] person2) {if (person1[0] ! person2[0]) {return person2[0] - perso…

Java项目实战II基于Java+Spring Boot+MySQL的车辆管理系统(开发文档+源码+数据库)

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 "随着…

Arthas jvm(查看当前JVM的信息)

文章目录 二、命令列表2.1 jvm相关命令2.1.3 jvm&#xff08;查看当前JVM的信息&#xff09; 二、命令列表 2.1 jvm相关命令 2.1.3 jvm&#xff08;查看当前JVM的信息&#xff09; 基础语法&#xff1a; jvm [arthas18139]$ jvmRUNTIME …

【Delphi】通过 LiveBindings Designer 链接控件示例

本教程展示了如何使用 LiveBindings Designer 可视化地创建控件之间的 LiveBindings&#xff0c;以便创建只需很少或无需源代码的应用程序。 在本教程中&#xff0c;您将创建一个高清多设备应用程序&#xff0c;该应用程序使用 LiveBindings 绑定多个对象&#xff0c;以更改圆…

十七、RC振荡电路

振荡电路 1、振荡电路的组成、作用、起振的相位条件以及振荡电路起振和平衡幅度条件&#xff0c; 2、RC电路阻抗与频率、相位与频率的关系曲线; 3、RC振荡电路的相位条件分析和振荡频率

【yolo算法打架行为检测行人检测】

yolo打架行为检测 yolo算法打架行为检测yolo行人检测 yolo算法打架行为检测 数据集和模型YOLO算法打架行为检测数据集1万数据集 分两个类别&#xff1a;正常&#xff0c;打架行为&#xff1b; train: ../train/images val: ../valid/images test: ../test/images nc: 2 names…

一次RPC调用过程是怎么样的?

注册中心 RPC&#xff08;Remote Procedure Call&#xff09;翻译成中文就是 {远程过程调用}。RPC 框架起到的作用就是为了实现&#xff0c;调用远程方法时&#xff0c;能够做到和调用本地方法一样&#xff0c;让开发人员更专注于业务开发&#xff0c;不用去考虑网络编程等细节…

演示jvm锁存在的问题

文章目录 1、AlbumInfoApiController --》testLock()2、redis添加键值对3、AlbumInfoServiceImpl --》testLock() 没有加锁4、使用ab工具测试4.1、安装 ab 工具4.2、查看 redis 中的值 5、添加本地锁 synchronized6、集群情况下问题演示 jvm锁&#xff1a;synchronized lock 只…

尚品汇-H5移动端整合系统(五十五)

目录&#xff1a; &#xff08;1&#xff09;运行前端页面 &#xff08;2&#xff09;启动前端页面 &#xff08;3&#xff09;添加搜索分类接口 &#xff08;4&#xff09;购物车模块修改 &#xff08;5&#xff09;登录模块 &#xff08;6&#xff09;订单模块 &#…

Golang | Leetcode Golang题解之第423题从英文中重建数字

题目&#xff1a; 题解&#xff1a; func originalDigits(s string) string {c : map[rune]int{}for _, ch : range s {c[ch]}cnt : [10]int{}cnt[0] c[z]cnt[2] c[w]cnt[4] c[u]cnt[6] c[x]cnt[8] c[g]cnt[3] c[h] - cnt[8]cnt[5] c[f] - cnt[4]cnt[7] c[s] - cnt[6]…

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

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

[python]从零开始的PySide安装配置教程

一、PySide是什么&#xff1f; PySide 是 Qt for Python 项目的一部分&#xff0c;它提供了与 PyQt 类似的功能&#xff0c;使开发者能够使用 Python 编程语言来构建基于 Qt 的图形用户界面 (GUI) 应用程序。PySide 是由 Qt 公司官方维护的&#xff0c;而 PyQt 则是由第三方开发…

【Pyside】pycharm2024配置conda虚拟环境

知识拓展 Pycharm 是一个由 JetBrains 开发的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它主要用于 Python 编程语言的开发。Pycharm 提供了代码编辑、调试、版本控制、测试等多种功能&#xff0c;以提高 Python 开发者的效率。 Pycharm 与 Python 的关系 Pycharm 是…

【JavaEE】——多线程(join阻塞,计算,引用,状态)

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯&#xff0c;你们的点赞收藏是我前进最大的动力&#xff01;&#xff01;希望本文内容能够帮助到你&#xff01; 目录 一&#xff1a;join等待线程结束 1&#xff1a;知识回顾 2&#xff1a;join的功能就是“阻塞等待” …

java之斗地主部分功能的实现

今天我们要实现斗地主中发牌和洗牌这两个功能&#xff0c;该如何去实现呢&#xff1f; 1.创建牌类&#xff1a;52张牌每一张牌包含两个属性:牌的大小和牌的花色。 故我们优先创建一个牌的类(Card)&#xff1a;包含大小和花色。 public class Card { //单张牌的大小及类型/…

无人机+自组网:中继通信增强技术详解

无人机与自组网技术的结合&#xff0c;特别是通过中继通信增强技术&#xff0c;为无人机在复杂环境中的通信提供了稳定、高效、可靠的解决方案。以下是对该技术的详细解析&#xff1a; 一、无人机自组网技术概述 无人机自组网技术是一种利用无人机作为节点&#xff0c;通过无…

proteus仿真学习(1)

一&#xff0c;创建工程 一般选择默认模式&#xff0c;不配置pcb文件 可以选用芯片型号也可以不选 不选则从零开始布局&#xff0c;没有初始最小系统。选用则有初始最小系统以及基础的main函数 本次学习使用从零开始&#xff0c;不配置固件 二&#xff0c;上手软件 1.在元件…

6--SpringBootWeb案例(详解)

目录 环境搭建 部门管理 查询部门 接口文档 代码 删除部门 接口文档 代码 新增部门 接口文档 代码 已有前端&#xff0c;根据接口文档完成后端功能的开发 成品如下&#xff1a; 环境搭建 1. 准备数据库表 (dept 、 emp) -- 部门管理 create table dept( id int un…