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

news2024/9/22 13:44:05

音频解析

音频解码是指将压缩的音频数据转换为可以再生的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_OBJECT

public:
    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 context

    AvPacketQueue videoQueue;
    AvPacketQueue subtitleQueue;

    AVStream *videoStream;

    double videoClk;    // video frame timestamp

    AudioDecoder *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.coloradmin.cn/o/2127270.html

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

相关文章

k8s以及prometheus

#生成控制器文件并建立控制器 [rootk8s-master ~]# kubectl create deployment bwmis --image timinglee/myapp:v1 --replicas 2 --dry-runclient -o yaml > bwmis.yaml [rootk8s-master ~]# kubectl expose deployment bwmis --port 80 --target-port 80 --dry-runclient…

【深海王国】初中生也能画的电路板?目录合集

Hi٩(๑ ^ o ^ ๑)۶, 各位深海王国的同志们&#xff0c;早上下午晚上凌晨好呀~辛勤工作的你今天也辛苦啦 (o゜▽゜)o☆ 今天大都督为大家带来系列文章《初中生也能画的电路板》&#xff0c;帮你一周内快速入门PCB设计&#xff0c;手把手教你从元器件库添加、电路原理图绘制、…

如何解决在idea中的hadoop日志错误

在idea中操作hadoop的时候&#xff0c;每次运行代码都会发现有个日志错误&#xff0c;虽然不影响程序运行&#xff0c;但是无法打印日志。这是缺少依赖&#xff0c;和windows上缺少log4j的文件 解决方案&#xff1a; 1、导入slf4j依赖 2、导入hadoop中的log4j文件 1、从hado…

校园安全无小事,EasyCVR视频综合管理平台助力智慧校园视频监控系统全面升级

随着信息技术的飞速发展&#xff0c;智慧校园作为教育信息化的重要载体&#xff0c;正逐步成为提升校园安全管理、优化教育资源配置、增强师生互动体验的关键手段。其中&#xff0c;高效、智能的视频监控系统作为智慧校园不可或缺的一部分&#xff0c;扮演着至关重要的角色。TS…

Benvista PhotoZoom Pro / Classic 9.0.2 Win/mac + Plug-in中文破解版

对数码照片放大的质量不满意&#xff1f; 使用 BenVista PhotoZoom Classic9 调整图像大小&#xff0c;并通过我们屡获殊荣的独特 S-Spline 技术获得出色的效果&#xff01; 更高质量&#xff1a;PhotoZoom Classic9 专门用于在保持质量的同时放大照片。 该软件配备了 BenVista…

C++核心编程和桌面应用开发 第一天

目录 1.C的编程方式 2.双冒号::运算符 3.命名空间 3.1作用 3.2命名空间内的东西 3.3注意事项 4.using的用法 4.1using的声明 4.2using编译指令 5.C相较于C的增强 5.1全局变量检测增强 5.2函数检测增强 5.3类型转换检测增强 5.4结构体增强 5.5三目运算符增强 5.…

如何高效阅读论文呢???

论文题目《多模态数据融合研究综述》&#xff0c;介绍了多模态数据融合技术以及对齐方法&#xff0c;最后是展望以及未来的发展。 泛读这篇论文不在状态&#xff0c;求助前辈们该如何高效的阅读论文呢&#xff1f;&#xff08;我是刚入学的研一学生&#xff0c;对于读论文这块…

【H2O2|全栈】关于CSS(2)CSS基础(二)

目录 CSS基础知识 前言 准备工作 选择器的组合 盒模型 示例网页代码 后代选择器 亲代选择器 相邻兄弟选择器 后续兄弟选择器 多个元素选择器 通配符选择器 优先级 其他应用 伪类 锚链接的属性 列表的属性 list-style-type list-style-position list-style…

1.任务的创建与状态

1.什么叫现场? 就是程序暂停瞬间所有寄存器的值 2.如何"保存现场"? 就是把"所有寄存器"保存进哪里? 保存进"内存",这块内存被称为栈 3.栈来自哪里? a.在FreeRTOS里定义了一个大数组 b.FreeRTOS里的malloc函数,从这个大数组里分配内存 c.创…

MySQL一:在Ubuntu下安装MySQL数据库

