QT + FFMPEG实现基本播放器(二):FFMPEG解码功能实现

QT + FFMPEG实现基本播放器(二):FFMPEG解码功能实现

对于视频的解码模块,使用FFMPEG进行实现。

将在上一节中实现的OpenFileHanle槽函数中,增加以下的代码,将提取到的文件名称,传给FFmpegThread,FFmpegThread使用单例模式管理FFMPEG

int ret = FFmpegThread::Get()->OpenFile((const char *)strFileName.toLocal8Bit());

if(ret < 0)
{
    QMessageBox::information(this, "err", "file open failed!");
}
else
{
}

FFmpegThread::Get()->OpenFile 将会调用FFmpegThread::Get()->Init进行初始化工作:

int FFmpegThread::Init(const char* pcFileName)
{

   
    //打开媒体文件
    int result = avformat_open_input(&g_MedieInfo.avFormatContext, pcFileName, NULL, NULL);
    if (result < 0)
    {
        qDebug() << "open input error" << pcFileName;

        return -1;
    }

    /*
        获取流信息,主要用于给每个媒体流(音频/视频)的AVStream结构体赋值
        发现它其实已经实现了解码器的查找,解码器的打开,视音频帧的读取,视音频帧的解码等工作。换句话说,该函数实际上已经“走通”的解码的整个流程。
        下面看一下除了成员变量赋值之外,该函数的几个关键流程。
        1.查找解码器:find_decoder()
        2.打开解码器:avcodec_open2()
        3.读取完整的一帧压缩编码的数据:read_frame_internal()
        注:av_read_frame()内部实际上就是调用的read_frame_internal()。
        4.解码一些压缩编码数据:try_decode_frame()
    */
    result = avformat_find_stream_info(g_MedieInfo.avFormatContext, NULL);
    if (result < 0)
    {
        qDebug()<< "find stream info error";
        return -1;
    }

    /*----------视频流部分开始----------*/

    //获取音视频对应的stream_index
    AVCodec *videoDecoder;
    g_MedieInfo.videoStreamIndex = av_find_best_stream(g_MedieInfo.avFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, &videoDecoder, 0);
    if (g_MedieInfo.videoStreamIndex < 0)
    {
        qDebug() << "find video stream index error";
        return -1;
    }

    //获取视频流
    AVStream *videoStream = g_MedieInfo.avFormatContext->streams[g_MedieInfo.videoStreamIndex];

    //获取视频流解码器,或者指定解码器
    AVCodecContext * videoCodec = videoStream->codec;
    videoDecoder = avcodec_find_decoder(videoCodec->codec_id);
    if (videoDecoder == NULL)
    {
        qDebug() << "video decoder not found";
        return -1;
    }

    //设置加速解码
    videoCodec->lowres = videoDecoder->max_lowres;
    videoCodec->flags2 |= AV_CODEC_FLAG2_FAST;

    //打开视频解码器
    result = avcodec_open2(videoCodec, videoDecoder, NULL);
    if (result < 0)
    {
        qDebug() << "open video codec error";
        return -1;
    }

    //获取分辨率大小
    int videoWidth = videoStream->codec->width;
    int videoHeight = videoStream->codec->height;

    VideoThread::Get()->SetVideoInfo(videoWidth,videoHeight);

    //获取帧率大小
    g_MedieInfo.m_FrameRate = videoStream->avg_frame_rate.num/videoStream->avg_frame_rate.den;

    qDebug() <<"rate "<<g_MedieInfo.m_FrameRate;

    //如果没有获取到宽高则返回
    if (videoWidth == 0 || videoHeight == 0)
    {
        qDebug()<< "find width height error";
        return -1;
    }

    m_totalMs = g_MedieInfo.avFormatContext->duration / 1000000 * 1000; //视频的时间,结果是多少豪秒


     /*----------视频流部分结束----------*/




     /*----------音频流部分开始----------*/
    //循环查找音频流索引,与上面的av_find_best_stream效果一致
    g_MedieInfo.audioStreamIndex = -1;
    for (uint i = 0; i < g_MedieInfo.avFormatContext->nb_streams; i++)
    {
        if (g_MedieInfo.avFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            g_MedieInfo.audioStreamIndex = i;
            break;
        }
    }

    //有些没有音频流,所以这里不用返回
    if (g_MedieInfo.audioStreamIndex == -1)
    {
        qDebug()<< "find audio stream index error";
    }
    else
    {
        //获取音频流
        AVStream *audioStream = g_MedieInfo.avFormatContext->streams[g_MedieInfo.audioStreamIndex];
        AVCodecContext * audioCodec = audioStream->codec;

        //获取音频流解码器,或者指定解码器
        AVCodec * audioDecoder = avcodec_find_decoder(audioCodec->codec_id);
        if (audioDecoder == NULL)
        {
            qDebug() << "audio codec not found";
            return -1;
        }

        //打开音频解码器
        result = avcodec_open2(audioCodec, audioDecoder, NULL);
        if (result < 0)
        {
            qDebug()<< "open audio codec error";
            return -1;
        }

        AudioThread::Get()->SetAudioInfo(audioCodec->sample_rate,16,audioCodec->channels);
    }

    /*----------音频流部分结束----------*/

    //比较上一次文件的宽度高度,当改变时,需要重新分配内存
    if (oldWidth != videoWidth || oldHeight != videoHeight)
    {
        if(g_MedieInfo.buffer != NULL)
        {
            av_free(g_MedieInfo.buffer);
            g_MedieInfo.buffer = NULL;
        }

        int byte = avpicture_get_size(AV_PIX_FMT_RGB32, videoWidth, videoHeight);
        g_MedieInfo.buffer = (uint8_t *)av_malloc(byte * sizeof(uint8_t));
        oldWidth = videoWidth;
        oldHeight = videoHeight;
    }

    //定义像素格式
    AVPixelFormat srcFormat = videoCodec->pix_fmt;
    AVPixelFormat dstFormat = AV_PIX_FMT_RGB32;

    //初始化VideoThread
    VideoThread::Get()->InitvidioFrameConvert(dstFormat);

    //图像转换
    g_MedieInfo.swsContext = sws_getContext(videoWidth, videoHeight, srcFormat, videoWidth, videoHeight, dstFormat, SWS_BICUBIC, NULL, NULL, NULL);

    //输出视频信息
   // av_dump_format(g_MedieInfo.avFormatContext, 0, pcFileName, 0);

    init_clock(&g_MedieInfo.m_PalyClock);

    qDebug() << "init ffmpeg finsh";


    return 0;
}


