root/src/bin/listusb/usb_video.cpp
/*
 * Copyright 2016, Adrien Destugues, pulkomandy@pulkomandy.tk
 * Distributed under terms of the MIT license.
 */

#include <stdio.h>

#include <usb/USB_video.h>

#include "listusb.h"


void
DumpVideoCSInterfaceDescriptorHeader(
        const usb_videocontrol_header_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Header)\n",
                descriptor->descriptor_subtype);
        printf("                    UVC Release ....... %d.%d\n",
                descriptor->bcd_release_no >> 8, descriptor->bcd_release_no & 0xFF);
        printf("                    Total Length ...... %u\n",
                descriptor->total_length);
        printf("                    Clock Frequency ... %" B_PRIu32 "\n",
                descriptor->clock_frequency);
        printf("                    Interfaces ........ ");

        for (uint8 i = 0; i < descriptor->in_collection; i++)
                printf("%u, ", descriptor->interface_numbers[i]);
        printf("\n");
}


static const char*
TerminalTypeName(uint16 terminalType)
{
        switch (terminalType) {
                case USB_VIDEO_VENDOR_USB_IO:
                        return "Vendor specific";
                case USB_VIDEO_STREAMING_USB_IO:
                        return "Streaming";

                case USB_VIDEO_VENDOR_IN:
                        return "Vendor specific input";
                case USB_VIDEO_CAMERA_IN:
                        return "Camera";
                case USB_VIDEO_MEDIA_TRANSPORT_IN:
                        return "Media transport input";

                case USB_VIDEO_VENDOR_OUT:
                        return "Vendor specific output";
                case USB_VIDEO_DISPLAY_OUT:
                        return "Display";
                case USB_VIDEO_MEDIA_TRANSPORT_OUT:
                        return "Media transport output";

                case USB_VIDEO_VENDOR_EXT:
                        return "Vendor specific format";
                case USB_VIDEO_COMPOSITE_EXT:
                        return "Composite";
                case USB_VIDEO_SVIDEO_EXT:
                        return "S-Video";
                case USB_VIDEO_COMPONENT_EXT:
                        return "Component";

                default:
                        return "Unknown";
        }
}


void
DumpVideoCSInterfaceDescriptorOutputTerminal(
        const usb_video_output_terminal_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Output Terminal)\n",
                descriptor->descriptor_subtype);
        printf("                    Terminal ID ....... %u\n",
                descriptor->terminal_id);
        printf("                    Terminal Type ..... 0x%04x (%s)\n",
                descriptor->terminal_type,
                        TerminalTypeName(descriptor->terminal_type));
        printf("                    Associated Terminal %u\n",
                descriptor->associated_terminal);
        printf("                    Source ID ......... %u\n",
                descriptor->source_id);
        printf("                    Terminal .......... %u\n",
                descriptor->terminal);
}


void
DumpVideoCSInterfaceDescriptorInputTerminal(
        const usb_video_camera_input_terminal_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Input Terminal)\n",
                descriptor->descriptor_subtype);
        printf("                    Terminal ID ....... %u\n",
                descriptor->terminal_id);
        printf("                    Terminal Type ..... 0x%04x (%s)\n",
                descriptor->terminal_type,
                        TerminalTypeName(descriptor->terminal_type));
        printf("                    Terminal .......... %u\n",
                descriptor->terminal);

        if (descriptor->terminal_type == USB_VIDEO_CAMERA_IN)
        {
                printf("                    Min. Focal length . %u\n",
                        descriptor->camera.objective_focal_length_min);
                printf("                    Max. Focal length . %u\n",
                        descriptor->camera.objective_focal_length_min);
                printf("                    Focal length ...... %u\n",
                        descriptor->camera.ocular_focal_length);
                printf("                    Controls .......... %02x%02x%02x\n",
                        descriptor->camera.controls[0],
                        descriptor->camera.controls[1],
                        descriptor->camera.controls[2]);
        }
}


