Qt结合FFmpeg解码本地视频生成.h264文件和.yuv文件

目录

1、视频解码流程分析

2、FFmpeg相关解码函数

3、创建一个类专门用来视频解码

.h文件

构造函数

打开视频文件

获取视频文件流信息并查找是否有视频流

查找视频流对应的解码器

打开解码器

循环读取每一帧并生成.h264、.yuv文件

关闭解码用到的组件

主函数测试

4、测试效果

运行效果

查看h264文件

查看yuv文件


一个视频包含有音频流、视频流、字幕流等信息,下面为视频流的解码过程以及代码实现。

1、视频解码流程分析

/*解码流程分析:
     * 1.注册所有组件
     * 2.打开文件
     * 3.获取文件信息
     * 4.看看有没有相关视频信息
     * 5.有视频信息,找他对应的解码器
     * 6.打开解码器
     * 7.获取一帧视频流(码流)信息
     * 8.读取,解码的动作
     * 9.得到像素数据
     * 重复7、8、9的动作,直到没有码流数据
     * 10.关闭解码器
     * 11.关闭视频流文件
     *
     * */

2、FFmpeg相关解码函数

av_register_all():注册所有组件。

avformat_open_input():打开输入视频文件。

avformat_find_stream_info():获取视频文件信息。

avcodec_find_decoder():查找解码器。

avcodec_open2():打开解码器。

av_read_frame():从输入文件读取一帧压缩数据。

avcodec_decode_video2():解码一帧压缩数据。

avcodec_close():关闭解码器。

avformat_close_input():关闭输入视频文件。

3、创建一个类专门用来视频解码

.h文件

#include 

extern "C"
{
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
}

class fDecode
{
public:
    fDecode();
    void openMvFile(QString mvPath);//打开视频文件
    void findVideoStream();//查找视频流
    void findDecoder();//查找对应的解码器
    void openDecoder();//打开解码器
    void readFrame();//读取每一帧
    void closeAll();//关闭用到的组件

private:
    AVFrame *in_frame,*out_frame;//保存解码后的像素数据

    int avcodec_index;//视频流所在输入视频的AVStream []数组的索引

    AVFormatContext* avformat_context;//封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息

    AVCodecContext* avcodec_context;//编码器上下文结构体,保存了视频(音频)编解码相关信息

    AVCodec* avcodec;//每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体
};

构造函数

fDecode::fDecode()
{    
    //注册组件
    avcodec_register_all();
    qDebug()<<"————注册所有组件————";
    qDebug()<<"注册成功!";
}

打开视频文件

void fDecode::openMvFile(QString mvPath)
{
    qDebug()<<"————打开视频文件————";
    avformat_context = avformat_alloc_context();//封装格式上下文结构体开空间
    int res = avformat_open_input(&avformat_context,mvPath.toStdString().c_str(),NULL,NULL);//return 0 on success
    if (res != 0)
    {
        qDebug()<<"打开失败";
        exit(0);
    }
    else
    {
        qDebug()<<"打开成功!";
    }
}

获取视频文件流信息并查找是否有视频流

void fDecode::findVideoStream()
{
    qDebug()<<"————获取文件信息————";
    this->avcodec_index = -1;
    int res = avformat_find_stream_info(avformat_context, NULL);//return >=0 if OK
    if (res < 0)
    {
        qDebug()<<"获取文件信息失败";
        exit(0);
    }
    else
    {
        qDebug()<<"获取文件信息成功!";
        qDebug()<<"视频时长为:"<duration/1000000.0<<"秒";
        qDebug()<<"视频平均混合码率为:"<bit_rate/1000<<"Kbps";
        qDebug()<<"————获取文件视频流信息————";
        //nb_streams :输入视频的AVStream 个数
        for (int i = 0; i < avformat_context->nb_streams; i++)//遍历流
        {
            //找到视频流
            if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                this->avcodec_index = i;
                qDebug()<<"获取文件视频流信息成功!";
                break;
            }
        }
        if (-1 == this->avcodec_index)
        {
            qDebug()<<"没有找到视频流";
            exit(0);
        }
    }
    return;
}

查找视频流对应的解码器

void fDecode::findDecoder()
{
    qDebug()<<"————查找对应的解码器————";

    //avcodec_index为视频流所在输入视频的AVStream []数组的索引,通过上一步查找视频流得到的
    //avcodec_context 编码器上下文结构体,保存了视频(音频)编解码相关信息

    avcodec_context = avformat_context->streams[this->avcodec_index]->codec;

    //根据解码器id查找解码器对应的结构体
    avcodec = NULL;
    avcodec = avcodec_find_decoder(avcodec_context->codec_id);
    if (NULL == avcodec)
    {
        qDebug()<<"没有找到视频解码器";
        exit(0);
    }
    else
    {
        qDebug()<<"找到对应的解码器为:"<name;
    }
}

