YUV422対応のUSBカメラをVMware Player上のLinuxでV4L2を使って動作させてみることにしました。
環境
- ELECOM UCAM-DLE300TBK
- ELECOM UCAM-DLM130HSV
- luvcview-20070512.tar.gz
USBカメラの動作テスト
luvcviewを使って動作テストを行った。
素でインストールしたDebian wheezy 32bit/64bitで動作した。
VMware player上では真っ黒で画像が表示されなかった。
luvcviewを実行した後は、自作プログラムが動作しない場合があるので、一度カメラを抜き差しする。
luvcview -f YUV -s 640x480 luvcview -f YUV -s 320x240 luvcview -f YUV -s 240x120
ELCOM UCAM-DLE300TBK
対応フォーマット
実際にUSBカメラ「ELCOM UCAM-DLE300TBK」を接続してみると、V4L2では以下のフォーマットしか対応していなかった。
pixelformat = 'YUYV', description = 'YUV 4:2:2 (YUYV)' pixelformat = 'MJPG', description = 'MJPEG'
ヘッダファイル
「/usr/include/linux/videodev2.h」の317、370行目に記述されている。
#define V4L2_PIX_FMT_YUYV v4l2_fourcc('Y', 'U', 'Y', 'V') /* 16 YUV 4:2:2 */ #define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G') /* Motion-JPEG */
YUYV→RGB変換式
実質、YUYVにしか対応していないのでRGBへの変換式を作る。
U = U - 128 V = V - 128 R = Y + 1.402V G = Y - 0.344U - 0.714V B = Y + 1.772U
係数に256(8bit)を掛ける(四捨五入)と、
U = U - 128 V = V - 128 R = Y + 359V G = Y - 88U - 183V B = Y + 454U
YUV422のポンチ絵
必要なライブラリ
sudo aptitude install libjpeg62-dev sudo aptitude install libgtk2.0-dev sudo aptitude install libgtk-3-dev
コンパイル・実行
make -f Makefile-yuv-gtk2 ./v4l2-yuv -d /dev/video0 又は、 ./v4l2-yuv
参考
「8bitフルスケールYUVと8bitフルスケールRGBの相互変換」項目の式「YUV to RGB 」。
http://vision.kuee.kyoto-u.ac.jp/~hiroaki/firewire/yuv.html
参考
fourcc.orgにあるYUYVの説明。
http://www.fourcc.org/yuv.php#YUYV
参考
V4L2を使ったYUYVのサンプル。
http://ivis-mynikki.blogspot.jp/2011/01/v4l2_31.html
参考
10進数を2進数に変換にする計算方法。
ググってください。
参考
EIZOのYUVの資料。
http://www.eizo.co.jp/products/tech/files/2010/WP10-009.pdf
参考
YUYV→RGB変換の方法。
http://marsee101.blog19.fc2.com/blog-entry-1300.html
参考
YUV420→RGB変換の方法。
http://code.google.com/p/android/issues/detail?id=823
参考
C++で作ったRGB⇔YUV変換アプリケーションとソース。
http://www.codeproject.com/Articles/402391/RGB-to-YUV-conversion-with-different-chroma-sampli
Makefile
# Makefile for v4ls-yuv.c CC = gcc #CFLAGS = -Wall -g CFLAGS = GTK_INCLUDE = `/usr/bin/pkg-config --cflags gtk+-2.0` GTK_LIB = `/usr/bin/pkg-config --libs gtk+-2.0` JPEG_LIB = -l jpeg TARGET = v4l2-yuv OBJECTS = color.o utils.o $(TARGET).o $(TARGET): $(OBJECTS) $(CC) -o $@ $(JPEG_LIB) $(GTK_LIB) $^ rm -f $(OBJECTS) .c.o: $(CC) -c $(CFLAGS) $(GTK_INCLUDE) $< clean: rm -f $(OBJECTS) $(TARGET)
その他のソース
YUV422からRGB24の変換にluvcviewのソースの一部を使っている。
http://sourceforge.jp/projects/sfnet_v4l-lib/releases/
・utils.h
・utils.c
・color.h
・color.c
v4l2-yuv.c
// // V4L2 capture sample program for YUV422 // // 2013.12.10 modified // 2013.12.08 release // // ■環境 // gcc ver.4.7.2 // Gtk+2.0 // Jpeg Library ver.6 // Linux kernel ver.3.2 // // ■コンパイル・実行 // make -f makefile // // ./v4l2-yuv // ./v4l2-yuv -d /dev/video0 // // ■luvcview(http://sourceforge.jp/projects/sfnet_v4l-lib/releases/) // utils.c // color.c // color.h // #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <getopt.h> // getopt_long() #include <fcntl.h> // low-level I/O #include <unistd.h> #include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/time.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <asm/types.h> // videodev2.h #include <linux/videodev2.h> #include <gtk/gtk.h> #include <jpeglib.h> // JPEG Library #include "utils.h" // luvcview #include "color.h" // luvcview // V4L2 format for YUYV (YUV422) #define PIXELFORMAT V4L2_PIX_FMT_YUYV // 設定項目 ここから static int save_jpeg = 0; // JPEGで保存 static int save_ppm = 0; // PPMで保存 #define CAPTURE_WIDTH 320 // キャプチャの横サイズ #define CAPTURE_HEIGHT 240 // キャプチャの縦サイズ #define CAPTURE_CHANNEL 3 // キャプチャのchannel //#define USE_GTK_TIMEOUT_ADD 0 // gtk_timeout_add()を使用する。無効の場合はgtk_idle_add()を使用する。 //#define GTK_INTERVAL 33 // キャプチャ間隔[ms] gtk_timeout_add()を使用した場合に使用する。 // ここまで #define PKGNAME "V4L2 Capture" #define CLEAR( x ) memset( &(x), 0, sizeof (x) ) #define BTN_MAX 3 // GTK のボタン数 static void CheckArg( int, char ** ); static void Usage( FILE *, int, char ** ); static void Open_Device( void ); static void Close_Device( void ); static void Init_Device( void ); static void Init_userp( unsigned int ); static void Init_mmap( void ); static void Init_read( unsigned int ); static void UnInit_Device( void ); static void Start_Capture( void ); static void Stop_Capture( void ); static void MainLoop( void ); static int Read_Frame( void ); static void Process_Image( const void * ); static int Xioctl( int, int, void * ); static void Errno_Exit( const char * ); static void Start_Proc( GtkWidget *, gpointer ); static void Stop_Proc( GtkWidget *, gpointer ); static void Quit_Proc( GtkWidget *, gpointer ); static void CreateCaptureWindow( void ); static void GetImageMemory( void ); typedef enum { IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR, } io_method; struct buffer { void *start; size_t length; }; static char *dev_name = NULL; static io_method io = IO_METHOD_MMAP; static int fd = -1; struct buffer *buffers = NULL; static unsigned int n_buffers = 0; static unsigned int ImgNum = 0; // 画像番号 static guint gtk_id; static unsigned char *rgb; // GTK GtkWidget *gtk_top; GtkWidget *gtk_second; GtkWidget *gtk_darea; GtkWidget *gtk_table; GtkWidget *gtk_label; GtkWidget *gtk_btn; // // エラー処理 // static void Errno_Exit( const char *s ) { fprintf(stderr, "%s error %d, %s\n", s, errno, strerror (errno)); exit( EXIT_FAILURE ); } // // デバイス操作 // static int Xioctl( int fd, int request, void *arg ) { int r; do { r = ioctl( fd, request, arg ); } while ( r == -1 && errno == EINTR ); return r; } /* // // translate YUV422Packed to rgb24 // luvcview(http://sourceforge.jp/projects/sfnet_v4l-lib/releases/) // utils.c // color.c // color.h // // ここから luvcview #define CLIP(color) (unsigned char)(((color)>0xFF)?0xff:(((color)<0)?0:(color))) static int *LutRv = NULL; static int *LutGu = NULL; static int *LutGv = NULL; static int *LutBu = NULL; void initLut(void) { int i; #define CoefRv 1402 #define CoefGu 714 // 344 #define CoefGv 344 // 714 #define CoefBu 1772 LutRv = malloc(256*sizeof(int)); LutGu = malloc(256*sizeof(int)); LutGv = malloc(256*sizeof(int)); LutBu = malloc(256*sizeof(int)); for (i= 0;i < 256;i++){ LutRv[i] = (i-128)*CoefRv/1000; LutBu[i] = (i-128)*CoefBu/1000; LutGu[i] = (128-i)*CoefGu/1000; LutGv[i] = (128-i)*CoefGv/1000; } } void freeLut(void) { free(LutRv); free(LutGu); free(LutGv); free(LutBu); } unsigned char R_FROMYV(unsigned char y, unsigned char v) { return CLIP((y) + LutRv[(v)]); } unsigned char G_FROMYUV(unsigned char y, unsigned char u, unsigned char v) { return CLIP((y) + LutGu[(u)] + LutGv[(v)]); } unsigned char B_FROMYU(unsigned char y, unsigned char u) { return CLIP((y) + LutBu[(u)]); } unsigned int Pyuv422torgb24(unsigned char * input_ptr, unsigned char * output_ptr, unsigned int image_width, unsigned int image_height) { unsigned int i, size; unsigned char Y, Y1, U, V; unsigned char *buff = input_ptr; unsigned char *output_pt = output_ptr; size = image_width * image_height /2; // printf("size; %d \n", size); for (i = size; i > 0; i--) { Y = buff[0] ; U = buff[1] ; Y1 = buff[2]; V = buff[3]; buff += 4; *output_pt++ = R_FROMYV(Y,V); *output_pt++ = G_FROMYUV(Y,U,V); //b *output_pt++ = B_FROMYU(Y,U); //v *output_pt++ = R_FROMYV(Y1,V); *output_pt++ = G_FROMYUV(Y1,U,V); //b *output_pt++ = B_FROMYU(Y1,U); //v } return 0; } // ここまで luvcview */ // // キャプチャした画像の操作 // static void Process_Image( const void *p ) { int i, j, ct, size, tmp; int r, g, b; int y, u, y1, v; unsigned char *reader, *writer; unsigned char fname_jpeg[FILENAME_MAX]; unsigned char fname_ppm[FILENAME_MAX]; unsigned char ext[4+1]; // 拡張子4文字 + 終端文字 FILE *fp; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; unsigned char *line; fputc ('.', stdout); fflush (stdout); writer = (unsigned char *)rgb; reader = (unsigned char *)p; // initLutは起動直後、freeLutはプログラム終了時に実行した方が良い。 ///* initLut(); Pyuv422torgb24(p, rgb, CAPTURE_WIDTH, CAPTURE_HEIGHT); freeLut(); //*/ /* ct = 0; for (i= 0; i < CAPTURE_HEIGHT; i++) { for (j= 0; j < CAPTURE_WIDTH; j++) { if ( ct == 0 ) y = reader[0] - 16; else y = reader[2] - 16; // if ( ct == 0 ) y = reader[0] << 8; // else y = reader[2] << 8; u = reader[1] - 128; v = reader[3] - 128; // 8bitに丸める r = (y + 359 * v) >> 8; g = (y - 88 * u - 183 * v) >> 8; b = (y + 454 * u) >> 8; *writer++ = (r > 255) ? 255 : ((r < 0) ? 0 : r); *writer++ = (g > 255) ? 255 : ((g < 0) ? 0 : g); *writer++ = (b > 255) ? 255 : ((b < 0) ? 0 : b); if ( ct > 0 ) { ct = 0; reader += 4; } ct++; } } */ // DrawAreaに描画 gdk_draw_rgb_image( gtk_darea->window, gtk_darea->style->fg_gc[GTK_STATE_NORMAL], 0, 0, CAPTURE_WIDTH, CAPTURE_HEIGHT, GDK_RGB_DITHER_MAX, writer, CAPTURE_CHANNEL * CAPTURE_WIDTH ); // JPEG if ( save_jpeg == 1 ) { strcpy( ext, ".jpg" ); // 終端文字の付加 ext[4+1] = '\0'; sprintf( fname_jpeg, "out%05d%s", ImgNum, ext ); fp = fopen(fname_jpeg, "w"); if ( fp == NULL ) { Errno_Exit("Error: Save image file"); } cinfo.err = jpeg_std_error( &jerr ); jpeg_create_compress( &cinfo ); jpeg_stdio_dest(&cinfo, fp); cinfo.image_width = CAPTURE_WIDTH; cinfo.image_height = CAPTURE_HEIGHT; cinfo.input_components = CAPTURE_CHANNEL; // color component per pixel cinfo.in_color_space = JCS_RGB; jpeg_set_defaults( &cinfo ); jpeg_set_quality(&cinfo, 100, TRUE); // 品質 [0-100] jpeg_start_compress(&cinfo, TRUE); for (i=0, line = writer; i < CAPTURE_HEIGHT; i++, line += CAPTURE_CHANNEL * CAPTURE_WIDTH) { jpeg_write_scanlines(&cinfo, &line, 1); } jpeg_finish_compress( &cinfo ); jpeg_destroy_compress( &cinfo ); fclose( fp ); } // PPM if ( save_ppm == 1 ) { strcpy( ext, ".ppm" ); ext[4+1] = '\0'; // 終端文字の付加 sprintf( fname_ppm, "out%05d%s", ImgNum, ext ); fp = fopen(fname_ppm, "w"); if ( fp == NULL ) { Errno_Exit("Error: Save image file"); } fprintf(fp, "P6 %d %d 255\n", CAPTURE_WIDTH, CAPTURE_HEIGHT); fwrite(writer, 1, CAPTURE_WIDTH * CAPTURE_HEIGHT * CAPTURE_CHANNEL, fp); fclose( fp ); } ImgNum++; } // // フレームを読み込む // static int Read_Frame( void ) { unsigned int i; struct v4l2_buffer buf; switch ( io ) { case IO_METHOD_READ: if ( read(fd, buffers[0].start, buffers[0].length) == -1 ) { switch ( errno ) { case EAGAIN: return 0; case EIO: // Could ignore EIO, see spec. // fall through default: Errno_Exit("read"); } } Process_Image( buffers[0].start ); break; case IO_METHOD_MMAP: CLEAR( buf ); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; if ( Xioctl(fd, VIDIOC_DQBUF, &buf) == -1 ) { switch ( errno ) { case EAGAIN: return 0; case EIO: // Could ignore EIO, see spec. // fall through default: Errno_Exit("VIDIOC_DQBUF"); } } assert( buf.index < n_buffers ); Process_Image( buffers[buf.index].start ); if ( Xioctl(fd, VIDIOC_QBUF, &buf) == -1 ) { Errno_Exit("VIDIOC_QBUF"); } break; case IO_METHOD_USERPTR: CLEAR( buf ); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; if ( Xioctl(fd, VIDIOC_DQBUF, &buf) == -1 ) { switch ( errno ) { case EAGAIN: return 0; case EIO: // Could ignore EIO, see spec. // fall through default: Errno_Exit("VIDIOC_DQBUF"); } } for (i = 0; i < n_buffers; ++i) { if ( buf.m.userptr == (unsigned long) buffers[i].start && buf.length == buffers[i].length ) { break; } } assert( i < n_buffers ); Process_Image( (void *) buf.m.userptr ); if ( -1 == Xioctl(fd, VIDIOC_QBUF, &buf) ) { Errno_Exit("VIDIOC_QBUF"); } break; } return 1; } // // ループ // static void MainLoop( void ) { for (;;) { fd_set fds; struct timeval tv; int r; FD_ZERO( &fds ); FD_SET( fd, &fds ); // Timeout tv.tv_sec = 2; tv.tv_usec = 0; r = select( fd + 1, &fds, NULL, NULL, &tv ); if ( r == -1 ) { if ( EINTR == errno ) { continue; } Errno_Exit("select"); } if ( r == 0 ) { fprintf(stderr, "select timeout\n"); exit( EXIT_FAILURE ); } if ( Read_Frame() ) { break; } // EAGAIN - continue select loop } } static void GetImageMemory( void ) { rgb = ( unsigned char *)malloc( CAPTURE_CHANNEL * CAPTURE_WIDTH * CAPTURE_HEIGHT ); if ( ! rgb ) { fprintf(stderr, "Out of memory rgb \n"); exit( EXIT_FAILURE ); } } // // キャプチャ停止 // static void Stop_Capture( void ) { enum v4l2_buf_type type; switch ( io ) { case IO_METHOD_READ: // Nothing to do break; case IO_METHOD_MMAP: case IO_METHOD_USERPTR: type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( -1 == Xioctl(fd, VIDIOC_STREAMOFF, &type) ) { Errno_Exit("VIDIOC_STREAMOFF"); } break; } } // // キャプチャ開始 // static void Start_Capture( void ) { unsigned int i; enum v4l2_buf_type type; switch ( io ) { case IO_METHOD_READ: break; case IO_METHOD_MMAP: for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf; CLEAR( buf ); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = i; if ( Xioctl(fd, VIDIOC_QBUF, &buf) == -1 ) { Errno_Exit("VIDIOC_QBUF"); } } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( Xioctl(fd, VIDIOC_STREAMON, &type) == -1 ) { Errno_Exit("VIDIOC_STREAMON"); } break; case IO_METHOD_USERPTR: for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer buf; CLEAR( buf ); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_USERPTR; buf.m.userptr = (unsigned long) buffers[i].start; buf.length = buffers[i].length; if ( -1 == Xioctl(fd, VIDIOC_QBUF, &buf) ) { Errno_Exit("VIDIOC_QBUF"); } } type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( Xioctl(fd, VIDIOC_STREAMON, &type) == -1 ) { Errno_Exit("VIDIOC_STREAMON"); } break; } } // // デバイスの終了処理 // static void UnInit_Device( void ) { unsigned int i; switch ( io ) { case IO_METHOD_READ: free ( buffers[0].start ); break; case IO_METHOD_MMAP: for ( i = 0; i < n_buffers; ++i ) { if ( munmap (buffers[i].start, buffers[i].length) == -1 ) { Errno_Exit("munmap"); } } break; case IO_METHOD_USERPTR: for ( i = 0; i < n_buffers; ++i ) { free( buffers[i].start ); } break; } free( buffers ); free( rgb ); } // // メモリ割り当て malloc calloc // static void Init_read( unsigned int buffer_size ) { // ヒープメモリからsize[byte]のブロックをn個割り当てる // void *calloc(size_t n, size_t size); buffers = calloc(1, sizeof (*buffers)); if ( ! buffers ) { fprintf(stderr, "Out of memory\n"); exit( EXIT_FAILURE ); } buffers[0].length = buffer_size; buffers[0].start = malloc( buffer_size ); if ( ! buffers[0].start ) { fprintf(stderr, "Out of memory\n"); exit( EXIT_FAILURE ); } } // // メモリ割り当て V4L2_MEMORY_MMAP // static void Init_mmap( void ) { struct v4l2_requestbuffers req; CLEAR( req ); req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if ( Xioctl(fd, VIDIOC_REQBUFS, &req) == -1 ) { if ( EINVAL == errno ) { fprintf(stderr, "%s does not support " "memory mapping\n", dev_name); exit( EXIT_FAILURE ); } else { Errno_Exit("VIDIOC_REQBUFS"); } } if ( req.count < 2 ) { fprintf(stderr, "Insufficient buffer memory on %s\n", dev_name); exit( EXIT_FAILURE ); } buffers = calloc(req.count, sizeof (*buffers)); if ( ! buffers ) { fprintf(stderr, "Out of memory mmap \n"); exit( EXIT_FAILURE ); } for ( n_buffers = 0; n_buffers < req.count; ++n_buffers ) { struct v4l2_buffer buf; CLEAR( buf ); buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; buf.index = n_buffers; if ( Xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1 ) { Errno_Exit("VIDIOC_QUERYBUF mmap"); } buffers[n_buffers].length = buf.length; buffers[n_buffers].start = mmap ( NULL, // Start Anywhere buf.length, PROT_READ | PROT_WRITE, // required MAP_SHARED, // recommended fd, buf.m.offset ); if ( MAP_FAILED == buffers[n_buffers].start ) { Errno_Exit("mmap"); } } } // // メモリ割り当て V4L2_MEMORY_USERPTR // static void Init_userp( unsigned int buffer_size ) { struct v4l2_requestbuffers req; CLEAR( req ); req.count = 4; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_USERPTR; if ( Xioctl(fd, VIDIOC_REQBUFS, &req) == -1 ) { if ( EINVAL == errno ) { fprintf(stderr, "%s does not support " "user pointer i/o\n", dev_name); exit( EXIT_FAILURE ); } else { Errno_Exit("VIDIOC_REQBUFS"); } } buffers = calloc(4, sizeof (*buffers)); if ( ! buffers ) { fprintf(stderr, "Out of memory\n"); exit( EXIT_FAILURE ); } for (n_buffers = 0; n_buffers < 4; ++n_buffers) { buffers[n_buffers].length = buffer_size; buffers[n_buffers].start = malloc( buffer_size ); if ( ! buffers[n_buffers].start ) { fprintf(stderr, "Out of memory\n"); exit( EXIT_FAILURE ); } } } // // デバイスの初期化 // static void Init_Device( void ) { struct v4l2_capability cap; struct v4l2_cropcap cropcap; struct v4l2_crop crop; struct v4l2_format fmt; unsigned int min; if ( Xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1 ) { if ( EINVAL == errno ) { fprintf(stderr, "%s is no V4L2 device\n", dev_name); exit( EXIT_FAILURE ); } else { Errno_Exit("VIDIOC_QUERYCAP"); } } if ( ! ( cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ) ) { fprintf(stderr, "%s is no video capture device\n", dev_name); exit( EXIT_FAILURE ); } switch ( io ) { case IO_METHOD_READ: if ( ! ( cap.capabilities & V4L2_CAP_READWRITE ) ) { fprintf(stderr, "%s does not support read i/o\n", dev_name); exit( EXIT_FAILURE ); } break; case IO_METHOD_MMAP: case IO_METHOD_USERPTR: if ( ! ( cap.capabilities & V4L2_CAP_STREAMING ) ) { fprintf(stderr, "%s does not support streaming i/o\n", dev_name); exit( EXIT_FAILURE ); } break; } // Select video input, video standard and tune here. cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ( Xioctl(fd, VIDIOC_CROPCAP, &cropcap) == -1 ) { // Errors ignored. fprintf(stderr, "VIDIOC_CROPCAP error. Ignored ! \n"); } crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; crop.c = cropcap.defrect; // reset to default if ( Xioctl(fd, VIDIOC_S_CROP, &crop) == -1 ) { switch ( errno ) { case EINVAL: break; // Cropping not supported. default: break; // Errors ignored. } } CLEAR( fmt ); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.width = CAPTURE_WIDTH; fmt.fmt.pix.height = CAPTURE_HEIGHT; fmt.fmt.pix.pixelformat = PIXELFORMAT; fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; if ( -1 == Xioctl(fd, VIDIOC_S_FMT, &fmt) ) { Errno_Exit("VIDIOC_S_FMT"); } // Note VIDIOC_S_FMT may change width and height. // Buggy driver paranoia. min = 2 * fmt.fmt.pix.width; if ( fmt.fmt.pix.bytesperline < min ) { fmt.fmt.pix.bytesperline = min; } min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; if ( fmt.fmt.pix.sizeimage < min ) { fmt.fmt.pix.sizeimage = min; } switch ( io ) { case IO_METHOD_READ: Init_read( fmt.fmt.pix.sizeimage ); break; case IO_METHOD_MMAP: Init_mmap(); break; case IO_METHOD_USERPTR: Init_userp( fmt.fmt.pix.sizeimage ); break; } } // // デバイスを閉じる // static void Close_Device( void ) { if ( close (fd) == -1 ) { Errno_Exit("close"); } fd = -1; } // // デバイスを開く // static void Open_Device( void ) { struct stat st; // stat構造体を得る if ( stat(dev_name, &st) == -1 ) { fprintf(stderr, "Cannot identify '%s': %d, %s\n", dev_name, errno, strerror (errno)); exit( EXIT_FAILURE ); } // キャラクタデバイスかどうかの検査 if ( ! S_ISCHR( st.st_mode ) ) { fprintf(stderr, "%s is no device\n", dev_name); exit( EXIT_FAILURE ); } fd = open( dev_name, O_RDWR | O_NONBLOCK, 0 ); if ( fd == -1 ) { fprintf(stderr, "Cannot open '%s': %d, %s\n", dev_name, errno, strerror (errno)); exit( EXIT_FAILURE ); } } // // 使用方法 // static void Usage( FILE *fp, int argc, char **argv ) { fprintf(fp, "Usage: %s [options]\n\n" "Options:\n" "-d | --device name Video device name [default: /dev/video0]\n" "-h | --help Print this message\n" "-m | --mmap Use memory mapped buffers\n" "-r | --read Use read() calls\n" "-u | --userp Use application allocated buffers\n" "", argv[0]); } // // 描画用のWindow作成 // static void CreateCaptureWindow( void ) { gtk_second = gtk_window_new( GTK_WINDOW_TOPLEVEL ); gtk_widget_set_usize( GTK_WIDGET( gtk_second ), CAPTURE_WIDTH, CAPTURE_HEIGHT ); gtk_window_set_title( GTK_WINDOW( gtk_second ), PKGNAME ); // Drawエリア作成 gtk_darea = gtk_drawing_area_new(); gtk_container_add( GTK_CONTAINER( gtk_second ), gtk_darea ); gtk_widget_realize( gtk_second ); gtk_widget_realize( gtk_darea ); gtk_widget_show_all( gtk_second ); } // // 引数の設定 // static const char short_options [] = "d:hmru"; static const struct option long_options [] = { { "device", required_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "mmap", no_argument, NULL, 'm' }, { "read", no_argument, NULL, 'r' }, { "userp", no_argument, NULL, 'u' }, { 0, 0, 0, 0 } }; // // 引数の検査 // static void CheckArg( int argc, char **argv ) { dev_name = "/dev/video0"; for (;;) { int index; int c; c = getopt_long( argc, argv, short_options, long_options, &index ); if ( c == -1 ) { break; } switch ( c ) { case 0: // getopt_long() flag break; case 'd': dev_name = optarg; break; case 'h': Usage( stdout, argc, argv ); exit(EXIT_SUCCESS); case 'm': io = IO_METHOD_MMAP; break; case 'r': io = IO_METHOD_READ; break; case 'u': io = IO_METHOD_USERPTR; break; default: Usage( stderr, argc, argv ); exit( EXIT_FAILURE ); } } } // // キャプチャ開始 // static void Start_Proc( GtkWidget *w, gpointer p ) { ImgNum = 0; Start_Capture(); #ifdef USE_GTK_TIMEOUT_ADD gtk_id = gtk_timeout_add( GTK_INTERVAL, (GtkFunction) MainLoop, p ); printf("add \n"); #else gtk_id = gtk_idle_add( (GtkFunction) MainLoop, p ); printf("idle \n"); #endif } // // キャプチャ停止 // static void Stop_Proc( GtkWidget *w, gpointer p ) { #ifdef USE_GTK_TIMEOUT_ADD gtk_timeout_remove( gtk_id ); #else gtk_idle_remove( gtk_id ); #endif Stop_Capture(); } // // プログラム終了 // static void Quit_Proc( GtkWidget *w, gpointer p ) { UnInit_Device(); Close_Device(); gtk_main_quit(); } // // メイン // int main( int argc, char **argv ) { int i; gchar *btn_label[] = { "Capture", "Stop", "Exit" }; void *btn_func[] = { Start_Proc, Stop_Proc, Quit_Proc }; CheckArg( argc, argv ); // 初期設定 gtk_set_locale(); // 日本語可 gtk_init( &argc, &argv ); gdk_rgb_init(); gtk_top = gtk_window_new( GTK_WINDOW_TOPLEVEL ); gtk_window_set_title( GTK_WINDOW( gtk_top ), PKGNAME ); gtk_widget_show( gtk_top ); gtk_container_set_border_width( GTK_CONTAINER( gtk_top ), 20 ); // テーブルパッキング gtk_table = gtk_table_new( 5, 2, FALSE ); gtk_container_add( GTK_CONTAINER( gtk_top ), gtk_table ); gtk_table_set_col_spacings( GTK_TABLE( gtk_table ), 10 ); gtk_table_set_row_spacings( GTK_TABLE( gtk_table ), 5 ); gtk_widget_show( gtk_table ); // ボタン for ( i=0; i < BTN_MAX; i++ ) { gtk_btn = gtk_button_new_with_label( btn_label[i] ); gtk_table_attach_defaults( GTK_TABLE( gtk_table ), gtk_btn, 0, 2, i, i + 1 ); gtk_signal_connect( GTK_OBJECT( gtk_btn ), "clicked", GTK_SIGNAL_FUNC( btn_func[i] ), NULL ); gtk_widget_show( gtk_btn ); } Open_Device(); Init_Device(); GetImageMemory(); CreateCaptureWindow(); // Start_Capture(); // MainLoop(); // Stop_Capture(); // UnInit_Device(); // Close_Device(); gtk_main(); // exit( EXIT_SUCCESS ); return 0; }
Comments