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)