【FFMPEG基础(一)】解码源码

发布于:2024-07-11 ⋅ 阅读:(14) ⋅ 点赞:(0)

main函数

#include <QApplication>
#include "decodetorgb32.h"
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    DecodeToRGB32 toRGB32;
    int res=toRGB32.openVideo("../fileIn/Warcraft3_End.avi");
    if(res ==0)
    {
        if(toRGB32.findStream() ==0)
        {
            if(toRGB32.openDecoder()==0)
            {
                toRGB32.decodeRGB();
            }
        }
    }

    return a.exec();
}

decodetorgb32.h 文件

#ifndef DECODETORGB32_H
#define DECODETORGB32_H
#include <QString>
#include <QDebug>
#include <QImage>
#include <stdio.h>
extern "C"
{
    #include "libavcodec/avcodec.h"             //编解码库
    #include "libavdevice/avdevice.h"           //输入给输出设备库,读取摄像头
    #include "libavfilter/avfilter.h"           //音视频滤镜库;进行音视频处理和编辑
    #include "libavformat/avformat.h"           //文件格式和协议库
    #include "libavutil/avutil.h"               //音视频处理
    #include "libswresample/swresample.h"       //音频重采样
    #include "libswscale/swscale.h"             //图像进行格式转换

}

class DecodeToRGB32
{
public:
    DecodeToRGB32();
    /** 1、注册组件 2、打开视频
     * @brief openVideo
     * @param filename
     * @return  0-success  else error
     */
    int openVideo(QString filename);
    /** 3、查找流媒体数据  4、查找视频流
     * @brief findStream
     * @return 0-查找到视频流数据 1-查找流媒体数据  <0无流媒体数据
     */
    int findStream();
    /** 5、查找解码器  6、打开解码器
     * @brief openDecoder
     * @return
     */
    int openDecoder();
    /** 读取pkt
     * @brief decodeRGB
     */
    void decodeRGB();
private:
    AVFormatContext* pFormatContext;    //封装格式上下文结构体
    int video_index;                    //视频流所在的下标
    AVCodecContext *pCodecContext;      //编解码器上下文结构体
    AVCodec*deocder;                    //解码器
    AVPacket * pkt;                     //码流数据
    AVFrame * picture,* pictureRGB,*pictureYUV;
    //1:不纯净的携带脏数据的像素数据,2:存储转置后的纯净的RGB像素数据 ,3:存储转置后的纯净的YUV像素数据

};

#endif // DECODETORGB32_H

decodetorgb32 .cpp文件

#include "decodetorgb32.h"

DecodeToRGB32::DecodeToRGB32()
{

}
//1、注册组件 2、打开视频
int DecodeToRGB32::openVideo(QString filename)
{

    av_register_all();//注册所用组件
    //全局结构体开空间
    this->pFormatContext=avformat_alloc_context();
    //打开输入视频文件
    int res=avformat_open_input(&this->pFormatContext,filename.toUtf8(),nullptr,nullptr);
    if(res!=0)
    {
        qDebug()<<"open_input fail"<<res;//不等于0,文件打开失败
    }
    return res;
}
//3、查找流媒体数据  4、查找视频流
int DecodeToRGB32::findStream()
{
    //1.查找流媒体数据
    int res= avformat_find_stream_info(this->pFormatContext,nullptr);
    if(res<0)
    {
        qDebug()<<"find_stream_info fail"<<res;//小于0查找失败
        return res;
    }
    //2.查看是否有视频流
    for (int i=0;i<this->pFormatContext->nb_streams;i++)//输入视频的AVStream的个数
    {//                                streams:输入视频的AVStream数组/codec:该流对应的AVCodecContext/编解码器类型
        if(AVMEDIA_TYPE_VIDEO==this->pFormatContext->streams [i]->codec->codec_type)
        {
            this->video_index=i;
            return 0;
        }
    }
    return res;
}
//查找解码器,打开解码器
int DecodeToRGB32::openDecoder()
{
    int res=-1;
    //编解码器上下文结构体
    this->pCodecContext=this->pFormatContext->streams[this->video_index]->codec;//编解码器的AVCodec
    this->deocder= avcodec_find_decoder(this->pCodecContext->codec_id);//查找解码器
    if(nullptr==this->deocder)
    {
        qDebug()<<"find_decoder fail"<<res;
        return 1;
    }
    //打开解码器
    res=avcodec_open2(this->pCodecContext,this->deocder,nullptr);
    if(res!=0)
    {
        qDebug()<<"find_decoder fail"<<res;
    }
    return res;//0 找到了并打开   <0未打开解码器
}

