ffmpeg内部分析

来源:互联网 发布:域名和公网ip绑定 编辑:程序博客网 时间:2024/06/06 11:47

没有文档....  只有自己东看西看,总结点东西。


打开视频:

1.首先是  libavformat/allformats.c 中的av_register_all();

这一步注册库中含有 的所有可用的文件格式和编码器,这样当打开一个文件时,它们才能够自动选择相应的文件格式和编码器。要注意你只需调用一次 av_register_all(),所以,尽可能的在你的初始代码中使用它。如果你愿意,你可以仅仅注册个人的文件格式和编码,不过,通常你不得不这么 做却没有什么原因。


2.打开文件  libavformat/utils.c 中的av_open_input_file函数.

   int i = av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL);返回一个int , 不等于0则说明打开失败

  传入pFormatCtx指针变量地址,会根据打开的文件来填充FormatContext(只填充头信息),传入时,它必须为NULL;

   最 后三个参数描述了文件格式,缓冲区大小(size)和格式参数;我们通过简单地指明NULL或0告诉 libavformat 去自动探测文件格式并且使用默认的缓冲    区大小。


3.取出文件中的流信息 libavformat/utils.c 中的av_find_stream_info函数.

    int i = av_find_stream_info(pFormatCtx)  ; i<0出错

    这一步会用有效的信息把 AVFormatContext 的流域(streams field)填满


4.现在pFormatCtx已经充满了流信息,接下来需要找出里面的第一种视频流,

    int videoStream = -1;

    for( i = 0; i < pFormatCtx->nb_streams; i++ )

    {

            if(pFormatCtx->streams->codec.codec_type==CODEC_TYPE_VIDEO)
            {
                  videoStream=i;
                  break;
            }

    }

    如果videoStream不等-1,说明找到了第一种视频流,这个videoStream应该是流的位置 streams[videoStream]


5得到视频流编码上下文的指针

   pCodecCtx=&pFormatCtx->streams[videoStream]->codec;


6.找到真正的解码器.

AVCodec *pCodec;
pCodec=avcodec_find_decoder(pCodecCtx->codec_id);

pCodec == NULL为出错


7.通知解码器我们能够处理截断的bit流--ie,
// bit流帧边界可以在包中
if(pCodec->capabilities & CODEC_CAP_TRUNCATED)

{

        pCodecCtx->flags|=CODEC_FLAG_TRUNCATED;

}


8. 打开解码器    libavcodec/utils.c 中的avcodec_open函数
    int i = avcodec_open(pCodecCtx, pCodec);  i<0说明打开失败


9.给视频帧分配空间以便存储解码后的图片:  

   AVFrame *pFrame;
   pFrame=avcodec_alloc_frame();


10. 别人封装好的一个函数,它返回下一帧

      bool GetNextFrame(AVFormatContext *pFormatCtx, AVCodecContext *pCodecCtx, 
    int videoStream, AVFrame *pFrame)
{
    static AVPacket packet;
    static int      bytesRemaining=0;
    static uint8_t  *rawData;
    static bool     fFirstTime=true;
    Int bytesDecoded;
    Int frameFinished;

//  我们第一次调用时,将 packet.data 设置为NULL指明它不用释放了
    if(fFirstTime)
    {
        fFirstTime=false;
        packet.data=NULL;
    }

// 解码直到成功解码完整的一帧
    while(true)
    {
         //  除非解码完毕,否则一直在当前包中工作
        while(bytesRemaining > 0)
        {
        //  解码下一块数据
            bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame,
                &frameFinished, rawData, bytesRemaining);

                // 出错了?
            if(bytesDecoded < 0)
            {
                fprintf(stderr, "Error while decoding frame/n");
                return false;
            }

            bytesRemaining-=bytesDecoded;
            rawData+=bytesDecoded;

                // 我们完成当前帧了吗?接着我们返回
            if(frameFinished)
                return true;
        }

        // 读取下一包,跳过所有不属于这个流的包
        do
        {
            // 释放旧的包
            if(packet.data!=NULL)
                av_free_packet(&packet);

            // 读取新的包
            if(av_read_packet(pFormatCtx, &packet)<0)
                goto loop_exit;
        } while(packet.stream_index!=videoStream);

        bytesRemaining=packet.size;
        rawData=packet.data;
    }

loop_exit:

        // 解码最后一帧的余下部分
    bytesDecoded=avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, 
        rawData, bytesRemaining);

        // 释放最后一个包
    if(packet.data!=NULL)
        av_free_packet(&packet);

    return frameFinished!=0;
}



  这个函数的好处是,不用理会 两帧之间的边界也可以在包的中间部分  这种情况

  接下来我们要做的就是在一个循环中,调用 GetNextFrame () 直到它返回false。


   while(GetNextFrame(pFormatCtx, pCodecCtx, videoStream, pFrame))
   {
           img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, 
           pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
           // 处理视频帧(存盘等等)
           DoSomethingWithTheImage(pFrameRGB);
}


RGB 图象pFrameRGB (AVFrame *类型)的空间分配如下:

AVFrame *pFrameRGB;
int     numBytes;
uint8_t *buffer;

// 分配一个AVFrame 结构的空间
pFrameRGB=avcodec_alloc_frame();
if(pFrameRGB==NULL)
    handle_error();



// 确认所需缓冲区大小并且分配缓冲区空间
numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,
    pCodecCtx->height);
buffer=new uint8_t[numBytes];

// 在pFrameRGB中给图象位面赋予合适的缓冲区
avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,
    pCodecCtx->width, pCodecCtx->height);

清除

好了,我们已经处理了我们的视频,现在需要做的就是清除我们自己的东西:
// 释放 RGB 图象
delete [] buffer;
av_free(pFrameRGB);

// 释放YUV 帧
av_free(pFrame);

// 关闭解码器(codec)
avcodec_close(pCodecCtx);

// 关闭视频文件
av_close_input_file(pFormatCtx);


   

   

原创粉丝点击