MCUXpresso_MIMXRT1052xxxxB/boards/evkbimxrt1050/usb_examples/usb_host_video_camera/freertos/host_video.c
2022-04-08 22:46:35 +08:00

1168 lines
55 KiB
C

/*
* Copyright 2019 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "usb_host_config.h"
#include "usb_host.h"
#include "usb_host_video.h"
#include "host_video.h"
#include "fsl_sd.h"
#include "ff.h"
#include "diskio.h"
#include "fsl_sd_disk.h"
#include "app.h"
/*******************************************************************************
* Definitions
******************************************************************************/
/*******************************************************************************
* Prototypes
******************************************************************************/
/*!
* @brief usb host video command complete callback.
*
* This function is used as callback function for completed command.
*
* @param param the host video instance pointer.
* @param data data buffer pointer.
* @param dataLen data length.
* @param status transfer result status.
*/
static void USB_HostVideoCommandCallback(void *param, uint8_t *data, uint32_t dataLen, usb_status_t status);
/*!
* @brief usb host video control transfer callback.
*
* This function is used as callback function for control transfer .
*
* @param param the host video instance pointer.
* @param data data buffer pointer.
* @param dataLen data length.
* @param status transfer result status.
*/
static void USB_HostVideoControlCallback(void *param, uint8_t *data, uint32_t dataLen, usb_status_t status);
/*!
* @brief host video stream iso in transfer callback.
*
* This function is used as callback function when call USB_HosVideoStreamRecv .
*
* @param param the host video instance pointer.
* @param data data buffer pointer.
* @param dataLen data length.
* @param status transfer result status.
*/
static void USB_HostVideoStreamDataInCallback(void *param, uint8_t *data, uint32_t dataLen, usb_status_t status);
/*******************************************************************************
* Variables
******************************************************************************/
usb_device_handle g_VideoDeviceHandle;
usb_host_video_camera_instance_t g_Video;
/*the stream interface handle , this handle is init in the class init function*/
usb_host_interface_handle g_VideoStreamInterfaceHandle;
/*the control interface handle , this handle is init in the class init function*/
usb_host_interface_handle g_VideoControlInterfaceHandle;
/* the current video camera's probe value */
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static usb_host_video_probe_commit_controls_t s_VideoProbe;
/* probe buffer to transfer */
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE) static usb_host_video_probe_commit_controls_t s_Probe[3];
/* usb video stream transfer buffer */
USB_DMA_NONINIT_DATA_ALIGN(USB_DATA_ALIGN_SIZE)
static uint8_t s_streamBuffer[USB_VIDEO_STREAM_BUFFER_COUNT][HIGH_SPEED_ISO_MAX_PACKET_SIZE_ZERO_ADDITION];
/* the pointer to picture ping/pong buffer */
static uint32_t *s_pictureBuffer[2];
/* byte length for picture ping/pong buffer which indicates the actual picture size */
static uint32_t s_pictureBufferDataLength[2] = {0};
/* picture index, the index for the first saved picture is 1 */
static volatile int s_PictureIndex = 1;
/* GUID, Globally Unique Identifier used to identify stream-encoding format */
const uint8_t g_YUY2FormatGUID[] = {0x59, 0x55, 0x59, 0x32, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71};
const uint8_t g_NV12FormatGUID[] = {0x4E, 0x56, 0x31, 0x32, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71};
const uint8_t g_M420FormatGUID[] = {0x4D, 0x34, 0x32, 0x30, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71};
const uint8_t g_I420FormatGUID[] = {0x49, 0x34, 0x32, 0x30, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71};
/* File system object */
static FATFS g_fileSystem;
const TCHAR g_DriverNumberBuffer[] = {SDDISK + '0', ':', '/', '\0'};
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief get proper endpoint maxpacksize and the subordinate interface alternate.
*
* This function is used select proper interface alternate setting according to the expected max packet size
*
* @param classHandle the class handle.
* @param expectMaxPacketSize the expected max packet size
* @param interfaceAlternate the pointer to the selected interface alternate setting
* @param unMultipleIsoPacketSize the actually used endpoint max packet size
*
* @retval kStatus_USB_Success successfully get the proper interface aternate setting
* @retval kStatus_USB_InvalidHandle The streamInterface is NULL pointer.
* @retval kStatus_USB_Error There is no proper interface alternate
*/
static uint8_t USB_HostGetProperEndpointInterfaceInfo(usb_host_class_handle classHandle,
uint16_t expectMaxPacketSize,
uint8_t *interfaceAlternate,
uint16_t *unMultipleIsoPacketSize)
{
usb_host_video_instance_struct_t *videoInstance = (usb_host_video_instance_struct_t *)classHandle;
usb_host_interface_t *streamInterface;
streamInterface = (usb_host_interface_t *)videoInstance->streamIntfHandle;
usb_host_video_descriptor_union_t descriptor;
uint8_t epCount = 0;
uint16_t length = 0;
uint8_t alternate = 0;
if (NULL == streamInterface)
{
return kStatus_USB_InvalidHandle;
}
if (NULL == streamInterface->interfaceDesc)
{
return kStatus_USB_InvalidParameter;
}
descriptor.bufr = streamInterface->interfaceExtension;
length = 0;
while (length < streamInterface->interfaceExtensionLength)
{
if (descriptor.common->bDescriptorType == USB_DESCRIPTOR_TYPE_INTERFACE)
{
break;
}
length += descriptor.common->bLength;
descriptor.bufr += descriptor.common->bLength;
}
while (length < streamInterface->interfaceExtensionLength)
{
if (descriptor.common->bDescriptorType == USB_DESCRIPTOR_TYPE_INTERFACE)
{
alternate = descriptor.interface->bAlternateSetting;
epCount = descriptor.interface->bNumEndpoints;
while (epCount)
{
if (descriptor.endpoint->bDescriptorType == USB_DESCRIPTOR_TYPE_ENDPOINT)
{
if ((USB_SHORT_FROM_LITTLE_ENDIAN_DATA(descriptor.endpoint->wMaxPacketSize) <=
expectMaxPacketSize) &&
(*unMultipleIsoPacketSize <
USB_SHORT_FROM_LITTLE_ENDIAN_DATA(descriptor.endpoint->wMaxPacketSize)))
{
*unMultipleIsoPacketSize =
USB_SHORT_FROM_LITTLE_ENDIAN_DATA(descriptor.endpoint->wMaxPacketSize);
/* save interface alternate for the current proper endpoint */
*interfaceAlternate = alternate;
}
epCount--;
}
length += descriptor.common->bLength;
descriptor.bufr += descriptor.common->bLength;
}
}
else
{
length += descriptor.common->bLength;
descriptor.bufr += descriptor.common->bLength;
}
}
if ((0 == *unMultipleIsoPacketSize) || (0 == *interfaceAlternate))
{
return kStatus_USB_Error;
}
else
{
return kStatus_USB_Success;
}
}
/*!
* @brief usb host video command complete callback.
*
* This function is used as callback function for completed command.
*
* @param param the host video instance pointer.
* @param data data buffer pointer.
* @param dataLen data length.
* @param status transfer result status.
*/
static void USB_HostVideoCommandCallback(void *param, uint8_t *data, uint32_t dataLen, usb_status_t status)
{
usb_host_video_camera_instance_t *videoInstance = (usb_host_video_camera_instance_t *)param;
videoInstance->isControlTransferring = 0;
}
/*!
* @brief usb host video control transfer callback.
*
* This function is used as callback function for control transfer .
*
* @param param the host video instance pointer.
* @param data data buffer pointer.
* @param dataLen data length.
* @param status transfer result status.
*/
static void USB_HostVideoControlCallback(void *param, uint8_t *data, uint32_t dataLen, usb_status_t status)
{
usb_host_video_camera_instance_t *videoInstance = (usb_host_video_camera_instance_t *)param;
if (status != kStatus_USB_Success)
{
usb_echo("data transfer error = %d , status \r\n");
return;
}
if (videoInstance->runState == kUSB_HostVideoRunIdle)
{
if (videoInstance->runWaitState == kUSB_HostVideoRunSetControlInterface)
{
videoInstance->runState = kUSB_HostVideoRunSetControlInterfaceDone;
}
else if (videoInstance->runWaitState == kUSB_HostVideoRunWaitSetStreamInterface)
{
videoInstance->runState = kUSB_HostVideoRunSetInterfaceDone;
}
else if (videoInstance->runWaitState == kUSB_HostVideoRunWaitGetSetProbeCommit)
{
videoInstance->runState = kUSB_HostVideoRunGetSetProbeCommitDone;
}
}
}
static void USB_HostVideoSdcardBufferState(usb_host_video_camera_instance_t *videoAppInstance,
uint8_t index,
uint8_t state)
{
OSA_SR_ALLOC();
OSA_ENTER_CRITICAL();
videoAppInstance->pictureBufferState[index] = state;
OSA_EXIT_CRITICAL();
}
/*!
* @brief host video stream iso in transfer callback.
*
* This function is used as callback function when call USB_HosVideoStreamRecv .
*
* @param param the host video instance pointer.
* @param data data buffer pointer.
* @param dataLen data length.
* @param status transfer result status.
*/
static void USB_HostVideoStreamDataInCallback(void *param, uint8_t *data, uint32_t dataLen, usb_status_t status)
{
usb_host_video_camera_instance_t *videoAppInstance = (usb_host_video_camera_instance_t *)param;
uint32_t headLength;
uint32_t dataSize;
uint8_t endOfFrame;
uint8_t frame_id;
uint32_t presentationTime;
static uint8_t s_savePicture = 0;
static uint32_t s_currentFrameTimeStamp = 0;
OSA_SR_ALLOC();
if (videoAppInstance->devState != kStatus_DEV_Attached)
{
return;
}
endOfFrame = ((usb_host_video_payload_header_t *)s_streamBuffer[videoAppInstance->streamBufferIndex])
->HeaderInfo.bitMap.end_of_frame;
frame_id = ((usb_host_video_payload_header_t *)s_streamBuffer[videoAppInstance->streamBufferIndex])
->HeaderInfo.bitMap.frame_id;
presentationTime = USB_LONG_FROM_LITTLE_ENDIAN_ADDRESS(
((usb_host_video_payload_header_t *)s_streamBuffer[videoAppInstance->streamBufferIndex])->dwPresentationTime);
headLength =
((usb_host_video_payload_header_t *)s_streamBuffer[videoAppInstance->streamBufferIndex])->bHeaderLength;
if (dataLen > 0)
{
/* the standard header is 12 bytes */
if (dataLen > 11)
{
dataSize = dataLen - headLength;
/* there is payload for this transfer */
if (dataSize)
{
if (s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex] == 0U)
{
if (s_currentFrameTimeStamp != presentationTime)
{
s_currentFrameTimeStamp = presentationTime;
}
}
/* presention time should be the same for the same frame, if not, discard this picture */
else if (presentationTime != s_currentFrameTimeStamp)
{
s_savePicture = 0;
}
/* the current picture buffers are not available, now discard the receiving picture */
if ((videoAppInstance->pictureBufferState[videoAppInstance->pictureBufferIndex] && s_savePicture))
{
s_savePicture = 0;
}
else if (s_savePicture)
{
if (dataSize > (videoAppInstance->videoCameraPictureBufferSize -
s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex])) /* error here */
{
s_savePicture = 0;
s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex] = 0U;
}
else
{
/* the same frame id indicates they belong to the same frame */
if (frame_id == videoAppInstance->expect_frame_id)
{
/* copy data to picture buffer */
memcpy((void *)(((uint8_t *)&s_pictureBuffer[videoAppInstance->pictureBufferIndex][0]) +
s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex]),
(void *)(((uint8_t *)&s_streamBuffer[videoAppInstance->streamBufferIndex][0]) +
headLength),
dataSize);
OSA_ENTER_CRITICAL();
s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex] += dataSize;
OSA_EXIT_CRITICAL();
}
else /* for the payload that has different frame id, discard it */
{
s_savePicture = 0;
}
}
}
else
{
/* no action */
}
}
if (s_savePicture)
{
if (endOfFrame)
{
if (s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex] != 0)
{
USB_HostVideoSdcardBufferState(videoAppInstance, videoAppInstance->pictureBufferIndex, 1);
videoAppInstance->sdcardPictureLength[videoAppInstance->pictureBufferIndex] =
s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex];
/* toggle the expected frame id */
videoAppInstance->expect_frame_id = 1 - videoAppInstance->expect_frame_id;
s_savePicture = 1;
s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex] = 0U;
/* switch to another buffer to save picture frame */
videoAppInstance->pictureBufferIndex = 1 - videoAppInstance->pictureBufferIndex;
}
}
}
else
{
/* the last frame of one picture */
if (endOfFrame)
{
if (videoAppInstance->discardFirstPicture)
{
videoAppInstance->discardFirstPicture = 0;
videoAppInstance->expect_frame_id = 1 - frame_id;
}
if (s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex] != 0)
{
videoAppInstance->discardPictureCount++;
}
s_pictureBufferDataLength[videoAppInstance->pictureBufferIndex] = 0U;
if (!videoAppInstance->discardFirstPicture)
{
videoAppInstance->expect_frame_id = 1 - frame_id;
}
s_savePicture = 1;
}
}
}
}
/* prime transfer for this usb stream buffer again */
USB_HosVideoStreamRecv(
videoAppInstance->classHandle, (uint8_t *)&s_streamBuffer[videoAppInstance->streamBufferIndex][0],
videoAppInstance->unMultipleIsoMaxPacketSize, USB_HostVideoStreamDataInCallback, videoAppInstance);
videoAppInstance->streamBufferIndex++;
if (videoAppInstance->streamBufferIndex == USB_VIDEO_STREAM_BUFFER_COUNT)
{
videoAppInstance->streamBufferIndex = 0;
}
}
/*!
* @brief write picture into sd card.
*/
static void USB_HostVideoWriteSDCard(void *param)
{
usb_host_video_camera_instance_t *videoAppInstance = (usb_host_video_camera_instance_t *)param;
static uint8_t s_FileOpened = 0;
static FIL fileObj;
char fileName[32];
FRESULT fileStatus;
uint32_t writeSize = 0;
OSA_SR_ALLOC();
/* sdcard write picture buffer has had picture */
if (videoAppInstance->pictureBufferState[videoAppInstance->sdcardWriteBufferIndex])
{
if (!s_FileOpened)
{
if (s_PictureIndex > USB_VIDEO_PICTURE_COUNT) /* finished pictures, start overlay from the first pic */
{
s_PictureIndex = 1;
}
if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_MJPEG)
{
sprintf(fileName, "%spic%d.jpg", &g_DriverNumberBuffer[0], s_PictureIndex);
}
else if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_UNCOMPRESSED)
{
sprintf(fileName, "%spic%d.yuv", &g_DriverNumberBuffer[0], s_PictureIndex);
}
else
{
return;
}
fileStatus = f_open(&fileObj, _T(fileName), FA_WRITE | FA_CREATE_ALWAYS);
if (FR_OK != fileStatus)
{
usb_echo("sdcard operate fail\r\n");
return;
}
s_FileOpened = 1;
}
fileStatus = f_write(&fileObj, (uint8_t *)&s_pictureBuffer[videoAppInstance->sdcardWriteBufferIndex][0],
videoAppInstance->sdcardPictureLength[videoAppInstance->sdcardWriteBufferIndex],
(UINT *)&writeSize);
if ((fileStatus != FR_OK) ||
(videoAppInstance->sdcardPictureLength[videoAppInstance->sdcardWriteBufferIndex] != writeSize))
{
usb_echo("sdcard operate fail\r\n");
}
OSA_ENTER_CRITICAL();
USB_HostVideoSdcardBufferState(videoAppInstance, videoAppInstance->sdcardWriteBufferIndex, 0);
videoAppInstance->sdcardPictureLength[videoAppInstance->sdcardWriteBufferIndex] = 0;
OSA_EXIT_CRITICAL();
/* one frame picture finihsed and sync it */
s_FileOpened = 0;
fileStatus = f_sync(&fileObj);
if (FR_OK != fileStatus)
{
usb_echo("file sync fail\r\n");
return;
}
s_PictureIndex++;
/* toggle the sdcard write picture index */
videoAppInstance->sdcardWriteBufferIndex = 1 - videoAppInstance->sdcardWriteBufferIndex;
}
}
/*!
* @brief host usb video task function.
*
* This function implements the host video action, it is used to create task.
*
* @param param the host video instance pointer.
*/
void USB_HostVideoTask(void *param)
{
static usb_status_t status = kStatus_USB_Success;
usb_host_video_stream_payload_frame_common_desc_t *frameDesc = NULL;
usb_host_video_camera_instance_t *videoAppInstance = (usb_host_video_camera_instance_t *)param;
uint32_t index, count;
uint32_t minResolution = 0xFFFFFFFF;
uint8_t minSolutionFrameIndex = 0xFF;
uint32_t frameInterval = 0;
uint32_t resolution = 0;
uint32_t speed = 0U;
uint8_t i = 0;
/* device state changes */
if (videoAppInstance->devState != videoAppInstance->prevState)
{
videoAppInstance->prevState = videoAppInstance->devState;
switch (videoAppInstance->devState)
{
case kStatus_DEV_Idle:
break;
case kStatus_DEV_Attached:
videoAppInstance->runState = kUSB_HostVideoRunSetControlInterface;
videoAppInstance->isControlTransferring = 0;
videoAppInstance->isStreamTransferring = 0;
videoAppInstance->streamBufferIndex = 0;
videoAppInstance->pictureBufferIndex = 0;
videoAppInstance->sdcardWriteBufferIndex = 0;
videoAppInstance->discardPictureCount = 0;
videoAppInstance->pictureBufferState[0] = 0;
videoAppInstance->pictureBufferState[1] = 0;
videoAppInstance->cameraDeviceFormatType = 0;
videoAppInstance->unMultipleIsoMaxPacketSize = 0;
videoAppInstance->currentStreamInterfaceAlternate = 0;
videoAppInstance->expect_frame_id = 0xFF;
videoAppInstance->discardFirstPicture =
1; /* dicard the first picture because the first picture may be not complete */
s_pictureBufferDataLength[0] = 0;
s_pictureBufferDataLength[1] = 0;
videoAppInstance->step = 0;
s_PictureIndex = 1; /* the first picture index is 1 */
USB_HostVideoInit(videoAppInstance->deviceHandle, &videoAppInstance->classHandle);
usb_echo("USB video attached\r\n");
break;
case kStatus_DEV_Detached:
videoAppInstance->devState = kStatus_DEV_Idle;
videoAppInstance->runState = kUSB_HostVideoRunIdle;
videoAppInstance->runWaitState = kStatus_DEV_Idle;
/* free the two picture buffer */
vPortFree((void *)s_pictureBuffer[0]);
vPortFree((void *)s_pictureBuffer[1]);
USB_HostVideoDeinit(videoAppInstance->deviceHandle, videoAppInstance->classHandle);
videoAppInstance->classHandle = NULL;
usb_echo("USB video detached\r\n");
if (s_PictureIndex > 0)
{
usb_echo("the last saved picture index is %d\r\n", s_PictureIndex - 1U);
}
usb_echo("the total discarded picture count is %d\r\n", videoAppInstance->discardPictureCount);
break;
default:
break;
}
}
/* run state */
switch (videoAppInstance->runState)
{
case kUSB_HostVideoRunIdle:
break;
case kUSB_HostVideoRunSetControlInterface:
videoAppInstance->runWaitState = kUSB_HostVideoRunSetControlInterface;
videoAppInstance->runState = kUSB_HostVideoRunIdle;
if (USB_HostVideoControlSetInterface(videoAppInstance->classHandle, videoAppInstance->controlIntfHandle, 0,
USB_HostVideoControlCallback, &g_Video) != kStatus_USB_Success)
{
usb_echo("set interface error\r\n");
}
break;
case kUSB_HostVideoRunSetControlInterfaceDone:
videoAppInstance->runWaitState = kUSB_HostVideoRunWaitSetStreamInterface;
videoAppInstance->runState = kUSB_HostVideoRunIdle;
videoAppInstance->currentStreamInterfaceAlternate = 0;
if (USB_HostVideoStreamSetInterface(videoAppInstance->classHandle, videoAppInstance->streamIntfHandle,
videoAppInstance->currentStreamInterfaceAlternate,
USB_HostVideoControlCallback, &g_Video) != kStatus_USB_Success)
{
usb_echo("set interface error\r\n");
}
break;
case kUSB_HostVideoRunSetInterfaceDone:
videoAppInstance->runState = kUSB_HostVideoRunFindOptimalSetting;
break;
case kUSB_HostVideoRunFindOptimalSetting:
/* Firstly get MJPEG format descriptor */
status = USB_HostVideoStreamGetFormatDescriptor(videoAppInstance->classHandle,
USB_HOST_DESC_SUBTYPE_VS_FORMAT_MJPEG,
(void *)&videoAppInstance->videoStreamFormatDescriptor);
if (status == kStatus_USB_InvalidHandle)
{
usb_echo("videoAppInstance->classHandle is invalid\r\n");
}
else if (status == kStatus_USB_Error)
{
/* the camera device doesn't support MJPEG format, try to get UNCOMPRESSED format */
status = USB_HostVideoStreamGetFormatDescriptor(videoAppInstance->classHandle,
USB_HOST_DESC_SUBTYPE_VS_FORMAT_UNCOMPRESSED,
(void *)&videoAppInstance->videoStreamFormatDescriptor);
if (status == kStatus_USB_InvalidHandle)
{
usb_echo("videoAppInstance->classHandle is invalid\r\n");
}
else if (status == kStatus_USB_Error)
{
usb_echo(" host can't support this format camera device\r\n");
videoAppInstance->runState = kUSB_HostVideoRunIdle;
}
else
{
}
}
else
{
}
/* Successfully get MJPEG or UNCOMPRESSED format descriptor */
if (status == kStatus_USB_Success)
{
count = videoAppInstance->videoStreamFormatDescriptor->bNumFrameDescriptors;
videoAppInstance->cameraDeviceFormatType =
videoAppInstance->videoStreamFormatDescriptor->bDescriptorSubtype;
if (videoAppInstance->cameraDeviceFormatType ==
USB_HOST_DESC_SUBTYPE_VS_FORMAT_MJPEG) /* camera device supports mjpeg format */
{
videoAppInstance->videoStreamMjpegFormatDescriptor =
(usb_host_video_stream_payload_mjpeg_format_desc_t *)
videoAppInstance->videoStreamFormatDescriptor;
}
else if (videoAppInstance->cameraDeviceFormatType ==
USB_HOST_DESC_SUBTYPE_VS_FORMAT_UNCOMPRESSED) /* camera device supports uncompressed format */
{
videoAppInstance->videoStreamUncompressedFormatDescriptor =
(usb_host_video_stream_payload_uncompressed_format_desc_t *)
videoAppInstance->videoStreamFormatDescriptor;
}
/* Choose a minimum resolution video stream frame descriptor */
for (index = 1; index <= count; index++)
{
/* Get the subordinate frame descriptor */
if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_MJPEG)
{
status = USB_HostVideoStreamGetFrameDescriptor(
videoAppInstance->classHandle, videoAppInstance->videoStreamFormatDescriptor,
USB_HOST_DESC_SUBTYPE_VS_FRAME_MJPEG, index, (void *)&frameDesc);
}
else if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_UNCOMPRESSED)
{
status = USB_HostVideoStreamGetFrameDescriptor(
videoAppInstance->classHandle, videoAppInstance->videoStreamFormatDescriptor,
USB_HOST_DESC_SUBTYPE_VS_FRAME_UNCOMPRESSED, index, (void *)&frameDesc);
}
/* choose a frame descriptor that has a minimum resolution */
if ((kStatus_USB_Success == status) && (NULL != frameDesc))
{
resolution = ((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(frameDesc->wHeight))) *
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(frameDesc->wWitd)));
if (minResolution > resolution)
{
minResolution = resolution;
minSolutionFrameIndex = index;
if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_MJPEG)
{
videoAppInstance->videoStreamMjpegFrameDescriptor =
(usb_host_video_stream_payload_mjpeg_frame_desc_t *)frameDesc;
videoAppInstance->videoStreamUncompressedFrameDescriptor = NULL;
}
else if (videoAppInstance->cameraDeviceFormatType ==
USB_HOST_DESC_SUBTYPE_VS_FORMAT_UNCOMPRESSED)
{
videoAppInstance->videoStreamUncompressedFrameDescriptor =
(usb_host_video_stream_payload_uncompressed_frame_desc_t *)frameDesc;
videoAppInstance->videoStreamMjpegFrameDescriptor = NULL;
}
}
else
{
}
}
}
/* successfully get frame descriptor that has a minimum resolution, or go into idle state */
if (minSolutionFrameIndex != 0xFF)
{
videoAppInstance->runState = kUSB_HostVideoRunGetSetProbeCommit;
videoAppInstance->step = 0;
}
else
{
status = kStatus_USB_Error;
videoAppInstance->runState = kUSB_HostVideoRunIdle;
}
}
break;
case kUSB_HostVideoRunGetSetProbeCommit:
switch (videoAppInstance->step)
{
case 0:
/* get the current setting of device camera */
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_CUR,
(void *)&s_Probe[0], USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
/* the maximum setting for device camera */
case 1:
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_MAX,
(void *)&s_Probe[1], USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 2:
/* the minimum value for device camera */
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_MIN,
(void *)&s_Probe[2], USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 3:
/* the seleted frame can support multiple interval, choose the maximum one */
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_MJPEG)
{
if (videoAppInstance->videoStreamMjpegFrameDescriptor->bFrameIntervalType > 0)
{
frameInterval = USB_LONG_FROM_LITTLE_ENDIAN_ADDRESS(
((uint8_t *)&(
videoAppInstance->videoStreamMjpegFrameDescriptor->dwMinFrameInterval[0]) +
(videoAppInstance->videoStreamMjpegFrameDescriptor->bFrameIntervalType - 1) * 4));
}
else
{
frameInterval = USB_LONG_FROM_LITTLE_ENDIAN_ADDRESS(
((uint8_t *)&(
videoAppInstance->videoStreamMjpegFrameDescriptor->dwMinFrameInterval[0]) +
4));
}
/* save the frame interval, frame index and format index */
USB_LONG_TO_LITTLE_ENDIAN_DATA(frameInterval, s_VideoProbe.dwFrameInterval);
s_VideoProbe.bFormatIndex =
videoAppInstance->videoStreamMjpegFormatDescriptor->bFormatIndex;
s_VideoProbe.bFrameIndex = videoAppInstance->videoStreamMjpegFrameDescriptor->bFrameIndex;
}
else if (videoAppInstance->cameraDeviceFormatType ==
USB_HOST_DESC_SUBTYPE_VS_FORMAT_UNCOMPRESSED)
{
if (videoAppInstance->videoStreamUncompressedFrameDescriptor->bFrameIntervalType > 0)
{
frameInterval = USB_LONG_FROM_LITTLE_ENDIAN_ADDRESS((
(uint8_t *)&(videoAppInstance->videoStreamUncompressedFrameDescriptor
->dwMinFrameInterval[0]) +
(videoAppInstance->videoStreamUncompressedFrameDescriptor->bFrameIntervalType - 1) *
4));
}
else
{
frameInterval = USB_LONG_FROM_LITTLE_ENDIAN_ADDRESS(
((uint8_t *)&(videoAppInstance->videoStreamUncompressedFrameDescriptor
->dwMinFrameInterval[0]) +
4));
}
/* save the frame interval, frame index and format index */
USB_LONG_TO_LITTLE_ENDIAN_DATA(frameInterval, s_VideoProbe.dwFrameInterval);
s_VideoProbe.bFormatIndex =
videoAppInstance->videoStreamUncompressedFormatDescriptor->bFormatIndex;
s_VideoProbe.bFrameIndex =
videoAppInstance->videoStreamUncompressedFrameDescriptor->bFrameIndex;
}
videoAppInstance->isControlTransferring = 1;
/* set device camera using the new probe */
status = USB_HostVideoSetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_SET_CUR,
(void *)&s_VideoProbe, USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 4:
case 5:
case 6:
/* get the current/min/max state of device camera */
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
if (videoAppInstance->step == 4)
{
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_CUR,
(void *)&s_Probe[0], USB_HostVideoCommandCallback, &g_Video);
}
else if (videoAppInstance->step == 5)
{
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_MAX,
(void *)&s_Probe[1], USB_HostVideoCommandCallback, &g_Video);
}
else
{
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_MIN,
(void *)&s_Probe[2], USB_HostVideoCommandCallback, &g_Video);
}
videoAppInstance->step++;
}
break;
case 7:
/* do multiple get/set cur requests to make sure a stable setting is obtained */
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
memcpy((void *)&s_VideoProbe, (void *)&s_Probe[0], 22);
status = USB_HostVideoSetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_SET_CUR,
(void *)&s_VideoProbe, USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 8:
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_CUR,
(void *)&s_Probe[0], USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 9:
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
memcpy((void *)&s_VideoProbe, (void *)&s_Probe[0], 26);
status = USB_HostVideoSetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_SET_CUR,
(void *)&s_VideoProbe, USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 10:
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
status = USB_HostVideoGetProbe(videoAppInstance->classHandle, USB_HOST_VIDEO_GET_CUR,
(void *)&s_Probe[0], USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 11:
/* configure the hardware with the negotiated parameters */
if (videoAppInstance->isControlTransferring == 0)
{
videoAppInstance->isControlTransferring = 1;
memcpy((void *)&s_VideoProbe, (void *)&s_Probe[0], 26);
/* delay 20ms to make sure device is ready and then configure actually */
SDK_DelayAtLeastUs(20000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
status = USB_HostVideoSetCommit(videoAppInstance->classHandle, USB_HOST_VIDEO_SET_CUR,
(void *)&s_VideoProbe, USB_HostVideoCommandCallback, &g_Video);
videoAppInstance->step++;
}
break;
case 12:
if (videoAppInstance->isControlTransferring == 0)
{
USB_HostHelperGetPeripheralInformation(videoAppInstance->deviceHandle, kUSB_HostGetDeviceSpeed,
&speed);
/* According to the device speed mode, choose a proper video stream interface */
videoAppInstance->unMultipleIsoMaxPacketSize = 0;
if (USB_SPEED_FULL == speed)
{
/* if device is full speed, the selected endpoint's maxPacketSize is maximum device supports
* and is not more than 1023B */
USB_HostGetProperEndpointInterfaceInfo(videoAppInstance->classHandle,
FULL_SPEED_ISO_MAX_PACKET_SIZE,
&videoAppInstance->currentStreamInterfaceAlternate,
&videoAppInstance->unMultipleIsoMaxPacketSize);
}
else if (USB_SPEED_HIGH == speed)
{
/* if device is high speed, the selected endpoint's maxPacketSize is maximum device supports
* and is not more than 1024B */
USB_HostGetProperEndpointInterfaceInfo(videoAppInstance->classHandle,
HIGH_SPEED_ISO_MAX_PACKET_SIZE_ZERO_ADDITION,
&videoAppInstance->currentStreamInterfaceAlternate,
&videoAppInstance->unMultipleIsoMaxPacketSize);
}
if (videoAppInstance->currentStreamInterfaceAlternate)
{
/* set interface by the proper alternate setting */
videoAppInstance->runWaitState = kUSB_HostVideoRunWaitGetSetProbeCommit;
videoAppInstance->runState = kUSB_HostVideoRunIdle;
if (USB_HostVideoStreamSetInterface(
videoAppInstance->classHandle, videoAppInstance->streamIntfHandle,
videoAppInstance->currentStreamInterfaceAlternate, USB_HostVideoControlCallback,
&g_Video) != kStatus_USB_Success)
{
usb_echo("set interface error\r\n");
}
}
else
{
/* no proper alternate setting */
videoAppInstance->runWaitState = kUSB_HostVideoRunIdle;
videoAppInstance->runState = kUSB_HostVideoRunIdle;
usb_echo("no proper alternate setting\r\n");
}
}
break;
default:
break;
}
break;
case kUSB_HostVideoRunGetSetProbeCommitDone:
{
OSA_SR_ALLOC();
/* malloc enough buffer for one raw data or mjpeg picture */
if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_MJPEG)
{
/* print the camera device format info*/
usb_echo("Camera setting is: %d(w)*%d(h)@%dfps\r\n",
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamMjpegFrameDescriptor->wWitd))),
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamMjpegFrameDescriptor->wHeight))),
10000000 / USB_LONG_FROM_LITTLE_ENDIAN_ADDRESS(s_VideoProbe.dwFrameInterval));
/* the picture buffer byte size */
videoAppInstance->videoCameraPictureBufferSize =
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamMjpegFrameDescriptor->wHeight))) *
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamMjpegFrameDescriptor->wWitd)));
/* consider the compression rate of MJPEG, allocate a reasonable size, compression rate can be changed
* according to the actual situation */
videoAppInstance->videoCameraPictureBufferSize = videoAppInstance->videoCameraPictureBufferSize / 100U;
videoAppInstance->videoCameraPictureBufferSize =
videoAppInstance->videoCameraPictureBufferSize * USB_MJPEG_COMPRESSION_RATIO;
s_pictureBuffer[0] = (uint32_t *)pvPortMalloc(videoAppInstance->videoCameraPictureBufferSize);
s_pictureBuffer[1] = (uint32_t *)pvPortMalloc(videoAppInstance->videoCameraPictureBufferSize);
usb_echo("picture format is MJPEG\r\n");
}
else if (videoAppInstance->cameraDeviceFormatType == USB_HOST_DESC_SUBTYPE_VS_FORMAT_UNCOMPRESSED)
{
usb_echo("Camera setting is: %d(w)*%d(h)@%dfps\r\n",
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamUncompressedFrameDescriptor->wWitd))),
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamUncompressedFrameDescriptor->wHeight))),
10000000 / USB_LONG_FROM_LITTLE_ENDIAN_ADDRESS(s_VideoProbe.dwFrameInterval));
/* Uncompressed format has no compression */
videoAppInstance->videoCameraPictureBufferSize =
((uint32_t)(videoAppInstance->videoStreamUncompressedFormatDescriptor->bBitsPerPixel / 8U *
USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamUncompressedFrameDescriptor->wHeight))) *
((uint32_t)(USB_SHORT_FROM_LITTLE_ENDIAN_ADDRESS(
videoAppInstance->videoStreamUncompressedFrameDescriptor->wWitd)));
/* allocate one more max packet size buffer to avoid array out of bounds access */
videoAppInstance->videoCameraPictureBufferSize += videoAppInstance->unMultipleIsoMaxPacketSize;
s_pictureBuffer[0] = (uint32_t *)pvPortMalloc(videoAppInstance->videoCameraPictureBufferSize);
s_pictureBuffer[1] = (uint32_t *)pvPortMalloc(videoAppInstance->videoCameraPictureBufferSize);
usb_echo("picture format is ");
if (memcmp(&videoAppInstance->videoStreamUncompressedFormatDescriptor->guidFormat[0],
&g_YUY2FormatGUID[0], 16) == 0)
{
usb_echo("YUY2\r\n");
}
else if (memcmp(&videoAppInstance->videoStreamUncompressedFormatDescriptor->guidFormat[0],
&g_NV12FormatGUID[0], 16) == 0)
{
usb_echo("NV12\r\n");
}
else if (memcmp(&videoAppInstance->videoStreamUncompressedFormatDescriptor->guidFormat[0],
&g_M420FormatGUID[0], 16) == 0)
{
usb_echo("M420\r\n");
}
else if (memcmp(&videoAppInstance->videoStreamUncompressedFormatDescriptor->guidFormat[0],
&g_I420FormatGUID[0], 16) == 0)
{
usb_echo("I420\r\n");
}
else
{
/* directly print GUID*/
for (uint8_t index = 0; index < 16; ++index)
{
usb_echo("%x ", videoAppInstance->videoStreamUncompressedFormatDescriptor->guidFormat[index]);
}
usb_echo("\r\n");
}
}
if ((s_pictureBuffer[0] == NULL) || (s_pictureBuffer[1] == NULL))
{
usb_echo(
"picture buffer malloc failed, please make sure the heap size is enough. If it is raw data now, "
"please use other format like MJPEG \r\n");
videoAppInstance->runState = kUSB_HostVideoRunIdle;
videoAppInstance->runWaitState = kUSB_HostVideoRunIdle;
return;
}
/* delay to make sure the device camera is ready, delay 2ms */
SDK_DelayAtLeastUs(2000 * 1000U, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
usb_echo("Start getting %d pictures from device camera and write them into sdcard\r\n",
USB_VIDEO_PICTURE_COUNT);
usb_echo("usb host firstly choose mjepg format, if device can't support, will choose raw data format\r\n");
usb_echo("if finish %d pictures, overlay from the first picture\r\n", USB_VIDEO_PICTURE_COUNT);
OSA_ENTER_CRITICAL();
/* prime multiple transfers */
for (i = 0; i < USB_VIDEO_STREAM_BUFFER_COUNT; i++)
{
USB_HosVideoStreamRecv(videoAppInstance->classHandle, (uint8_t *)&s_streamBuffer[i][0],
videoAppInstance->unMultipleIsoMaxPacketSize, USB_HostVideoStreamDataInCallback,
&g_Video);
}
OSA_EXIT_CRITICAL();
videoAppInstance->runState = kUSB_HostVideoStart;
break;
}
case kUSB_HostVideoStart:
USB_HostVideoWriteSDCard(param);
break;
default:
break;
}
}
usb_status_t USB_HostVideoEvent(usb_device_handle deviceHandle,
usb_host_configuration_handle configurationHandle,
uint32_t eventCode)
{
usb_status_t status = kStatus_USB_Success;
uint8_t id;
usb_host_configuration_t *configuration;
uint8_t interface_index;
usb_host_interface_t *hostInterface;
uint32_t info_value = 0U;
switch (eventCode)
{
case kUSB_HostEventAttach:
/* judge whether is configurationHandle supported */
configuration = (usb_host_configuration_t *)configurationHandle;
g_VideoDeviceHandle = NULL;
g_VideoControlInterfaceHandle = NULL;
g_VideoStreamInterfaceHandle = NULL;
for (interface_index = 0U; interface_index < configuration->interfaceCount; ++interface_index)
{
hostInterface = &configuration->interfaceList[interface_index];
id = hostInterface->interfaceDesc->bInterfaceClass;
if (id != USB_HOST_VIDEO_CLASS_CODE)
{
continue;
}
id = hostInterface->interfaceDesc->bInterfaceSubClass;
if (id == USB_HOST_VIDEO_SUBCLASS_CODE_CONTROL)
{
g_VideoDeviceHandle = deviceHandle;
g_VideoControlInterfaceHandle = hostInterface;
continue;
}
else if (id == USB_HOST_VIDEO_SUBCLASS_CODE_STREAM)
{
g_VideoDeviceHandle = deviceHandle;
g_VideoStreamInterfaceHandle = hostInterface;
continue;
}
else
{
}
}
if (g_VideoDeviceHandle != NULL)
{
return kStatus_USB_Success;
}
status = kStatus_USB_NotSupported;
break;
case kUSB_HostEventNotSupported:
break;
case kUSB_HostEventEnumerationDone:
if (g_Video.devState == kStatus_DEV_Idle)
{
if ((g_VideoDeviceHandle != NULL) && (g_VideoControlInterfaceHandle != NULL))
{
g_Video.devState = kStatus_DEV_Attached;
g_Video.deviceHandle = g_VideoDeviceHandle;
g_Video.controlIntfHandle = g_VideoControlInterfaceHandle;
g_Video.streamIntfHandle = g_VideoStreamInterfaceHandle;
USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDevicePID, &info_value);
usb_echo("video device attached:pid=0x%x", info_value);
USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDeviceVID, &info_value);
usb_echo("vid=0x%x ", info_value);
USB_HostHelperGetPeripheralInformation(deviceHandle, kUSB_HostGetDeviceAddress, &info_value);
usb_echo("address=%d\r\n", info_value);
}
}
else
{
usb_echo("not idle vide instance\r\n");
}
break;
case kUSB_HostEventDetach:
if (g_Video.devState != kStatus_DEV_Idle)
{
g_Video.devState = kStatus_DEV_Detached;
}
break;
default:
break;
}
return status;
}
/*!
* @brief host video sdcard initialization function.
*
* This function implements the sdcard detect and initialization
*
*/
usb_status_t USB_HostVideoAppSDcardInit(void)
{
FATFS *fs;
FIL fileObj;
uint32_t freeClusterNumber;
uint32_t freeMemorySizeMB;
char fileName[25];
usb_echo("please insert SD card\r\n");
if (f_mount(&g_fileSystem, g_DriverNumberBuffer, 1U))
{
usb_echo("f_mount failed.\r\n");
return kStatus_USB_Error;
}
else
{
usb_echo("sdcard inserted\r\n");
}
if (f_getfree(&g_DriverNumberBuffer[0], (DWORD *)&freeClusterNumber, &fs))
{
usb_echo("f_getfree failed.\r\n");
return kStatus_USB_Error;
}
else
{
freeMemorySizeMB = freeClusterNumber * (fs->csize) / 2048;
usb_echo("sdcard free size: %d MB\r\n", freeMemorySizeMB);
}
/* this operation is to make sure the sdcard and fatfs system is ready fully */
sprintf(fileName, "%svideo.txt", &g_DriverNumberBuffer[0]);
if (f_open(&fileObj, _T(fileName), FA_WRITE | FA_CREATE_ALWAYS) == FR_OK)
{
f_close(&fileObj);
(void)f_unlink(fileName);
}
return kStatus_USB_Success;
}