LinuxでUSBビデオカメラを使用します。 パート2

こんにちは、Habr!



Linuxでのカムコーダープログラミングに関する一連の記事を続けます。 最初の部分[1]では、ビデオデバイスの主要なパラメーターを開いて読み取るメカニズムを調べました。 簡単なcatvdユーティリティが作成されました。 今日は小さなプログラムの機能を拡張します 、ただし、最初にioctl関数のラッパーを作成する必要があります。

Xioctlメソッドコード
int videodevice::xioctl(int fd, int request, void *arg) { int r; r = ioctl (fd, request, arg); if(r == -1) { if (errno == EAGAIN) return EAGAIN; stringstream ss; ss << "ioctl code " << request << " "; errno_exit(ss.str()); } return r; }
      
      







このラッパーを使用すると、エラーが発生した場合にプログラムを中断してメッセージを表示できます。



カメラから画像を読み取ってファイルに保存してみましょう。

GetFrameメソッドコード
 void videodevice::getFrame(string file_name) { initMMAP(); startCapturing(); long int i = 0; for (;;) { if(readFrame(file_name)) break; i++; } cout << "iter == " << i << endl; stopCapturing(); freeMMAP(); }
      
      







readFrameメソッド-受信した画像の読み取りと処理を行います。

メソッドinitMMAP()、freeMMAP()-デバイスのメモリバッファを作成/クリアします。

メソッドstartCapturing()、stopCapturing()-ビデオデバイスのストリーミングモードを有効/無効にします。 カメラ上のこれらの関数の存在は、フラグV4L2_CAP_STREAMING [*]で確認できます。



initMMAPメソッドを調べてみましょう

InitMMAPメソッドコード
 void videodevice::initMMAP() { struct v4l2_requestbuffers req; req.count = 1; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; xioctl(fd, VIDIOC_REQBUFS, &req); devbuffer = (buffer*) calloc(req.count, sizeof(*devbuffer)); struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; xioctl(fd, VIDIOC_QUERYBUF, &buf); devbuffer->length = buf.length; devbuffer->start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (devbuffer->start == MAP_FAILED) errno_exit("mmap"); }
      
      







関数VIDIOC_REQBUFS [↓]を使用すると、デバイス内のメモリバッファーを初期化できます。 V4l2_requestbuffers構造体は初期化パラメーターを設定します

 struct v4l2_requestbuffers { __u32 count; //  __u32 type; //    __u32 memory; //   . __u32 reserved[2]; //   };
      
      





バッファを初期化したら、メモリ領域にマッピングする必要があります(マッピング)。

関数VIDIOC_QUERYBUF [↓]を使用すると、メモリマッピング領域の作成に使用されるバッファーパラメーターを読み取ることができます。 v4l2_bufferの構造は大きいため、必要なフィールドについて説明します。

 struct v4l2_buffer { //  VIDIOC_QUERYBUF    __u32 index; //     ( v4l2_requestbuffers.cout > 1) __u32 type; //  (   v4l2_requestbuffers.type) //  VIDIOC_QUERYBUF        memory-mapping union { __u32 offset; //       } m; __u32 length; //   };
      
      





システム関数mmap() [3]を使用すると、RAM内のファイルまたはデバイスのメモリ領域を表示できます。 mmap()を使用するには、接続する必要があります

 <sys/mman.h>
      
      





次に、カメラをキャプチャモードに切り替える必要があります。

StartCapturingメソッドコード
 void videodevice::startCapturing() { struct v4l2_buffer buf; memset(&buf, 0, sizeof(buf)); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = 0; xioctl(fd, VIDIOC_QBUF, &buf); enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; xioctl(fd, VIDIOC_STREAMON, &type); }
      
      







関数VIDIOC_QBUF [↓]は、デバイスドライバーによってバッファを処理キューに入れます。 使用されるフィールドは、VIDIOC_REQBUFSまたはVIDIOC_QUERYBUFと同じです。

VIDIOC_STREAMON [↓]関数は、カメラをキャプチャモードに切り替えます。



これでカメラの電源が入り、画像がキャプチャされます。 しかし、写真はまだ受信する必要があります。

ReadFrameメソッドコード
 int videodevice::readFrame(string file_name) { struct v4l2_buffer buf; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if (xioctl(fd, VIDIOC_DQBUF, &buf) == EAGAIN) return 0; buffer *temp = devbuffer; FILE *out_file = fopen(file_name.c_str(),"w"); fwrite(temp->start,temp->length,1,out_file); fclose(out_file); return 1; }
      
      







VIDIOC_DQBUF [↓]関数は、ドライバー処理キューからバッファーを解放します。 その結果、EAGAINエラーが発生する場合があります。 これには危険はありません; VIDIOC_DQBUFを再度呼び出す必要があります。 これは、ドライバーがまだ要求を処理しており、キューからバッファーを解放できないためです。 この関数の実装が成功すれば、私たちは自分の絵を手に入れることができます。 記事の冒頭で、イテレータがコードに追加されました。 反復子を使用すると、VIDIOC_DQBUFが成功するまでアイドルループが繰り返される回数を追跡できます。



編集

 $ cmake . $ make
      
      







プログラムの出力は次のとおりです。

 $./getimage Open device /dev/video0 Init mmap Start capturing read frame from buffer and write to file iter == 831013 stop Capturing free mmap Close device /dev/video0
      
      





「iter == 831013」から、画像がかなりの時間バッファに落ちていることがわかります。 速度を上げるには、いくつかのバッファを使用して、最初の空き領域から画像を引き出すことができます。



今日は、メモリバッファの初期化と、そこからの画像の読み取りと見なされていました。 画像は生の形式で保存されます。 Shotwellプログラムを開くことができます。 次の記事では、画像のテクスチャへの出力(SDL2経由)が考慮され、一部の画像形式とカメラ設定が影響を受けます。



この記事で使用されているリソース:

  1. LinuxでUSBビデオカメラを使用します。 パート1
  2. Linux APIのビデオ
  3. mmapの詳細()


使用される機能へのリンク:



ソースコード



All Articles