FFmpeg开发(三)——(FFmpeg的简单应用)

FFmpeg开发(三)——(FFmpeg的简单应用)

前两篇介绍了FFmpeg的安装和基本知识。

FFmpeg开发(一)一Qt Creator配置FFmpeg

FFmpeg开发(二)——(FFmpeg基础知识介绍)

今天整理下ffmpeg的简单应用。

在上篇中,可以知道FFmpeg的解码流程。

 通过上图,可以看到解码流程。(图片来源于网络)

按照流程,我们整理出一个完整的流程代码:

int main()
{

    QString fileName = "C:/Users/feng/Desktop/test.mp4";

    //ffmpeg相关变量预先定义与分配
    AVFormatContext *m_pAVFormatCtx;    //解封装上下文
    SwsContext  *m_pSwsCtx;             //视频转码上下文

    AVCodecContext *avCodecCtx;         //解码上下文
    AVFrame *m_pAVFrame = 0;            // ffmpeg单帧缓存
    AVFrame *m_pAVFrameRGB32 = 0;       // ffmpeg单帧缓存转换颜色空间后的缓存
    AVPacket *m_pAVPacket = 0;          // ffmpag单帧数据包

    int gotPicture = 0;          // 解码时数据是否解码成功
    int numBytes = 0;            // 解码后的数据长度
    uchar *outBuffer = 0;        // 解码后的数据存放缓存区
    char m_errorBuff[1024];      //打开时发生的错误信息

    int m_totalMs;               //总时长
    int m_videoStream;           //视频流
    int m_audioStream;           //音频流
    int m_fps;                   //每秒的视频帧数

    int m_pts;                   //获得当前解码帧的时间

    int m_sampleRate;            //样本率
    int m_sampleSize;            //样本大小
    int m_channel;               //通道数


     m_pAVFormatCtx= avformat_alloc_context();   
     m_pAVPacket = av_packet_alloc();                  
     m_pAVFrame = av_frame_alloc();                    
     m_pAVFrameRGB32 = av_frame_alloc();               

    // 步骤一:注册所有容器和编解码器
    av_register_all();  

    // 步骤二:打开文件(ffmpeg成功则返回0)
    int ret = avformat_open_input(&m_pAVFormatCtx, fileName , 0, 0);//打开解封装器
    if (ret != 0)//打开错误时
    {
        av_strerror(ret, m_errorBuff, sizeof(m_errorBuff));//错误信息
        printf("open %s failed :%s\n", path, m_errorBuff);
        return 0;
    }
    
    m_totalMs = m_pAVFormatCtx->duration / (AV_TIME_BASE);//获取视频的总时间

    // 步骤三:提取流信息,提取视频信息
    for(int index = 0; index < m_pAVFormatContext->nb_streams; index++)
    {
        avCodecCtx= m_pAVFormatContext->streams[index]->codec;
        switch (avCodecCtx->codec_type)
            {
            case AVMEDIA_TYPE_VIDEO: //视频流
                m_videoStream = i;
    
                break;
            case AVMEDIA_TYPE_AUDIO: //音频流
                m_audioStream = i;//音频流
                break;

            default:
                break;
            }
            // 已经找打视频品流
            if(m_videoStream != -1)
            {
                break;
            }
    }

    if(m_videoStream == -1 || !avCodecCtx)
    {
        return -1;
    }

    // 步骤四:对找到的视频流寻解码器
    pAVCodec = avcodec_find_decoder(pAVCodecContext->codec_id);
    if(!pAVCodec)
    {
            LOG << "Fialed to avcodec_find_decoder(pAVCodecContext->codec_id):"
                << pAVCodecContext->codec_id;
            goto END;
    }

    AVCodec *avCodec = avcodec_find_decoder(avCodecCtx->codec_id);//查找解码器
    if (!avCodec)//未找到解码器
    {
           printf("video code not find\n");
           return -1;
    }

    // 步骤五:打开解码器
    int err = avcodec_open2(avCodecCtx, avCodec, NULL);//打开解码器
    if (err != 0)//未打开解码器
    {
            char buf[1024] = { 0 };
            av_strerror(err, buf, sizeof(buf));
            printf("not open!  %s",buf);
            return 0;
    }
    printf("open codec success!\n");

    // 步骤六:对拿到的原始数据格式进行缩放转换为指定的格式高宽大小
    pSwsContext = sws_getContext(avCodecCtx->width,
                                 avCodecCtx->height,
                                 avCodecCtx->pix_fmt,
                                 avCodecCtx->width,
                                 avCodecCtx->height,
                                 AV_PIX_FMT_RGBA,
                                 SWS_FAST_BILINEAR,
                                 0,
                                 0,
                                 0);
    numBytes = avpicture_get_size(AV_PIX_FMT_RGBA,
                                  avCodecCtx->width,
                                  avCodecCtx->height);
    outBuffer = (uchar *)av_malloc(numBytes);

    avpicture_fill((AVPicture *)m_pAVFrameRGB32 ,
                   outBuffer,
                   AV_PIX_FMT_RGBA,
                   avCodecCtx->width,
                   avCodecCtx->height);

    // 步骤七:读取一帧数据的数据包
    while(av_read_frame(m_pAVFormatContext, m_pAVPacket ) >= 0)
    {
        if(pAVPacket->stream_index == m_videoStream )
        {
                
    // 步骤八:对读取的数据包进行解码
         ret = avcodec_decode_video2(avCodecCtx, m_pAVFrame , &gotPicture, m_pAVPacket );
         if(ret < 0)
         {
             break;
         }
         // 等于0代表拿到了解码的帧数据
         if(!gotPicture)
         {
             break;
          }else{
             ws_scale(m_pSwsCtx,
                     (const uint8_t * const *)m_pAVFrame ->data,
                      m_pAVFrame ->linesize,
                      0,
                      avCodecCtx->height,
                      m_pAVFrameRGB32 ->data,
                       m_pAVFrameRGB32 ->linesize);

             QImage imageTemp((uchar *)outBuffer,
                             avCodecCtx->width,
                             avCodecCtx->height,
                             QImage::Format_RGBA8888);

             QImage image = imageTemp.copy();
                   
              }
                av_free_packet(pAVPacket);
          }
          QThread::msleep(100);
      }

}

下一篇,根据这些流程写一个视频播放器。

本文原创作者:冯一川([email protected]),未经作者授权同意,请勿转载。

Guess you like

Origin blog.csdn.net/ifeng12358/article/details/121356691