| /********************************************************************* |
| * Software License Agreement (BSD License) |
| * |
| * Copyright (C) 2010-2012 Ken Tossell |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of the author nor other contributors may be |
| * used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| *********************************************************************/ |
| /** |
| * @defgroup diag Diagnostics |
| * @brief Interpretation of devices, error codes and negotiated stream parameters |
| */ |
| |
| #include "libuvc/libuvc.h" |
| #include "libuvc/libuvc_internal.h" |
| |
| /** @internal */ |
| typedef struct _uvc_error_msg { |
| uvc_error_t err; |
| const char *msg; |
| } _uvc_error_msg_t; |
| |
| static const _uvc_error_msg_t uvc_error_msgs[] = { |
| {UVC_SUCCESS, "Success"}, |
| {UVC_ERROR_IO, "I/O error"}, |
| {UVC_ERROR_INVALID_PARAM, "Invalid parameter"}, |
| {UVC_ERROR_ACCESS, "Access denied"}, |
| {UVC_ERROR_NO_DEVICE, "No such device"}, |
| {UVC_ERROR_NOT_FOUND, "Not found"}, |
| {UVC_ERROR_BUSY, "Busy"}, |
| {UVC_ERROR_TIMEOUT, "Timeout"}, |
| {UVC_ERROR_OVERFLOW, "Overflow"}, |
| {UVC_ERROR_PIPE, "Pipe"}, |
| {UVC_ERROR_INTERRUPTED, "Interrupted"}, |
| {UVC_ERROR_NO_MEM, "Out of memory"}, |
| {UVC_ERROR_NOT_SUPPORTED, "Not supported"}, |
| {UVC_ERROR_INVALID_DEVICE, "Invalid device"}, |
| {UVC_ERROR_INVALID_MODE, "Invalid mode"}, |
| {UVC_ERROR_CALLBACK_EXISTS, "Callback exists"} |
| }; |
| |
| /** @brief Print a message explaining an error in the UVC driver |
| * @ingroup diag |
| * |
| * @param err UVC error code |
| * @param msg Optional custom message, prepended to output |
| */ |
| void uvc_perror(uvc_error_t err, const char *msg) { |
| if (msg && *msg) { |
| fputs(msg, stderr); |
| fputs(": ", stderr); |
| } |
| |
| fprintf(stderr, "%s (%d)\n", uvc_strerror(err), err); |
| } |
| |
| /** @brief Return a string explaining an error in the UVC driver |
| * @ingroup diag |
| * |
| * @param err UVC error code |
| * @return error message |
| */ |
| const char* uvc_strerror(uvc_error_t err) { |
| size_t idx; |
| |
| for (idx = 0; idx < sizeof(uvc_error_msgs) / sizeof(*uvc_error_msgs); ++idx) { |
| if (uvc_error_msgs[idx].err == err) { |
| return uvc_error_msgs[idx].msg; |
| } |
| } |
| |
| return "Unknown error"; |
| } |
| |
| /** @brief Print the values in a stream control block |
| * @ingroup diag |
| * |
| * @param devh UVC device |
| * @param stream Output stream (stderr if NULL) |
| */ |
| void uvc_print_stream_ctrl(uvc_stream_ctrl_t *ctrl, FILE *stream) { |
| if (stream == NULL) |
| stream = stderr; |
| |
| fprintf(stream, "bmHint: %04x\n", ctrl->bmHint); |
| fprintf(stream, "bFormatIndex: %d\n", ctrl->bFormatIndex); |
| fprintf(stream, "bFrameIndex: %d\n", ctrl->bFrameIndex); |
| fprintf(stream, "dwFrameInterval: %u\n", ctrl->dwFrameInterval); |
| fprintf(stream, "wKeyFrameRate: %d\n", ctrl->wKeyFrameRate); |
| fprintf(stream, "wPFrameRate: %d\n", ctrl->wPFrameRate); |
| fprintf(stream, "wCompQuality: %d\n", ctrl->wCompQuality); |
| fprintf(stream, "wCompWindowSize: %d\n", ctrl->wCompWindowSize); |
| fprintf(stream, "wDelay: %d\n", ctrl->wDelay); |
| fprintf(stream, "dwMaxVideoFrameSize: %u\n", ctrl->dwMaxVideoFrameSize); |
| fprintf(stream, "dwMaxPayloadTransferSize: %u\n", ctrl->dwMaxPayloadTransferSize); |
| fprintf(stream, "bInterfaceNumber: %d\n", ctrl->bInterfaceNumber); |
| } |
| |
| static const char *_uvc_name_for_format_subtype(uint8_t subtype) { |
| switch (subtype) { |
| case UVC_VS_FORMAT_UNCOMPRESSED: |
| return "UncompressedFormat"; |
| case UVC_VS_FORMAT_MJPEG: |
| return "MJPEGFormat"; |
| case UVC_VS_FORMAT_FRAME_BASED: |
| return "FrameFormat"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| /** @brief Print camera capabilities and configuration. |
| * @ingroup diag |
| * |
| * @param devh UVC device |
| * @param stream Output stream (stderr if NULL) |
| */ |
| void uvc_print_diag(uvc_device_handle_t *devh, FILE *stream) { |
| if (stream == NULL) |
| stream = stderr; |
| |
| if (devh->info->ctrl_if.bcdUVC) { |
| uvc_streaming_interface_t *stream_if; |
| int stream_idx = 0; |
| |
| uvc_device_descriptor_t *desc; |
| uvc_get_device_descriptor(devh->dev, &desc); |
| |
| fprintf(stream, "DEVICE CONFIGURATION (%04x:%04x/%s) ---\n", |
| desc->idVendor, desc->idProduct, |
| desc->serialNumber ? desc->serialNumber : "[none]"); |
| |
| uvc_free_device_descriptor(desc); |
| |
| fprintf(stream, "Status: %s\n", devh->streams ? "streaming" : "idle"); |
| |
| fprintf(stream, "VideoControl:\n" |
| "\tbcdUVC: 0x%04x\n", |
| devh->info->ctrl_if.bcdUVC); |
| |
| DL_FOREACH(devh->info->stream_ifs, stream_if) { |
| uvc_format_desc_t *fmt_desc; |
| |
| ++stream_idx; |
| |
| fprintf(stream, "VideoStreaming(%d):\n" |
| "\tbEndpointAddress: %d\n\tFormats:\n", |
| stream_idx, stream_if->bEndpointAddress); |
| |
| DL_FOREACH(stream_if->format_descs, fmt_desc) { |
| uvc_frame_desc_t *frame_desc; |
| int i; |
| |
| switch (fmt_desc->bDescriptorSubtype) { |
| case UVC_VS_FORMAT_UNCOMPRESSED: |
| case UVC_VS_FORMAT_MJPEG: |
| case UVC_VS_FORMAT_FRAME_BASED: |
| fprintf(stream, |
| "\t\%s(%d)\n" |
| "\t\t bits per pixel: %d\n" |
| "\t\t GUID: ", |
| _uvc_name_for_format_subtype(fmt_desc->bDescriptorSubtype), |
| fmt_desc->bFormatIndex, |
| fmt_desc->bBitsPerPixel); |
| |
| for (i = 0; i < 16; ++i) |
| fprintf(stream, "%02x", fmt_desc->guidFormat[i]); |
| |
| fprintf(stream, " (%4s)\n", fmt_desc->fourccFormat ); |
| |
| fprintf(stream, |
| "\t\t default frame: %d\n" |
| "\t\t aspect ratio: %dx%d\n" |
| "\t\t interlace flags: %02x\n" |
| "\t\t copy protect: %02x\n", |
| fmt_desc->bDefaultFrameIndex, |
| fmt_desc->bAspectRatioX, |
| fmt_desc->bAspectRatioY, |
| fmt_desc->bmInterlaceFlags, |
| fmt_desc->bCopyProtect); |
| |
| DL_FOREACH(fmt_desc->frame_descs, frame_desc) { |
| uint32_t *interval_ptr; |
| |
| fprintf(stream, |
| "\t\t\tFrameDescriptor(%d)\n" |
| "\t\t\t capabilities: %02x\n" |
| "\t\t\t size: %dx%d\n" |
| "\t\t\t bit rate: %d-%d\n" |
| "\t\t\t max frame size: %d\n" |
| "\t\t\t default interval: 1/%d\n", |
| frame_desc->bFrameIndex, |
| frame_desc->bmCapabilities, |
| frame_desc->wWidth, |
| frame_desc->wHeight, |
| frame_desc->dwMinBitRate, |
| frame_desc->dwMaxBitRate, |
| frame_desc->dwMaxVideoFrameBufferSize, |
| 10000000 / frame_desc->dwDefaultFrameInterval); |
| if (frame_desc->intervals) { |
| for (interval_ptr = frame_desc->intervals; |
| *interval_ptr; |
| ++interval_ptr) { |
| fprintf(stream, |
| "\t\t\t interval[%d]: 1/%d\n", |
| (int) (interval_ptr - frame_desc->intervals), |
| 10000000 / *interval_ptr); |
| } |
| } else { |
| fprintf(stream, |
| "\t\t\t min interval[%d] = 1/%d\n" |
| "\t\t\t max interval[%d] = 1/%d\n", |
| frame_desc->dwMinFrameInterval, |
| 10000000 / frame_desc->dwMinFrameInterval, |
| frame_desc->dwMaxFrameInterval, |
| 10000000 / frame_desc->dwMaxFrameInterval); |
| if (frame_desc->dwFrameIntervalStep) |
| fprintf(stream, |
| "\t\t\t interval step[%d] = 1/%d\n", |
| frame_desc->dwFrameIntervalStep, |
| 10000000 / frame_desc->dwFrameIntervalStep); |
| } |
| } |
| break; |
| default: |
| fprintf(stream, "\t-UnknownFormat (%d)\n", |
| fmt_desc->bDescriptorSubtype ); |
| } |
| } |
| } |
| |
| fprintf(stream, "END DEVICE CONFIGURATION\n"); |
| } else { |
| fprintf(stream, "uvc_print_diag: Device not configured!\n"); |
| } |
| } |
| |
| /** @brief Print all possible frame configuration. |
| * @ingroup diag |
| * |
| * @param devh UVC device |
| * @param stream Output stream (stderr if NULL) |
| */ |
| void uvc_print_frameformats(uvc_device_handle_t *devh) { |
| |
| if (devh->info->ctrl_if.bcdUVC) { |
| uvc_streaming_interface_t *stream_if; |
| int stream_idx = 0; |
| DL_FOREACH(devh->info->stream_ifs, stream_if) { |
| uvc_format_desc_t *fmt_desc; |
| ++stream_idx; |
| |
| DL_FOREACH(stream_if->format_descs, fmt_desc) { |
| uvc_frame_desc_t *frame_desc; |
| int i; |
| |
| switch (fmt_desc->bDescriptorSubtype) { |
| case UVC_VS_FORMAT_UNCOMPRESSED: |
| case UVC_VS_FORMAT_MJPEG: |
| case UVC_VS_FORMAT_FRAME_BASED: |
| printf(" \%s(%d)\n" |
| " bits per pixel: %d\n" |
| " GUID: ", |
| _uvc_name_for_format_subtype(fmt_desc->bDescriptorSubtype), |
| fmt_desc->bFormatIndex, |
| fmt_desc->bBitsPerPixel); |
| |
| for (i = 0; i < 16; ++i) |
| printf("%02x", fmt_desc->guidFormat[i]); |
| |
| printf(" (%4s)\n", fmt_desc->fourccFormat ); |
| |
| printf(" default frame: %d\n" |
| " aspect ratio: %dx%d\n" |
| " interlace flags: %02x\n" |
| " copy protect: %02x\n", |
| fmt_desc->bDefaultFrameIndex, |
| fmt_desc->bAspectRatioX, |
| fmt_desc->bAspectRatioY, |
| fmt_desc->bmInterlaceFlags, |
| fmt_desc->bCopyProtect); |
| |
| DL_FOREACH(fmt_desc->frame_descs, frame_desc) { |
| uint32_t *interval_ptr; |
| |
| printf(" FrameDescriptor(%d)\n" |
| " capabilities: %02x\n" |
| " size: %dx%d\n" |
| " bit rate: %d-%d\n" |
| " max frame size: %d\n" |
| " default interval: 1/%d\n", |
| frame_desc->bFrameIndex, |
| frame_desc->bmCapabilities, |
| frame_desc->wWidth, |
| frame_desc->wHeight, |
| frame_desc->dwMinBitRate, |
| frame_desc->dwMaxBitRate, |
| frame_desc->dwMaxVideoFrameBufferSize, |
| 10000000 / frame_desc->dwDefaultFrameInterval); |
| if (frame_desc->intervals) { |
| for (interval_ptr = frame_desc->intervals; |
| *interval_ptr; |
| ++interval_ptr) { |
| printf(" interval[%d]: 1/%d\n", |
| (int) (interval_ptr - frame_desc->intervals), |
| 10000000 / *interval_ptr); |
| } |
| } else { |
| printf(" min interval[%d] = 1/%d\n" |
| " max interval[%d] = 1/%d\n", |
| frame_desc->dwMinFrameInterval, |
| 10000000 / frame_desc->dwMinFrameInterval, |
| frame_desc->dwMaxFrameInterval, |
| 10000000 / frame_desc->dwMaxFrameInterval); |
| if (frame_desc->dwFrameIntervalStep) |
| printf(" interval step[%d] = 1/%d\n", |
| frame_desc->dwFrameIntervalStep, |
| 10000000 / frame_desc->dwFrameIntervalStep); |
| } |
| } |
| break; |
| default: |
| printf("\t-UnknownFormat (%d)\n",fmt_desc->bDescriptorSubtype ); |
| } |
| } |
| } |
| } else { |
| printf("uvc_print_frameformats: Device not configured!\n"); |
| } |
| } |