Qt与FFMPEG联合开发--封装解码音视频线程

目录

一:界面显示与解码的同步进行--线程

二:测试

三:因为路径问题导致的图片不显示

四:QString字符串分割函数---获取文件名称


一:界面显示与解码的同步进行--线程

解码利用线程的方式进行改造

videodecode.h .cpp

#ifndef VIDEODECODE_H
#define VIDEODECODE_H

#include <QObject>
#include<QThread>//使用线程

//当前C++兼容C语言
extern "C"
{
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
//avformat:封装格式处理
#include <libavformat/avformat.h>
//swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
//avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
//avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
}

class videoDecodeThread : public QThread//利用线程基类
{
    Q_OBJECT
public:
    //    explicit videoDecodeThread(QObject *parent = 0);
    videoDecodeThread(QString videoname);
    QString videoname;

    //视频文件上下文格式
    AVFormatContext* avformat_context;
    //编解码器上下文格式
    AVCodecContext* avcodec_context;
    //解码器上下文格式
    AVCodec* avcodec;

    AVPacket* av_packet;
    //流的标志位
    int av_stream_index;

    //线程函数的引入
    void run();
    virtual ~videoDecodeThread();
    //定义停止的标志位
    bool m_stop;



signals:

public slots:
};

#endif // VIDEODECODE_H
#include "videodecode.h"
#include<QDebug>
#include<QCoreApplication>
#include<QThread>

//解码初始化操作
//1.注册所有组件
//2.打开视频输入文件
//3.查找视频流信息
//4.查找解码器
//5.打开解码器

//videoDecode::videoDecode(QObject *parent) : QObject(parent)
//{
//}

videoDecodeThread::videoDecodeThread(QString videoname)
{
    //初始线程未启动
    this->m_stop = false;
    qDebug()<<"1.注册所有组件";
    av_register_all();

    qDebug()<<"2.打开视频输入文件";
    QString filename = QCoreApplication::applicationDirPath();
    qDebug()<<"获取程序运行目录 "<<filename;
    QString cinputFilePath = "test.avi";  //本地视频文件放入程序运行目录
    avformat_context = avformat_alloc_context();
    //参数一:封装格式上下文->AVFormatContext->包含了视频信息(视频格式、大小等等...)
    //参数二:打开文件(入口文件)->url
    qDebug()<<"打开"<<videoname<<"视频文件进行播放";
    int avformat_open_result = avformat_open_input(&avformat_context,videoname.toStdString().c_str(),NULL,NULL);
    if (avformat_open_result != 0)
    {
        //获取异常信息
        char* error_info = new char[32];
        av_strerror(avformat_open_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);

    };

    qDebug()<<"3.查找视频流信息";
    //参数一:封装格式上下文->AVFormatContext
    //参数二:配置
    //返回值:0>=返回OK,否则失败
    int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context, NULL);
    if (avformat_find_stream_info_result < 0){
        //获取失败
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    }

    qDebug()<<"4.查找解码器";
    //第一点:获取当前解码器是属于什么类型解码器->找到了视频流
    //音频解码器、视频解码器、字幕解码器等等...
    //获取视频解码器流引用
    av_stream_index = -1;
    for (int i = 0; i < avformat_context->nb_streams; ++i) {
        //循环遍历每一流
        //视频流、音频流、字幕流等等...
        if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            //找到了
            av_stream_index = i;
            break;
        }
    }
    if (av_stream_index == -1)
    {
        qDebug()<<QString("没有找到视频流");
    }
    //第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
    //编解码器上下文
    avcodec_context = avformat_context->streams[av_stream_index]->codec;

    //第三点:根据解码器上下文->获取解码器ID
    avcodec = avcodec_find_decoder(avcodec_context->codec_id);
    if (avcodec == NULL)
    {
        qDebug()<<QString("没有找到视频解码器");
    }

    qDebug()<<"5.打开解码器";
    int avcodec_open2_result = avcodec_open2(avcodec_context,avcodec,NULL);
    if (avcodec_open2_result != 0)
    {
        char* error_info = new char[32];
        av_strerror(avformat_find_stream_info_result, error_info, 1024);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    }

    qDebug()<<"视频详细信息输出";
    //此函数自动打印输入或输出的详细信息
    av_dump_format(avformat_context, 0, cinputFilePath.toStdString().c_str(), 0);
    qDebug()<<"----------------解码准备工作完成-----------------";

}

void videoDecodeThread::run()
{
    qDebug()<<"----------------开始循环解码操作-----------------";

    qDebug()<<"6.循环解码";

    //读取帧数据换成到哪里->缓存到packet里面
    av_packet = (AVPacket*)av_malloc(sizeof(AVPacket));
    //解码的状态类型(0:表示解码完毕,非0:表示正在解码)
    int current_frame_index = 0;

    //>=0:说明有数据,继续读取   <0:说明读取完毕,结束
    //从视频文件上下文中读取包--- 有数据就一直读取
    while (av_read_frame(avformat_context,av_packet) >= 0)
    {
        //解码什么类型流(视频流、音频流、字幕流等等...)
        if (av_packet->stream_index == av_stream_index)
        {
            //遍历每一帧的信息进行打印
            current_frame_index++;
            //延时操作  1秒显示25帧--1000/25=40
            QThread::msleep(40);
            qDebug()<<QString("当前遍历第 %1 帧").arg(current_frame_index);
        }
    }
    qDebug()<<"7.关闭所有解码组件";
    av_packet_free(&av_packet);
    //关闭流
    avcodec_close(avcodec_context);
    avformat_free_context(avformat_context);

}

videoDecodeThread::~videoDecodeThread()
{
    qDebug()<<"7.关闭所有解码组件";
    av_packet_free(&av_packet);
    //关闭流
    avcodec_close(avcodec_context);
    avformat_free_context(avformat_context);

}

二:测试

    if(btnName == "儿童")
    {
        QMessageBox::question(NULL,"儿童",btn->text());

        QString cinputFilePath = "test.avi";
        videoDecodeThread *p = new videoDecodeThread(cinputFilePath);
        p->start();

    }

 

 

点击其他的按钮,找到对应的item项下的视频进行播放 

三:因为路径问题导致的图片不显示

    QString videopath = QDir::currentPath()+"/image";
    qDebug()<<videopath;
    QDir dir(videopath);//确定路径 从此dir路径下往下找

 

将image文件夹拷贝到bin文件目录下 

 

图片即可以正常显示

视频播放器中的视频图片尽量和片源名字保持一致

四:QString字符串分割函数---获取文件名称

void IndexWin::videoWidgetShowSlot(QListWidgetItem *item)
{
    qDebug()<<item->text()<<"界面创建";

    QStringList list = item->text().split(".");//QString字符串分割函数
    qDebug()<<list[0];
    //视频文件名称后缀:.avi
    QString cinputFilePath = list[0]+".avi";

//  解码线程的使用
    videoDecodeThread *p = new videoDecodeThread(cinputFilePath);
    //新建视频播放界面

//    通过设置按钮进行视频的播放
//    p->start();

}

 

注意点:视频文件名称后缀为.avi

 

若是没有根据工程目录下视频文件后缀名称命名---会导致程序的异常结束

    //视频文件名称后缀:.avi
    QString cinputFilePath = list[0]+".avi";

点击列表中的item进行解码操作---模拟视频播放器

猜你喜欢

转载自blog.csdn.net/m0_56051805/article/details/125053687