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 个字节的魔术 cookie,表示此数据包包含 V4L2_MPEG_STREAM_VBI_FMT_IVTV VBI 数据。如果使用所有行,则不再有空间放置位掩码。为了解决这个问题,引入了两个不同的魔术数字
“itv0”:在此魔术数字之后,紧跟两个无符号长整型。第一个无符号长整型的第 0-17 位表示捕获的第一场的哪些行。第一个无符号长整型的第 18-31 位和第二个无符号长整型的第 0-3 位用于第二场。
“ITV0”:此魔术数字假定捕获所有 VBI 行,即它隐式表示位掩码为 0xffffffff 和 0xf。
在这些魔术 cookie(以及 cookie “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)