uvcドライバメモ

uvc(http://developer.berlios.de/projects/linux-uvc)を使って,
QcamR Pro for Notebooks
http://www.logicool.co.jp/index.cfm/webcam_communications/webcams/devices/3055&cl=jp,ja
からキャプチャするソースを見せてもらった.


バイスとかドライバとか聞くだけで,混乱するのだが
今回のはあんまり低レイヤーの部分ではなかったのでがんばって読んでみた.
この中にycbcr_to_rgb()という関数が存在する.
名称通りRGB表色系をYCbCr表色系(YUV,YCCとも呼ばれる)に変換するものである.
変換式はこちら.http://image-d.isp.jp/commentary/color_cformula/YCbCr.html
R'G'B'はRGBをガンマ補正したものです.
これも含め他の色空間変換の式があるので,参考にしてみます.wikipedia:色空間
このエントリーはこの話.
その前に長い前置きを.


uvc_init_mmap()でデバイスをメモリマップ*1

mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset)

マップされた先は多分画像フレームのバッファに使われていると思う.
# つまり画素データ?
これをn_buffers分行う.


キャプチャ本体のuvc_read_frame_on_***(). # 事情により隠し

p = buffers[buf.index].start

で画像フレームのバッファをpに代入している.
という意味で捉えないとその後が合わない.(後述)
また,右辺がよくわからない.

struct v4l2_buffer buf
struct { void *start; size_t length} *buffers = NULL;
static unsigned int n_buffers = 0;

でそれぞれグローバル宣言されている.


buffers[buf.index]は先のmmap()で取得したバッファごとのポインタが入っている.
これを取得するときにはn_buffersを0からreq.count(4が代入されている)*2までループさせて取得している.

for (n_buffers = 0; n_buffers < req.count; n_buffers++) {
	struct v4l2_buffer buf;
	...
	if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
		...
	}
	...
	buf.index = n_buffers;
	...
	buffers[n_buffers].start = mmap(.....
	...

こんな感じに.v4l2_buffersについてはこちら
http://www.linuxtv.org/downloads/video4linux/API/V4L2_API/spec/x5953.htm#V4L2-BUFFER

だけどn_bufferはグローバルでstaticであり,この関数がuvc_read_frame_on_***()より
先に行われているので,n_buffersは多分5になっていると思う.
すると結局,
先のp = buffers[buf.index].startでいうbuf.indexは5になると考えられる.

勘違いしていた.ioctl()の中で使用されている&bufで
indexのフィールドを参照しているようで*3
もしかしたらこれを使って別々のメモリ領域にマップされている気がする.
そしてそれを探す手立てがbuf.indexなのかしら.


また,上記のforブロックの中でグローバルと同じ変数を宣言しているのはよいのだが,
そうすると...ああそうか.
グローバル宣言の方のbufはuvc_read_frame_on_***()だけで使っているのか.
そうするとp = buffers[buf.index].startのbuf.indexは
n_buffersの代入されたbufではなくて,グローバル宣言されている方だな.
uvc_read_frame_on_***()の前に呼び出されたuvc_start_capture()で代入されている0だな.
なんで0じゃなくて1が?
つまり,0番目からバッファは4つ確保されていて1番目を使用するということなのかしら.


とりあえずここまでが前置き.
p = buffers[buf.index].startに画素データが入っていると考えたのは,次の理由がある.
次のコードを見た方が早いので書くことにする.

q = キャプチャデータを保存する先の画素データ開始ポインタ;
for (i = 0; i< frame_size; ++i) {
	ycbcr_to_rgb(&p[0], &p[1], &[3], &r0, &g0, &b0);
	ycbcr_to_rgb(&p[2], &p[1], &[3], &r1, &g1, &b1);
	
	*q++ = r0;
	*q++ = g0;
	*q++ = b0;
	*q++ = r1;
	*q++ = g1;
	*q++ = b1;

	p += 4;
}

qには画素データがRGBデータが,
1バイトずつこの順でシーケンシャルに書かれることが期待されている.
ycbcr_to_rgb(&p[0], &p[1], &[3], &r0, &g0, &b0)
ycbcr_to_rgb(&p[2], &p[1], &[3], &r1, &g1, &b1)
は,r0, g0, b0が1画素目のRGB.r1, g1, b1が2画素目のRGB.
p[*]が不定期に並んでいるのは何故だろうと調べていると,
このページが役にたった.
http://www.linuxtv.org/downloads/video4linux/API/V4L2_API/spec/r4339.htm
このページにあるExampleがイメージフォーマットの例である.
startというのが(多分)pに代入されたbuffers[buf.index].startのことだろうか.
ここから例にあるとおりに並んでいるという.
これは4×4のピクセルイメージだという.
でも画素の数が足りない?
# RGBであれば一画素がR00G00B00で表されるから横セルが足りないかも.


このときはYUYVというものがV4L2の中だけで使われている表現だと考えていたのだが,
調べてみると
http://kumikomizine.jp/article/detail/49
http://st6uc.blog119.fc2.com/?mode=m&no=27
http://c.fc2.com/m.php?_mfc2a=i&_mfc2u=http%3A%2F%2Fwww.nnet.ne.jp%2F%7Ehi6%2Flab%2Fpixel%2Findex.html
YCbCrのことであり,YCC,YUV422と呼ばれる画像フォーマットらしい.
Cbを横2画素まとめて1バイト.
Crも同じ.
これをY1画素とCbをまとめて1ピクセル,Y1画素とCrをまとめて1ピクセルとすることで
RGBに対して2/3の容量になるわけだ.なるほど.
余談だが,NTSCの転送手段にも利用されているようです.


というわけでpすなわちbuffers[buf.index].startは画素データなのである.


さて,YUYVの項でまだよく分からない部分がある.
色差はRGBでいう各ピクセル毎に値を持っているはず(と思う)だが,
各CrCbは2画素のデータをどうまとめたものなのか,ということ.
その手の文献*4の色空間変換の部分を読んでみたのだが,いまいち分からず.
色差はRGBでいう各ピクセル毎に4ビットずつ持っていて,
上位4バイト,下位4バイトにまとめて1バイトだと考えていたのだが,
これだと変換式(冒頭で挙げたISP-JPのサイトを参考)と合わない.
かといって2画素目を1画素目から差し引いた値が入っているとすれば,
差分が入っているだけなので,RGBにするときに各画素の値を復元できない.
(つまり1画素目の値がないので差分を足しても2画素目も復元できない)
そのようなことも変換式の中では行われていない.
これは一体どういうことなのだろうか.


と分からないまま中途半端に終わる.

関係ないけど興味あったので.

家電,PCの映像端子について
http://www.kanshin.com/diary/1013137