对于在Init中的 g_MedieInfo是用来保存媒体信息的:

MEDIE_INFO_S g_MedieInfo;

typedef struct MedieInfo
{
    AVFormatContext *avFormatContext;//格式对象

    uint8_t *buffer;                //存储解码后图片buffer

    SwsContext *swsContext;         //处理图片数据对象

    int videoStreamIndex;           //视频流索引
    int audioStreamIndex;           //音频流索引

    int  m_FrameRate;



    AvPacketQueue<AVPacket> m_VideoPacketQueue;
    AvPacketQueue<AVPacket> m_AudioPacketQueue;

    PALY_CLOCK_S m_PalyClock;       //同步时钟

}MEDIE_INFO_S;

视频和音频的解码处理在FFmpegThread中进行,而视频包的处理则在VideoThread,音频包的处理在AudioThread中进行,三个线程分别进行处理,增加处理效率。

在FFmpegThread的run函数当中,读取数据保存到相应的队列当中:

void FFmpegThread::run()
{

    qDebug()<<__func__<<__LINE__;

    while (!m_IsQuit)
    {
        //还未打开程序
        if (!m_IsPaly)
        {
            continue;
        }


        AVPacket avPacket;
        if (av_read_frame(g_MedieInfo.avFormatContext, &avPacket) >= 0)
        {
            //判断当前包是视频还是音频
            int packetSize = avPacket.size;
            int index = avPacket.stream_index;


            if (index == g_MedieInfo.videoStreamIndex)
            {
                g_MedieInfo.m_VideoPacketQueue.push(avPacket);
            }
            else if (index == g_MedieInfo.audioStreamIndex)
            {
                g_MedieInfo.m_AudioPacketQueue.push(avPacket);
            }

        }

    }

    //线程结束后释放资源
    FreeSrc();
    m_IsPaly = false;
    m_IsQuit = false;

    qDebug() << "Stop Ffmpeg Thread";
}

以上则为使用FFmpeg进行解码的过程,av_read_frame得到的为原始的数据包,需要在音视频线程中进一步处理,才能使用,音视频线程的处理在后面的章节中讲解。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值