ffmpeg解码流程

一、概述

  案例:编写一个ffmpeg编解码流程案例

  ps:开发工具是Qt Creator 并且在Qt中做的测试

二、图

 

 

三、代码示例

复制代码
#include "xplayer.h"
#include "ui_xplayer.h"
#include <QDebug>
#include <iostream>

extern "C"{
    #include "libavformat/avformat.h"
    #include "libavutil/avutil.h"
    #include "libavcodec/avcodec.h"
    #include "libswscale/swscale.h"
    #include "libswresample/swresample.h"

}


static double r2d(AVRational r){
    qDebug()<<"百分数:"<<r.num<<"/"<<r.den;
    return r.den == 0?0:(double)r.num/(double)r.den;
}

XPlayer::XPlayer(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::XPlayer)
{
    ui->setupUi(this);

    //初始化封装库
    av_register_all();
    //初始化网络库(可以打开rtsp、rtmp、http协议的流媒体视频)
    avformat_network_init();
    //注册编解码器
    avcodec_register_all();

    //打开文件
    //参数设置
    AVDictionary *opts = NULL;
    //设置rtsp流以tcp方式打开
    av_dict_set(&opts,"rtsp_transport","tcp",0);
    //设置网络延迟时间未500毫秒
    av_dict_set(&opts,"max_delay","500",0);
    //解封装上下文
    AVFormatContext *ic = NULL;
    const char * path = "C:/Users/wei.yang/Downloads/test_circle_video.mp4";
    int ret = avformat_open_input(&ic,
                                  path,
                                  0//表示自动选择解封装器
                                  ,&opts//参数设置
                                  );
    if(ret!=0){
        //如果出错了就打印错误信息
        char buf[1024] = {0};
        av_strerror(ret,buf,sizeof(buf)-1);
        qDebug()<<"open"<<path<< "failed!!"<<buf;
        return;
    }
    qDebug()<<"打开视频文件成功";

    qDebug()<<"流个数:"<<ic->nb_streams;

    //获取流信息
    ret =  avformat_find_stream_info(ic,NULL);

    //计算视频总时长
    int totalMs = ic->duration/(AV_TIME_BASE/1000);
    qDebug()<<"总时长(毫秒:)"<<totalMs;

    //打印视频流详细信息
    av_dump_format(ic,0,path,0);


    //音视频索引,读取时区分音视频
    int videoStreamIndex = 0;
    int audioStreamIndex = 1;

    //获取音视频流信息
    for(int i=0;i<ic->nb_streams;i++){
       AVStream *as  = ic->streams[i];
       qDebug()<<"公共信息:";
       qDebug()<<"codec_id:"<<as->codecpar->codec_id;
       qDebug()<<"format:"<<as->codecpar->format;

       //音频AVMEDIA_TYPE_AUDIO
       if(as->codecpar->codec_type==AVMEDIA_TYPE_AUDIO){
            audioStreamIndex  = i;
            qDebug()<<"音频信息:";
            qDebug()<<"sample_rate:"<<as->codecpar->sample_rate;
            qDebug()<<"channels:"<<as->codecpar->channels;
            //一帧数据,单通道样本数
            qDebug()<<"frame_size:"<<as->codecpar->frame_size;
            //
       }
       //视频AVMEDIA_TYPE_VIDEO
       else if(as->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){
            videoStreamIndex = i;
            qDebug()<<"视频信息:";

            //宽高、fps
            qDebug()<<"width:"<<as->codecpar->width;
            qDebug()<<"height:"<<as->codecpar->height;
            //帧率fps
            qDebug()<<"video fps:"<<r2d(as->avg_frame_rate);

       }
    }

    ////////////////////////////////////
    ///视频解码器打开
    ///找到视频解码器
    AVCodec *vcodec = avcodec_find_decoder(ic->streams[videoStreamIndex]->codecpar->codec_id);
    if(!vcodec){
        qDebug()<<"can't find codec id:"<<ic->streams[videoStreamIndex]->codecpar->codec_id;
        return;
    }
    qDebug()<<"find the AVCodec "<<ic->streams[videoStreamIndex]->codecpar->codec_id;
    //创建视频解码器上下文
    AVCodecContext *vc = avcodec_alloc_context3(vcodec);
    //配置解码器上下文参数
    avcodec_parameters_to_context(vc,ic->streams[videoStreamIndex]->codecpar);
    //设置需要多少个线程解码
    vc->thread_count = 8;

    //打开解码器上下文
    ret = avcodec_open2(vc,0,0);
    if(ret!=0){
        char buf[1024] = {0};
        av_strerror(ret,buf,sizeof(buf)-1);
        qDebug()<<"video avcodec_open2 failed!:"<<buf;
        return;
    }

    qDebug()<<"video avcodec_open2 success!";

    ////////////////////////////////////
    ///音频解码器打开
    ///找音频解码器
    AVCodec *acodec = avcodec_find_decoder(ic->streams[audioStreamIndex]->codecpar->codec_id);
    if(!acodec){
        qDebug()<<"can't find codec id:"<<ic->streams[audioStreamIndex]->codecpar->codec_id;
        return;
    }
    qDebug()<<"find the AVCodec "<<ic->streams[audioStreamIndex]->codecpar->codec_id;
    //创建音频解码器上下文
    AVCodecContext *ac = avcodec_alloc_context3(acodec);
    //配置解码器上下文参数
    avcodec_parameters_to_context(ac,ic->streams[audioStreamIndex]->codecpar);
    //设置需要多少个线程解码
    ac->thread_count = 8;

    //打开解码器上下文
    ret = avcodec_open2(ac,0,0);
    if(ret!=0){
        char buf[1024] = {0};
        av_strerror(ret,buf,sizeof(buf)-1);
        qDebug()<<"audio avcodec_open2 failed!:"<<buf;
        return;
    }

    qDebug()<<"audio avcodec_open2 success!";



    //获取视频流 ic->streams[videoStreamIndex]
    av_find_best_stream(ic,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0);
    //malloc AVPacket并初始化
    AVPacket *pkt = av_packet_alloc();
    //初始化AVFrame
    AVFrame *frame = av_frame_alloc();

    //像素格式和尺寸转换上下文
    SwsContext *vctx = NULL;
    unsigned char *rgb = NULL;

    //音频重采样上下文初始化
    SwrContext *actx = swr_alloc();
    actx = swr_alloc_set_opts(actx,
                              av_get_default_channel_layout(2),//输出
                              AV_SAMPLE_FMT_S16,//输出
                              ac->sample_rate,//输出
                              av_get_default_channel_layout(ac->channels),//输入
                              ac->sample_fmt,//输入
                              ac->sample_rate,//输入
                              0,0);
    ret = swr_init(actx);
    if(ret!=0){
        char buf[1024] = {0};
        av_strerror(ret,buf,sizeof(buf)-1);
        qDebug()<<"swr_init error:"<<buf;
        return;
    }
    unsigned char *pcm = NULL;//音频原始数据


    for(;;){
        //读取一帧数据
        int ret = av_read_frame(ic,pkt);
        if(ret!=0){//代表这一帧数据读取失败,那就继续读取
            int ms = 3000;
            long long pos = (double)ms/(double)1000*r2d(ic->streams[pkt->stream_index]->time_base);
            av_seek_frame(ic,videoStreamIndex,
                          pos,//要seek的具体位置
                          AVSEEK_FLAG_BACKWARD//向后找
                          | AVSEEK_FLAG_FRAME//并且需要定位到关键帧位置
                          );
            continue;//继续
        }

        qDebug()<<"pkt->size:"<<pkt->size;
        //显示时间
        qDebug()<<"pkt->pts:"<<pkt->pts;

        //转换为毫秒,方便做同步
        qDebug()<<"pkt->pts ms:"<<pkt->pts*(r2d(ic->streams[pkt->stream_index]->time_base)*1000);

        //解码时间
        qDebug()<<"pkt->dts:"<<pkt->dts;

       AVCodecContext *cc= NULL;//解码器上下文
        if(pkt->stream_index==videoStreamIndex){
            qDebug()<<"视频流";
            cc = vc;
        }

        if(pkt->stream_index==audioStreamIndex){
            qDebug()<<"音频流";
            cc = ac;
        }

        //解码视频
        //发送packet到解码线程,send传NULL后调用多次receive取出所有缓冲帧数据
        ret  = avcodec_send_packet(cc,pkt);
        //释放,引用计数-1,为0释放空间
        av_packet_unref(pkt);
        if(ret!=0){//说明发送数据失败
            char buf[1024] = { 0 };
            av_strerror(ret, buf, sizeof(buf) - 1);
            qDebug() << "avcodec_send_packet  failed! :" << buf ;
            continue;
        }

        //循环从线程中取出数据
        for(;;){
            //从线程中获取解码接口,一次send可能对应多次receive
            ret = avcodec_receive_frame(cc,frame);
            if(ret!=0){
                    char buf[1024] = { 0 };
                    av_strerror(ret, buf, sizeof(buf) - 1);
                    qDebug() << "avcodec_receive_frame  failed! :" << buf ;
                    break;
             }
            qDebug()<<"receive frame:"<<frame->format<<" "<<frame->linesize[0];
            //视频
            if(cc==vc){
                qDebug()<<"视频宽高:width:"<<frame->width<<" height:"<<frame->height;
                vctx = sws_getCachedContext(
                            vctx,//传NULL会重新创建
                            frame->width,//输入宽
                            frame->height,//输入搞
                            (AVPixelFormat)frame->format,//输入格式YUV420P
                            frame->width,//输出宽
                            frame->height,//输出高
                            AV_PIX_FMT_RGBA,//输出格式RGBA
                            SWS_BILINEAR,//双线性插值
                            0,0,0
                            );

                if(vctx){
                    if(!rgb)rgb = new unsigned char[frame->width*frame->height*4];
                    uint8_t *data[2] = {0};
                    data[0] = rgb;
                    int lines[2]  ={0};
                    lines[0] = frame->width*4;
                    ret = sws_scale(vctx,
                                    frame->data,//输入数据
                                    frame->linesize,//输入行大小
                                    0,
                                    frame->height,//输入高度
                                    data,//输出数据大小
                                    lines//
                                    );
                }
                qDebug()<<"sws_scale="<<ret;
            }else{//音频
                uint8_t *data[2] = {0};
                if(!pcm)pcm = new uint8_t[frame->nb_samples*2*2];
                data[0] = pcm;
//                int swr_convert(struct SwrContext *s, uint8_t **out, int out_count,
//                                                const uint8_t **in , int in_count);

               ret = swr_convert(actx,
                                   data, frame->nb_samples,        //输出
                                   (const uint8_t**)frame->data,frame->nb_samples    //输入
                                   );
                qDebug()<<"swr_convert="<<ret;

            }
        }


        //释放引用计数
        av_packet_unref(pkt);
    }
    av_frame_free(&frame);
    //释放AVPacket空间
    av_packet_free(&pkt);


    //关闭打开的文件资源
    if(ic){
        avformat_close_input(&ic);
    }

}

XPlayer::~XPlayer()
{
    delete ui;
}
复制代码

 

posted on   飘杨......  阅读(382)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示