blob: 0d16465d1c2fc485edb7fec3cc9bc39608e4ae61 [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 <lib/fit/function.h>
#include <lib/fzl/vmo-pool.h>
#include <memory>
#include <usb/usb-request.h>
#include <usb/usb.h>
#include "src/camera/drivers/usb_video/usb_state.h"
namespace camera::usb_video {
// VideoFrame represents one frame of a video stream. Instances of this class
// are created when a new frame is detected, and destroyed when the frame is fully
// received into the buffer.
class VideoFrame {
// Both the UsbState and the VmoPool which produced the buffer passed to VideoFrame
// must be valid for the lifetime of the frame.
VideoFrame(fzl::VmoPool::Buffer buffer, const UsbState& usb_state, uint32_t frame_number)
: buffer_(std::move(buffer)), usb_state_(usb_state), frame_number_(frame_number) {
host_sof_ = usb_state_.GetHostSOF();
// Releases the write lock on the buffer, and returns information
// for transmitting that a frame is available for consumption.
// This should only be called when ProcessPayload indicates
// the frame is complete.
// The returned FrameAvailableEvent accounts for frame errors, or if we didn't have a buffer.
// Release will only return an error if there was a logical issue with the frame,
// in which case the frame should not be sent to the stream client.
zx::result<fuchsia::camera::FrameAvailableEvent> Release();
// Extracts the payload data from the usb request response,
// and stores it in the video buffer.
// Returns:
// ZX_ERR_STOP - If the video frame has been completed by
// this request. Release() can now be called.
// ZX_ERR_NEXT - If the video frame has already been completed.
// A new video frame should be created, and the
// usb_request given should be passed to the new
// frame.
// ZX_OK - The data in the request was added to the frame.
// Other Errors- There is an error with the request or the frame.
// No action needs to be taken. Although this frame
// will have errors, it is still necessary to wait until
// a new frame is started. The error flag will be set
// for this frame when Release is called.
zx_status_t ProcessPayload(usb_request_t* req);
uint32_t FrameNumber() const { return frame_number_; }
struct PayloadHeader {
static zx::result<PayloadHeader> ParseHeader(usb_request_t* req);
uint32_t length() const { return header.bHeaderLength; }
uint8_t fid() const { return header.bmHeaderInfo & USB_VIDEO_VS_PAYLOAD_HEADER_FID; }
uint8_t eof() const { return header.bmHeaderInfo & USB_VIDEO_VS_PAYLOAD_HEADER_EOF; }
uint8_t error() const { return header.bmHeaderInfo & USB_VIDEO_VS_PAYLOAD_HEADER_ERR; }
const usb_video_vs_payload_header header;
bool HasPayloadHeader() const {
return usb_state_.EndpointType() != USB_ENDPOINT_BULK || bulk_payload_bytes_ == 0;
bool IsPayloadComplete(usb_request_t* req);
zx_status_t CopyData(usb_request_t* req, uint32_t data_offset);
void ParseHeaderTimestamps(usb_request_t* req);
fzl::VmoPool::Buffer buffer_;
// Reference to the usb state. Assumed to be valid for the lifetime of the frame.
const UsbState& usb_state_;
// Used for log messages.
uint32_t frame_number_;
// Bytes received so far for the frame.
uint32_t bytes_ = 0;
// FID is a bit that is toggled when a new frame begins,
// and stays constant for the rest of the frame.
// Some devices will not set this bit.
int8_t fid_ = -1;
// Whether we've received the last payload for the frame.
// Some devices will not set this bit.
bool eof_ = false;
// Whether the frame contains an error.
bool error_ = false;
// Timekeeping:
// Presentation timestamp for the frame. This is when the device
// begins raw frame capture.
uint32_t pts_ = 0;
// Source time clock value for when the first video data of a
// video frame is put on the USB bus.
uint32_t stc_;
// The USB frame number at the time that STC was sampled.
// The largest value can have 11 bits set before wrapping around to zero.
uint16_t device_sof_;
// This is the 64 bit incremental frame number at the time the first
// payload was received by the USB video driver.
// The XHCI host handles the SOF value wrapping around, so this is 64 bits.
uint64_t host_sof_;
// The time at the midpoint of the capture operation, with respect
// to the monotonic clock.
zx_time_t capture_time_;
// Bulk transfer specific fields.
// Total bytes received so far for the current payload, including headers.
// A bulk payload may be split across multiple usb requests,
// whereas for isochronous it is always one payload per usb request.
uint64_t bulk_payload_bytes_ = 0;
// Counts the number of bad requests we have seen, for logging purposes.
static inline std::atomic<uint64_t> failure_count_ = 0;
} // namespace camera::usb_video