FFmpeg - 视频解码过程

来源:互联网 发布:iphone6网络 ios10 编辑:程序博客网 时间:2024/05/01 15:56

FFmpeg的文档做得不好,导致学会使用这个库是存在一定难度的。在对文件进行解码时,抄网上流传的例子就可以了。比如,常见的例子是这样的:

main(0{   av_register_all();    // Open video file    if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0)        return -1; // Couldn't open file    // Retrieve stream information    if(av_find_stream_info(pFormatCtx)<0)        return -1; // Couldn't find stream information    pCodecCtx=&pFormatCtx->streams[videoStream]->codec;    // Find the decoder for the video stream    pCodec=avcodec_find_decoder(pCodecCtx->codec_id);    if(pCodec==NULL)        return -1; // Codec not found   ...}



以解TS流文件为例,上述代码会把读文件、读TS包、解PMT, PAT、解PES等过程都内部做了,使用者即使不懂这些概念,也是可以写出一个解TS文件成图像的例子的。但如果输入的是一个TS流怎么办呢?--显然再用av_open_input_file()这种就行了。

对于流媒体TS,要对MPEG-2有一定了解。其步骤简述如下:

(1) 解复用,得到PAT, PMT,得到PES_pid

(2) 对PES解复用,得到其PTS和ES

(3) 把ES(图像编码数据)交给FFmpeg解码,得到图像

(4) 按PTS顺序,显示图像。如果是AVC/H264编码,其PTS顺序一般不是自然顺序。


那么,在第(3)步中,怎么使用FFmpeg来解ES呢?参考其examples,总结得到以下顺序:

////////////////////初始化 ///////////////////////////////

// 查找解码器ID    CodecID codec_id = CODEC_ID_NONE;        switch(stream_type)    {    default:        codec_id = CODEC_ID_H264;    }    // 查找解码器    m_pCodec = avcodec_find_decoder(codec_id);    if (!m_pCodec)    {        fprintf(stderr, "codec not found\n");        return -1;    }    // 打开解码器    m_pCodecContext = avcodec_alloc_context();    if (avcodec_open(m_pCodecContext, m_pCodec) < 0)    {        fprintf(stderr, "could not open codec\n");        return -1;    }    // 申请一帧的空间用于解码    m_pPicture = avcodec_alloc_frame();

//////////// 解码 /////////////////

 

// 解析PES得到ES    TS_PesDecoder pesdec;    pesdec.Parse(m_iDemux.Payload(), m_iDemux.PayloadSize());    // AVPacket应包含的是ES数据    AVPacket avpkt;    av_init_packet(&avpkt);    avpkt.size = pesdec.m_nEsLength;    avpkt.data = pesdec.m_pEs;        if (avpkt.size == 0) return -1 ;    // 解AVPacket,得到图像数据AVFrame    while (avpkt.size > 0)    {        int got_picture = 0;        int len = avcodec_decode_video2(m_pCodecContext, m_pPicture, &got_picture, &avpkt);        if (len < 0)        {            fprintf(stderr, "Error while decoding frame...\n");            break;        }        if (got_picture)        {            int width = m_pCodecContext->width;            int height = m_pCodecContext->height;            VideoFrame frame;            frame.nWidth = width;            frame.nHeight = height;            frame.nPts = pesdec.m_nPts / 45; // in ms            AVPicture& pic_rgb = frame.iPic;            avpicture_alloc(&pic_rgb, PIX_FMT_RGB24, width, height);            // 用swscale库把YUYV420P转成RGB24            SwsContext* img_convert_ctx = sws_getContext(width, height, PIX_FMT_YUV420P,                width, height, PIX_FMT_RGB24,                SWS_BICUBIC, NULL, NULL, NULL);            sws_scale(img_convert_ctx,                m_pPicture->data, m_pPicture->linesize,                0, height,                pic_rgb.data, pic_rgb.linesize);            sws_freeContext(img_convert_ctx);            printf("Saving a frame ....\n");            m_iMutex.Lock();            m_lstFrames.push_back(frame);            m_iMutex.Unlock();                    return 1;        }        avpkt.size -= len;        avpkt.data += len;    }




原创粉丝点击