In message: <CANtk6SgyiQZihxEPmEuAz8S7q=tyxpxyqpjs-ueteejstb2...@mail.gmail.com> Hiroo Ono (小野寛生) <hiroo.ono+free...@gmail.com> writes: > > FreeBSD で UVC なデバイスから動画をなんらかの形式のファイルにキャプチャすることはできるのでしょうか?
libusb を使う手はどうでしょう.利点として Mac や Android で もだいたい同じようにして使えることがあげられます. 添付ファイルは UVC の Uncompressed な形式の画像を出力します. 以下コマンドに渡して再生できていたと思います. mplayer - -demuxer rawvideo -rawvideo fps=30:w=640:h=480:yuy2 少しの変更で MJPEG も取り出せ,もう少し変更すればバルク転送 のデバイスにも対応できると思います. mplayer - -demuxer lavf -lavfdopts format=mjpeg fps=60 〜 UVC カメラを実験器具として利用しようと思って(レーザポインタ をアパチャー越しにセンサーにあててエアリーディスクが観察でき ないかとか)用意したのですが放置状態でした.USB3 のカメラモ ジュールなども廉価で出ているようで一つ買ってあるのですが(バ ルク転送で BY8 出力だったものとか)やはり放置状態です.(^_^; 齊藤
/* * Copyright (c) 2012 SAITOU Toshihide * All rights reserved. * * A sample program for LibUSB isochronous transfer using UVC cam. * * * Compiling and Running * * (Mac OS) $ cc -Wall -o ex44 ex44.c -I/opt/local/include/libusb-1.0 -L/opt/local/lib -lusb-1.0 * (FreeBSD) $ cc -Wall -o ex44 ex44.c -lusb * * $ ./ex44 * * * Reference * * [PL_UnComp] * (USB_Video_Payload_Uncompressed_1.5.pdf), * <http://www.usb.org/developers/devclass_docs>. * * [UVC1.5] Universal Serial Bus Device Class Definition for Video Devices, * (UVC 1.5 Class specification.pdf), * <http://www.usb.org/developers/devclass_docs>. * * [USB2.0] USB 2.0 Specification Universal Serial Bus Revision 2.0 specification, * (usb_20.pdf), <http://www.USB.org/developers/>. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <libusb.h> #include <fcntl.h> #include "payload.h" typedef struct uvc_device { uint16_t VID; uint16_t PID; uint8_t ConfIdx; // Configuration Index uint8_t ConfVal; // Configuration Value uint8_t IfNum; // Interface Number uint8_t AltIfNum; // Alternate Interface Number uint8_t Endpoint; // Interface 1 Alt 1 Endpoint 0 Address 0x82 uint8_t FrameIndex; // FrameIndex for 640x480 } uvc_device; uvc_device uvc_device_list[] = { { 0x0458, 0x7081, 0, 1, 1, 1, 0x82, 1 }, // BSW20K07HWH -- iBUFFALO, BSW20K07HWH { 0x056e, 0x7008, 0, 1, 1, 7, 0x82, 1 }, // UCAM-DLY300TA -- ELECOM, UCAM-DLY300TA { 0, 0, 0, 0, 0, 0, 0 } }; uvc_device uvc; #define FrameSize (640*480) #define FrameBufferSize (2*FrameSize) uint8_t padding[FrameBufferSize]; #define PKT_LEN 0xc00 #define PKTS_PER_XFER 0x40 #define NUM_TRANSFER 2 #define TIMEOUT 500 // 500 ms libusb_context *ctx = NULL; libusb_device_handle *handle; struct libusb_transfer * xfers[NUM_TRANSFER]; int fd; int total = 0; int16_t brightness; // for debug struct timeval tv1; struct timeval tv2; struct timezone tz; int totalFrame = 0; void signal_callback_handler(int signum) { uint8_t buf[2]; switch (signum) { case SIGUSR1: // BSW20K07HWH // min: 0xff81, max: 0x0080, res: 0x0001 // UCAM-DLY300TA // min: 0x000a, max: 0xfff6, res: 0x0001 // 前者は cur を段階的に設定できたが後者は 2 値のみ受け付けた. // 別のもの設定と連動しているかあるいは何れかの値がおかしい. // いずれも VC_PROCESSING_UNIT の bUnitID が 2 であり // 下記の通りこの値が固定かつ同一のコーディングとなっている. // PU_BRIGHTNESS_CONTROL(0x02), GET_MIN(0x82) [UVC1.5, p. 160, 158, 96] libusb_control_transfer(handle, 0xa1, 0x82, 0x0200, 0x0200, buf, 2, TIMEOUT); fprintf(stderr, "brightness min: %02x%02x\n", buf[1], buf[0]); // PU_BRIGHTNESS_CONTROL(0x02), GET_MAX(0x83) [UVC1.5, p. 160, 158, 96] libusb_control_transfer(handle, 0xa1, 0x83, 0x0200, 0x0200, buf, 2, TIMEOUT); fprintf(stderr, "brightness max: %02x%02x\n", buf[1], buf[0]); // PU_BRIGHTNESS_CONTROL(0x02), GET_RES(0x84) [UVC1.5, p. 160, 158, 96] libusb_control_transfer(handle, 0xa1, 0x84, 0x0200, 0x0200, buf, 2, TIMEOUT); fprintf(stderr, "brightness res: %02x%02x\n", buf[1], buf[0]); // PU_BRIGHTNESS_CONTROL(0x02), GET_CUR(0x81) [UVC1.5, p. 160, 158, 96] libusb_control_transfer(handle, 0xa1, 0x81, 0x0200, 0x0200, buf, 2, TIMEOUT); fprintf(stderr, "brightness cur: %02x%02x\n", buf[1], buf[0]); // change brightness brightness = buf[1]<<8 | buf[0]; brightness -= 30; buf[1] = brightness<<8; buf[0] = brightness & 0xff; // PU_BRIGHTNESS_CONTROL(0x02), SET_CUR(0x01) [UVC1.5, p. 160, 158, 96] libusb_control_transfer(handle, 0x21, 0x01, 0x0200, 0x0200, buf, 2, TIMEOUT); // PU_BRIGHTNESS_CONTROL(0x02), GET_CUR(0x81) [UVC1.5, p. 160, 158, 96] libusb_control_transfer(handle, 0xa1, 0x81, 0x0200, 0x0200, buf, 2, TIMEOUT); fprintf(stderr, "brightness: %02x%02x\n", buf[1], buf[0]); break; case SIGUSR2: // <UCAM-DLY300TA Elecom Corp.> はパッケージにオート・ // フォーカス対応とあるが対応する機能はディスクリプタ // の VC_EXTENSION_UNIT に示されているようだ.ただしこ // のビットマップの詳細はベンダーの情報がないと分から // ない.この拡張の Entity ID は aSourceID から 1 であ // り CS には CT_FOCUS_AUTO_CONTROL を使ってみるとやは // りオート・フォーカスの機能を制御できた. // CT_FOCUS_AUTO_CONTROL(0x08), GET_CUR(0x81) [UVC1.5, p. 160, 159, 86] libusb_control_transfer(handle, 0xa1, 0x81, 0x0800, 0x0100, buf, 1, TIMEOUT); buf[0] = !buf[0]; // CT_FOCUS_AUTO_CONTROL(0x08), SET_CUR(0x01) [UVC1.5, p. 160, 159, 86] libusb_control_transfer(handle, 0x21, 0x01, 0x0800, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "auto focus control: %02x\n", buf[0]); break; case SIGINT: gettimeofday(&tv2, &tz); fprintf(stderr, "time lapse: %ld\n", ((tv2.tv_sec - tv1.tv_sec) * 1000000) + tv2.tv_usec - tv1.tv_usec); fprintf(stderr, "fps: %d\n", (1000000*totalFrame) / (int)(((tv2.tv_sec - tv1.tv_sec) * 1000000) + tv2.tv_usec - tv1.tv_usec) ); if (libusb_cancel_transfer(xfers[0])) fprintf(stderr, "cancel transfer 0 failed\n"); if (libusb_cancel_transfer(xfers[1])) fprintf(stderr, "cancel transfer 1 failed\n"); libusb_free_transfer(xfers[0]); libusb_free_transfer(xfers[1]); // このインタフェースと代替設定は存在しないので失敗するが // これにより NOT STREAMING 状態への遷移には成功する. libusb_set_interface_alt_setting(handle, 1, 0); //libusb_reset_device(handle); //libusb_close(handle); libusb_exit(ctx); close(fd); exit(signum); default: break; } } static void cb(struct libusb_transfer *xfer) { uint8_t *p; int plen; int i; p = xfer->buffer; for (i = 0; i < xfer->num_iso_packets; i++, p += PKT_LEN) { if (xfer->iso_packet_desc[i].status == LIBUSB_TRANSFER_COMPLETED) { plen = xfer->iso_packet_desc[i].actual_length; // packet only contains an acknowledge? if (plen < 2) continue; // error packet if (p[1] & UVC_STREAM_ERR) // bmHeaderInfo continue; // subtract the header size plen -= p[0]; // check the data size before write if (plen + total > FrameBufferSize) { fprintf(stderr, "truncate the excess payload length.\n"); plen = FrameBufferSize - total; } write(fd, p + p[0], plen); // update padding data memcpy(padding + total, p + p[0], plen); total += plen; // ストリームが EOF の場合の処理 if (p[1] & UVC_STREAM_EOF) { if (total < FrameBufferSize) { fprintf(stderr, "insufficient frame data.\n"); // data padding with previous frame. write(fd, padding + total - plen, FrameBufferSize - total); } total = 0; fprintf(stderr, "%d\n", ++totalFrame); } } } if (libusb_submit_transfer(xfer) != 0) { fprintf(stderr, "submit transfer failed.\n"); } } int main( int argc, char **argv) { libusb_device **devs; libusb_device *dev; struct libusb_device_descriptor desc; struct libusb_config_descriptor *confDesc; uint8_t buf[64]; int i; int j; if (argv[1] != NULL) { if ( (fd = open(argv[1], O_CREAT|O_TRUNC|O_RDWR, 0600)) < 0) { fprintf(stderr, "open file failed.\n"); exit(0); } } else { fd = open("/dev/stdout", O_WRONLY, 0); } signal(SIGINT, signal_callback_handler); signal(SIGUSR1, signal_callback_handler); signal(SIGUSR2, signal_callback_handler); // get cam device // libusb_init(&ctx); // handle = libusb_open_device_with_vid_pid(ctx, VID, PID); // dev = libusb_get_device(handle); libusb_init(NULL); libusb_get_device_list(NULL, &devs); i = 0; while ((dev = devs[i++]) != NULL) { libusb_get_device_descriptor(dev, &desc); for (j = 0; uvc_device_list[j].VID != 0; j++) { uvc = uvc_device_list[j]; if (uvc.VID == desc.idVendor && uvc.PID == desc.idProduct) goto FOUND; } } FOUND: // libusb_free_device_list(devs, 1); libusb_open(dev, &handle); // if kernel driver is active, detach a kernel driver. libusb_get_config_descriptor(dev, uvc.ConfIdx, &confDesc); // kokokokokokokkokoko const struct libusb_interface *intf; const struct libusb_interface_descriptor *intfDesc; intf = confDesc->interface; intfDesc = &intf->altsetting[0]; printf("length: %d\n", intfDesc->extra_length); printf("addr: %ld\n", (long)intfDesc); intfDesc = &intf->altsetting[1]; printf("length: %d\n", intfDesc->extra_length); printf("addr: %ld\n", (long)intfDesc); exit(1); for (i=0; i<confDesc->bNumInterfaces; i++) { if (libusb_kernel_driver_active(handle, i) == 1) { fprintf(stderr, "detaching kernel driver for interface %d.\n", i); if (libusb_detach_kernel_driver(handle, i) != 0) { fprintf(stderr, "detach failed.\n"); } } } libusb_free_config_descriptor(confDesc); // set the active configuration if (libusb_set_configuration(handle, uvc.ConfVal) != 0) { fprintf(stderr, "set configuration failed.\n"); } // claim an interface in a given libusb_handle. if (libusb_claim_interface(handle, 0) != 0) { fprintf(stderr, "claim interface failed.\n"); } // ------------------------------------------------------------ // negotiate the streaming parameters // Device State Transition [UVC1.5, p. 107] // Video Probe and Commit Controls [UVC1.5, p. 134] // set some parameters described to be set by the host. [UVC1.5, p. 134] buf[0] = 0x01; // what fields shall be kept fixed (0x01: dwFrameInterval) buf[1] = 0x00; // buf[2] = 0x01; // Video format index (Uncompressed) buf[3] = uvc.FrameIndex; // Video frame index buf[4] = 0x15; // Frame interval in 100ns buf[5] = 0x16; // propose: 0x00051615 (33ms) buf[6] = 0x05; // buf[7] = 0x00; // buf[8] = 0x00; // Key frame rate in key-frame per videoframe units buf[9] = 0x00; // buf[10] = 0x00; // PFrame rate in PFrame/key frame units. buf[11] = 0x00; // buf[12] = 0x00; // Compression quality control in abstract units 1 to 10000. buf[13] = 0x00; // buf[14] = 0x00; // Window size for average bit rate control. buf[15] = 0x00; // Window size for average bit rate control. buf[16] = 0x00; // Internal video streaming interface latency in ms. buf[17] = 0x00; // buf[18] = 0x00; // Maximum video frame or codec-specific segment size in bytes (ro) buf[19] = 0x00; // buf[20] = 0x00; // buf[21] = 0x00; // buf[22] = 0x00; // Specifies the maximum number of bytes (ro) buf[23] = 0x00; // buf[24] = 0x00; // buf[25] = 0x00; // // VS_PROBE_CONTROL(0x01), SET_CUR(0x01) [UVC1.5, p. 161, 158] libusb_control_transfer(handle, 0x21, 0x01, 0x0100, 0x0001, buf, 26, TIMEOUT); // VS_PROBE_CONTROL(0x01), GET_MIN(0x82) [UVC1.5, p. 161, 158] libusb_control_transfer(handle, 0xa1, 0x82, 0x0100, 0x0001, buf, 26, TIMEOUT); #if DEBUG for (i=0; i<26; i++) { fprintf(stderr, "%02x ", buf[i]); } fprintf(stderr, "\n"); #endif // VS_COMMIT_CONTROL(0x02), SET_CUR(0x01) [UVC1.5, p. 161, 158] libusb_control_transfer(handle, 0x21, 0x01, 0x0200, 0x0001, buf, 26, TIMEOUT); // ------------------------------------------------------------ // set interface, set alt interface [USB2.0, p. 250] // claim an interface in a given libusb_handle. if (libusb_claim_interface(handle, uvc.IfNum) != 0) { fprintf(stderr, "claim interface failed.\n"); } // activate an alternate setting for an interface. if (libusb_set_interface_alt_setting(handle, uvc.IfNum, uvc.AltIfNum) != 0) { fprintf(stderr, "activate an alternate setting failed.\n"); } // ------------------------------------------------------------ // BSW20K07HWH.dump_curr_config_desc.txt でみると // UCAM-DLY300TA.dump_curr_config_desc.txt でみると // // Entity ID は bUnitID として示されている. // たとえば <VC_PROCESSING_UNIT> を上記のディスクリプタの例でみると // いずれも bUnitID: 02 となっていることがわかる. // PU_GAMMA_CONTROL(0x09), GET_MIN(0x82) [UVC1.5, p. 160, 100, 96] #if 0 // EU_MIN_FRAME_INTERVAL_CONTROL(0x04), SET_CUR(0x01) [UVC1.5, p. 160, 100, 86] buf[0] = 0x2a; // Frame interval in 100ns buf[1] = 0x2c; // propose: 0x000a2c2a (66ms) buf[2] = 0x0a; // buf[3] = 0x00; // libusb_control_transfer(handle, 0x21, 0x01, 0x0400, 0x0001, buf, 4, TIMEOUT); libusb_control_transfer(handle, 0xa1, 0x81, 0x0400, 0x0001, buf, 4, TIMEOUT); fprintf(stderr, "intval cur: 0x%02x%02x%02x%02x\n", buf[3], buf[2], buf[1], buf[0]); #endif // CT_AE_PRIORITY_CONTROL(0x03), SET_CUR(0x01) [UVC1.5, p. 159, 100, 86] // libusb_control_transfer(handle, 0x21, 0x01, 0x0300, 0x0100, buf, 4, TIMEOUT); // CT_AE_MODE_CONTROL(0x02), GET_CUR(0x81) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x81, 0x0200, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "CT_AE_MODE_CONTROL: 0x%02x\n", buf[0]); // CT_AE_PRIORITY_CONTROL(0x03), GET_CUR(0x81) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x81, 0x0300, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "CT_AE_PRIORITY_CONTROL: 0x%02x\n", buf[0]); // CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(0x04), GET_CUR(0x81) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x81, 0x0400, 0x0100, buf, 4, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(cur): 0x%02x%02x%02x%02x\n", buf[3], buf[2], buf[1], buf[0]); // CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(0x04), GET_MIN(0x82) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x82, 0x0400, 0x0100, buf, 4, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(min): 0x%02x%02x%02x%02x\n", buf[3], buf[2], buf[1], buf[0]); // CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(0x04), GET_MAX(0x83) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x83, 0x0400, 0x0100, buf, 4, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(max): 0x%02x%02x%02x%02x\n", buf[3], buf[2], buf[1], buf[0]); // CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(0x04), GET_MAX(0x84) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x84, 0x0400, 0x0100, buf, 4, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(res): 0x%02x%02x%02x%02x\n", buf[3], buf[2], buf[1], buf[0]); // CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(0x05), GET_CUR(0x81) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x81, 0x0500, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(cur): 0x%02x\n", buf[0]); // CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(0x05), GET_MIN(0x82) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x82, 0x0500, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(min): 0x%02x\n", buf[0]); // CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(0x05), GET_MAX(0x83) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x83, 0x0500, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(max): 0x%02x\n", buf[0]); // CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(0x05), GET_RES(0x84) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x84, 0x0500, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(res): 0x%02x\n", buf[0]); #if 1 buf[0] = 0x08; // 0x01 / 0x08 // CT_AE_MODE_CONTROL(0x02), SET_CUR(0x01) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0x21, 0x01, 0x0200, 0x0100, buf, 1, TIMEOUT); buf[0] = 0x00; // CT_AE_PRIORITY_CONTROL(0x03), SET_CUR(0x01) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0x21, 0x01, 0x0300, 0x0100, buf, 1, TIMEOUT); buf[0] = 0x08; buf[1] = 0x00; buf[2] = 0x00; buf[3] = 0x00; // CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL(0x04), SET_CUR(0x01) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0x21, 0x01, 0x0400, 0x0100, buf, 4, TIMEOUT); buf[0] = 0x01; // CT_AE_EXPOSURE_TIME_RELATIVE_CONTROL(0x05), SET_CUR(0x01) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0x21, 0x01, 0x0500, 0x0100, buf, 1, TIMEOUT); // CT_AE_MODE_CONTROL(0x02), GET_CUR(0x81) [UVC1.5, p. 159, 100, 86] libusb_control_transfer(handle, 0xa1, 0x81, 0x0200, 0x0100, buf, 1, TIMEOUT); fprintf(stderr, "CT_AE_MODE_CONTROL: 0x%02x\n", buf[0]); // CT_AE_EXPOSURE_TIME_ABSOLUTE_CONTROL では範囲を越える値を設 // 定するときちんと再設定しないと正しい表示とならない. // AE をマニュアルに設定するとオートの時の明るさを再現できない // のはなぜか? #endif // ------------------------------------------------------------ // do an isochronous transfer for (i=0; i<NUM_TRANSFER; i++) { xfers[i] = libusb_alloc_transfer(PKTS_PER_XFER); uint8_t *data = malloc(PKT_LEN*PKTS_PER_XFER); libusb_fill_iso_transfer( xfers[i], handle, uvc.Endpoint, data, PKT_LEN*PKTS_PER_XFER, PKTS_PER_XFER, cb, NULL, 0); libusb_set_iso_packet_lengths(xfers[i], PKT_LEN); } for (i=0; i<NUM_TRANSFER; i++) { if (libusb_submit_transfer(xfers[i]) != 0) { fprintf(stderr, "submit xfer failed.\n"); } } gettimeofday(&tv1, &tz); while (1) { libusb_handle_events(ctx); } }
_______________________________________________ freebsd-users-jp@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/freebsd-users-jp To unsubscribe, send any mail to "freebsd-users-jp-unsubscr...@freebsd.org"