V4L2とGtk+2.0を使ってキャプチャを行う。
「BT878KHF」と「SN9C102」のチップが載ったカメラが使える。
切り替えはソースファイルの「#define」で行う。
また、取得した画像をGtk+を使ってリアルタイムに表示する。
なお、添付してあるサンプルは「
【2013.12.27 追記】
SN9C102の場合、ここにあるソースではkernel ver.3以降では画像が正常に表示されない。(ver.3.2とver.3.11で確認)
kernel ver.3以降でV4L2関連に変更があったようだが、現在のところ追っていない。
bayer.cに問題があるのか、データの並びが変わったのかは不明。
【2010.1.15 追記】
最近は、BT878などのチップを搭載するカードが少なくなりました。
上で紹介しているUSBカメラやカードも、ほとんどが生産終了です。(T_T)
sn9c102のチップは少しクセがあるので、できれば素直なBT878を使いたいところです。
「SAA7130」、「SAA7131」はV4L2でサポートされていますが、現物を持っていないので試していません。
環境
- Debian lenny
- Kernel 2.6.26
- gcc ver.4.3.2
- Jpeg Library ver.6b-14
- Gtk+2.0(ver.2.12.12)
参考にしたサンプル
以下の「B. Video Capture Example」に掲載されているサンプルを使いました。
Process_Image()内で画像処理とか、画像の保存などの処理を行います。
http://v4l2spec.bytesex.org/spec/
V4L2_PIX_FMT_SBGGR8(Bayer RGB format)
「Bayer RGBフォーマット」を使用する場合は、ソース内で宣言が必要です。
「/usr/include/linux/videodev2.h」の305行目に記述されています。
http://www.siliconimaging.com/RGB%20Bayer.htm
http://v4l2spec.bytesex.org/spec/r3735.htm
#define PIXELFORMAT V4L2_PIX_FMT_SBGGR8
ライブラリのインストール
aptitude install libgtk2.0-dev aptitude install libjpeg62-dev
コンパイル・実行
make -f makefile ./v4l2-test ./v4l2-test -d /dev/video0
保存する画像形式
今回は、JPEGとPPM(P6)で保存出来るようにした。
デバイスファイル
デバイスファイルが存在しない場合は以下の操作を行う。
cd /dev makenod video0 c 81 0 chmod 666 video0 ln -s video0 video
その他
- 「bttv」は「V4L」、「V4L2」のインターフェイスを内蔵している。
- 「linux-2.6.11.5/drivers/media/video/bttv.h」で「videodev.h」を呼んでいる。
Gtk+20
Gtk+20では、以下の関数を使うことで、任意の周期で特定の関数を呼び出すことが出来る。
- gtk_idle_add
- gtk_timeout_add
Exceed ver.8.0.0.0を使用して表示する
- ExceedでTerminalを起動して環境変数を設定する。(bashの場合)
- export DISPLAY=[計算記名]:0.0
CCDカメラがどのデバイスファイルに割り当てられているかを調べる
- /var/log/syslog
- /var/log/message
- /proc/bus/usb/devices
Sonic SN9C102
V4L2対応。
「Bayer Code」というフィルタを使用しているため、RGBに並べ替えるプログラムが必要。
既にそのソースが「sonic-snap」で公開されていたので利用した。
V4L
- http://www.exploits.org/v4l/
- 「ARToolKit2.61/lib/SRC/VideoLinuxV4L/video.c」が参考になる。www.hitl.washington.edu/artoolkit/
V4L2
kernelソース sn9c102
kernel ver.2.6.26.2とver.2.6.32.7では、以下のディレクトリにsn9c102のソースがあります。
linux-2.6.26.2/Documentation/video4linux/sn9c102.txt linux-2.6.26.2/drivers/media/video/sn9c102 linux-2.6.32.7/Documentation/video4linux/sn9c102.txt linux-2.6.32.7/drivers/media/video/
その他
・http://linux.bytesex.org/v4l2/bttv.html(bttv)
・http://sourceforge.net/projects/sn-webcam(Webcam)
・http://www.linux-usb.org/(USB project)
必要なソース
「bayer.h」と「bayer.c」は「sonic-snap」から取ってくる。。
http://www.stolk.org/sonic-snap/
- makefile
- v4l2-test.c(本体)
- bayer.c
- bayer.h
Makefile
# Makefile for v4ls-test.c
CC = gcc
#CC = gcc-3.4
#CC = gcc-3.3
CFLAGS = -Wall -g
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-test
OBJECTS = $(TARGET).o bayer.o
$(TARGET): $(OBJECTS)
$(CC) -o $@ $(JPEG_LIB) $(GTK_LIB) $^
rm -f $(OBJECTS)
.c.o:
$(CC) -c $(CFLAGS) $(GTK_INCLUDE) $<
clean:
rm -f $(OBJECTS) $(TARGET)
v4l2-test.c
//
// V4L2 capture sample program for BT878A and SN9C102
//
// 2013.12.08 modified by heppoko memo
// 2007.08.28 for BT878A by heppoko memo
// 2005.09.03 for SN9C102 by heppoko memo
//
// 環境
// gcc ver.4.3.2
// Gtk+2.0
// Jpeg Library ver.6
// Linux kernel ver.2.6.26
//
// コンパイル・実行
// make -f makefile
//
// ./v4l2-test
// ./v4l2-test -d /dev/video0
#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 "bayer.h" // bayer2rgb24()</jpeglib.h></gtk></linux></asm></sys></sys></sys></sys></sys></errno.h></unistd.h></fcntl.h></getopt.h></assert.h></string.h></stdlib.h></stdio.h>
static int save_jpeg = 0; // JPEGで保存
static int save_ppm = 0; // PPMで保存
#define CAPTURE_WIDTH 640 // キャプチャの横サイズ
#define CAPTURE_HEIGHT 480 // キャプチャの縦サイズ
#define CAPTURE_CHANNEL 3 // キャプチャのchannel&lt;
#define SN9C102 0 // SN9C102 有効(無効の場合は BT878A)
//#define USE_GTK_TIMEOUT_ADD 0 // gtk_timeout_add()を使用する。無効の場合はgtk_idle_add()を使用する。
//#define GTK_INTERVAL 33 // キャプチャ間隔[ms] gtk_timeout_add()を使用した場合に使用する。
#ifdef SN9C102
#define PIXELFORMAT V4L2_PIX_FMT_SBGGR8 // SN9C102
#else
#define PIXELFORMAT V4L2_PIX_FMT_BGR24 // BT878A
#endif
#define PKGNAME "V4L2 Capture"
#define CLEAR( x ) memset( &amp;(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 &amp;&amp; errno == EINTR );
return r;
}
//
// キャプチャした画像の操作
//
static void Process_Image( const void *p )
{
int i, j;
unsigned char *reader, *writer;
unsigned char b, g1, g2, r;
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;
#ifdef SN9C102
if ( CAPTURE_WIDTH == 176 )
{
for (j=0; j &lt; 144; j++)
{
for (i=0; i &lt; 176; i++)
{
b = reader[2 * i];
g1 = reader[2 * i + 1];
g2 = reader[352 + 2 * i];
r = reader[352 + 2 * i + 1];
*writer++ = r;
*writer++ = (g1 + g2) / 2;
*writer++ = b;
}
reader += 2 * 352;
}
}
else
{
bayer2rgb24(rgb, reader, CAPTURE_WIDTH, CAPTURE_HEIGHT); // bayer.c
}
#else // BT878A
for(i= 0; i &lt; CAPTURE_HEIGHT; i++)
{
for(j= 0; j &lt; CAPTURE_WIDTH; j++)
{
// BGR --&gt; RGB 順番の入れかえ
*writer++ = p + ( i * CAPTURE_WIDTH + j ) * CAPTURE_CHANNEL + 2;
*writer++ = p + ( i * CAPTURE_WIDTH + j ) * CAPTURE_CHANNEL + 1;
*writer++ = p + ( i * CAPTURE_WIDTH + j ) * CAPTURE_CHANNEL;
}
}
#endif
// DrawAreaに描画
gdk_draw_rgb_image( gtk_darea-&gt;window, gtk_darea-&gt;style-&gt;fg_gc[GTK_STATE_NORMAL], 0, 0, CAPTURE_WIDTH, CAPTURE_HEIGHT, GDK_RGB_DITHER_MAX, writer, CAPTURE_CHANNEL * CAPTURE_WIDTH );
if ( save_jpeg == 1 || save_ppm == 1 )
{
// 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( &amp;jerr );
jpeg_create_compress( &amp;cinfo );
jpeg_stdio_dest(&amp;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( &amp;cinfo );
jpeg_set_quality(&amp;cinfo, 100, TRUE); // 品質 [0-100]
jpeg_start_compress(&amp;cinfo, TRUE);
for (i=0, line = writer; i &lt; CAPTURE_HEIGHT; i++, line += CAPTURE_CHANNEL * CAPTURE_WIDTH)
{
jpeg_write_scanlines(&amp;cinfo, &amp;line, 1);
}
jpeg_finish_compress( &amp;cinfo );
jpeg_destroy_compress( &amp;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, &amp;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 &lt; n_buffers );
Process_Image( buffers[buf.index].start );
if ( Xioctl(fd, VIDIOC_QBUF, &amp;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, &amp;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 &lt; n_buffers; ++i)
{
if ( buf.m.userptr == (unsigned long) buffers[i].start &amp;&amp; buf.length == buffers[i].length )
{
break;
}
}
assert( i &lt; n_buffers );
Process_Image( (void *) buf.m.userptr );
if ( -1 == Xioctl(fd, VIDIOC_QBUF, &amp;buf) )
{
Errno_Exit("VIDIOC_QBUF");
}
break;
}
return 1;
}
//
// ループ
//
static void MainLoop( void )
{
for (;;)
{
fd_set fds;
struct timeval tv;
int r;
FD_ZERO( &amp;fds );
FD_SET( fd, &amp;fds );
// Timeout
tv.tv_sec = 2;
tv.tv_usec = 0;
r = select( fd + 1, &amp;fds, NULL, NULL, &amp;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, &amp;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 &lt; 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, &amp;buf) == -1 )
{
Errno_Exit("VIDIOC_QBUF");
}
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( Xioctl(fd, VIDIOC_STREAMON, &amp;type) == -1 )
{
Errno_Exit("VIDIOC_STREAMON");
}
break;
case IO_METHOD_USERPTR:
for (i = 0; i &lt; 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, &amp;buf) )
{
Errno_Exit("VIDIOC_QBUF");
}
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( Xioctl(fd, VIDIOC_STREAMON, &amp;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 &lt; n_buffers; ++i )
{
if ( munmap (buffers[i].start, buffers[i].length) == -1 )
{
Errno_Exit("munmap");
}
}
break;
case IO_METHOD_USERPTR:
for ( i = 0; i &lt; 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, &amp;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 &lt; 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 &lt; 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, &amp;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, &amp;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 &lt; 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, &amp;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 &amp; 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 &amp; 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 &amp; 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, &amp;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, &amp;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, &amp;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 &lt; min )
{
fmt.fmt.pix.bytesperline = min;
}
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if ( fmt.fmt.pix.sizeimage &lt; 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, &amp;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 [/dev/video]\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, &amp;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( &amp;argc, &amp;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 &lt; 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