static const char*
ProcessingControlString(int index)
{
        switch(index)
        {
                case 0:
                        return "Brightness, ";
                case 1:
                        return "Contrast, ";
                case 2:
                        return "Hue, ";
                case 3:
                        return "Saturation, ";
                case 4:
                        return "Sharpness, ";
                case 5:
                        return "Gamma, ";
                case 6:
                        return "White balance temp., ";
                case 7:
                        return "White balance component, ";
                case 8:
                        return "Backlight compensation, ";
                case 9:
                        return "Gain, ";
                case 10:
                        return "Power line frequency, ";
                case 11:
                        return "Automatic hue, ";
                case 12:
                        return "Automatic white balance temp., ";
                case 13:
                        return "Automatic white balance component, ";
                case 14:
                        return "Digital multiplier, ";
                case 15:
                        return "Digital multiplier limit, ";
                case 16:
                        return "Analog video standard, ";
                case 17:
                        return "Analog video lock status, ";
                case 18:
                        return "Automatic contrast, ";
                default:
                        return "Unknown, ";
        }
}


void
DumpVideoCSInterfaceDescriptorProcessingUnit(
        const usb_video_processing_unit_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Processing unit)\n",
                descriptor->descriptor_subtype);
        printf("                    Unit ID ........... %u\n",
                descriptor->unit_id);
        printf("                    Source ID ......... %u\n",
                descriptor->source_id);
        printf("                    Max Multiplier .... %f\n",
                descriptor->max_multiplier / 100.f);
        printf("                    Controls .......... ");
        uint32_t controls = (descriptor->controls[0] << 16)
                | (descriptor->controls[1] << 8)
                | descriptor->controls[2];
        for (int i = 0; i < 19; i++)
        {
                if (controls & (1 << (23 - i))) {
                        fputs(ProcessingControlString(i), stdout);
                }
        }
        printf("\n");
        printf("                    Processing ........ %u\n",
                descriptor->Processing());
        printf("                    Video Standards ... 0x%02x\n",
                descriptor->VideoStandards().video_standards);
}


void
DumpVideoCSInterfaceDescriptorExtensionUnit(
        const usb_generic_descriptor* descriptor)
{
        uint8 i = 0;

        printf("                    Type .............. 0x%02x\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Extension unit)\n",
                (uint8)descriptor->data[i++]);
        printf("                    Unit ID ........... %u\n",
                (uint8)descriptor->data[i++]);

        printf("                    GUID .............. ");
        for (i = 2; i < 16 + 2; i++)
                printf("%02x ", descriptor->data[i]);
        printf("\n");

        printf("                    Control count ..... %u\n",
                (uint8)descriptor->data[i++]);

        printf("                    Input pins ........ ");
        i = 20; // Skip the input pin count
        for (; i - 20 < descriptor->data[19]; i++)
                printf("%u, ", descriptor->data[i]);
        printf("\n");

        printf("                    Controls .......... ");
        uint8_t end = descriptor->data[i++];
        uint8_t start = i;
        for (; i - start < end; i++)
                printf("%02x", (uint8)descriptor->data[i]);
        printf("\n");

        printf("                    Extension ......... %u\n",
                (uint8)descriptor->data[i++]);
}


void
DumpVideoControlCSInterfaceDescriptor(const usb_generic_descriptor* descriptor)
{
        uint8 descriptorSubtype = descriptor->data[0];
        switch (descriptorSubtype) {
                case USB_VIDEO_VC_HEADER:
                        DumpVideoCSInterfaceDescriptorHeader(
                                (usb_videocontrol_header_descriptor*)descriptor);
                        break;
                case USB_VIDEO_VC_INPUT_TERMINAL:
                        DumpVideoCSInterfaceDescriptorInputTerminal(
                                (usb_video_camera_input_terminal_descriptor*)descriptor);
                        break;
                case USB_VIDEO_VC_OUTPUT_TERMINAL:
                        DumpVideoCSInterfaceDescriptorOutputTerminal(
                                (usb_video_output_terminal_descriptor*)descriptor);
                        break;
                case USB_VIDEO_VC_PROCESSING_UNIT:
                        DumpVideoCSInterfaceDescriptorProcessingUnit(
                                (usb_video_processing_unit_descriptor*)descriptor);
                        break;
                case USB_VIDEO_VC_EXTENSION_UNIT:
                        DumpVideoCSInterfaceDescriptorExtensionUnit(descriptor);
                        break;
                default:
                        DumpDescriptorData(descriptor);
        }
}


