From 89f8d2b958e151b3d4618bab878ad88a1cc4c00a Mon Sep 17 00:00:00 2001 From: Jakub Wlodek Date: Fri, 25 Jul 2025 17:04:34 -0400 Subject: [PATCH] New version of libuvc library --- uvcSupport/libuvc/device.c | 136 ++++++++++++++++++--- uvcSupport/libuvc/frame-mjpeg.c | 82 +++++++++---- uvcSupport/libuvc/frame.c | 4 + uvcSupport/libuvc/init.c | 6 +- uvcSupport/libuvc/libuvc/libuvc.h | 11 ++ uvcSupport/libuvc/libuvc/libuvc_config.h | 4 +- uvcSupport/libuvc/libuvc/libuvc_internal.h | 19 ++- uvcSupport/libuvc/stream.c | 99 +++++++++------ 8 files changed, 276 insertions(+), 85 deletions(-) diff --git a/uvcSupport/libuvc/device.c b/uvcSupport/libuvc/device.c index 3d49b68..ad446b2 100644 --- a/uvcSupport/libuvc/device.c +++ b/uvcSupport/libuvc/device.c @@ -42,10 +42,10 @@ int uvc_already_open(uvc_context_t *ctx, struct libusb_device *usb_dev); void uvc_free_devh(uvc_device_handle_t *devh); -uvc_error_t uvc_get_device_info(uvc_device_t *dev, uvc_device_info_t **info); +uvc_error_t uvc_get_device_info(uvc_device_handle_t *devh, uvc_device_info_t **info); void uvc_free_device_info(uvc_device_info_t *info); -uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info); +uvc_error_t uvc_scan_control(uvc_device_handle_t *devh, uvc_device_info_t *info); uvc_error_t uvc_parse_vc(uvc_device_t *dev, uvc_device_info_t *info, const unsigned char *block, size_t block_size); @@ -263,6 +263,49 @@ uint8_t uvc_get_device_address(uvc_device_t *dev) { return libusb_get_device_address(dev->usb_dev); } +static uvc_error_t uvc_open_internal(uvc_device_t *dev, struct libusb_device_handle *usb_devh, uvc_device_handle_t **devh); + +#if LIBUSB_API_VERSION >= 0x01000107 +/** @brief Wrap a platform-specific system device handle and obtain a UVC device handle. + * The handle allows you to use libusb to perform I/O on the device in question. + * + * On Linux, the system device handle must be a valid file descriptor opened on the device node. + * + * The system device handle must remain open until uvc_close() is called. The system device handle will not be closed by uvc_close(). + * @ingroup device + * + * @param sys_dev the platform-specific system device handle + * @param context UVC context to prepare the device + * @param[out] devh Handle on opened device + * @return Error opening device or SUCCESS + */ +uvc_error_t uvc_wrap( + int sys_dev, + uvc_context_t *context, + uvc_device_handle_t **devh) { + uvc_error_t ret; + struct libusb_device_handle *usb_devh; + + UVC_ENTER(); + + uvc_device_t *dev = NULL; + int err = libusb_wrap_sys_device(context->usb_ctx, sys_dev, &usb_devh); + UVC_DEBUG("libusb_wrap_sys_device() = %d", err); + if (err != LIBUSB_SUCCESS) { + UVC_EXIT(err); + return err; + } + + dev = calloc(1, sizeof(uvc_device_t)); + dev->ctx = context; + dev->usb_dev = libusb_get_device(usb_devh); + + ret = uvc_open_internal(dev, usb_devh, devh); + UVC_EXIT(ret); + return ret; +} +#endif + /** @brief Open a UVC device * @ingroup device * @@ -275,8 +318,6 @@ uvc_error_t uvc_open( uvc_device_handle_t **devh) { uvc_error_t ret; struct libusb_device_handle *usb_devh; - uvc_device_handle_t *internal_devh; - struct libusb_device_descriptor desc; UVC_ENTER(); @@ -288,13 +329,28 @@ uvc_error_t uvc_open( return ret; } + ret = uvc_open_internal(dev, usb_devh, devh); + UVC_EXIT(ret); + return ret; +} + +static uvc_error_t uvc_open_internal( + uvc_device_t *dev, + struct libusb_device_handle *usb_devh, + uvc_device_handle_t **devh) { + uvc_error_t ret; + uvc_device_handle_t *internal_devh; + struct libusb_device_descriptor desc; + + UVC_ENTER(); + uvc_ref_device(dev); internal_devh = calloc(1, sizeof(*internal_devh)); internal_devh->dev = dev; internal_devh->usb_devh = usb_devh; - ret = uvc_get_device_info(dev, &(internal_devh->info)); + ret = uvc_get_device_info(internal_devh, &(internal_devh->info)); if (ret != UVC_SUCCESS) goto fail; @@ -366,7 +422,7 @@ uvc_error_t uvc_open( * @param dev Device to parse descriptor for * @param info Where to store a pointer to the new info struct */ -uvc_error_t uvc_get_device_info(uvc_device_t *dev, +uvc_error_t uvc_get_device_info(uvc_device_handle_t *devh, uvc_device_info_t **info) { uvc_error_t ret; uvc_device_info_t *internal_info; @@ -379,7 +435,7 @@ uvc_error_t uvc_get_device_info(uvc_device_t *dev, return UVC_ERROR_NO_MEM; } - if (libusb_get_config_descriptor(dev->usb_dev, + if (libusb_get_config_descriptor(devh->dev->usb_dev, 0, &(internal_info->config)) != 0) { free(internal_info); @@ -387,7 +443,7 @@ uvc_error_t uvc_get_device_info(uvc_device_t *dev, return UVC_ERROR_IO; } - ret = uvc_scan_control(dev, internal_info); + ret = uvc_scan_control(devh, internal_info); if (ret != UVC_SUCCESS) { uvc_free_device_info(internal_info); UVC_EXIT(ret); @@ -474,6 +530,53 @@ void uvc_free_device_info(uvc_device_info_t *info) { UVC_EXIT_VOID(); } +static uvc_error_t get_device_descriptor( + uvc_device_handle_t *devh, + uvc_device_descriptor_t **desc) { + uvc_device_descriptor_t *desc_internal; + struct libusb_device_descriptor usb_desc; + struct libusb_device_handle *usb_devh = devh->usb_devh; + uvc_error_t ret; + + UVC_ENTER(); + + ret = libusb_get_device_descriptor(devh->dev->usb_dev, &usb_desc); + + if (ret != UVC_SUCCESS) { + UVC_EXIT(ret); + return ret; + } + + desc_internal = calloc(1, sizeof(*desc_internal)); + desc_internal->idVendor = usb_desc.idVendor; + desc_internal->idProduct = usb_desc.idProduct; + + unsigned char buf[64]; + + int bytes = libusb_get_string_descriptor_ascii( + usb_devh, usb_desc.iSerialNumber, buf, sizeof(buf)); + + if (bytes > 0) + desc_internal->serialNumber = strdup((const char*) buf); + + bytes = libusb_get_string_descriptor_ascii( + usb_devh, usb_desc.iManufacturer, buf, sizeof(buf)); + + if (bytes > 0) + desc_internal->manufacturer = strdup((const char*) buf); + + bytes = libusb_get_string_descriptor_ascii( + usb_devh, usb_desc.iProduct, buf, sizeof(buf)); + + if (bytes > 0) + desc_internal->product = strdup((const char*) buf); + + *desc = desc_internal; + + UVC_EXIT(ret); + return ret; +} + /** * @brief Get a descriptor that contains the general information about * a device @@ -943,7 +1046,7 @@ uvc_error_t uvc_release_if(uvc_device_handle_t *devh, int idx) { * Find a device's VideoControl interface and process its descriptor * @ingroup device */ -uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info) { +uvc_error_t uvc_scan_control(uvc_device_handle_t *devh, uvc_device_info_t *info) { const struct libusb_interface_descriptor *if_desc; uvc_error_t parse_ret, ret; int interface_idx; @@ -957,12 +1060,13 @@ uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info) { uvc_device_descriptor_t* dev_desc; int haveTISCamera = 0; - uvc_get_device_descriptor ( dev, &dev_desc ); - if ( 0x199e == dev_desc->idVendor && ( 0x8101 == dev_desc->idProduct || - 0x8102 == dev_desc->idProduct )) { - haveTISCamera = 1; + if ( get_device_descriptor ( devh, &dev_desc ) == UVC_SUCCESS ) { + if ( 0x199e == dev_desc->idVendor && ( 0x8101 == dev_desc->idProduct || + 0x8102 == dev_desc->idProduct )) { + haveTISCamera = 1; + } + uvc_free_device_descriptor ( dev_desc ); } - uvc_free_device_descriptor ( dev_desc ); for (interface_idx = 0; interface_idx < info->config->bNumInterfaces; ++interface_idx) { if_desc = &info->config->interface[interface_idx].altsetting[0]; @@ -991,7 +1095,7 @@ uvc_error_t uvc_scan_control(uvc_device_t *dev, uvc_device_info_t *info) { while (buffer_left >= 3) { // parseX needs to see buf[0,2] = length,type block_size = buffer[0]; - parse_ret = uvc_parse_vc(dev, info, buffer, block_size); + parse_ret = uvc_parse_vc(devh->dev, info, buffer, block_size); if (parse_ret != UVC_SUCCESS) { ret = parse_ret; @@ -1029,8 +1133,10 @@ uvc_error_t uvc_parse_vc_header(uvc_device_t *dev, switch (info->ctrl_if.bcdUVC) { case 0x0100: info->ctrl_if.dwClockFrequency = DW_TO_INT(block + 7); + break; case 0x010a: info->ctrl_if.dwClockFrequency = DW_TO_INT(block + 7); + break; case 0x0110: break; default: diff --git a/uvcSupport/libuvc/frame-mjpeg.c b/uvcSupport/libuvc/frame-mjpeg.c index 8d7c545..71cd108 100644 --- a/uvcSupport/libuvc/frame-mjpeg.c +++ b/uvcSupport/libuvc/frame-mjpeg.c @@ -122,32 +122,10 @@ static void insert_huff_tables(j_decompress_ptr dinfo) { COPY_HUFF_TABLE(dinfo, ac_huff_tbl_ptrs[1], ac_chromi); } -/** @brief Convert an MJPEG frame to RGB - * @ingroup frame - * - * @param in MJPEG frame - * @param out RGB frame - */ -uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out) { +static uvc_error_t uvc_mjpeg_convert(uvc_frame_t *in, uvc_frame_t *out) { struct jpeg_decompress_struct dinfo; struct error_mgr jerr; size_t lines_read; - - if (in->frame_format != UVC_FRAME_FORMAT_MJPEG) - return UVC_ERROR_INVALID_PARAM; - - if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0) - return UVC_ERROR_NO_MEM; - - out->width = in->width; - out->height = in->height; - out->frame_format = UVC_FRAME_FORMAT_RGB; - out->step = in->width * 3; - out->sequence = in->sequence; - out->capture_time = in->capture_time; - out->capture_time_finished = in->capture_time_finished; - out->source = in->source; - dinfo.err = jpeg_std_error(&jerr.super); jerr.super.error_exit = _error_exit; @@ -164,7 +142,13 @@ uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out) { insert_huff_tables(&dinfo); } - dinfo.out_color_space = JCS_RGB; + if (out->frame_format == UVC_FRAME_FORMAT_RGB) + dinfo.out_color_space = JCS_RGB; + else if (out->frame_format == UVC_FRAME_FORMAT_GRAY8) + dinfo.out_color_space = JCS_GRAYSCALE; + else + goto fail; + dinfo.dct_method = JDCT_IFAST; jpeg_start_decompress(&dinfo); @@ -186,3 +170,53 @@ uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out) { jpeg_destroy_decompress(&dinfo); return UVC_ERROR_OTHER; } + +/** @brief Convert an MJPEG frame to RGB + * @ingroup frame + * + * @param in MJPEG frame + * @param out RGB frame + */ +uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out) { + if (in->frame_format != UVC_FRAME_FORMAT_MJPEG) + return UVC_ERROR_INVALID_PARAM; + + if (uvc_ensure_frame_size(out, in->width * in->height * 3) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = UVC_FRAME_FORMAT_RGB; + out->step = in->width * 3; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->capture_time_finished = in->capture_time_finished; + out->source = in->source; + + return uvc_mjpeg_convert(in, out); +} + +/** @brief Convert an MJPEG frame to GRAY8 + * @ingroup frame + * + * @param in MJPEG frame + * @param out GRAY8 frame + */ +uvc_error_t uvc_mjpeg2gray(uvc_frame_t *in, uvc_frame_t *out) { + if (in->frame_format != UVC_FRAME_FORMAT_MJPEG) + return UVC_ERROR_INVALID_PARAM; + + if (uvc_ensure_frame_size(out, in->width * in->height) < 0) + return UVC_ERROR_NO_MEM; + + out->width = in->width; + out->height = in->height; + out->frame_format = UVC_FRAME_FORMAT_GRAY8; + out->step = in->width; + out->sequence = in->sequence; + out->capture_time = in->capture_time; + out->capture_time_finished = in->capture_time_finished; + out->source = in->source; + + return uvc_mjpeg_convert(in, out); +} diff --git a/uvcSupport/libuvc/frame.c b/uvcSupport/libuvc/frame.c index 4eb42ac..1d1dd41 100644 --- a/uvcSupport/libuvc/frame.c +++ b/uvcSupport/libuvc/frame.c @@ -440,6 +440,10 @@ uvc_error_t uvc_uyvy2bgr(uvc_frame_t *in, uvc_frame_t *out) { */ uvc_error_t uvc_any2rgb(uvc_frame_t *in, uvc_frame_t *out) { switch (in->frame_format) { +#ifdef LIBUVC_HAS_JPEG + case UVC_FRAME_FORMAT_MJPEG: + return uvc_mjpeg2rgb(in, out); +#endif case UVC_FRAME_FORMAT_YUYV: return uvc_yuyv2rgb(in, out); case UVC_FRAME_FORMAT_UYVY: diff --git a/uvcSupport/libuvc/init.c b/uvcSupport/libuvc/init.c index 041fe58..4802835 100644 --- a/uvcSupport/libuvc/init.c +++ b/uvcSupport/libuvc/init.c @@ -51,11 +51,11 @@ for USB Video Class (UVC) devices, such as consumer webcams. \li Support for "extended" (vendor-defined) settings \section misc Misc. -\p The source code can be found at https://github.com/ktossell/libuvc. To build -the library, install libusb 1.0+ and run: +\p The source code can be found at https://github.com/libuvc/libuvc/. To build +the library, install libusb 1.0+ and run: \code -$ git clone https://github.com/ktossell/libuvc.git +$ git clone https://github.com/libuvc/libuvc.git $ cd libuvc $ mkdir build $ cd build diff --git a/uvcSupport/libuvc/libuvc/libuvc.h b/uvcSupport/libuvc/libuvc/libuvc.h index a64ed3b..d387150 100644 --- a/uvcSupport/libuvc/libuvc/libuvc.h +++ b/uvcSupport/libuvc/libuvc/libuvc.h @@ -72,6 +72,7 @@ enum uvc_frame_format { UVC_FRAME_FORMAT_BGR, /** Motion-JPEG (or JPEG) encoded images */ UVC_FRAME_FORMAT_MJPEG, + UVC_FRAME_FORMAT_H264, /** Greyscale images */ UVC_FRAME_FORMAT_GRAY8, UVC_FRAME_FORMAT_GRAY16, @@ -84,6 +85,8 @@ enum uvc_frame_format { UVC_FRAME_FORMAT_SBGGR8, /** YUV420: NV12 */ UVC_FRAME_FORMAT_NV12, + /** YUV: P010 */ + UVC_FRAME_FORMAT_P010, /** Number of formats understood */ UVC_FRAME_FORMAT_COUNT, }; @@ -553,6 +556,13 @@ uvc_error_t uvc_find_devices( uvc_device_t ***devs, int vid, int pid, const char *sn); +#if LIBUSB_API_VERSION >= 0x01000107 +uvc_error_t uvc_wrap( + int sys_dev, + uvc_context_t *context, + uvc_device_handle_t **devh); +#endif + uvc_error_t uvc_open( uvc_device_t *dev, uvc_device_handle_t **devh); @@ -791,6 +801,7 @@ uvc_error_t uvc_yuyv2uv(uvc_frame_t *in, uvc_frame_t *out); #ifdef LIBUVC_HAS_JPEG uvc_error_t uvc_mjpeg2rgb(uvc_frame_t *in, uvc_frame_t *out); +uvc_error_t uvc_mjpeg2gray(uvc_frame_t *in, uvc_frame_t *out); #endif #ifdef __cplusplus diff --git a/uvcSupport/libuvc/libuvc/libuvc_config.h b/uvcSupport/libuvc/libuvc/libuvc_config.h index 5fb212a..45ac230 100644 --- a/uvcSupport/libuvc/libuvc/libuvc_config.h +++ b/uvcSupport/libuvc/libuvc/libuvc_config.h @@ -3,8 +3,8 @@ #define LIBUVC_VERSION_MAJOR 0 #define LIBUVC_VERSION_MINOR 0 -#define LIBUVC_VERSION_PATCH 6 -#define LIBUVC_VERSION_STR "0.0.6" +#define LIBUVC_VERSION_PATCH 7 +#define LIBUVC_VERSION_STR "0.0.7" #define LIBUVC_VERSION_INT \ ((@libuvc_VERSION_MAJOR@ << 16) | \ (@libuvc_VERSION_MINOR@ << 8) | \ diff --git a/uvcSupport/libuvc/libuvc/libuvc_internal.h b/uvcSupport/libuvc/libuvc/libuvc_internal.h index d26ccdb..0d1c22d 100644 --- a/uvcSupport/libuvc/libuvc/libuvc_internal.h +++ b/uvcSupport/libuvc/libuvc/libuvc_internal.h @@ -50,10 +50,18 @@ #ifdef UVC_DEBUGGING #include +#ifdef __ANDROID__ +#include +#define UVC_DEBUG(format, ...) __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d/%s] " format "\n", basename(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) +#define UVC_ENTER() __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d] begin %s\n", basename(__FILE__), __LINE__, __FUNCTION__) +#define UVC_EXIT(code) __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d] end %s (%d)\n", basename(__FILE__), __LINE__, __FUNCTION__, code) +#define UVC_EXIT_VOID() __android_log_print(ANDROID_LOG_DEBUG, "libuvc", "[%s:%d] end %s\n", basename(__FILE__), __LINE__, __FUNCTION__) +#else #define UVC_DEBUG(format, ...) fprintf(stderr, "[%s:%d/%s] " format "\n", basename(__FILE__), __LINE__, __FUNCTION__, ##__VA_ARGS__) #define UVC_ENTER() fprintf(stderr, "[%s:%d] begin %s\n", basename(__FILE__), __LINE__, __FUNCTION__) #define UVC_EXIT(code) fprintf(stderr, "[%s:%d] end %s (%d)\n", basename(__FILE__), __LINE__, __FUNCTION__, code) #define UVC_EXIT_VOID() fprintf(stderr, "[%s:%d] end %s\n", basename(__FILE__), __LINE__, __FUNCTION__) +#endif #else #define UVC_DEBUG(format, ...) #define UVC_ENTER() @@ -212,12 +220,17 @@ typedef struct uvc_device_info { avoids problems with scheduling delays on slow boards causing missed transfers. A better approach may be to make the transfer thread FIFO scheduled (if we have root). - We could/should change this to allow reduce it to, say, 5 by default - and then allow the user to change the number of buffers as required. + Default number of transfer buffers can be overwritten by defining + this macro. */ +#ifndef LIBUVC_NUM_TRANSFER_BUFS +#if defined(__APPLE__) && defined(__MACH__) +#define LIBUVC_NUM_TRANSFER_BUFS 20 +#else #define LIBUVC_NUM_TRANSFER_BUFS 100 +#endif +#endif -#define LIBUVC_XFER_BUF_SIZE ( 16 * 1024 * 1024 ) #define LIBUVC_XFER_META_BUF_SIZE ( 4 * 1024 ) struct uvc_stream_handle { diff --git a/uvcSupport/libuvc/stream.c b/uvcSupport/libuvc/stream.c index c409fba..89dac69 100644 --- a/uvcSupport/libuvc/stream.c +++ b/uvcSupport/libuvc/stream.c @@ -103,9 +103,10 @@ struct format_table_entry *_get_format_entry(enum uvc_frame_format format) { ABS_FMT(UVC_FRAME_FORMAT_ANY, 2, {UVC_FRAME_FORMAT_UNCOMPRESSED, UVC_FRAME_FORMAT_COMPRESSED}) - ABS_FMT(UVC_FRAME_FORMAT_UNCOMPRESSED, 5, + ABS_FMT(UVC_FRAME_FORMAT_UNCOMPRESSED, 8, {UVC_FRAME_FORMAT_YUYV, UVC_FRAME_FORMAT_UYVY, UVC_FRAME_FORMAT_GRAY8, - UVC_FRAME_FORMAT_GRAY16, UVC_FRAME_FORMAT_NV12}) + UVC_FRAME_FORMAT_GRAY16, UVC_FRAME_FORMAT_NV12, UVC_FRAME_FORMAT_P010, + UVC_FRAME_FORMAT_BGR, UVC_FRAME_FORMAT_RGB}) FMT(UVC_FRAME_FORMAT_YUYV, {'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) FMT(UVC_FRAME_FORMAT_UYVY, @@ -116,6 +117,12 @@ struct format_table_entry *_get_format_entry(enum uvc_frame_format format) { {'Y', '1', '6', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) FMT(UVC_FRAME_FORMAT_NV12, {'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_P010, + {'P', '0', '1', '0', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) + FMT(UVC_FRAME_FORMAT_BGR, + {0x7d, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}) + FMT(UVC_FRAME_FORMAT_RGB, + {0x7e, 0xeb, 0x36, 0xe4, 0x4f, 0x52, 0xce, 0x11, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}) FMT(UVC_FRAME_FORMAT_BY8, {'B', 'Y', '8', ' ', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) FMT(UVC_FRAME_FORMAT_BA81, @@ -128,10 +135,12 @@ struct format_table_entry *_get_format_entry(enum uvc_frame_format format) { {'R', 'G', 'G', 'B', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) FMT(UVC_FRAME_FORMAT_SBGGR8, {'B', 'G', 'G', 'R', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) - ABS_FMT(UVC_FRAME_FORMAT_COMPRESSED, 1, - {UVC_FRAME_FORMAT_MJPEG}) + ABS_FMT(UVC_FRAME_FORMAT_COMPRESSED, 2, + {UVC_FRAME_FORMAT_MJPEG, UVC_FRAME_FORMAT_H264}) FMT(UVC_FRAME_FORMAT_MJPEG, {'M', 'J', 'P', 'G'}) + FMT(UVC_FRAME_FORMAT_H264, + {'H', '2', '6', '4', 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}) default: return NULL; @@ -583,6 +592,14 @@ uvc_error_t uvc_get_still_ctrl_format_size( return uvc_probe_still_ctrl(devh, still_ctrl); } +static int _uvc_stream_params_negotiated( + uvc_stream_ctrl_t *required, + uvc_stream_ctrl_t *actual) { + return required->bFormatIndex == actual->bFormatIndex && + required->bFrameIndex == actual->bFrameIndex && + required->dwMaxPayloadTransferSize >= actual->dwMaxPayloadTransferSize; +} + /** @internal * Negotiate streaming parameters with the device * @@ -592,16 +609,16 @@ uvc_error_t uvc_get_still_ctrl_format_size( uvc_error_t uvc_probe_stream_ctrl( uvc_device_handle_t *devh, uvc_stream_ctrl_t *ctrl) { - - uvc_query_stream_ctrl( - devh, ctrl, 1, UVC_SET_CUR - ); + uvc_stream_ctrl_t required_ctrl = *ctrl; - uvc_query_stream_ctrl( - devh, ctrl, 1, UVC_GET_CUR - ); + uvc_query_stream_ctrl( devh, ctrl, 1, UVC_SET_CUR ); + uvc_query_stream_ctrl( devh, ctrl, 1, UVC_GET_CUR ); + + if(!_uvc_stream_params_negotiated(&required_ctrl, ctrl)) { + UVC_DEBUG("Unable to negotiate streaming format"); + return UVC_ERROR_INVALID_MODE; + } - /** @todo make sure that worked */ return UVC_SUCCESS; } @@ -755,19 +772,22 @@ void _uvc_process_payload(uvc_stream_handle_t *strmh, uint8_t *payload, size_t p variable_offset += 6; } - if (header_len > variable_offset) - { + if (header_len > variable_offset) { // Metadata is attached to header - memcpy(strmh->meta_outbuf + strmh->meta_got_bytes, payload + variable_offset, header_len - variable_offset); - strmh->meta_got_bytes += header_len - variable_offset; + size_t meta_len = header_len - variable_offset; + if (strmh->meta_got_bytes + meta_len > LIBUVC_XFER_META_BUF_SIZE) + meta_len = LIBUVC_XFER_META_BUF_SIZE - strmh->meta_got_bytes; /* Avoid overflow. */ + memcpy(strmh->meta_outbuf + strmh->meta_got_bytes, payload + variable_offset, meta_len); + strmh->meta_got_bytes += meta_len; } } if (data_len > 0) { + if (strmh->got_bytes + data_len > strmh->cur_ctrl.dwMaxVideoFrameSize) + data_len = strmh->cur_ctrl.dwMaxVideoFrameSize - strmh->got_bytes; /* Avoid overflow. */ memcpy(strmh->outbuf + strmh->got_bytes, payload + header_len, data_len); strmh->got_bytes += data_len; - - if (header_info & (1 << 1)) { + if (header_info & (1 << 1) || strmh->got_bytes == strmh->cur_ctrl.dwMaxVideoFrameSize) { /* The EOF bit is set, so publish the complete frame */ _uvc_swap_buffers(strmh); } @@ -1020,9 +1040,9 @@ uvc_error_t uvc_stream_open_ctrl(uvc_device_handle_t *devh, uvc_stream_handle_t // Set up the streaming status and data space strmh->running = 0; - /** @todo take only what we need */ - strmh->outbuf = malloc( LIBUVC_XFER_BUF_SIZE ); - strmh->holdbuf = malloc( LIBUVC_XFER_BUF_SIZE ); + + strmh->outbuf = malloc( ctrl->dwMaxVideoFrameSize ); + strmh->holdbuf = malloc( ctrl->dwMaxVideoFrameSize ); strmh->meta_outbuf = malloc( LIBUVC_XFER_META_BUF_SIZE ); strmh->meta_holdbuf = malloc( LIBUVC_XFER_META_BUF_SIZE ); @@ -1232,7 +1252,7 @@ uvc_error_t uvc_stream_start( } } - if ( ret != UVC_SUCCESS && transfer_id > 0 ) { + if ( ret != UVC_SUCCESS && transfer_id >= 0 ) { for ( ; transfer_id < LIBUVC_NUM_TRANSFER_BUFS; transfer_id++) { free ( strmh->transfers[transfer_id]->buffer ); libusb_free_transfer ( strmh->transfers[transfer_id]); @@ -1323,15 +1343,24 @@ void _uvc_populate_frame(uvc_stream_handle_t *strmh) { frame->height = frame_desc->wHeight; switch (frame->frame_format) { + case UVC_FRAME_FORMAT_BGR: + frame->step = frame->width * 3; + break; case UVC_FRAME_FORMAT_YUYV: frame->step = frame->width * 2; break; case UVC_FRAME_FORMAT_NV12: frame->step = frame->width; break; + case UVC_FRAME_FORMAT_P010: + frame->step = frame->width * 2; + break; case UVC_FRAME_FORMAT_MJPEG: frame->step = 0; break; + case UVC_FRAME_FORMAT_H264: + frame->step = 0; + break; default: frame->step = 0; break; @@ -1371,7 +1400,6 @@ uvc_error_t uvc_stream_get_frame(uvc_stream_handle_t *strmh, time_t add_secs; time_t add_nsecs; struct timespec ts; - struct timeval tv; if (!strmh->running) return UVC_ERROR_INVALID_PARAM; @@ -1397,6 +1425,7 @@ uvc_error_t uvc_stream_get_frame(uvc_stream_handle_t *strmh, #if _POSIX_TIMERS > 0 clock_gettime(CLOCK_REALTIME, &ts); #else + struct timeval tv; gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; @@ -1415,13 +1444,10 @@ uvc_error_t uvc_stream_get_frame(uvc_stream_handle_t *strmh, int err = pthread_cond_timedwait(&strmh->cb_cond, &strmh->cb_mutex, &ts); //TODO: How should we handle EINVAL? - switch(err){ - case EINVAL: - *frame = NULL; - return UVC_ERROR_OTHER; - case ETIMEDOUT: - *frame = NULL; - return UVC_ERROR_TIMEOUT; + if (err) { + *frame = NULL; + pthread_mutex_unlock(&strmh->cb_mutex); + return err == ETIMEDOUT ? UVC_ERROR_TIMEOUT : UVC_ERROR_OTHER; } } @@ -1473,15 +1499,12 @@ uvc_error_t uvc_stream_stop(uvc_stream_handle_t *strmh) { pthread_mutex_lock(&strmh->cb_mutex); + /* Attempt to cancel any running transfers, we can't free them just yet because they aren't + * necessarily completed but they will be free'd in _uvc_stream_callback(). + */ for(i=0; i < LIBUVC_NUM_TRANSFER_BUFS; i++) { - if(strmh->transfers[i] != NULL) { - int res = libusb_cancel_transfer(strmh->transfers[i]); - if(res < 0 && res != LIBUSB_ERROR_NOT_FOUND ) { - free(strmh->transfers[i]->buffer); - libusb_free_transfer(strmh->transfers[i]); - strmh->transfers[i] = NULL; - } - } + if(strmh->transfers[i] != NULL) + libusb_cancel_transfer(strmh->transfers[i]); } /* Wait for transfers to complete/cancel */