From ec5a155e804ec4c36589f1e98fd1e1269629258d Mon Sep 17 00:00:00 2001 From: cuevavirus <53134627+cuevavirus@users.noreply.github.com> Date: Fri, 22 May 2020 21:04:15 -0400 Subject: [PATCH 1/4] Add frame descriptor for 1280x720 When streaming at 1280x720, I was able to reach a framerate of slightly above 30 FPS, around 31-32, but I limited the framerate at 30 FPS for this descriptor. With the Sharpscale plugin, the Vita is able to submit 1280x720 framebuffers to the kernel. --- README.md | 1 + include/usb_descriptors.h | 19 +++++++++++++++++-- src/main.c | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index a9e5a4d..c7664c3 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 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..ac331e6 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)) From 67bf413708e84a2ecc59f9316eeaeb354f1d26c2 Mon Sep 17 00:00:00 2001 From: cuevavirus <53134627+cuevavirus@users.noreply.github.com> Date: Sat, 23 May 2020 21:21:54 -0400 Subject: [PATCH 2/4] Dynamically allocate memory for UVC frame With a 1280x720 buffer for the UVC frame, the kernel frequently runs out of memory. This patch allocates only as much memory as needed and only when needed. In a different approach, I tried to allocate a memblock in uvc_handle_video_streaming_req_recv and free it in uvc_handle_video_abort, along with using a mutex in these two functions as well as in send_frame. However, that did not prevent use after free from occuring occasionally when switching resolution modes, because mutexes cannot be locked from interrupts. --- src/main.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/main.c b/src/main.c index ac331e6..86b1b21 100644 --- a/src/main.c +++ b/src/main.c @@ -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); From ff386fc9d1fbd77b7b411632e3c11fbaf80dab6a Mon Sep 17 00:00:00 2001 From: cuevavirus <53134627+cuevavirus@users.noreply.github.com> Date: Sat, 23 May 2020 22:29:53 -0400 Subject: [PATCH 3/4] Fix fast mutex work area size As explained in the commit message of the parent commit, mutexes cannot be locked from interrupts. For reference on fast mutexes see: https://github.com/DolceSDK/headers/commit/a7a21cbb4c0e4014fa39735b39ab33d0eb3e1197 --- debug/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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() { From d34ef1e520fccb013251a6c6429933b626071704 Mon Sep 17 00:00:00 2001 From: cuevavirus <53134627+cuevavirus@users.noreply.github.com> Date: Sat, 23 May 2020 22:36:27 -0400 Subject: [PATCH 4/4] Add note about colour settings --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c7664c3..ee68f21 100644 --- a/README.md +++ b/README.md @@ -42,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.