void
DumpVideoControlCSInterruptEndpointDescriptor(const usb_generic_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (Endpoint)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Interrupt)\n",
                (uint8)descriptor->data[0]);
        printf("                    Max Transfer Size . %u\n",
                (uint16)((descriptor->data[1] << 8) | descriptor->data[2]));
}


void
DumpVideoControlCSEndpointDescriptor(const usb_generic_descriptor* descriptor)
{
        uint8 descriptorSubtype = descriptor->data[0];
        switch (descriptorSubtype) {
                case EP_SUBTYPE_INTERRUPT:
                        DumpVideoControlCSInterruptEndpointDescriptor(descriptor);
                        break;
                default:
                        DumpDescriptorData(descriptor);
        }
}


void
DumpVideoStreamInputHeaderDescriptor(const usb_generic_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (VideoStream Interface)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Input header)\n",
                (uint8)descriptor->data[0]);
        printf("                    Format count ...... %u\n",
                (uint8)descriptor->data[1]);
        printf("                    Total length ...... %u\n",
                (uint16)((descriptor->data[2] << 8) | descriptor->data[3]));
        printf("                    Endpoint .......... 0x%02x\n",
                (uint8)descriptor->data[4]);
        printf("                    Info .............. 0x%02x\n",
                (uint8)descriptor->data[5]);
        printf("                    Terminal Link ..... 0x%02x\n",
                (uint8)descriptor->data[6]);
        printf("                    Still capture ..... 0x%02x\n",
                (uint8)descriptor->data[7]);
        printf("                    Trigger support ... %u\n",
                (uint8)descriptor->data[8]);
        printf("                    Trigger usage ..... %u\n",
                (uint8)descriptor->data[9]);

        uint8 nformat = descriptor->data[1];
        uint8 formatsize = descriptor->data[10];
        uint8 i, j;

        for (i = 0; i < nformat; i++)
        {
                printf("                    Format %2d ......... 0x", i);
                for (j = 0; j < formatsize; j++)
                        printf("%02x", (uint8)descriptor->data[11 + i * formatsize + j]);
                printf("\n");
        }
}


void
DumpVideoStillImageDescriptor(const usb_generic_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (VideoStream Interface)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (Still Image)\n",
                (uint8)descriptor->data[0]);
        printf("                    Endpoint .......... %u\n",
                (uint8)descriptor->data[1]);

        uint8 npatterns = descriptor->data[2];
        uint8 i;
        printf("                    Resolutions ....... ");
        for (i = 0; i < npatterns; i++)
        {
                // FIXME these are reverse-endian compared to everything else.
                // Is my webcam wrong, or is it some quirk in the spec?
                printf("%ux%u, ",
                        (uint16)((descriptor->data[i * 4 + 4] << 8) | (descriptor->data[i * 4 + 3])),
                        (uint16)((descriptor->data[i * 4 + 6] << 8) | (descriptor->data[i * 4 + 5])));
        }
        printf("\n");

        i = i * 4 + 3;

        npatterns = descriptor->data[i];
        while (npatterns > 0)
        {
                printf("                    Compression ....... %u\n",
                        (uint8)descriptor->data[i]);
                npatterns--;
        }
}


static const char*
VSInterfaceString(int subtype)
{
        switch(subtype) {
                case USB_VIDEO_VS_UNDEFINED:
                        return "Undefined";
                case USB_VIDEO_VS_INPUT_HEADER:
                        return "Input header";
                case USB_VIDEO_VS_OUTPUT_HEADER:
                        return "Output header";
                case USB_VIDEO_VS_STILL_IMAGE_FRAME:
                        return "Still image";
                case USB_VIDEO_VS_FORMAT_UNCOMPRESSED:
                        return "Uncompressed format";
                case USB_VIDEO_VS_FRAME_UNCOMPRESSED:
                        return "Uncompressed frame";
                case USB_VIDEO_VS_FORMAT_MJPEG:
                        return "MJPEG format";
                case USB_VIDEO_VS_FRAME_MJPEG:
                        return "MJPEG frame";
                case USB_VIDEO_VS_FORMAT_MPEG2TS:
                        return "MPEG2TS format";
                case USB_VIDEO_VS_FORMAT_DV:
                        return "DV format";
                case USB_VIDEO_VS_COLORFORMAT:
                        return "Color format";
                case USB_VIDEO_VS_FORMAT_FRAME_BASED:
                        return "Frame based format";
                case USB_VIDEO_VS_FRAME_FRAME_BASED:
                        return "Frame based frame";
                case USB_VIDEO_VS_FORMAT_STREAM_BASED:
                        return "Stream based format";
                case USB_VIDEO_VS_FORMAT_H264:
                        return "H264 format";
                case USB_VIDEO_VS_FRAME_H264:
                        return "H264 frame";
                case USB_VIDEO_VS_FORMAT_H264_SIMULCAST:
                        return "H264 simulcast";
                case USB_VIDEO_VS_FORMAT_VP8:
                        return "VP8 format";
                case USB_VIDEO_VS_FRAME_VP8:
                        return "VP8 frame";
                case USB_VIDEO_VS_FORMAT_VP8_SIMULCAST:
                        return "VP8 simulcast";
                default:
                        return "Unknown";
        };
}


