理解FFMPEG视频编码

6

我从FFmpeg的编码示例中得到了这个。我可以在音频编码方面略微遵循作者的示例,但是当我看着C代码时,我感到困惑(我在块编号中加入了注释以帮助我参考我所说的内容)...

static void video_encode_example(const char *filename)
{
AVCodec *codec;
AVCodecContext *c= NULL;
int i, out_size, size, x, y, outbuf_size;
FILE *f;
AVFrame *picture;
uint8_t *outbuf, *picture_buf;              //BLOCK ONE
printf("Video encoding\n");

/* find the mpeg1 video encoder */
codec = avcodec_find_encoder(CODEC_ID_MPEG1VIDEO);
if (!codec) {
    fprintf(stderr, "codec not found\n");
    exit(1);                                //BLOCK TWO
}

c= avcodec_alloc_context();
picture= avcodec_alloc_frame();
/* put sample parameters */
c->bit_rate = 400000;
/* resolution must be a multiple of two */
c->width = 352;
c->height = 288;
/* frames per second */
c->time_base= (AVRational){1,25};
c->gop_size = 10; /* emit one intra frame every ten frames */
c->max_b_frames=1;
c->pix_fmt = PIX_FMT_YUV420P;                   //BLOCK THREE

/* open it */
if (avcodec_open(c, codec) < 0) {
    fprintf(stderr, "could not open codec\n");
    exit(1);
}
f = fopen(filename, "wb");
if (!f) {
    fprintf(stderr, "could not open %s\n", filename);
    exit(1);
}                                               //BLOCK FOUR

/* alloc image and output buffer */
outbuf_size = 100000;
outbuf = malloc(outbuf_size);
size = c->width * c->height;
picture_buf = malloc((size * 3) / 2); /* size for YUV 420 */
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;              //BLOCK FIVE

/* encode 1 second of video */
for(i=0;i<25;i++) {
    fflush(stdout);
    /* prepare a dummy image */
    /* Y */
    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;
        }
    }                                            //BLOCK SIX

    /* Cb and Cr */
    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;
        }
    }                                           //BLOCK SEVEN

    /* encode the image */
    out_size = avcodec_encode_video(c, outbuf, outbuf_size, picture);
    printf("encoding frame %3d (size=%5d)\n", i, out_size);
    fwrite(outbuf, 1, out_size, f);
}                                              //BLOCK EIGHT

/* get the delayed frames */
for(; out_size; i++) {
    fflush(stdout);
    out_size = avcodec_encode_video(c, outbuf, outbuf_size, NULL);
    printf("write frame %3d (size=%5d)\n", i, out_size);
    fwrite(outbuf, 1, out_size, f);
}                                             //BLOCK NINE

/* add sequence end code to have a real mpeg file */
outbuf[0] = 0x00;
outbuf[1] = 0x00;
outbuf[2] = 0x01;
outbuf[3] = 0xb7;
fwrite(outbuf, 1, 4, f);
fclose(f);
free(picture_buf);
free(outbuf);
avcodec_close(c);
av_free(c);
av_free(picture);
}                                            //BLOCK TEN

以下是我从作者的代码块中得到的内容:
第一块:初始化变量和指针。我在ffmpeg源代码中找不到AVFrame结构体,所以我不知道它引用了什么。
第二块:使用文件中的编解码器,如果找不到则关闭。
第三块:设置示例视频参数。唯一让我困惑的是gop大小。我读过关于帧内帧的内容,但我仍然不知道它们是什么。
第四块:打开文件进行写入...
第五块:这里是他们开始让我困惑的地方。部分原因可能是我不知道AVFrame具体是什么,但为什么他们只使用图像大小的3/2?
第六和第七块:我不明白他们试图通过这个算法实现什么。
第八块:看起来avcodec函数在这里完成所有工作,目前不需要关注它。
第九块:由于它在25帧循环之外,我假设它获取剩余的帧?
第十块:关闭、释放内存等...
我知道这是一大段让人困惑的代码,任何输入都会有所帮助。我在工作中被安排超出了我的能力范围。提前感谢您的帮助。

今天早上花了很多时间查看第六个和第七个块,当对多维数组执行数学运算时,我必须说我和昨天下班时一样卡住了 :( - SetSlapShot
区块六和七有什么神秘的地方吗?注释“/准备一个虚拟图像/”应该为您提供所需的所有信息。所做的一切就是生成一些像素数据,最终将成为随帧索引变化的渐变。 - HonkyTonk
投票关闭,因为问题过于宽泛。请每个问题集中于一个要点。 - Ciro Santilli OurBigBook.com
2个回答

4

我分享我的理解[回复晚了!]

YUV420p:

YUV 420P或YCbCr是RGB表示的替代方案,它包含3个平面,即Y(亮度分量)、U(Y-Cb)和V(Y-Cr)分量。[@Find(Y-Cb-Cr-Cg = Constant,我们不需要存储Cg分量,因为它通常可以计算出来。)]就像RGB888一样,需要每个像素3个字节,YUV420需要每个像素1.5个字节。[@Find(这12位用于以什么比例使用哪个组件?)] 这里的P代表渐进式,意味着帧是渐进式的,也就是说V跟随U,U跟随Y,而YUV帧是一个字节数组,简单明了! 另一个是I代表交错,意味着UV平面数据以特定方式在Y平面数据之间交错。[@Find(什么方式)]


4

正如HonkyTonk已经回答的那样,注释已经说明了:准备一个虚拟图像。我猜你可能会对生成虚拟图像的方式感到困惑,尤其是如果你不熟悉YUV/YCbCr颜色空间的话。 请阅读Wikipedia上的基础知识介绍

许多视频编解码器使用YUV颜色空间。这经常让那些只习惯处理RGB的程序员感到困惑。简而言之,对于这种变体(YUV 4:2:0平面),图像中的每个像素都有一个Y样本(请注意,Y循环迭代每个(x,y)对),而每个2x2像素块共享一个U/Cb样本和一个V/Cr样本(请注意,在第七块中迭代是在宽度/2和高度/2上进行的)。

看起来生成的图案是某种渐变。如果你想产生一个已知的变化,请将Y/Cb/Cr设置为0,虚拟图像将全部变为绿色。将Cb和Cr设置为128,将Y设置为255,就可以得到一个白色的框架;将Y滑动到0以查看黑色;将Y设置为中间的任何值,同时保持Cb和Cr在128,以查看不同灰度的阴影。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接