4. cx2341x 驱动

4.1. 未压缩文件格式

cx23416 可以生成(并且 cx23415 也可以读取)原始 YUV 输出。YUV 帧的格式为 16x16 线性平铺 NV12 (V4L2_PIX_FMT_NV12_16L16)。

该格式为 YUV 4:2:0,每个像素使用 1 个 Y 字节,每四个像素使用 1 个 U 和 V 字节。

数据编码为两个宏块平面,第一个包含 Y 值,第二个包含 UV 宏块。

Y 平面从左到右、从上到下分为 16x16 像素的块。每个块依次逐行传输。

因此,前 16 字节是左上角块的第一行,接下来的 16 字节是左上角块的第二行,依此类推。传输完这个块后,第一个块右侧块的第一行将开始传输,依此类推。

UV 平面从左到右、从上到下分为 16x8 UV 值的块。每个块依次逐行传输。

因此,前 16 字节是左上角块的第一行,包含 8 对 UV 值(共 16 字节)。接下来的 16 字节是左上角块 8 对 UV 值的第二行,依此类推。传输完这个块后,第一个块右侧块的第一行将开始传输,依此类推。

下面的代码提供了如何将 V4L2_PIX_FMT_NV12_16L16 转换为单独的 Y、U 和 V 平面的示例。此代码假定帧为 720x576 (PAL) 像素。

帧的宽度始终为 720 像素,无论实际指定的宽度是多少。

如果高度不是 32 行的倍数,则捕获的视频末尾会缺少宏块,从而无法使用。因此,高度必须是 32 的倍数。

4.1.1. 原始格式 C 示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static unsigned char frame[576*720*3/2];
static unsigned char framey[576*720];
static unsigned char frameu[576*720 / 4];
static unsigned char framev[576*720 / 4];

static void de_macro_y(unsigned char* dst, unsigned char *src, int dstride, int w, int h)
{
unsigned int y, x, i;

// descramble Y plane
// dstride = 720 = w
// The Y plane is divided into blocks of 16x16 pixels
// Each block in transmitted in turn, line-by-line.
for (y = 0; y < h; y += 16) {
        for (x = 0; x < w; x += 16) {
        for (i = 0; i < 16; i++) {
                memcpy(dst + x + (y + i) * dstride, src, 16);
                src += 16;
        }
        }
}
}

static void de_macro_uv(unsigned char *dstu, unsigned char *dstv, unsigned char *src, int dstride, int w, int h)
{
unsigned int y, x, i;

// descramble U/V plane
// dstride = 720 / 2 = w
// The U/V values are interlaced (UVUV...).
// Again, the UV plane is divided into blocks of 16x16 UV values.
// Each block in transmitted in turn, line-by-line.
for (y = 0; y < h; y += 16) {
        for (x = 0; x < w; x += 8) {
        for (i = 0; i < 16; i++) {
                int idx = x + (y + i) * dstride;

                dstu[idx+0] = src[0];  dstv[idx+0] = src[1];
                dstu[idx+1] = src[2];  dstv[idx+1] = src[3];
                dstu[idx+2] = src[4];  dstv[idx+2] = src[5];
                dstu[idx+3] = src[6];  dstv[idx+3] = src[7];
                dstu[idx+4] = src[8];  dstv[idx+4] = src[9];
                dstu[idx+5] = src[10]; dstv[idx+5] = src[11];
                dstu[idx+6] = src[12]; dstv[idx+6] = src[13];
                dstu[idx+7] = src[14]; dstv[idx+7] = src[15];
                src += 16;
        }
        }
}
}

/*************************************************************************/
int main(int argc, char **argv)
{
FILE *fin;
int i;

if (argc == 1) fin = stdin;
else fin = fopen(argv[1], "r");

if (fin == NULL) {
        fprintf(stderr, "cannot open input\n");
        exit(-1);
}
while (fread(frame, sizeof(frame), 1, fin) == 1) {
        de_macro_y(framey, frame, 720, 720, 576);
        de_macro_uv(frameu, framev, frame + 720 * 576, 720 / 2, 720 / 2, 576 / 2);
        fwrite(framey, sizeof(framey), 1, stdout);
        fwrite(framev, sizeof(framev), 1, stdout);
        fwrite(frameu, sizeof(frameu), 1, stdout);
}
fclose(fin);
return 0;
}

4.2. 嵌入式 V4L2_MPEG_STREAM_VBI_FMT_IVTV VBI 数据的格式

作者:Hans Verkuil <hverkuil@xs4all.nl>

本节描述了嵌入在 MPEG-2 程序流中的 VBI 数据的 V4L2_MPEG_STREAM_VBI_FMT_IVTV 格式。此格式部分受 ivtv 驱动(Conexant cx23415/6 芯片的驱动)的一些硬件限制,特别是 VBI 数据的最大大小。任何超出此长度的数据在通过 cx23415 回放 MPEG 流时都会被截断。

这种格式的优点是它非常紧凑,并且所有行的所有 VBI 数据都可以在不超过最大允许大小的情况下存储。

VBI 数据流 ID 为 0xBD。嵌入式数据的最大大小为 4 + 43 * 36 字节,其中包括 4 字节的头部,以及 2 * 18 行 VBI 数据,每行包含 1 字节的头部和 42 字节的负载。任何超出此限制的数据都会被 cx23415/6 固件截断。除了 VBI 行数据之外,我们还需要 36 位用于确定捕获哪些行的位掩码,以及 4 字节的魔术字,表示此数据包包含 V4L2_MPEG_STREAM_VBI_FMT_IVTV VBI 数据。如果所有行都已使用,则不再有空间容纳位掩码。为解决此问题,引入了两个不同的魔术数字:

‘itv0’:此魔术数字后跟两个无符号长整型。第一个无符号长整型的位 0-17 表示捕获了第一个场的哪些行。第一个无符号长整型的位 18-31 和第二个无符号长整型的位 0-3 用于第二个场。

‘ITV0’:此魔术数字假定所有 VBI 行都被捕获,即它隐含地表示位掩码为 0xffffffff 和 0xf。

在这些魔术字(如果是 ‘itv0’ 魔术字,则还包括 8 字节的位掩码)之后,捕获的 VBI 行开始传输。

对于每一行,第一个字节的最低有效 4 位包含数据类型。可能的值如下表所示。负载位于接下来的 42 字节中。

以下是可能的数据类型列表

#define IVTV_SLICED_TYPE_TELETEXT       0x1     // Teletext (uses lines 6-22 for PAL)
#define IVTV_SLICED_TYPE_CC             0x4     // Closed Captions (line 21 NTSC)
#define IVTV_SLICED_TYPE_WSS            0x5     // Wide Screen Signal (line 23 PAL)
#define IVTV_SLICED_TYPE_VPS            0x7     // Video Programming System (PAL) (line 16)