この WWW ページはまだ未完成で、書き加えるべき部分が多くあります。文章および内容に関する間違いなどの指摘を歓迎します。 メールでお知らせください。質問、感想などもお待ちしております。
Video4Linux は、その名のとおり Linux でビデオキャプチャデバイスを使うための API 仕様の名前です。ビデオキャプチャだけでなく、チューナ操作のための API も揃っており、これを使ってテレビや AM/FM ラジオ、Teletext などの放送も楽しむことができます。
この API に準拠したビデオデバイスには、今のところ次のものがあります。これらのドライバは、2.1 系列のカーネルソースコードの配布に標準的に含まれています。
この文章では、 Video4Linux に準拠した Bt848 用ドライバである bttv ドライバを用いて、 Video4Linux API を用いたプログラミングの方法を説明していきたいと思います。この Bt848 は、メインメモリやビデオカードの VRAM に CPU を介せず直接データを転送することができるすぐれものの PCI ビデオキャプチャチップです。
今回用いるカードは KOUWELL KW-606 という PCI カードです。秋葉原で 10,000 円くらいで多くでまわっています。私は、今は亡き A-Master で購入しました。 KW-606 にはビデオ入力に S 端子入力、さらにチューナがついています。このチューナも Video4Linux を使って操作することができます。
さて、この Video4Linux の機能を駆使して X Windows System でテレビを見たりするのは xawtv や xwintv といった優秀なアプリケーションにまかせておくことにして、本稿ではとりあえずキャプチャした結果をメインメモリに効率よく取りこむ方法について説明します。メモリに取りこめてしまえば、もうこっちのものですからね。煮るなり焼くなり好きなよーにして遊んでください。
今回用いる bttv ドライバは Linux カーネル 2.1.119 に付属のものですが、 Linux 2.0 系列をお使いの方も、 bttv ドライバの WWW ページ から最新のドライバをインストールすれば、同じようにプログラミングできるはずです。
なお KOUWELL KW-606 でなくとも、 Bt848 を塔載した安価なビデオキャプチャカードは、多くの種類が出回っています。それらのカードでは本稿のプログラムは少々の変更で動作させることが可能と思われます。また、Bt848 を使ったカードでなくても、API が Video4Linux に準拠したデバイスドライバがあれば、同じ流れが適用できるはずです。
bttv ドライバを用いる場合のビデオキャプチャの手順は、だいたい以下に従います。 bttv 以外の場合、ドライバによっては
mmap
など一部の操作がその機能がないために実行できなかったりします。その詳細は、最初の Video4Linux
デバイス仕様の取得によって知ることができます。
Video4Linux デバイス仕様の取得
キャプチャカードに関する情報を得ます。
キャプチャカードの素性を決めうちしているのなら、 やらなくてもかまいません。
使用可能なチャネル情報の取得
キャプチャカードで使用可能なチャネル(映像ソース)の種類を取得します。
これも、キャプチャカードの素性を決めうちしているのなら やる必要はありません。
キャプチャするチャネルの設定
キャプチャするチャネルを選択します。 同時に、映像信号のフォーマット(NTSC, PAL,
SECAM など)も 選択します。
ピクチャの設定
明るさ、コントラスト、ヒューなどのパラメータを設定します。
mmapmmap
システムコールによって、 キャプチャデータを取得するためのバッファ領域を確保します。
キャプチャ開始の指示mmap
したバッファに対して、 キャプチャを開始するように指示します。
キャプチャ終了まで待つ
他にすることがなければ、キャプチャ処理が終了するまで待ちます。
画像処理
キャプチャ処理の結果を用いて、目的の画像処理を行います。
さて、それでは上記の手順に従ったサンプルのソースコード v4ltest.c
を見ていくことにします。常に
API 仕様
およびヘッダファイル videodev.h
を参照しながら、このソースコードと以下の説明を読むようにしてください。
デバイス仕様を取得するには、オープンした Video4Linux デバイスに対して VIDIOCGCAP
ioctl
を発行します。 ioctl
については各種 UNIX
の解説書およびマニュアルページを参考にしてください。ほとんど ioctl
の練習のようなコードです。
27 struct video_capability vd; ... 44 /* Video4Linux デバイス仕様の取得 */ 45 if(ioctl(fd, VIDIOCGCAP, &vd)<0) { 46 perror("ioctl(VIDIOCGCAP)"); 47 return -1; 48 }
これをやるとvd
の中に fd
がさす Video4Linux デバイスの仕様が格納されます。
struct video_capability
の詳細については、 API 仕様 およびヘッダファイル
videodev.h
を参照してください。 v4ltest.c
の
49--68 行目でこの結果をすべて書きだしています。 KW-606 で実行した場合は、次のようになりました。
vd.name: "BT848(Miro)" vd.type=0x000000ef CAPTURE TUNER TELETEXT OVERLAY CLIPPING FRAMERAM SCALES vd.channels=4 vd.audios=4 vd.maxwidth=768 vd.maxheight=576 vd.minwidth=32 vd.minheight=32
チャネルの数は VIDIOCGCAP ioctl
によって struct
video_capability
の channels
メンバに格納されているので、その数だけ
VIDIOCGCHAN ioctl
を発行してすべてのチャネルに関する情報を集めます。
28 struct video_channel vc[10]; ... 78 /* 各チャネル仕様取得 */ 79 for(n=0; n<vd.channels; n++) { 80 vc[n].channel=n; 81 if(ioctl(fd, VIDIOCGCHAN, &vc[n])<0) { 82 perror("ioctl(VIDIOCGCHAN)"); 83 return -1; 84 } ... 98 }
struct video_channel
の詳細については、 API 仕様 およびヘッダファイル
videodev.h
を参照してください。 v4ltest.c
の
85--97 行で、各チャネルに対してこの結果を書きだしています。 KW-606 で実行した場合は、次のようになりました。
vc[0].channel=0 vc[0].name="Television" vc[0].tuners=1 vc[0].flags=0x00000003 TUNER AUDIO vc[0].type=0x00000001 TV vc[0].norm=0 vc[1].channel=1 vc[1].name="Composite1" vc[1].tuners=0 vc[1].flags=0x00000002 AUDIO vc[1].type=0x00000002 CAMERA vc[1].norm=0 vc[2].channel=2 vc[2].name="SVHS" vc[2].tuners=0 vc[2].flags=0x00000002 AUDIO vc[2].type=0x00000002 CAMERA vc[2].norm=0 vc[3].channel=3 vc[3].name="Composite3" vc[3].tuners=0 vc[3].flags=0x00000002 AUDIO vc[3].type=0x00000002 CAMERA vc[3].norm=0
キャプチャの対象とするチャネルを VIDIOCSCHAN ioctl
によって選びます。ここでは、 KW-606
であることを前提にしてチャネル #1("Composite1", ビデオ端子入力) を選択するように決めうちしてしまっています。
101 /* "Composite1"を選択 */ 102 vc[1].norm=VIDEO_MODE_NTSC; 103 if(ioctl(fd, VIDIOCSCHAN, &vc[1])<0) { 104 perror("ioctl(VIDIOCSCHAN)"); 105 return -1; 106 }
この ioctl
は API 仕様
によると整数の引数をとるとされていますが、 Linux 2.1.119 付属の bttv ドライバでは上記のように struct
video_channel
を渡さないと ioctl
がエラーになってしまいました。
API 仕様
には言及がありませんが、さらに重要な設定として、 struct video_channel
のメンバ
norm
に、ビデオフォーマットを指定する値を書きこんでおきます。この値は ヘッダファイル
videodev.h
にある次の値がそのまま使えるようです。
#define VIDEO_MODE_PAL 0 #define VIDEO_MODE_NTSC 1 #define VIDEO_MODE_SECAM 2 #define VIDEO_MODE_AUTO 3
以上については bttv を用いたアプリケーションである xawtv のソースコードを参考にしました。これらは、bttv ドライバ固有の仕様の可能性があります。
明るさや色彩の設定を、 VIDIOCSPICT ioctl
によって変更できます。
29 struct video_picture vp; ... 109 /* 色調とかの設定(たいがい 0-65535) */ 110 vp.brightness=32767; 111 vp.hue=32767; 112 vp.colour=32767; 113 vp.contrast=32767; 114 vp.whiteness=32767; 115 vp.depth=24; /* color depth */ 116 vp.palette=VIDEO_PALETTE_RGB24; /* パレット形式 */ 117 if(ioctl(fd, VIDIOCSPICT, &vp)) { 118 perror("ioctl(VIDIOCSPICT)"); 119 return -1; 120 }
struct video_picture
の詳細については、 API 仕様 およびヘッダファイル
videodev.h
を参照してください。設定値は 0 から 65535 に正規化されています。なお、メンバ
depth
および palette
の設定は、このサンプルプログラムでは意味を持たないようです。
mmap
システムコールによって、 Bt848 がデータを転送するメモリ空間を指定します。 mmap
で確保すべきメモリの量は、 VIDIOCGMBF ioctl
によって取得できます。
30 struct video_mbuf vm; ... 123 /* mmap 情報の取得 */ 124 if(ioctl(fd, VIDIOCGMBUF, &vm)<0) { 125 perror("ioctl(VIDIOCGMBUF)"); 126 return -1; 127 }
struct video_mbuf
の詳細については、 API 仕様 およびヘッダファイル
videodev.h
を参照してください。 v4ltest.c
の
128--132 行で、この結果を書きだしています。 KW-606 で実行した場合は、次のようになりました。
vm.size=0x002a2000 vm.frames=2 vm.offsets[0]=0x00000000 vm.offsets[1]=0x00151000
メンバ size
は、 mmap
によって確保すべきメモリの量です。メンバ
frames
はこのデバイスが扱うことのできるフレームの数です。メンバ offsets
は、各フレームの
mmap
によって得られたメモリ領域の先頭からのオフセットを格納する、要素が frames
個の配列です。この例ではフレームは 2 つ存在し、それぞれのフレームは 0x151000
の大きさを持つことがわかります。合計のサイズは 0x15100*2=0x2a2000
です。
この struct video_mbuf
の内容を用いて、 mmap
システムコールを発行します。
mmap
システムコールの詳細については各種 UNIX の解説書およびマニュアルページを参考にしてください。
char *map; ... 135 /* mmap */ 136 if((map=mmap(0, vm.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0))==(char *)-1) { 137 perror("mmap"); 138 return -1; 139 }
これによって、 Bt848 用のキャプチャデータが転送されるバッファが map
を先頭とするメモリ空間に 2
画面分確保されました。
さて、以上でキャプチャを行う準備はできました。実際にキャプチャを始めるには、VIDIOCMCAPTURE ioctl
を発行します。
31 struct video_mmap vmm; ... 142 /* frame #0 にキャプチャ開始指示 */ 143 vmm.frame=0; 144 vmm.width=WIDTH; 145 vmm.height=HEIGHT; 146 vmm.format=VIDEO_PALETTE_RGB24; 147 if(ioctl(fd, VIDIOCMCAPTURE, &vmm)<0) { 148 perror("ioctl(VIDIOCMCAPTURE)"); 149 return -1; 150 }
struct video_mmap
の詳細については、 API 仕様 およびヘッダファイル
videodev.h
を参照してください。メンバ frame
に、キャプチャすべきフレーム番号を指定します。フレームは struct video_mbuf
のメンバ
frames
の数だけあります。さらに、キャプチャのサイズおよびフォーマットは、メンバ width
,
height
, format
によってこのときに指定することができます。
この ioctl
システムコールはキャプチャが完了する前に終了し、すぐに制御が戻ります。
VIDIOCMCAPTURE
ioctl
を発行しただけではキャプチャ処理はまだ完了していないので、画像処理に移る前にこれを待たねばなりません。それには
VIDIOCSYNC ioctl
を発行します。この ioctl
はキャプチャ処理終了を待つべきフレーム番号を指定する、整数の引数をひとつとります。
int n; ... 153 /* frame #0 キャプチャ終了待ち */ 154 n=0; 155 if(ioctl(fd, VIDIOCSYNC, &n)<0) { 156 perror("ioctl(VIDIOCSYNC)"); 157 return -1; 158 }
この ioctl
システムコールは、キャプチャ処理が終了するまでブロックし制御を戻しません。
以上で一画面分のキャプチャが完了しました。あとは map
で始まるバッファにアクセスして必要な画像処理を行います。
v4ltest.c
では、キャプチャしたデータを標準出力に
ppm フォーマットとして書きだすことにしました。各ピクセル 24bit のパック形式で格納されていますが、そのままでは raw ppm フォーマットとは
RGB の順序が異なるため、すべてのピクセルに対してこれを正すということをやっています。
25 char *map, *p; 26 char tmp; ... 161 /* RGB 順番の入れかえ */ 162 for(n=0, p=map+vm.offset[0]; n<WIDTH*HEIGHT; n++, p+=3) { 163 tmp=p[0]; p[0]=p[2]; p[2]=tmp; 164 } 165 166 167 /* ppm の書きだし */ 168 printf("P6 %d %d 255\n", WIDTH, HEIGHT); fflush(stdout); 169 write(1, map+vm.offsets[0], WIDTH*HEIGHT*3);
bttv ドライバのように、 struct video_mbuf
のメンバ frames
が 2
以上の場合、キャプチャ処理と画像処理をそれぞれ別のフレームに対して行うことにより、これらを同時に行うことができます。 frames
が
2 の場合を例にとって説明します。
ioctl
を発行します (VIDIOCMCAPTURE
ioctl
)。
ioctl
を発行します。
VIDIOCSYNC ioctl
)。
ioctl
を発行します。
VIDIOCMCAPTURE ioctl
は、以前のキャプチャ処理が終了する前に続けて発行しても有効なことに注意します。 1.
と 2. で連続して VIDIOCMCAPTURE ioctl
を発行した結果、フレーム #0
のキャプチャ処理が完了すると自動的にフレーム #1 のキャプチャ処理に移ります。
これによって、同時にキャプチャ処理と画像処理を行うことができ、さらに CPU も効率的に活用することができるようになります。
サターンやプレイステーションなどのゲーム機の映像信号を入力し、コンピュータビジョンの技術を駆使して人間には到底真似のできないような完璧なプレイをやってのけるプログラムを作っていただける方はいませんか?
ぷよぷよとか格闘げーとか対戦ものだとかなり熱いかもしれません。何種類か作って PC 同士で戦わせたりして。 PC とゲーム機コントローラとのインタフェースに関しては、相談に応じますので、ご連絡ください。
http://roadrunner.swansea.uk.linux.org/v4l.shtml
http://www.thp.Uni-Koeln.DE/~rjkm/linux/bttv.html
http://www.cs.tu-berlin.de/~kraxel/linux/index.html#xawtv
http://www.mathematik.uni-kl.de/~wenk/xwintv.html