目录 前言 1.查看操作系统版本 2.添加MySQLAPT源 2.1下载发布包 ​编辑 2.2安装发布包 3.安装MySQL 4.查看MySQL状态 5.开启自启动 ​编辑 6.登录MySQL 前言 操作系统版本为Ubuntu 22.04.6LTS 1.查看操作系统版本 lsb_release -a 2.添加MySQLAPT源 2.1下载发布包 M…

【深度学习】神经网络-怎么分清DNN、CNN、RNN?

怎么分清DNN、CNN、RNN&#xff1f; 最“大”的概念是人工神经网络&#xff08;Artificial Neural Network, ANN&#xff09;&#xff0c;它是较为广泛的术语&#xff0c;通常指的是一类模拟生物神经网络的数学模型&#xff0c;其中包括神经元、权重和连接。在这个术语下&#…

~数据分析知识分享~

近来有一些小伙伴咨询数据分析相关的一些知识内容 我收集了一些相关信息 在这里简单做一些分享和介绍&#xff0c;后续有相关的内容我也会持续的更新&#xff01;感谢大家的支持与陪伴&#xff01; 拆解问题一个原则四类方法 数据分析工程技术 数据分析四种类型 六个方向 分析…

[000-002-01].第03节:Linux系统下Oracle的安装与使用

2.1.Docker安装Oracle 在CentOS7中使用Docker安装Oracle&#xff1a; 1.安装Docker,详细请参考&#xff1a;https://blog.csdn.net/weixin_43783284/article/details/1211403682.拉取镜像&#xff1a; docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g3.下载…

Zookeeper下载与安装教程(国产化生产环境无联网服务器部署实操)

请放心观看&#xff0c;已在正式环境部署验证&#xff0c;流程无问题&#xff01; 此外&#xff0c;建议更换默认的2181端口&#xff0c;避免后期服务器漏扫麻烦&#xff0c;不少漏扫软件是扫描到默认端口后给出漏洞报错&#xff01;我所用服务器环境是麒麟银河aarch64系统&am…

Navicat 17 新特性 | 聚焦 MongoDB

随着 Navicat 17 的盛大发布&#xff0c;其一系列创新特性赢得了广大用户的热烈反响。它不仅在模型设计上实现了突破性优化&#xff0c;提升了查询与配置的效率&#xff0c;还大幅优化了用户界面的交互体验&#xff0c;原生支持国产平台与操作系统&#xff0c;同时增强 BI 能力…

UE场景根节点非静态 Landscape Root Component is Not Static 问题解决

错误截图 RenderQueue&#xff08;影片渲染队列&#xff09;渲染卡顿、黑屏&#xff0c;打开log查看到如下图所示报错&#xff1a; 错误原因 一种错误原因是你在操作sequence时无意中把landscape写入到了sequence中&#xff0c;有变换&#xff08;transform&#xff09;轨道…

MySql注入之Bypass总结

在真实的渗透测试环境中&#xff0c;经常会遇到被WAF拦截的情况&#xff0c;如果不知道怎么绕过WAF&#xff0c;想要继续渗透就很难了&#xff0c;因此&#xff0c;今天来总结一下 mysql 注入时&#xff0c;如何绕过WAF。 一、测试环境 集成环境: phpStudy数据库: mysql 5.7H…

第十八节:学习统一异常处理(自学Spring boot 3.x的第五天)

这节记录下如何通过AOP方式统一处理异常拦截。 第一步&#xff1a; 新建一个exception包&#xff0c;创建一个ExcetionHandler.java&#xff08;名字随意取&#xff09; package cn.wcyf.wcai.exception;import cn.wcyf.wcai.common.Result; import org.springframework.web…

双顶堆算法求中位数——从LeetCode题海中总结常见套路

前言:双顶堆算法求是非常经典的一种求中位数算法,是堆必知必会的经典知识点。具体来说,就是如何求出数据流中的中位数。数据流的特点是高速插入,数据会不断涌入结构中,那么也就面临着需要多次动态调整以获得中位数。我们需要保证最大效率的情况下求出中位数,当然不能全部…

Pycharm 输入三个引号没有自动生成函数(方法)注释

配置项路径&#xff1a;pycharm–>Settins–>Tools–>Python Integrated Tools–>Docstrings–>Docstrings format选择对应的工程&#xff0c;如果有多个工程的话将 Docstrings format 的值从 Plain 换成 reStructuredText