QT中使用FFmpeg视频解码

视频解码

流程图

步骤 

加入需要的头文件

1,注册所有组件 av_register_all()

2,打开视频文件 avformat_open_input(),(判断是否打开成功)

3,取视频相关信息:视频码流,音频码流,文字码流

4,查找流信息: avformat_find_stream_infp()

5,从查找到的流信息中找到视频码流信息

6,找到解码器 avcodec_find_decoder()(判断是否找到)

7,打开解码器 avcodec_open2()(判断是否打开成功)

8,读取码流中的一帧码流数据 av_read_frame()

9,解码读到的这一帧码流数据,得到一帧的像素数据,YUV,RGB 进行保存

Avcodec_decode_video2()

10,重复 8,9,动作,直到视频的所有帧都处理完

11,关闭解码器 avcode_close()

12,关闭视频文件 avcode_clode_input()

解码类package_decoder代码

package_decoder.h

#ifndef PACKAGE_DECODER_H
#define PACKAGE_DECODER_H
 
#include <QDebug>
#include <QImage>
#include <QThread>
//#include "package_ecoder.h"
 
 
extern "C"
{
    #include <libavcodec/avcodec.h>
    #include <libavdevice/avdevice.h>
    #include <libavformat/avformat.h>
    #include <libswresample/swresample.h>
    #include <libavfilter/avfilter.h>
    #include <libavutil/adler32.h>
    #include <libswscale/swscale.h>
}
 
 
class package_decoder:public QThread
{
    Q_OBJECT
public:
    package_decoder();
 
    int opendecoderbycideo(const char *filename);
    void videodecode();
    void videodecodergb();
    void setstate(int state);
    void setspeed(int speed);
 
     void run();
 
 
private:
    AVFormatContext *forcontent;
    const char *filename;
    int videoType;
    AVCodecContext *codec;
    AVCodec *decoder;
    AVPacket *pkt;
    FILE *fp;
    FILE *fpyuv;
    int size;
    AVFrame *pictureyuv;
    AVFrame *picturergb;
    AVFrame *picture;
    AVInputFormat *fmt;
    SwsContext * rgbsws;
    SwsContext * sws;
 
    //package_ecoder *ecoder;
 
    int state;
    int speed;
signals:
    void sendimage(QImage img);
    void sendyuv(AVFrame * yuv);
    void sendend();
 
};
 
#endif // PACKAGE_DECODER_H

package_decoder.cpp

#include "package_decoder.h"
 
 
package_decoder::package_decoder()
{
    av_register_all();
    avdevice_register_all();//注册摄像头
    forcontent =avformat_alloc_context();//分配内存空间
 
    pkt = (AVPacket *)malloc(sizeof (AVPacket));//分配内存空间
    pictureyuv=nullptr;
    picturergb=nullptr;//保存rgb像素文件
    picture=nullptr;//保存包含损坏数据的像素文件
    pictureyuv =av_frame_alloc();
    picture =av_frame_alloc();//开空间
    picturergb =av_frame_alloc();
    rgbsws =nullptr;
    sws=nullptr;
    //ecoder = new package_ecoder;
    state=1;
    speed=1;
 
}
 
/*
函数名:opendecoderbycideo
返回值: 如果返回值为1,执行成功;返回-1,执行失败
*/
int package_decoder::opendecoderbycideo(const char *filename)
{
    int sign=-1;
 
    fmt= av_find_input_format("dshow");//推流(windows系统)
    //打开视频文件
    int res=avformat_open_input(&forcontent,filename,fmt,nullptr);
    //int res=avformat_open_input(&forcontent,filename,nullptr,nullptr);
    if(res!=0)
    {
        return -1;
    }
    //查找流数据
    res=avformat_find_stream_info(forcontent,nullptr);
    if(res<0)
    {
        return -1;
    }
    //从查找到的流信息中找到视频码流信息
    videoType=-1;//标记视频流索引
    for(int i=0;i<forcontent->nb_streams;i++)
    {
        if(forcontent->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO)//视频流
        {
            videoType=i;
            break;
        }
    }
    if(videoType==-1)
    {
        return -1;
    }
    //根据视频流信息中的编解码器id去找合适的解码器
    codec=forcontent->streams[videoType]->codec;
    decoder = avcodec_find_decoder(codec->codec_id);
    //判断是否找到对应的解码器
    if(decoder==nullptr)//没找到解码器
    {
        return -1;
    }
    //找到解码器后,打开解码器
    res=avcodec_open2(codec,decoder,nullptr);
    if(res==0)
    {
        sign=1;
    }
    else
    {
        return -1;
    }
    return sign;
 
}
 
