ひっさびさの いかお です。
前回の宣言通りつくってみました。
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()とかしてる段階で感じるけど)な感じが
オタク心をくすぐるか?