blob: a195cf416522d8ff1d5007b453ba5015a4df3126 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/camera/drivers/usb_video/descriptors.h"
#include <endian.h>
#include <lib/affine/ratio.h>
#include <lib/ddk/debug.h>
#include <stdlib.h>
#include <map>
#include <set>
#include <vector>
namespace camera::usb_video {
namespace {
static constexpr uint32_t MJPEG_BITS_PER_PIXEL = 24;
static constexpr uint32_t NANOSECS_IN_SEC = 1e9;
UvcPixelFormat guid_to_pixel_format(const uint8_t guid[GUID_LENGTH]) {
struct {
uint8_t guid[GUID_LENGTH];
UvcPixelFormat pixel_format;
} GUID_LUT[] = {
{USB_VIDEO_GUID_YUY2_VALUE, UvcPixelFormat::YUY2},
{USB_VIDEO_GUID_NV12_VALUE, UvcPixelFormat::NV12},
{USB_VIDEO_GUID_M420_VALUE, UvcPixelFormat::M420},
{USB_VIDEO_GUID_I420_VALUE, UvcPixelFormat::I420},
};
for (const auto& g : GUID_LUT) {
if (memcmp(g.guid, guid, GUID_LENGTH) == 0) {
return g.pixel_format;
}
}
return UvcPixelFormat::INVALID;
}
std::map<int, std::string> desc2string = {
{0x01, "USB_DT_DEVICE"},
{0x02, "USB_DT_CONFIG"},
{0x03, "USB_DT_STRING"},
{0x04, "USB_DT_INTERFACE"},
{0x05, "USB_DT_ENDPOINT"},
{0x06, "USB_DT_DEVICE_QUALIFIER"},
{0x07, "USB_DT_OTHER_SPEED_CONFIG"},
{0x08, "USB_DT_INTERFACE_POWER"},
{0x0b, "USB_DT_INTERFACE_ASSOCIATION"},
{0x21, "USB_DT_HID"},
{0x22, "USB_DT_HIDREPORT"},
{0x23, "USB_DT_HIDPHYSICAL"},
{0x24, "USB_DT_CS_INTERFACE"},
{0x25, "USB_DT_CS_ENDPOINT"},
{0x30, "USB_DT_SS_EP_COMPANION"},
{0x31, "USB_DT_SS_ISOCH_EP_COMPANION"},
};
std::string DType2String(uint8_t type) {
auto iter = desc2string.find(type);
if (iter == desc2string.end()) {
return "Invalid Descriptor Type";
}
return iter->second;
}
const std::set<uint8_t> kFormatSubtypes = {
USB_VIDEO_VS_FORMAT_UNCOMPRESSED, USB_VIDEO_VS_FORMAT_MJPEG,
USB_VIDEO_VS_FORMAT_MPEG2TS, USB_VIDEO_VS_FORMAT_DV,
USB_VIDEO_VS_FORMAT_FRAME_BASED, USB_VIDEO_VS_FORMAT_STREAM_BASED,
USB_VIDEO_VS_FORMAT_H264, USB_VIDEO_VS_FORMAT_H264_SIMULCAST,
USB_VIDEO_VS_FORMAT_VP8, USB_VIDEO_VS_FORMAT_VP8_SIMULCAST};
const std::set<uint8_t> kFrameSubtypes = {USB_VIDEO_VS_FRAME_UNCOMPRESSED, USB_VIDEO_VS_FRAME_MJPEG,
USB_VIDEO_VS_FRAME_FRAME_BASED, USB_VIDEO_VS_FRAME_H264,
USB_VIDEO_VS_FRAME_VP8};
// Which frame types are allowed for each format.
// For each format, only one frame type is allowed.
const std::map<uint8_t, uint8_t> kAllowedFrames = {
{USB_VIDEO_VS_FORMAT_UNCOMPRESSED, USB_VIDEO_VS_FRAME_UNCOMPRESSED},
{USB_VIDEO_VS_FORMAT_MJPEG, USB_VIDEO_VS_FRAME_MJPEG},
{USB_VIDEO_VS_FORMAT_MPEG2TS, USB_VIDEO_VS_FRAME_MJPEG},
{USB_VIDEO_VS_FORMAT_DV, USB_VIDEO_VS_FRAME_MJPEG},
{USB_VIDEO_VS_FORMAT_FRAME_BASED, USB_VIDEO_VS_FRAME_FRAME_BASED},
{USB_VIDEO_VS_FORMAT_STREAM_BASED, USB_VIDEO_VS_FRAME_FRAME_BASED},
{USB_VIDEO_VS_FORMAT_H264, USB_VIDEO_VS_FRAME_H264},
{USB_VIDEO_VS_FORMAT_H264_SIMULCAST, USB_VIDEO_VS_FRAME_H264},
{USB_VIDEO_VS_FORMAT_VP8, USB_VIDEO_VS_FRAME_VP8},
{USB_VIDEO_VS_FORMAT_VP8_SIMULCAST, USB_VIDEO_VS_FRAME_VP8}};
std::map<UvcPixelFormat, fuchsia::sysmem::PixelFormatType> Uvc2SysmemPixelFormat = {
{UvcPixelFormat::BGRA32, fuchsia::sysmem::PixelFormatType::BGRA32},
{UvcPixelFormat::I420, fuchsia::sysmem::PixelFormatType::I420},
{UvcPixelFormat::M420, fuchsia::sysmem::PixelFormatType::M420},
{UvcPixelFormat::NV12, fuchsia::sysmem::PixelFormatType::NV12},
{UvcPixelFormat::YUY2, fuchsia::sysmem::PixelFormatType::YUY2},
{UvcPixelFormat::MJPEG, fuchsia::sysmem::PixelFormatType::MJPEG}};
// Verifies that the iterator is valid, and that the
// memory it points to is valid for the full length given.
zx::result<uint8_t> VerifyDescriptor(usb_desc_iter_t* iter) {
usb_descriptor_header_t* header = usb_desc_iter_peek(iter);
if (header == NULL) {
return zx::error(ZX_ERR_STOP);
}
if (usb_desc_iter_get_structure(iter, header->b_length) == NULL) {
return zx::error(ZX_ERR_BAD_STATE);
}
return zx::ok(header->b_descriptor_type);
}
// Verify and extract arbitrary struct from the usb descriptor iterator.
template <class T>
zx::result<T> GetStruct(usb_desc_iter_t* iter, uint8_t required_type = 0) {
auto type_or = VerifyDescriptor(iter);
if (type_or.is_error()) {
return type_or.take_error();
}
// Optionally match the descriptor type:
if (required_type != 0 && *type_or != required_type) {
zxlogf(DEBUG, "Expected %s descriptor type, got %s", DType2String(required_type).c_str(),
DType2String(*type_or).c_str());
return zx::error(ZX_ERR_NEXT);
}
// Verify that we can safely cast the blob data pointer to the given type, and then
// return by value to copy out the data from the descriptor blob.
if (void* data = usb_desc_iter_get_structure(iter, sizeof(T))) {
return zx::ok(*reinterpret_cast<T*>(data));
}
return zx::error(ZX_ERR_BAD_STATE);
}
zx::result<usb_interface_descriptor_t> VerifyStdInterface(usb_desc_iter_t* iter) {
return GetStruct<usb_interface_descriptor_t>(iter, USB_DT_INTERFACE);
}
zx::result<usb_cs_interface_descriptor_t> VerifyCSInterface(usb_desc_iter_t* iter) {
return GetStruct<usb_cs_interface_descriptor_t>(iter, USB_DT_CS_INTERFACE);
}
zx::result<usb_endpoint_info_descriptor_t> VerifyEndpoint(usb_desc_iter_t* iter) {
return GetStruct<usb_endpoint_info_descriptor_t>(iter, USB_DT_ENDPOINT);
}
bool IsVideoControlInterface(usb_desc_iter_t* iter) {
auto header_or = VerifyStdInterface(iter);
return header_or.is_ok() && header_or->b_interface_class == USB_CLASS_VIDEO &&
header_or->b_interface_sub_class == USB_SUBCLASS_VIDEO_CONTROL;
}
bool IsVideoStreamingInterface(usb_desc_iter_t* iter) {
auto header_or = VerifyStdInterface(iter);
return header_or.is_ok() && header_or->b_interface_class == USB_CLASS_VIDEO &&
header_or->b_interface_sub_class == USB_SUBCLASS_VIDEO_STREAMING;
}
zx::result<uint32_t> GetClockFrequency(usb_desc_iter_t* iter) {
auto cs_descrip_or = VerifyCSInterface(iter);
if (cs_descrip_or.is_error()) {
return cs_descrip_or.take_error();
}
if (cs_descrip_or->b_descriptor_sub_type != USB_VIDEO_VC_HEADER) {
return zx::error(ZX_ERR_BAD_STATE);
}
auto vc_header = reinterpret_cast<usb_video_vc_header_desc*>(
usb_desc_iter_get_structure(iter, sizeof(usb_video_vc_header_desc)));
if (vc_header == NULL) {
return zx::error(ZX_ERR_BAD_STATE);
}
return zx::ok(vc_header->dwClockFrequency);
}
zx::result<usb_video_vs_input_header_desc_short> GetInputHeader(usb_desc_iter_t* iter) {
auto cs_descrip_or = VerifyCSInterface(iter);
if (cs_descrip_or.is_error()) {
return cs_descrip_or.take_error();
}
if (cs_descrip_or->b_descriptor_sub_type != USB_VIDEO_VS_INPUT_HEADER) {
return zx::error(ZX_ERR_BAD_STATE);
}
return GetStruct<usb_video_vs_input_header_desc_short>(iter);
}
// VerifyFormat returns the pointer to the data in the iterator blob.
// This is because the actual header is much longer, and is type specific to the
// format. This returned pointer will later be re-cast to the full header type,
// after verifying that that the size of the type is less than the bLength field
// of the header.
zx::result<const usb_video_format_header*> VerifyFormat(usb_desc_iter_t* iter) {
auto cs_descrip_or = VerifyCSInterface(iter);
if (cs_descrip_or.is_error()) {
return cs_descrip_or.take_error();
}
if (kFormatSubtypes.count(cs_descrip_or->b_descriptor_sub_type) == 0) {
zxlogf(DEBUG, "Descriptor subtype 0x%x is not a format!", cs_descrip_or->b_descriptor_sub_type);
return zx::error(ZX_ERR_WRONG_TYPE);
}
auto header = reinterpret_cast<usb_video_format_header*>(
usb_desc_iter_get_structure(iter, sizeof(usb_video_format_header)));
if (header == NULL) {
return zx::error(ZX_ERR_BAD_STATE);
}
return zx::ok(header);
}
// VerifyFrame returns the pointer to the data in the iterator blob.
// This is because the actual header is much longer, and is type specific to the
// format. This returned pointer will later be re-cast to the full header type,
// after verifying that that the size of the type is less than the bLength field
// of the header.
zx::result<const usb_video_frame_header*> VerifyFrame(usb_desc_iter_t* iter) {
auto cs_descrip_or = VerifyCSInterface(iter);
if (cs_descrip_or.is_error()) {
return cs_descrip_or.take_error();
}
if (kFrameSubtypes.count(cs_descrip_or->b_descriptor_sub_type) == 0) {
zxlogf(DEBUG, "Descriptor subtype 0x%x is not a frame!", cs_descrip_or->b_descriptor_sub_type);
return zx::error(ZX_ERR_WRONG_TYPE);
}
auto header = reinterpret_cast<usb_video_frame_header*>(
usb_desc_iter_get_structure(iter, sizeof(usb_video_frame_header)));
if (header == NULL) {
return zx::error(ZX_ERR_BAD_STATE);
}
return zx::ok(header);
}
// ParseUvcFormat is passed two pointers to the data in the usb descriptor blob.
// Each pointer is cast to a struct that represents the first few bytes of
// a longer, format specific struct. The pointer has already been checked to be valid
// out to pointer[0] bytes, so casting to any struct whose length is less than
// pointer[0] is guaranteed to be operating in a valid memory space.
// The UVC spec dictates the format of the struct based on the bDescriptorSubType
// field of the header.
// The memory is assumed to be valid for the duration of this function.
// Other than checking to prevent divide-by-zero errors, we don't validate the
// frame parameters. None of the frame parameters affect the operation of this driver; this
// driver simply relays the reported frame parameters to a client program so the client
// can select the desired frame configuration.
// TODO(fxbug.dev/104233): Use of dwMaxVideoFrameBufferSize has been deprecated. The
// dwMaxVideoFrameSize field obtained from the Video Probe and Commit control exchange
// should be used instead.
zx::result<UvcFormat> ParseUvcFormat(const usb_video_format_header* format,
const usb_video_frame_header* frame) {
// make sure the format type and frame type match:
auto iter = kAllowedFrames.find(format->bDescriptorSubType);
if (iter == kAllowedFrames.end()) {
zxlogf(ERROR, "Unsupported frame type");
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
if (iter->second != frame->bDescriptorSubType) {
zxlogf(ERROR, "Mismatched format and frame types");
return zx::error(ZX_ERR_BAD_STATE);
}
switch (format->bDescriptorSubType) {
case USB_VIDEO_VS_FORMAT_UNCOMPRESSED: {
if (format->bLength < sizeof(usb_video_vs_uncompressed_format_desc) ||
frame->bLength < sizeof(usb_video_vs_frame_desc)) {
return zx::error(ZX_ERR_BAD_STATE);
}
auto format_desc = reinterpret_cast<const usb_video_vs_uncompressed_format_desc*>(format);
auto frame_desc = reinterpret_cast<const usb_video_vs_frame_desc*>(frame);
// Prevent divide by zero error from bad data:
if (frame_desc->wHeight == 0) {
zxlogf(ERROR, "Frame description claims image height == 0!");
return zx::error(ZX_ERR_BAD_STATE);
}
return zx::ok(UvcFormat{.format_index = format_desc->bFormatIndex,
.frame_index = frame_desc->bFrameIndex,
.pixel_format = guid_to_pixel_format(format_desc->guidFormat),
.bits_per_pixel = format_desc->bBitsPerPixel,
.default_frame_interval = frame_desc->dwDefaultFrameInterval,
.width = frame_desc->wWidth,
.height = frame_desc->wHeight,
.stride = frame_desc->dwMaxVideoFrameBufferSize / frame_desc->wHeight,
.default_frame_index = format_desc->bDefaultFrameIndex});
}
case USB_VIDEO_VS_FORMAT_MJPEG: {
if (format->bLength < sizeof(usb_video_vs_mjpeg_format_desc) ||
frame->bLength < sizeof(usb_video_vs_frame_desc)) {
return zx::error(ZX_ERR_BAD_STATE);
}
auto format_desc = reinterpret_cast<const usb_video_vs_mjpeg_format_desc*>(format);
auto frame_desc = reinterpret_cast<const usb_video_vs_frame_desc*>(frame);
// Prevent divide by zero error from bad data:
if (frame_desc->wHeight == 0) {
zxlogf(ERROR, "Frame description claims image height == 0!");
return zx::error(ZX_ERR_BAD_STATE);
}
return zx::ok(UvcFormat{.format_index = format_desc->bFormatIndex,
.frame_index = frame_desc->bFrameIndex,
.pixel_format = UvcPixelFormat::MJPEG,
.bits_per_pixel = MJPEG_BITS_PER_PIXEL,
.default_frame_interval = frame_desc->dwDefaultFrameInterval,
.width = frame_desc->wWidth,
.height = frame_desc->wHeight,
.stride = frame_desc->dwMaxVideoFrameBufferSize / frame_desc->wHeight,
.default_frame_index = format_desc->bDefaultFrameIndex});
}
case USB_VIDEO_VS_FORMAT_FRAME_BASED: {
if (format->bLength < sizeof(usb_video_vs_frame_based_format_desc) ||
frame->bLength < sizeof(usb_video_vs_frame_based_frame_desc)) {
return zx::error(ZX_ERR_BAD_STATE);
}
auto format_desc = reinterpret_cast<const usb_video_vs_frame_based_format_desc*>(format);
auto frame_desc = reinterpret_cast<const usb_video_vs_frame_based_frame_desc*>(frame);
return zx::ok(UvcFormat{.format_index = format_desc->bFormatIndex,
.frame_index = frame_desc->bFrameIndex,
.pixel_format = guid_to_pixel_format(format_desc->guidFormat),
.bits_per_pixel = format_desc->bBitsPerPixel,
.default_frame_interval = frame_desc->dwDefaultFrameInterval,
.width = frame_desc->wWidth,
.height = frame_desc->wHeight,
.stride = frame_desc->dwBytesPerLine,
.default_frame_index = format_desc->bDefaultFrameIndex});
}
default:
zxlogf(ERROR, "unsupported format bDescriptorSubtype %u", format->bDescriptorSubType);
}
return zx::error(ZX_ERR_NOT_SUPPORTED);
}
zx::result<std::vector<UvcFormat>> GetFormat(usb_desc_iter_t* iter) {
std::vector<UvcFormat> formats;
zx::result<const usb_video_format_header*> format_header_or = VerifyFormat(iter);
if (format_header_or.is_error()) {
return format_header_or.take_error();
}
usb_desc_iter_advance(iter);
// Now load the frames that come after the format. we should have bNumFrameDescriptors
for (uint8_t i = 0; i < format_header_or.value()->bNumFrameDescriptors; ++i) {
zx::result<const usb_video_frame_header*> frame_header_or = VerifyFrame(iter);
if (frame_header_or.is_error()) {
return frame_header_or.take_error();
}
auto uvc_format_or = ParseUvcFormat(*format_header_or, *frame_header_or);
if (uvc_format_or.is_error()) {
return uvc_format_or.take_error();
}
formats.push_back(*uvc_format_or);
usb_desc_iter_advance(iter);
}
// Optionally we may have still image frame or color matching descriptors:
// UVC Spec. 3.9.2.5:
// The Still Image Frame descriptor is only applicable for a VS interface that supports
// method 2 or 3 of still image capture in conjunction with frame-based Payload formats
// (e.g., MJPEG, uncompressed, etc.). The Still Image Frame descriptor defines the
// characteristics of the still image capture for these frame-based formats.
// A single still Image Frame descriptor follows the Frame descriptor(s) for
// each Format descriptor group.
// UVC Spec. 3.9.2.6:
// The Color Matching descriptor is an optional descriptor used to describe the
// color profile of the video data in an unambiguous way. Only one instance is allowed
// for a given format and if present, the Color Matching descriptor shall be placed
// following the Video and Still Image Frame descriptors for that format.
auto cs_descrip_or = VerifyCSInterface(iter);
while (cs_descrip_or.is_ok() &&
(cs_descrip_or->b_descriptor_sub_type == USB_VIDEO_VS_STILL_IMAGE_FRAME ||
cs_descrip_or->b_descriptor_sub_type == USB_VIDEO_VS_COLORFORMAT)) {
usb_desc_iter_advance(iter);
cs_descrip_or = VerifyCSInterface(iter);
}
return zx::ok(formats);
}
// From section 9.6.6 of the Universal Serial Bus Specification Revision 2.0:
// in regards to wMaxPacketSize in table 9-13:
// For all endpoints, bits 10..0 specify the maximum packet size (in bytes).
// For high-speed isochronous and interrupt endpoints:
// Bits 12..11 specify the number of additional transaction opportunities per microframe:
// 00 = None (1 transaction per microframe)
// 01 = 1 additional (2 per microframe)
// 10 = 2 additional (3 per microframe)
// 11 = Reserved
// Note that for super speed devices, additional descriptors need to be parsed.
uint32_t UsbEPIsocBandwidth(const usb_endpoint_info_descriptor_t& ep) {
uint16_t wMaxPacketSize = static_cast<uint16_t>(le16toh((ep).w_max_packet_size));
uint32_t transactions_per_microframe = ((wMaxPacketSize >> 11) & 3) + 1;
uint32_t max_packet_size = wMaxPacketSize & 0x07FF;
return transactions_per_microframe * max_packet_size;
}
// Alternate settings consist of a VideoStreaming Standard Interface
// followed by an endpoint descriptor.
zx::result<StreamingEndpointSetting> GetAlternateSetting(usb_desc_iter_t* iter) {
if (!IsVideoStreamingInterface(iter)) {
return zx::error(ZX_ERR_BAD_STATE);
}
// The VideoStreaming Standard Interface indicates the beginning of the streaming settings.
auto vs_interface_or = GetStruct<usb_interface_info_descriptor_t>(iter);
if (vs_interface_or.is_error()) {
zxlogf(ERROR, "Failed to copy out streaming interface");
return vs_interface_or.take_error();
}
usb_desc_iter_advance(iter);
auto endpoint_or = VerifyEndpoint(iter);
if (endpoint_or.is_error()) {
return endpoint_or.take_error();
}
usb_desc_iter_advance(iter);
return zx::ok(StreamingEndpointSetting{
.address = endpoint_or->b_endpoint_address,
.alt_setting = vs_interface_or->b_alternate_setting,
.isoc_bandwidth = UsbEPIsocBandwidth(endpoint_or.value()),
.ep_type = usb_ep_type(&(endpoint_or.value())),
});
}
} // anonymous namespace
zx::result<StreamingSetting> LoadStreamingSettings(usb_desc_iter_t* iter) {
StreamingSetting settings;
// Skip past the first few headers
while (VerifyDescriptor(iter).is_ok() && !IsVideoControlInterface(iter)) {
zxlogf(DEBUG, "Skipping interface to get to Control");
usb_desc_iter_advance(iter);
}
// Check if we quit the while loop because of a bad iterator:
if (!VerifyDescriptor(iter).is_ok()) {
return zx::error(ZX_ERR_BAD_STATE);
}
// We don't actually need anything from the Standard VideoControl Interface header.
// It mostly tells us that the following Class Specific (CS) interface headers relate
// to control.
usb_desc_iter_advance(iter);
// Next we expect the Video Control header, then following the video control header
// will be one or more Unit and/or Terminal Descriptors.
// The control descriptors describe various camera controls such as zoom, gain etc.
// More information about the control descriptors can be found in section 3.7 of the
// USB Video Class specification.
// Currently, the only value we take from the control descriptors is the clock frequency,
// which we use for calculating frame timestamps.
// Retrieve that frequency from the Video Control header:
auto freq_or = GetClockFrequency(iter);
if (freq_or.is_error()) {
return freq_or.take_error();
}
settings.hw_clock_frequency = *freq_or;
// Now, proceed to ignore the rest of the control descriptors and the interrupt descriptors
// which follow that. We're going to skip all the way to the VideoStreaming Standard Interface
// descriptor.
while (VerifyDescriptor(iter).is_ok() && !IsVideoStreamingInterface(iter)) {
zxlogf(DEBUG, "Skipping interface to get to Streaming interface");
usb_desc_iter_advance(iter);
}
// Check if we quit the while loop because of a bad iterator:
if (!VerifyDescriptor(iter).is_ok()) {
return zx::error(ZX_ERR_BAD_STATE);
}
// The VideoStreaming Standard Interface indicates the beginning of the streaming settings.
auto vs_interface_or = GetStruct<usb_interface_info_descriptor_t>(iter);
if (vs_interface_or.is_error()) {
zxlogf(ERROR, "Failed to copy out streaming interface");
return vs_interface_or.take_error();
}
settings.vs_interface = *vs_interface_or;
usb_desc_iter_advance(iter);
// Next we expect the input header:
auto input_header_or = GetInputHeader(iter);
if (input_header_or.is_error()) {
return input_header_or.take_error();
}
settings.input_header = *input_header_or;
usb_desc_iter_advance(iter);
// Now we expect a series of formats, totaling to settings.input_header.bNumFormats
for (int i = 0; i < settings.input_header.bNumFormats; ++i) {
// We expect GetFormat to advance the iterator before it returns.
auto format_or = GetFormat(iter);
if (format_or.is_error()) {
return format_or.take_error();
}
for (auto& s : *format_or) {
settings.formats.push_back(s);
}
}
// Next are the alternate settings. These indicate a different bandwidth, but do not
// repeat the streaming formats.
while (IsVideoStreamingInterface(iter)) {
// GetAlternateSetting will advance the iterator.
auto endpoint_setting_or = GetAlternateSetting(iter);
if (endpoint_setting_or.is_error()) {
return endpoint_setting_or.take_error();
}
settings.endpoint_settings.push_back(*endpoint_setting_or);
}
// Now check the full set of alternate settings:
if (settings.endpoint_settings.size() == 0) {
zxlogf(ERROR, "No alternate settings given");
return zx::error(ZX_ERR_BAD_STATE);
}
if (settings.endpoint_settings[0].ep_type == USB_ENDPOINT_BULK) {
if (settings.endpoint_settings.size() > 1 || settings.endpoint_settings[0].alt_setting != 0) {
zxlogf(ERROR, "bulk endpoint shall support only alternate setting zero.");
return zx::error(ZX_ERR_BAD_STATE);
}
} else {
// For isochronous endpoints, just make sure all the types are consistent:
for (const StreamingEndpointSetting& setting : settings.endpoint_settings) {
// The streaming settings should all be of the same type,
// in this case all USB_ENDPOINT_ISOCHRONOUS.
if (setting.ep_type != USB_ENDPOINT_ISOCHRONOUS) {
zxlogf(ERROR, "expected isochronous endpoint, got %u", setting.ep_type);
return zx::error(ZX_ERR_BAD_STATE);
}
}
}
return zx::ok(std::move(settings));
}
fuchsia::camera::VideoFormat UvcFormat::ToFidl() const {
fuchsia::camera::VideoFormat ret = {
.format =
{
.width = width,
.height = height,
.layers = 1,
},
// The frame descriptor frame interval is expressed in 100ns units.
// e.g. a frame interval of 333333 is equivalent to 30fps (1e7 / 333333).
.rate = {.frames_per_sec_numerator = NANOSECS_IN_SEC / 100,
.frames_per_sec_denominator = default_frame_interval},
};
ret.format.planes[0].bytes_per_row = stride;
if (pixel_format == UvcPixelFormat::I420) {
ret.format.layers = 3;
ret.format.planes[1].bytes_per_row = stride / 2;
ret.format.planes[2].bytes_per_row = stride / 2;
}
if (pixel_format == UvcPixelFormat::NV12) {
ret.format.layers = 2;
ret.format.planes[1].bytes_per_row = stride;
}
// Convert Pixel Format:
ret.format.pixel_format.type = fuchsia::sysmem::PixelFormatType::INVALID;
if (auto iter = Uvc2SysmemPixelFormat.find(pixel_format); iter != Uvc2SysmemPixelFormat.end()) {
ret.format.pixel_format.type = iter->second;
}
return ret;
}
bool FrameRatesEqual(fuchsia::camera::FrameRate a, fuchsia::camera::FrameRate b) {
affine::Ratio::Reduce(&a.frames_per_sec_numerator, &a.frames_per_sec_denominator);
affine::Ratio::Reduce(&b.frames_per_sec_numerator, &b.frames_per_sec_denominator);
return (a.frames_per_sec_numerator == b.frames_per_sec_numerator) &&
(a.frames_per_sec_denominator == b.frames_per_sec_denominator);
}
bool UvcFormat::operator==(const fuchsia::camera::VideoFormat& vf) const {
fuchsia::camera::VideoFormat uvf = ToFidl();
if (!FrameRatesEqual(vf.rate, uvf.rate)) {
return false;
}
for (uint32_t i = 0; i < uvf.format.planes.size(); i++) {
if (vf.format.planes[i].byte_offset != uvf.format.planes[i].byte_offset ||
vf.format.planes[i].bytes_per_row != uvf.format.planes[i].bytes_per_row) {
return false;
}
}
if (!fidl::Equals(vf.format.pixel_format, uvf.format.pixel_format)) {
return false;
}
if (vf.format.width != uvf.format.width || vf.format.height != uvf.format.height) {
return false;
}
return true;
}
} // namespace camera::usb_video