//
 
 
/*
解码
*/
void package_decoder::videodecode()
{
    size =codec->width*codec->height;//计算一帧码流数据的大小
    av_new_packet(pkt,size);//开空间,用于存储一帧码流数据
    //像素数据
    pictureyuv->width=codec->width;
    pictureyuv->height=codec->height;
    pictureyuv->format=codec->pix_fmt;//格式设置
 
    //获取到一帧图像大小(yuv)
    int numByte=avpicture_get_size(AV_PIX_FMT_YUV420P,codec->width,codec->height);
    uint8_t * buffer=(uint8_t *)av_malloc(numByte *sizeof (uint8_t));//分配内存空间存像素数据
    //像素数据填充到AVFvame
    avpicture_fill((AVPicture *)pictureyuv,buffer,AV_PIX_FMT_YUV420P,codec->width,codec->height);
    //转换的规则设置(剔除压缩后的坏数据)
    sws=sws_getContext(codec->width,codec->height,codec->pix_fmt,codec->width,codec->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,nullptr,nullptr,nullptr);
 
    //编码前准备
    //ecoder->condeInit(codec->width,codec->height);
    int num=0;
    while(av_read_frame(forcontent,pkt)==0)//读到了一帧
    {
        if(pkt->stream_index==videoType)//视频流
        {
            fwrite(pkt->data,1,pkt->size,fp);//保存码流数据
            int got_picture=-1;
            avcodec_decode_video2(codec,picture,&got_picture,pkt);
            if(got_picture!=0)//解码得到了数据
            {
                num++;
                if(num==100)
                {
                    while(1)
                    {
 
                    }
                }
                //进行损坏数据的删除
                sws_scale(sws,picture->data,picture->linesize,0,picture->height,pictureyuv->data,pictureyuv->linesize);
 
 
                fwrite(pictureyuv->data[0],1,size,fpyuv);
                fwrite(pictureyuv->data[1],1,size/4,fpyuv);
                fwrite(pictureyuv->data[2],1,size/4,fpyuv);
                //ecoder->codecFrame(pictureyuv);
 
 
            }
 
 
        }
        av_packet_unref(pkt);//清空
    }
    //写入尾巴帧
   // ecoder->writeEnd();
   qDebug()<<"写入成功";
   fclose(fp);
   fclose(fpyuv);
}
 
void package_decoder::videodecodergb()
{
 
    size =codec->width*codec->height;//计算一帧码流数据的大小
    av_new_packet(pkt,size);//开空间,用于存储一帧码流数据
    //像素数据
    picturergb->width=codec->width;
    picturergb->height=codec->height;
    picturergb->format=codec->pix_fmt;//格式设置
 
    pictureyuv->width=codec->width;
    pictureyuv->height=codec->height;
    pictureyuv->format=codec->pix_fmt;//格式设置
 
    int numrgb=avpicture_get_size(AV_PIX_FMT_RGB32,codec->width,codec->height);
    uint8_t * rgbbuffer=(uint8_t *)av_malloc(numrgb *sizeof (uint8_t));//分配内存空间存像素数据
    //像素数据填充到AVFvame
    avpicture_fill((AVPicture *)picturergb,rgbbuffer,AV_PIX_FMT_RGB32,codec->width,codec->height);
    //转换的规则设置(剔除压缩后的坏数据)
    rgbsws=sws_getContext(codec->width,codec->height,codec->pix_fmt,codec->width,codec->height,AV_PIX_FMT_RGB32,SWS_BICUBIC,nullptr,nullptr,nullptr);
 
    //yuv//
    //像素数据
    //获取到一帧图像大小(yuv)
    int numByte=avpicture_get_size(AV_PIX_FMT_YUV420P,codec->width,codec->height);
    uint8_t * buffer=(uint8_t *)av_malloc(numByte *sizeof (uint8_t));//分配内存空间存像素数据
    //像素数据填充到AVFvame
    avpicture_fill((AVPicture *)pictureyuv,buffer,AV_PIX_FMT_YUV420P,codec->width,codec->height);
    //转换的规则设置(剔除压缩后的坏数据)
    sws=sws_getContext(codec->width,codec->height,codec->pix_fmt,codec->width,codec->height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,nullptr,nullptr,nullptr);
 
    //编码前准备
    //ecoder->condeInit(codec->width,codec->height);
 
 
    int num=0;
    QString path;
        while(av_read_frame(forcontent,pkt)>=0)//读到了一帧
        {
 
            if(num==50)
            {
                emit sendend();
                //break;
            }
 
            //暂停播放
            while(this->state%2==0)
            {
 
            }
 
            if(pkt->stream_index==videoType)//视频流
            {
 
                int got_picture=-1;
                avcodec_decode_video2(codec,picture,&got_picture,pkt);
                if(got_picture!=0)//解码得到了数据
                {
                    //进行损坏数据的删除
 
                    sws_scale(sws,picture->data,picture->linesize,0,picture->height,pictureyuv->data,pictureyuv->linesize);
                    //编码
                   // ecoder->codecFrame(pictureyuv);
 
 
                     sws_scale(rgbsws,picture->data,picture->linesize,0,picture->height,picturergb->data,picturergb->linesize);
 
                    //每25帧保存一次图片
                            QImage img = QImage((uchar *)rgbbuffer,codec->width,codec->height,QImage::Format_RGB32);
                            //path = QString("RGB32/image%1.png").arg(num);
                            //img.save(path);
                            emit sendimage(img);
                            emit sendyuv(pictureyuv);
 
                            num++;
                            if(speed%3==1)//正常倍数
                            {
                                 msleep(50);
                            }
                            else if(speed%3==2)//1.5倍数
                            {
                                 msleep(50/1.5);
                            }
                            else if(speed%3==0)//2倍数
                            {
                                msleep(50/2);
                            }
 
 
 
 
 
                }
            }
 
            av_packet_unref(pkt);//清空
            av_frame_unref(picture);
        }
     //写入尾巴帧
    //ecoder->writeEnd();
    qDebug()<<"写入成功";
}
 
void package_decoder::setstate(int state)
{
    this->state=state;
}
 
void package_decoder::setspeed(int speed)
{
    this->speed=speed;
}
 
void package_decoder::run()
{
    const char *filename="video=USB2.0 HD UVC WebCam";//视频文件路径
    //const char *filename="Warcraft3_End.avi";
    //根据打开一个视频文件打开一个解码器
    this->opendecoderbycideo(filename);
    //解码
    this->videodecodergb();
 
}

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值