【Qt+FFmpeg】解码播放本地视频(一)

目录

一、解码线程的设计

1.解码初始化

(1)注册所有组件

(2)打开本地片源

 (3) 查找视频流

 (4)查找对应的解码器

 (5)打开解码器​

 2.run函数循环解码

(1)图片转成RGB格式

(2)发送图片信号

(3)发送一张图片计数+1

(4)延时操作——1s显示25帧图片

二、播放窗口类的设计

1.创建窗口

2.创建解码线程

3.开启解码线程

4.绑定信号槽接收图片信号

5.界面刷新显示

一、解码线程的设计

1.解码初始化

(1)注册所有组件

av_register_all();//第一步注册所有组件

(2)打开本地片源

 (3) 查找视频流

 (4)查找对应的解码器

 (5)打开解码器

 以上就是解码的初始化工作,附上完整源码

#include "video.h"

video::video(QString filename)
{
    this->m_stop=false;
    this->current_frame_index = 0;
    openVideoFile(filename);
}
void video::openVideoFile(QString filename)
{
    qDebug()<<"1.注册所有组件";
    av_register_all();//第一步注册所有组件
    qDebug()<<"2.打开视频输入文件";
    //给avformat_context开空间
    avformat_context = avformat_alloc_context();
    //avformat_open_input参数一:视频文件格式上下文->AVFormatContext->包含了视频信息(视频名、时长、大小等)
    //参数二:文件路径
    qDebug()<<"打开"<<filename<<"视频文件进行播放";
    int avformat_open_result = avformat_open_input(&avformat_context,filename.toStdString().c_str(),NULL,NULL);
    if (avformat_open_result != 0)
    {
        //获取异常信息
        char* error_info = new char[32];
        av_strerror(avformat_open_result, error_info, 100);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    };
    qDebug()<<"3.查找视频流信息";
    //参数一:视频封装格式上下文->AVFormatContext; 参数二:配置,可以不设置
    //返回值:>=0找到,否则失败
    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, 100);
        qDebug()<<QString("异常信息 %1").arg(error_info);//打印异常信息
    }
    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("没有找到视频流");
    }
    qDebug()<<"4.查找解码器";
    //第一点:获取当前解码器是属于什么类型解码器->找到了视频流
    //第二点:根据视频流->查找到视频解码器上下文->视频压缩数据
    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, 100);
        qDebug()<<QString("异常信息 %1").arg(error_info);
    }

    qDebug()<<"视频详细信息输出";
    qDebug()<<"视频名称"<<avformat_context->filename;
    qDebug()<<"视频时长/s"<<avformat_context->duration/1000000;
    qDebug()<<QString("视频分辨率%1x%2").arg(avcodec_context->width).arg(avcodec_context->height);

    //自动打印输入或输出的详细信息
    av_dump_format(avformat_context,0, filename.toStdString().c_str(),0);
}

 2.run函数循环解码

首先要做的是初始化包数据、输入输出帧数据、缓冲区、转换图片格式参数准备等

然后循环读取视频帧,进行循环解码

(1)图片转成RGB格式

(2)发送图片信号

(3)发送一张图片计数+1

(4)延时操作——1s显示25帧图片