void DecodeToRGB32::decodeRGB()
{
    //7、准备数据

    this->pkt =(AVPacket*)malloc(sizeof(AVPacket));//存储一帧压缩编码数据
    int size =this->pCodecContext->width*this->pCodecContext->height;
    av_new_packet(this->pkt,size);
    //初始化像素数据
    this->picture  =av_frame_alloc();//创建一个存储解码的像素数结构体
    this->picture->width=this->pCodecContext->width;
    this->picture->height=this->pCodecContext->height;
    this->picture->format=this->pCodecContext->pix_fmt;
    this->pictureRGB=av_frame_alloc();
    this->pictureRGB->width=this->pCodecContext->width;
    this->pictureRGB->height=this->pCodecContext->height;
    this->pictureRGB->format=this->pCodecContext->pix_fmt;
    this->pictureYUV=av_frame_alloc();
    this->pictureYUV->width=this->pCodecContext->width;
    this->pictureYUV->height=this->pCodecContext->height;
    this->pictureYUV->format=this->pCodecContext->pix_fmt;
    //计算一帧RGB32的像素大小
    int imgByte=avpicture_get_size(AV_PIX_FMT_RGB32,this->pCodecContext->width,this->pCodecContext->height);
    //动态开空间
    uint8_t*buffer=(uint8_t*)malloc(imgByte*sizeof(uint8_t));
    //图片的数据填充
    avpicture_fill((AVPicture*)this->pictureRGB,buffer,AV_PIX_FMT_RGB32,
                   this->pCodecContext->width,this->pCodecContext->height);
    //制定转置的规则
    SwsContext*swContext=sws_getContext(this->pCodecContext->width,this->pCodecContext->height,this->pCodecContext->pix_fmt,
                   this->pCodecContext->width,this->pCodecContext->height,AV_PIX_FMT_RGB32,
                   SWS_BICUBIC,nullptr,nullptr,nullptr);
    /**准备YUV数据**********************/
    //计算一帧YUV420P的像素大小
    int imgByteYUV=avpicture_get_size(AV_PIX_FMT_YUV420P,this->pCodecContext->width,this->pCodecContext->height);
    //动态开空间
    uint8_t*bufferYUV=(uint8_t*)malloc(imgByte*sizeof(uint8_t));
    //图片的数据填充
    avpicture_fill((AVPicture*)this->pictureYUV,bufferYUV,AV_PIX_FMT_YUV420P,
                   this->pCodecContext->width,this->pCodecContext->height);
    //制定转置的规则
    SwsContext*swContextYUV=sws_getContext(this->pCodecContext->width,this->pCodecContext->height,this->pCodecContext->pix_fmt,
                   this->pCodecContext->width,this->pCodecContext->height,AV_PIX_FMT_YUV420P,
                   SWS_BICUBIC,nullptr,nullptr,nullptr);
    int num=0;
    FILE * pyuv=fopen("../fileOut/save.yuv","wb+");//二进制方式写入
    char path[256]={0};
    //8、读取码流数据、解封装    获取一帧压缩的码流数据
    while(av_read_frame(this->pFormatContext,this->pkt)==0)//0成功
    {
        //判断码流数据类型是视频流
        if(pkt->stream_index==this->video_index)
        {
            int got_num =-1;
            //参数一:编解码器上下文结构体  二;AVFrame* 像素数据     三:int*标志位  四:一帧压缩码流数据
            avcodec_decode_video2(this->pCodecContext,this->picture,&got_num,this->pkt);
            if(got_num !=0)
            {
                qDebug()<<"解码成功:"<<num;
                //剔除无效数据,获取纯净的RGB32数据
                /**
                  * parmam1:转置规则
                  * srcSlice 数据
                  * srcStride 每一行的数据
                  * srcSliceY 从第几行开始扫描
                  * srcSliceH 图片高度
                  * dst       目标数据
                  * dstStrde  一行的宽度
                 * @brief sws_scale
                 */
                //存储图片 -播放
                sws_scale(swContext,picture->data,picture->linesize,0,this->picture->height,
                          this->pictureRGB->data,this->pictureRGB->linesize);
                //用于存储 size更小
                sws_scale(swContextYUV,picture->data,picture->linesize,0,this->picture->height,
                          this->pictureYUV->data,this->pictureYUV->linesize);
                fwrite(this->pictureYUV->data[0],this->pCodecContext->width*this->pCodecContext->height,1,pyuv);//  1 -Y
                fwrite(this->pictureYUV->data[1],this->pCodecContext->width*this->pCodecContext->height/4,1,pyuv);//  1/4 -U
                fwrite(this->pictureYUV->data[2],this->pCodecContext->width*this->pCodecContext->height/4,1,pyuv);//  1/4 -V
                //pictureRGB 转储为QImage
                QImage img((uchar*)buffer,this->pictureRGB->width,this->pictureRGB->height,
                           QImage::Format_RGB32);
                sprintf(path,"../fileOut/image%d.png",num);
                img.save(path);
                num++;
                if(num>5) break;
            }
        }
    }
    qDebug()<<num;
    fclose(pyuv);
}