void
DumpVideoFormatDescriptor(const usb_generic_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (VideoStream Interface)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (%s)\n",
                (uint8)descriptor->data[0], VSInterfaceString(descriptor->data[0]));
        printf("                    Index ............. 0x%02x\n",
                (uint8)descriptor->data[1]);
        printf("                    Frame number ...... 0x%02x\n",
                (uint8)descriptor->data[2]);

        printf("                    GUID .............. ");
        for (uint8 i = 3; i < 16 + 3; i++)
                printf("%02x ", descriptor->data[i]);
        printf("\n");

        printf("                    Bits per pixel .... %u\n",
                (uint8)descriptor->data[19]);
        printf("                    Default frame idx . 0x%02x\n",
                (uint8)descriptor->data[20]);
        printf("                    Aspect ratio ...... %u:%u\n",
                (uint8)descriptor->data[21], (uint8)descriptor->data[22]);
        printf("                    Interlace flags ... 0x%02x\n",
                (uint8)descriptor->data[23]);
        printf("                    Copy protect ...... %u\n",
                (uint8)descriptor->data[24]);
}


void
DumpVideoFrameDescriptor(const usb_video_frame_descriptor* descriptor)
{
        printf("                    Type .............. 0x%02x (VideoStream Interface)\n",
                descriptor->descriptor_type);
        printf("                    Subtype ........... 0x%02x (%s)\n",
                descriptor->descriptor_subtype,
                VSInterfaceString(descriptor->descriptor_subtype));
        printf("                    Index ............. 0x%02x\n",
                descriptor->frame_index);
        printf("                    Capabilities ...... 0x%02x\n",
                descriptor->capabilities);
        printf("                    Resolution ........ %u x %u\n",
                descriptor->width, descriptor->height);
        printf("                    Bit rates ......... %" B_PRIu32 " - %" B_PRIu32 "\n",
                descriptor->min_bit_rate, descriptor->max_bit_rate);
        printf("                    Frame buffer size . %" B_PRIu32 "\n",
                descriptor->max_video_frame_buffer_size);
        printf("                    Frame interval .... %.4fms\n",
                descriptor->default_frame_interval / 10000.f);
        for (uint8 i = 0; i < descriptor->frame_interval_type; i++)
        {
                printf("                    Frame interval %2d . %.4fms\n",
                        i, descriptor->discrete_frame_intervals[i] / 10000.f);
        }
        // TODO if frame__interval_type is 0, dump continuous frame intervals
}


static const char*
ColorPrimariesString(uint8_t value)
{
        switch (value) {
                case 0:
                        return "Unspecified";
                case 1:
                        return "BT.709, sRGB";
                case 2:
                        return "BT.470-2 (M)";
                case 3:
                        return "BT.470-2 (B, G)";
                case 4:
                        return "SMPTE 170M";
                case 5:
                        return "SMPTE 240M";
                default:
                        return "??";
        }
}


static const char*
TransferCharacteristicsString(uint8_t value)
{
        switch (value) {
                case 0:
                        return "Unspecified";
                case 1:
                        return "BT.709";
                case 2:
                        return "BT.470-2 (M)";
                case 3:
                        return "BT.470-2 (B, G)";
                case 4:
                        return "SMPTE 170M";
                case 5:
                        return "SMPTE 240M";
                case 6:
                        return "Linear (V = Lc)";
                case 7:
                        return "sRGB";
                default:
                        return "??";
        }
}

