カテゴリー別アーカイブ: 映像

V4l2でアプリケーション作りました

カテゴリー: 映像 | 投稿日: | 投稿者:

ひっさびさの いかお です。

前回の宣言通りつくってみました。

MJPEGで撮影できるUVCカメラで動画撮影し、とれた映像をそのまま書いたら
JPEGじゃね?

と思って作ってみますた

カメラはlogicoolのC920Tです。
多少不安定な感じだけど、あっさり認識した

[6734110.675010] usbhid 3-2:1.0: >couldn't find an input interrupt endpoint
[6734110.675925] usbcore: registered new interface driver usbhid
[6734110.675927] usbhid: USB HID core driver
[6734966.300322] usb 3-1: >new high-speed USB device number 11 using xhci_hcd
[6734966.319401] usb 3-1: >New USB device found, idVendor=046d, idProduct=082d
[6734966.319405] usb 3-1: >New USB device strings: Mfr=0, Product=2, SerialNumber=1
[6734966.319408] usb 3-1: >Product: HD Pro Webcam C920
[6734966.319410] usb 3-1: >SerialNumber: A0313D6F
[6734966.319925] uvcvideo: Found UVC 1.00 device HD Pro Webcam C920 (046d:082d)
[6734966.320342] input: HD Pro Webcam C920 as /devices/pci0000:00/0000:00:14.0/usb3/3-1/3-1:1.0/input/input37
[6734968.751553] xhci_hcd 0000:00:14.0: >WARN Event TRB for slot 10 ep 0 with no TDs queued?

中核部分はこんな感じ