void video::run()
{   
    qDebug()<<"6.循环解码";
    //packet开空间
    av_packet = (AVPacket*)av_malloc(sizeof(AVPacket));

    //输入->环境一帧数据->缓冲区->类似于一张图
    pFramein = av_frame_alloc();
    //输出->帧数据->数据格式->RGB
    pFrameRGB = av_frame_alloc();
    //只有指定了AVFrame的像素格式、画面大小才能真正分配内存
    //缓冲区分配内存
    pOutbuffer = (uint8_t *)av_malloc(avpicture_get_size(
                                          AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height));
    //初始化缓冲区 类似于memset
    avpicture_fill((AVPicture *)pFrameRGB, pOutbuffer,
                   AV_PIX_FMT_RGB32, avcodec_context->width, avcodec_context->height);

    //解码的状态类型(0:表示解码完毕,非0:表示正在解码)
    //int current_frame_index = 0;

    //用于转码(缩放)的参数,转之前的宽高,转之后的宽高,格式等
    //准备一个视频像素数据格式上下文
    //参数一:输入帧数据宽
    //参数二:输入帧数据高
    //参数三:输入帧数据格式
    //参数四:输出帧数据宽
    //参数五:输出帧数据高
    //参数六:输出帧数据格式->AV_PIX_FMT_RGB32
    //参数七:视频像素数据格式转换算法类型
    //参数八:字节对齐类型(C/C++里面)->提高读取效率
    SwsContext* pSwsContext = sws_getContext(avcodec_context->width,
                                             avcodec_context->height,
                                             avcodec_context->pix_fmt,
                                             avcodec_context->width,
                                             avcodec_context->height,
                                             AV_PIX_FMT_RGB32,
                                             SWS_BICUBIC,NULL,NULL,NULL);

    int ret=0;
    while (m_stop == false)
    {
        //>=0:说明有数据,继续读取   <0:说明读取完毕,结束
        //从视频文件上下文中读取包--- 有数据就一直读取
        if (av_read_frame(avformat_context,av_packet) >= 0)
        {
            //解码什么类型流(视频流、音频流、字幕流等等...)
            if (av_packet->stream_index == av_stream_index)
            {
                //发送一个包数据进行解码
                avcodec_send_packet(avcodec_context, av_packet);
                //接收一个包数据,解压成一帧
                ret = avcodec_receive_frame(avcodec_context,pFramein);
                if (ret == 0)
                {
                    //图片格式的转换  输入 输出
                    sws_scale(pSwsContext, (const unsigned char* const*)pFramein->data, pFramein->linesize, 0, avcodec_context->height,
                              pFrameRGB->data,  pFrameRGB->linesize);

                    QImage  *tmpImg  = new QImage((uchar *)pOutbuffer, avcodec_context->width,
                                                  avcodec_context->height,QImage::Format_RGB32);

                    QImage image=tmpImg->copy();
                    qDebug()<<"接收图片信号"<<image;
                    //计数第几张图片
                    current_frame_index++;
                    //发送图片信号
                    emit sigGetOneFrame(image);   
                    //emit SendOneData(current_frame_index);
                    //延时操作  1秒显示25帧--1000/25=40
                    QThread::msleep(timeSpeed);
                    //获取的视频信息
                    qDebug()<<QString("当前遍历第 %1 帧").arg(current_frame_index);
                }
            }
        }
        else
        {
            qDebug()<<"播放完毕";
            this->current_frame_index=0;//清空计数
            this->stop();
        }
        av_free_packet(av_packet);
    }

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

}
void video::stop()
{
    m_stop = true;
}

头文件

#ifndef VIDEO_H
#define VIDEO_H
#include <QString>
#include <QDebug>
#include "mythread.h"
#include <QImage>

//当前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>
#include "libswresample/swresample.h"
}
class video : public myThread//自定义线程类,可以直接继承QThread
{
    Q_OBJECT
public:
    video(QString filename);
    ~video();
    //打开视频文件
    void openVideoFile(QString filename);
    void run();//重写run函数,线程执行任务
    void stop();
    //定义停止的标志位
    bool m_stop;
private:
    //视频文件上下文格式
    AVFormatContext* avformat_context;
    //编解码器上下文格式
    AVCodecContext* avcodec_context;
    //解码器上下文格式
    AVCodec* avcodec;
    //数据包
    AVPacket* av_packet;
    //保存视频流的索引
    int av_stream_index;
    //输入和输出的帧数据
    AVFrame *pFramein;
    AVFrame *pFrameRGB;
    uint8_t * pOutbuffer;
    int ret;
    QImage m_image;
    //计算第几帧图片
    int current_frame_index;
signals:   
    //发送解码得到的一帧帧像素数据--保存的图片信息
    void sigGetOneFrame(QImage image);
};


#endif // VIDEO_H

二、播放窗口类的设计

构造函数中

1.创建窗口

2.创建解码线程

3.开启解码线程

4.绑定信号槽接收图片信号

  

5.界面刷新显示

绘图事件

playVideoWidget::playVideoWidget(QString filename)
{

    this->resize(700,468);
    this->setWindowTitle("播放器");
    this->filename=filename;

//创建解码线程
    toH264=new video(filename);
    connect(toH264,SIGNAL(sigGetOneFrame(QImage)),this,SLOT(recviceSlots(QImage)));//接收图片信号槽
    //启动线程
    toH264->start();
}

void playVideoWidget::recviceSlots(QImage imge)
{
    //接收解码线程发送的一帧图片数据
    qDebug()<<"recive imge";
    this->image=imge;
    //更新界面
    this->update();
}

void playVideoWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    if(!this->image.isNull())//如果图片不为空
    {
        painter.drawImage(QRect(0,0,this->width(),this->height()),this->image);
    }
}

效果

 这样就可以播放本地视频了

感谢观看!!!!

以上就是全部内容,如果对您有帮助,欢迎点赞评论,或者发现有哪里写错的,欢迎指正!

  • 7
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

logani

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值