static const char*
MatrixCoefficientsString(uint8_t value)
{
        switch (value) {
                case 0:
                        return "Unspecified";
                case 1:
                        return "BT.709";
                case 2:
                        return "FCC";
                case 3:
                        return "BT.470-2 (B, G)";
                case 4:
                        return "SMPTE 170M (BT.601)";
                case 5:
                        return "SMPTE 240M";
                default:
                        return "??";
        }
}


static void
DumpVideoStreamColorFormatDescriptor(const usb_video_color_matching_descriptor* descriptor)
{
        printf("                    Type ..................... 0x%02x (VideoStream Interface)\n",
                descriptor->descriptor_type);
        printf("                    Subtype .................. 0x%02x (%s)\n",
                descriptor->descriptor_sub_type,
                VSInterfaceString(descriptor->descriptor_sub_type));
        printf("                    Color Primaries .......... 0x%02x (%s)\n",
                descriptor->color_primaries,
                ColorPrimariesString(descriptor->color_primaries));
        printf("                    Transfer characteristics . 0x%02x (%s)\n",
                descriptor->transfer_characteristics,
                TransferCharacteristicsString(descriptor->transfer_characteristics));
        printf("                    Matrix coefficients ...... 0x%02x (%s)\n",
                descriptor->matrix_coefficients,
                MatrixCoefficientsString(descriptor->matrix_coefficients));
}


void
DumpVideoStreamCSInterfaceDescriptor(const usb_generic_descriptor* descriptor)
{
        uint8 subtype = descriptor->data[0];
        switch (subtype) {
                case USB_VIDEO_VS_INPUT_HEADER:
                        DumpVideoStreamInputHeaderDescriptor(descriptor);
                        break;
                case USB_VIDEO_VS_STILL_IMAGE_FRAME:
                        DumpVideoStillImageDescriptor(descriptor);
                        break;
                case USB_VIDEO_VS_FORMAT_UNCOMPRESSED:
                case USB_VIDEO_VS_FORMAT_MJPEG:
                        DumpVideoFormatDescriptor(descriptor);
                        break;
                case USB_VIDEO_VS_FRAME_UNCOMPRESSED:
                case USB_VIDEO_VS_FRAME_MJPEG:
                        DumpVideoFrameDescriptor((usb_video_frame_descriptor*)descriptor);
                        break;
                case USB_VIDEO_VS_COLORFORMAT:
                        DumpVideoStreamColorFormatDescriptor((usb_video_color_matching_descriptor*)descriptor);
                        break;
                default:
                        DumpDescriptorData(descriptor);
                        break;
        }
}



void
DumpVideoDescriptor(const usb_generic_descriptor* descriptor, int subclass)
{
        switch (subclass) {
                case USB_VIDEO_INTERFACE_VIDEOCONTROL_SUBCLASS:
                        switch (descriptor->descriptor_type) {
                                case USB_VIDEO_CS_INTERFACE:
                                        DumpVideoControlCSInterfaceDescriptor(descriptor);
                                        break;
                                case USB_VIDEO_CS_ENDPOINT:
                                        DumpVideoControlCSEndpointDescriptor(descriptor);
                                        break;
                                default:
                                        DumpDescriptorData(descriptor);
                                        break;
                        }
                        break;
                case USB_VIDEO_INTERFACE_VIDEOSTREAMING_SUBCLASS:
                        switch (descriptor->descriptor_type) {
                                case USB_VIDEO_CS_INTERFACE:
                                        DumpVideoStreamCSInterfaceDescriptor(descriptor);
                                        break;
                                default:
                                        DumpDescriptorData(descriptor);
                                        break;
                        }
                        break;
                case USB_VIDEO_INTERFACE_COLLECTION_SUBCLASS:
                        switch (descriptor->descriptor_type) {
                                case USB_VIDEO_CS_INTERFACE:
                                        // TODO
                                        DumpDescriptorData(descriptor);
                                        break;
                                default:
                                        DumpDescriptorData(descriptor);
                                        break;
                        }
                        break;
                default:
                        DumpDescriptorData(descriptor);
                        break;
        }
}