////////////////////////////////////
// O P E N   D e v i c e          //)
////////////////////////////////////
        camFd   =       open(devName, O_RDWR);
        if(camFd == -1){
                fprintf(stderr, "Device(%s) open failed\n", devName);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto threadExit;
        }
        // setup resolution etc
        memset((void *)&fmt, 0x00, sizeof(fmt));
        fmt.type                =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width       =       startParm->width;
        fmt.fmt.pix.height      =       startParm->height;
        fmt.fmt.pix.pixelformat =       V4L2_PIX_FMT_MJPEG;
        ret = xioctl(camFd, VIDIOC_S_FMT, &fmt);
        if(ret < 0){
                fprintf(stderr, "Settup failed\n");
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        if(fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG){
                fprintf(stderr, "WARNING!! Assigned incorrect fmt:%d resolition:%dx%d\n",
                        (int)fmt.fmt.pix.pixelformat,
                        (int)fmt.fmt.pix.width,
                        (int)fmt.fmt.pix.height);
        }
        ///////////////////////////////////
        // memory mapping
        ///////////////////////////////////
        // request mmap
        memset((void*)&req, 0x00, sizeof(req));
        req.count       =       MMAP_CNT;
        req.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory      =       V4L2_MEMORY_MMAP;
        ret = ioctl(camFd, VIDIOC_REQBUFS, &req);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_REQBUFS failed\n");
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        if(req.count != MMAP_CNT){
                fprintf(stderr, "mmap allocate failed(%d)\n", req.count);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        // map to userland)
        for(i=0;i < MMAP_CNT;i++){
                memset((void*)&buf, 0x00, sizeof(buf));
                buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      =       V4L2_MEMORY_MMAP;
                buf.index       =       i;
                ret = xioctl(camFd, VIDIOC_QUERYBUF, &buf);
                if(ret < 0){
                        fprintf(stderr, "mmap VIDIOC_QUERYBUF[%d]\n", i);
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        goto closeExit;
                }
                mmap_p[i] = mmap(NULL, buf.length, PROT_READ, MAP_SHARED, camFd, buf.m.offset);
                if(mmap_p[i] == MAP_FAILED){
                        fprintf(stderr, "mmap(%d)\n", errno);
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        gExecuted       =       0;
                        goto closeExit;
                }
                mmap_l[i]       =       buf.length;
#if 1
                fprintf(stderr, "mmbuff[%d] length is \"%u\"\n", i, buf.length);
#endif
        }
        // Queueing before
        for(i=0;i < MMAP_CNT;i++){
                memset((void*)&buf, 0x00, sizeof(buf));
                buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory      =       V4L2_MEMORY_MMAP;
                buf.index       =       i;
                ret = xioctl(camFd, VIDIOC_QBUF, &buf);
                if(ret < 0){
                        fprintf(stderr, "VIDIOC_QBUF error\n");
                        *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                        goto closeExit;
                }
        }
        ////
        // start stream
        ////
        type    =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = xioctl(camFd, VIDIOC_STREAMON, &type);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_STREAMON error:%d\n", ret * -1);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
        // Capture Stanby
        startParm->stanby       =       1;
        //
        while(gExecuted){
                fd_set  fds;
                FD_ZERO(&fds);
                FD_SET(camFd, &fds);
                struct  timeval timeOut;
                timeOut.tv_sec  =       0;
                timeOut.tv_usec =       USEC_1MSEC;
                ret = select(camFd + 1, &fds, NULL, NULL, &timeOut);
                if(ret < 0){
                        if(errno == EINTR)
                                continue;
                        fprintf(stderr, "select() error\n");
                        *exitStatus     =       (int)FWCAMCAPT_OTHER;
                        break;
                }
                if(FD_ISSET(camFd, &fds)){
                        memset(&buf, 0, sizeof(buf));
                        buf.type        =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
                        buf.memory      =       V4L2_MEMORY_MMAP;
                        ret = xioctl(camFd, VIDIOC_DQBUF, &buf);
                        if(ret < 0){
                                fprintf(stderr, "VIDIOC_DQBUFi error\n");
                                *exitStatus     =       (int)FWCAMCAPT_OTHER;
                                break;
                        }
                        if(pthread_mutex_lock(&(startParm->bufMutex)) == 0){
                                memcpy(startParm->frameBuff, mmap_p[buf.index], buf.bytes[6734110.675010] usbhid 3-2:1.0: >couldn't find an input interrupt endpoint
used);
                                startParm->length       =       buf.bytesused;
                                pthread_mutex_unlock(&(startParm->bufMutex));
                        }
                        ret = xioctl(camFd, VIDIOC_QBUF, &buf);
                        if(ret < 0){
                                fprintf(stderr, "VIDIOC_QBUF error\n");
                                *exitStatus     =       (int)FWCAMCAPT_OTHER;
                                break;
                        }
                }
#if 0
                else
                        fprintf(stderr, "TO\n");
#endif
        }
        ////
        // stop stream
        ////
        type    =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = xioctl(camFd, VIDIOC_STREAMOFF, &type);
        if(ret < 0){
                fprintf(stderr, "VIDIOC_STREAMOFF error:%d\n", ret * -1);
                *exitStatus     =       (int)FWCAMCAPT_DEVERR;
                goto closeExit;
        }
////////////////////////////////////
// C L O S E   D e v i c e        //
////////////////////////////////////
closeExit:
        close(camFd);

これを説明していくと、基本的にこの中核処理はサブスレッドで動いていて、
カメラからMJPEGのフレームが来たら
startParm->frameBuff
にコピーしてるってわけです。

ここを適当なタイミング(1秒毎とか)覗いてフレームが
あったら(ここには書いてないけど)xxx.jpegという名前で書いてます
※ここでの書き込みと、フレームデータの参照が重ならないように
mutex()してます

それと、コードの中で
camFd = open(devName, O_RDWR);
ってのがあります。
最初、カメラからビデオを読むだけだからO_RDONLYで良いと浅はかに考えていたけど、
よく考えてみたらカメラデバイスをコントロールするため、ioctl()で制御してるんだから
書き込みがいるんだよね。

ちなみに
xioctl()
アットマークテクノさんのページ
を参考にしました(全体的にも参考にしてます)

内容的には以下のコード

//////////////////////////////////////////////////////////
// xioctl copy from 
// http://manual.atmark-techno.com/armadillo-810/armadillo-810_product_manual_ja-1.3.0/ch14.html
/////////////////////////////////////////////////////////
static int xioctl(int fd, int request, void *arg){
int     ret;
        for(; ; ){
                ret = ioctl(fd, request, arg);
                if(ret < 0){
                        if(errno == EINTR)
                                continue;
                        return -errno;
                }
                break;
        }
        return 0;
}

メモリマップとユーザーランドのやりとりの低レベル
(っていうかioctl()とかしてる段階で感じるけど)な感じが
オタク心をくすぐるか?