您的位置:首页 > 其它

FFMpeg中apiexample.c例子分析——解码分析

2010-05-12 16:42 393 查看




  我们直接从video_decode_example()函数开始讲,该函数实现了如何去解码一个视频文件,以.mpeg文件为例。
  (1)将缓存的末尾清0,从而确保读操作不会越界导致破坏mpeg流。
    uint8_t inbuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE];
    memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);
    可见,我们所分配的缓存,除了存放数据外,最后部分还预留了一小段空间。
  (2)avcodec_find_decoder()函数用于查找与codec ID相匹配的已注册的解码器。
  (3)avcodec_alloc_context()函数用于分配一个AVCodecContext并设置默认值,如果失败返回NULL,并可用av_free()进行释放。
  (4)avcodec_alloc_frame()函数用于分配一个AVFrame并设置默认值,如果失败返回NULL,并可用av_free()进行释放。
  (5)avcodec_open()函数用给定的AVCodec来初始化AVCodecContext。
    对于一些编解码器,像msmpeg4和mpeg4,其宽度和高度必须要初始化,因为这些信息在码流(bitstream)中是没有的。avcodec_open()会帮我们把宽度和高度设置好。
  (6)打开文件fopen。
  (7)循环解码。
  (7.1)读取文件fread(inbuf, 1, INBUF_SIZE, f );
    注意1:一些编解码器是基于流的(像mpegvideo,mpegaudio),这是使用它们的唯一一种方法,因为在解析它之前,你不知道压缩数据的大小。
    但是,一些其他编解码器(像msmpeg4,mpeg4)是基于帧的,所以对于某一帧,你必须调用这些编解码器来处理所有数据。你也必须在初始化这些编解码器前,先把宽度和高度设置了。
    注意2:一些编解码器允许原始参数(像帧大小,采样率)在任何一帧被改变。我们虽然已对此做了处理,但你也要重视起来。
    这儿,我们用一个基于流的解码器(mpeg1video),所以我们把数据输到解码器,看它是否可以解码一帧。
  (7.2)avcodec_decode_video()函数用于解码一个视频帧,从inbuf_ptr到picture,所采用的编解码器是c。
  (7.3)写入文件。
  (8)解码延时的帧,再写入文件。
    一些解码器,像MPEG,传输I帧和P帧时,都会有一帧的延时。所以我们必须要获取视频的最后一帧数据。
  (9)释放资源。
    fclose(f);
    avcodec_close(c);
    av_free(c);
    av_free(picture);

  (10)分配图像和输出缓存。

    申请100KB左右的内存作为输出缓存。
outbuf_size = 100000;
    outbuf = malloc( outbuf_size );
    根据帧的大小来确定YUV420所占内存大小,一个像素,RGB格式占用3个字节,而YUV420格式只占用两个字节。YUV420格式是指,每个像素都保留一个Y(亮度)分量,而在水平方向上,不是每行都取U和V分量,而是一行只取U分量,则其接着一行就只取V分量,以此重复,所以420不是指没有V,而是指一行采样只取U,另一行采样只取V。在取U和V时,每两个Y之间取一个U或V。但从4x4矩阵列来看,每4个矩阵点Y区域中,只有一个U和V,所以它们的比值是4:1。所以对于一个像素,RGB需要8 * 3 = 24位,即占3个字节;而YUV420P,8 + 8/4 + 8/4 = 12位,即占2个字节,其中8指Y分量,8/4指U和V分量。
    size = c->width * c->height;
    picture_buf = malloc( (size * 3) / 2 );
    picture->data[0] = picture_buf;
    picture->data[1] = picture->data[0] + size;
    picture->data[2] = picture->data[1] + size / 4;
    picture->linesize[0] = c->width;
    picture->linesize[1] = c->width / 2;
    picture->linesize[2] = c->width / 2;
    其中,data[0]存放Y,data[1]存放U,data[2]存放V【FixMe】。linesize[0]表示Y分量的宽度,linesize[1]表示U分量的宽度,linesize[2]表示V分量的宽度。
  (11)编码一秒钟的视频,帧率为25,所以需要循环25次,每次编码一帧。
  (11.1)准备一幅伪图像,即自己自定义往里面塞数据。
    for(y=0;y<c->height;y++){
for(x=0;x<c->width;x++){
picture->data[0][y*picture->linesize[0]+x]=x+y+i*3;
}
}
for(y=0;y<c->height/2;y++){
for(x=0;x<c->width/2;x++){
picture->data[1][y*picture->linesize[1]+x]=128+y+i*2;
picture->data[2][y*picture->linesize[2]+x]=64+x+i*5;
}
}
  (11.2)avcodec_encode_video()从picture中编码一帧视频数据,并存入到outbuf中,而期间所使用的编码器为c。
  (11.3)将编码完的数据写入到文件里。

  (12)对延时的帧数据进行编码。因为像MPEG4中,I帧、P帧和B帧之间存在一定的延时【FixMe】。同样是avcodec_encode_video(),然后写入文件。
  (13)添加结束代码,使其成为一个真正的mpeg文件。
    outbuf[0] = 0x00;
    outbuf[1] = 0x00;
    outbuf[2] = 0x01;
    outbuf[3] = 0xb7;
    fwrite( outbuf, 1, 4, f );
  这个结束代码表示什么???
  (14)释放资源。
    fclose(f);
    free(picture_buf);
    free(outbuf);
    avcodec_close(c);
    av_free(c);
    av_free(picture);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: