diff --git a/README.md b/README.md index a9e5a4d..ee68f21 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ controller directly performs a DMA transfer from the physical address of the res * 896x504 @ 30 FPS and (almost) 60 FPS * 864x488 @ 30 FPS and 60 FPS * 480x272 @ 30 FPS and 60 FPS +* 1280x720 @ 30 FPS ## Download and installation @@ -41,7 +42,7 @@ ur0:tai/udcd_uvc.skprx ## Troubleshooting -If the video looks glitched, try to change the video player configuration to use the *NV12* format or switch to another player (like PotPlayer or OBS). +If the video looks glitched, try to change the video player configuration to use the *NV12* format or switch to another player (like PotPlayer or OBS). If the colors look wrong, set color range to full and color space to BT.601 (Rec. 601). If you use Windows 10 you might have to change the Camera access permissions on the Privacy Settings. diff --git a/debug/console.c b/debug/console.c index 93e84b0..6b259a7 100644 --- a/debug/console.c +++ b/debug/console.c @@ -10,7 +10,7 @@ static int console_x = 16; static int console_y = 16; -static SceKernelLwMutexWork mutex; +static int64_t mutex[8]; int console_init() { diff --git a/include/usb_descriptors.h b/include/usb_descriptors.h index feb5ae2..42c9d3f 100644 --- a/include/usb_descriptors.h +++ b/include/usb_descriptors.h @@ -89,7 +89,7 @@ DECLARE_UVC_FRAME_UNCOMPRESSED(2); static struct __attribute__((packed)) { struct UVC_INPUT_HEADER_DESCRIPTOR(1, 1) input_header_descriptor; struct uvc_format_uncompressed format_uncompressed_nv12; - struct UVC_FRAME_UNCOMPRESSED(2) frames_uncompressed_nv12[4]; + struct UVC_FRAME_UNCOMPRESSED(2) frames_uncompressed_nv12[5]; struct uvc_color_matching_descriptor format_uncompressed_nv12_color_matching; } video_streaming_descriptors = { .input_header_descriptor = { @@ -112,7 +112,7 @@ static struct __attribute__((packed)) { .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED, .bFormatIndex = FORMAT_INDEX_UNCOMPRESSED_NV12, - .bNumFrameDescriptors = 4, + .bNumFrameDescriptors = 5, .guidFormat = UVC_GUID_FORMAT_NV12, .bBitsPerPixel = 12, .bDefaultFrameIndex = 1, @@ -182,6 +182,21 @@ static struct __attribute__((packed)) { .bFrameIntervalType = 2, .dwFrameInterval = {FPS_TO_INTERVAL(60), FPS_TO_INTERVAL(30)}, }, + (struct UVC_FRAME_UNCOMPRESSED(2)){ + .bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(2), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = UVC_VS_FRAME_UNCOMPRESSED, + .bFrameIndex = 5, + .bmCapabilities = 0, + .wWidth = 1280, + .wHeight = 720, + .dwMinBitRate = FRAME_BITRATE(1280, 720, 12, FPS_TO_INTERVAL(20)), + .dwMaxBitRate = FRAME_BITRATE(1280, 720, 12, FPS_TO_INTERVAL(30)), + .dwMaxVideoFrameBufferSize = VIDEO_FRAME_SIZE_NV12(1280, 720), + .dwDefaultFrameInterval = FPS_TO_INTERVAL(30), + .bFrameIntervalType = 2, + .dwFrameInterval = {FPS_TO_INTERVAL(30), FPS_TO_INTERVAL(20)}, + }, }, .format_uncompressed_nv12_color_matching = { .bLength = sizeof(video_streaming_descriptors.format_uncompressed_nv12_color_matching), diff --git a/src/main.c b/src/main.c index b20e1eb..86b1b21 100644 --- a/src/main.c +++ b/src/main.c @@ -33,7 +33,7 @@ #define UVC_DRIVER_NAME "VITAUVC00" #define UVC_USB_PID 0x1337 -#define MAX_UVC_VIDEO_FRAME_SIZE VIDEO_FRAME_SIZE_NV12(960, 544) +#define MAX_UVC_VIDEO_FRAME_SIZE VIDEO_FRAME_SIZE_NV12(1280, 720) #define UVC_PAYLOAD_HEADER_SIZE 16 #define UVC_PAYLOAD_SIZE(frame_size) (UVC_PAYLOAD_HEADER_SIZE + (frame_size)) @@ -85,10 +85,13 @@ static SceUID uvc_event_flag_id; static int uvc_thread_run; static int stream; -static SceUID uvc_frame_buffer_uid; +static SceUID uvc_frame_buffer_uid = -1; static struct uvc_frame *uvc_frame_buffer_addr; SceUID uvc_frame_req_evflag; +static int uvc_frame_init(unsigned int size); +static int uvc_frame_term(); + #if defined(DISPLAY_OFF_OLED) || defined(DISPLAY_OFF_LCD) static int prev_brightness; #endif @@ -667,8 +670,21 @@ static int send_frame(void) case FORMAT_INDEX_UNCOMPRESSED_NV12: { const struct UVC_FRAME_UNCOMPRESSED(2) *frames = video_streaming_descriptors.frames_uncompressed_nv12; - int dst_width = frames[uvc_probe_control_setting.bFrameIndex - 1].wWidth; - int dst_height = frames[uvc_probe_control_setting.bFrameIndex - 1].wHeight; + int cur_frame_index = uvc_probe_control_setting.bFrameIndex; + int dst_width = frames[cur_frame_index - 1].wWidth; + int dst_height = frames[cur_frame_index - 1].wHeight; + + static int last_frame_index = 0; + if (uvc_frame_buffer_uid < 0 || cur_frame_index != last_frame_index) { + uvc_frame_term(); + ret = uvc_frame_init(VIDEO_FRAME_SIZE_NV12(dst_width, dst_height)); + if (ret < 0) { + LOG("Error allocating the UVC frame (0x%08X)\n", ret); + return ret; + } else { + last_frame_index = cur_frame_index; + } + } ret = convert_and_send_frame_nv12(fid, &fb_info, dst_width, dst_height); if (ret < 0) { @@ -740,12 +756,14 @@ static int uvc_thread(SceSize args, void *argp) while (uvc_thread_run) { unsigned int out_bits; - ksceKernelWaitEventFlagCB(uvc_event_flag_id, 1, - SCE_EVENT_WAITOR | SCE_EVENT_WAITCLEAR_PAT, - &out_bits, NULL); + int ret = ksceKernelWaitEventFlagCB(uvc_event_flag_id, 1, + SCE_EVENT_WAITOR | SCE_EVENT_WAITCLEAR_PAT, + &out_bits, (SceUInt32[]){1000000}); - if (stream) + if (ret == 0 && stream) send_frame(); + else if (ret == 0x80028005) /* SCE_KERNEL_ERROR_WAIT_TIMEOUT */ + uvc_frame_term(); } ksceDisplayUnregisterVblankStartCallback(display_vblank_cb_uid); @@ -790,6 +808,7 @@ static int uvc_frame_init(unsigned int size) if (ret < 0) { LOG("Error getting CSC desr memory addr: 0x%08X\n", ret); ksceKernelFreeMemBlock(uvc_frame_buffer_uid); + uvc_frame_buffer_uid = -1; return ret; } @@ -851,12 +870,6 @@ int uvc_start(void) goto err_activate; } - ret = uvc_frame_init(MAX_UVC_PAYLOAD_TRANSFER_SIZE); - if (ret < 0) { - LOG("Error allocating the UVC frame (0x%08X)\n", ret); - goto err_uvc_frame_init; - } - ret = uvc_frame_req_init(); if (ret < 0) { LOG("Error allocating USB request (0x%08X)\n", ret); @@ -872,8 +885,6 @@ int uvc_start(void) return 0; err_alloc_uvc_frame_req: - uvc_frame_term(); -err_uvc_frame_init: ksceUdcdDeactivate(); err_activate: ksceUdcdStop(UVC_DRIVER_NAME, 0, NULL);