打开解码器

void fDecode::openDecoder()
{
    qDebug()<<"————打开解码器————";
    //avcodec_context、 avcodec 为上一步得到的
    int res = avcodec_open2(avcodec_context,avcodec,NULL);//zero on success, a negative value on error
    if (0 != res)
    {
        qDebug()<<"打开解码器失败";
        exit(0);
    }
    else
    {
        qDebug()<<"打开解码器成功!";
    }
}

循环读取每一帧并生成.h264、.yuv文件

void fDecode::readFrame()
{
    qDebug()<<"————读取每一帧————";
    AVPacket *pkt = (AVPacket *)malloc(sizeof(AVPacket));//存储一帧压缩编码数据
    in_frame= av_frame_alloc();//保存解码后的像素数据
    out_frame = av_frame_alloc();//保存解码后剔除损坏数据后的像素数据

    //一帧码流数据解码后得到的像素数据有多大
    int numByte = avpicture_get_size(AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height);

    //开的空间用来保存像素数据
    uint8_t *buf = (uint8_t *)av_malloc(numByte * sizeof(uint8_t));

    //像素数据的填充
    avpicture_fill((AVPicture *)out_frame, buf, AV_PIX_FMT_YUV420P, avcodec_context->width, avcodec_context->height);

    //保存转换规则
    SwsContext *sws_content = nullptr;

    //转换规则的设置
    sws_content = sws_getContext(avcodec_context->width, avcodec_context->height ,avcodec_context->pix_fmt,
                                 avcodec_context->width, avcodec_context->height ,AV_PIX_FMT_YUV420P,
                                 SWS_BICUBIC,nullptr,nullptr,nullptr);
    FILE *fp_H264=NULL;
    FILE *fp_yuv=NULL;
    //以二进制格式创建或打开文件
    fp_H264=fopen("test.h264","wb+");
    fp_yuv=fopen("test.yuv","wb+");
    if(fp_H264 == NULL || fp_yuv == NULL)
    {
        qDebug()<<"file open fail!";
        exit(0);
    }
    
    int size = avcodec_context->width * avcodec_context->height;
    av_new_packet(pkt,size);
   
    //循环读取每一帧
    while (av_read_frame(avformat_context,pkt) == 0) //return 0 if OK, < 0 on error or end of file
    {
        if(pkt->stream_index == this->avcodec_index)  //如果这一帧是视频流
        {
            fwrite(pkt->data,pkt->size,1,fp_H264);//写入h264文件

            //解码——>得到yuv AVFrame
            //got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero.
            int got_picture_ptr = -1;
            avcodec_decode_video2(avcodec_context, in_frame, &got_picture_ptr, pkt);
            if(got_picture_ptr != 0)//做解码的操作
            {
                //把解码得到的损坏的数据剔除
                sws_scale(sws_content, in_frame->data, in_frame->linesize, 0, in_frame->height,  //原数据
                          out_frame->data, out_frame->linesize);  //输出数据
                fwrite(out_frame->data[0], size, 1, fp_yuv);//写入y数据
                fwrite(out_frame->data[1], size/4, 1, fp_yuv);//写入u数据
                fwrite(out_frame->data[2], size/4, 1, fp_yuv);//写入v数据
                }
            }
        }
        av_packet_unref(pkt);//清空
    }
    //关闭h264、yuv文件
    fclose(fp_H264);
    fclose(fp_yuv);
    qDebug()<<"写入成功";
}

关闭解码用到的组件

void fDecode::closeAll()
{
    qDebug()<<"————关闭所有组件————";
    av_frame_free(&in_frame);
    av_frame_free(&out_frame);
    avcodec_close(avcodec_context);
    avformat_free_context(avformat_context);
    qDebug()<<"关闭成功";
}

主函数测试

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    qDebug()<<"ffmpeg版本号:"<

4、测试效果

运行效果

Qt结合FFmpeg解码本地视频生成.h264文件和.yuv文件_第1张图片

        当前目录下会生成test.h264文件和test.yuv文件

        H.264压缩方法比较复杂。包含了帧内预测、帧间预测、熵编码、环路滤波等环节构成。可以将图像数据压缩100倍以上,所以可以发现未压缩的像素数据yuv文件比压缩后的码流数据h624文件大非常多。

查看h264文件

Qt结合FFmpeg解码本地视频生成.h264文件和.yuv文件_第2张图片

查看yuv文件

 Qt结合FFmpeg解码本地视频生成.h264文件和.yuv文件_第3张图片

你可能感兴趣的:(FFmpeg音视频,音视频,qt,ffmpeg)