11#include "py/runtime.h"
22#include <stdint.h>
3- #include "shared-module/uvc/__init__.h"
4- #include "shared-module/uvc/uvc_usb_descriptors.h"
3+ #include "class/video/video_device.h"
54#include "shared-bindings/displayio/Bitmap.h"
5+ #include "shared-bindings/uvc/__init__.h"
66#include "shared-module/bitmapfilter/macros.h"
7- #include "class/video/video_device .h"
8- #include "supervisor/ shared/tick .h"
7+ #include "shared-module/uvc/__init__ .h"
8+ #include "shared-module/uvc/uvc_usb_descriptors .h"
99#include "supervisor/background_callback.h"
10-
11- #if CFG_TUD_VIDEO_STREAMING_BULK
12- static const uint8_t usb_uvc_descriptor_template [] = {
13- TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK (0xaa , 0x80 , FRAME_WIDTH , FRAME_HEIGHT , 10 , 64 )
14- };
15-
16- // Use the "descriptor.c" file to generate/verify these offsets
17- const uint8_t interface_indices [] = {2 , 10 , 29 , 59 };
18- const uint8_t string_indices [] = {7 , 16 , 65 };
19- const uint8_t endpoint_indices [] = {72 , 153 };
20-
21- #else
22- static const uint8_t usb_uvc_descriptor_template [] = {
23- TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR (0xaa , 0x80 , FRAME_WIDTH , FRAME_HEIGHT , 10 , CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE )
24- };
25-
26- // Use the "descriptor.c" file to generate/verify these offsets
27- const uint8_t interface_indices [] = {2 , 10 , 29 , 59 , 153 };
28- const uint8_t string_indices [] = {7 , 16 , 65 , 159 };
29- const uint8_t endpoint_indices [] = {72 , 162 };
30- #endif
10+ #include "supervisor/shared/tick.h"
11+ #include "device/usbd.h"
3112
3213static unsigned frame_num = 0 ;
3314static unsigned tx_busy = 0 ;
34- static unsigned interval_ms = 1000 / FRAME_RATE ;
15+ static unsigned interval_ms = 1000 / DEFAULT_FRAME_RATE ;
3516
3617// TODO must dynamically allocate this, otherwise everyone pays for it
37- static uint8_t frame_buffer_yuyv [ FRAME_WIDTH * FRAME_HEIGHT * 16 / 8 ] ;
38- static uint16_t frame_buffer_rgb565 [ FRAME_WIDTH * FRAME_HEIGHT ] ;
18+ static uint8_t * frame_buffer_yuyv ;
19+ static uint16_t * frame_buffer_rgb565 ;
3920
4021displayio_bitmap_t uvc_bitmap_obj = {
4122 .base = {.type = & displayio_bitmap_type },
42- .width = FRAME_WIDTH ,
43- .height = FRAME_HEIGHT ,
44- .data = (uint32_t * )frame_buffer_rgb565 ,
45- .stride = FRAME_WIDTH ,
4623 .bits_per_value = 16 ,
4724 .x_shift = 1 ,
4825 .x_mask = 1 ,
4926 .bitmask = 0xffff ,
27+ // (other fields set when enabling uvc)
5028};
5129
30+ static bool uvc_is_enabled = false;
31+ static mp_int_t uvc_frame_width , uvc_frame_height ;
32+
33+ bool shared_module_uvc_enable (mp_int_t frame_width , mp_int_t frame_height ) {
34+ if (tud_connected ()) {
35+ return false;
36+ }
37+
38+ // this will free any previously allocated framebuffer as a side-effect
39+ shared_module_uvc_disable ();
40+
41+ if (frame_width & 1 ) {
42+ // frame_width must be even, round it up
43+ frame_width ++ ;
44+ }
45+
46+ uvc_frame_width = frame_width ;
47+ uvc_frame_height = frame_height ;
48+
49+ size_t framebuffer_size = uvc_frame_width * uvc_frame_height * 2 ;
50+ frame_buffer_yuyv = port_malloc (framebuffer_size , false);
51+ frame_buffer_rgb565 = port_malloc (framebuffer_size , false);
52+ if (!frame_buffer_yuyv || !frame_buffer_rgb565 ) {
53+ // this will free either of the buffers allocated just above, in
54+ // case one succeeded and the other failed.
55+ shared_module_uvc_disable ();
56+ m_malloc_fail (2 * framebuffer_size );
57+ }
58+ memset (frame_buffer_yuyv , 0 , framebuffer_size );
59+ memset (frame_buffer_rgb565 , 0 , framebuffer_size );
60+
61+ uvc_bitmap_obj .data = (uint32_t * )frame_buffer_rgb565 ;
62+ uvc_bitmap_obj .width = uvc_frame_width ;
63+ uvc_bitmap_obj .height = uvc_frame_height ;
64+ uvc_bitmap_obj .stride = uvc_frame_width / 2 ; /* in uint32_t units */
65+
66+ uvc_is_enabled = true;
67+
68+ return true;
69+ }
70+
71+ bool shared_module_uvc_disable (void ) {
72+ if (tud_connected ()) {
73+ return false;
74+ }
75+ uvc_bitmap_obj .data = NULL ; // should be redundant
76+ uvc_is_enabled = false;
77+ port_free (frame_buffer_yuyv );
78+ port_free (frame_buffer_rgb565 );
79+ frame_buffer_yuyv = NULL ;
80+ frame_buffer_rgb565 = NULL ;
81+ return true;
82+ }
83+
5284bool usb_uvc_enabled (void ) {
53- return true; // TODO
85+ return uvc_is_enabled ;
5486}
5587
5688size_t usb_uvc_descriptor_length (void ) {
57- return sizeof (usb_uvc_descriptor_template );
89+ #if CFG_TUD_VIDEO_STREAMING_BULK
90+ return sizeof ((char []) {TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK (0 , 0 , DEFAULT_FRAME_WIDTH , DEFAULT_FRAME_HEIGHT , DEFAULT_FRAME_RATE , CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE , 0 , 0 )});
91+ #else
92+ return sizeof ((char []) {TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR (0 , 0 , DEFAULT_FRAME_WIDTH , DEFAULT_FRAME_HEIGHT , DEFAULT_FRAME_RATE , 64 , 0 , 0 )});
93+ #endif
5894}
5995
6096static void convert_framebuffer (void ) {
6197 uint8_t * dest = frame_buffer_yuyv ;
6298 uint16_t * src = frame_buffer_rgb565 ;
6399
64- for (int i = 0 ; i < FRAME_WIDTH * FRAME_HEIGHT / 2 ; i ++ ) {
100+ static int i = 0 ;
101+ if ((i ++ ) % 100 == 0 ) {
102+ mp_printf (& mp_plat_print , "convert_framebuffer width=%d height=%d total pixel pairs = %d\n" ,
103+ uvc_frame_width , uvc_frame_height , uvc_frame_width * uvc_frame_height / 2 );
104+ }
105+ for (int i = 0 ; i < uvc_frame_width * uvc_frame_height / 2 ; i ++ ) {
65106 uint16_t p1 = IMAGE_GET_RGB565_PIXEL_FAST (src , 0 );
66107 uint16_t p2 = IMAGE_GET_RGB565_PIXEL_FAST (src , 1 );
67108 src += 2 ;
68109
69110 int y1 = COLOR_RGB565_TO_Y (p1 );
111+ int y2 = COLOR_RGB565_TO_Y (p2 );
112+ if (y2 > y1 ) {
113+ p1 = p2 ; /* Use UV value of the brighter pixel */
114+ }
70115 int u = COLOR_RGB565_TO_U (p1 ) + 128 ; // openmv UV are signed in the range [-127,128]
71116 int v = COLOR_RGB565_TO_V (p1 ) + 128 ;
72- int y2 = COLOR_RGB565_TO_Y (p2 );
73117
74118 * dest ++ = y1 ;
75119 * dest ++ = u ;
@@ -80,31 +124,22 @@ static void convert_framebuffer(void) {
80124
81125size_t usb_uvc_add_descriptor (uint8_t * descriptor_buf , descriptor_counts_t * descriptor_counts , uint8_t * current_interface_string ) {
82126 usb_add_interface_string (* current_interface_string , "CircuitPython UVC" );
83-
84- memcpy ( descriptor_buf , usb_uvc_descriptor_template , sizeof ( usb_uvc_descriptor_template ));
85-
86- for ( size_t i = 0 ; i < TU_ARRAY_SIZE ( string_indices ); i ++ ) {
87- descriptor_buf [ string_indices [ i ]] = * current_interface_string ;
88- }
89-
127+ const uint8_t usb_uvc_descriptor [] = {
128+ #if CFG_TUD_VIDEO_STREAMING_BULK
129+ TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK ( * current_interface_string , descriptor_counts -> current_endpoint | 0x80 , uvc_frame_width , uvc_frame_height , DEFAULT_FRAME_RATE , 64 , descriptor_counts -> current_interface , descriptor_counts -> current_interface + 1 )
130+ #else
131+ TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR ( * current_interface_string , descriptor_counts -> current_endpoint | 0x80 , uvc_frame_width , uvc_frame_height , DEFAULT_FRAME_RATE , CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE , descriptor_counts -> current_interface , descriptor_counts -> current_interface + 1 )
132+ #endif
133+ };
90134 (* current_interface_string )++ ;
91-
92- for (size_t i = 0 ; i < TU_ARRAY_SIZE (interface_indices ); i ++ ) {
93- descriptor_buf [interface_indices [i ]] += descriptor_counts -> current_interface ;
94- }
95135 descriptor_counts -> current_interface += 2 ;
96-
97- for (size_t i = 0 ; i < TU_ARRAY_SIZE (endpoint_indices ); i ++ ) {
98- // the only endpoint is an IN endpoint so set the 0x80 bit accordingly
99- descriptor_buf [endpoint_indices [i ]] = descriptor_counts -> current_endpoint | 0x80 ;
100- }
101136 descriptor_counts -> num_out_endpoints ++ ;
102137 descriptor_counts -> current_endpoint ++ ;
103138 descriptor_counts -> current_interface ++ ;
104139
105- supervisor_enable_tick ( );
140+ memcpy ( descriptor_buf , usb_uvc_descriptor , sizeof ( usb_uvc_descriptor ) );
106141
107- return sizeof (usb_uvc_descriptor_template );
142+ return sizeof (usb_uvc_descriptor );
108143}
109144
110145background_callback_t uvc_cb ;
@@ -135,7 +170,7 @@ STATIC void uvc_cb_fun(void *unused) {
135170 already_sent = 1 ;
136171 start_ms = supervisor_ticks_ms32 ();
137172 convert_framebuffer ();
138- bool result = tud_video_n_frame_xfer (0 , 0 , (void * )frame_buffer_yuyv , FRAME_WIDTH * FRAME_HEIGHT * 16 / 8 );
173+ bool result = tud_video_n_frame_xfer (0 , 0 , (void * )frame_buffer_yuyv , uvc_frame_width * uvc_frame_height * 16 / 8 );
139174 (void )result ;
140175 // printf("(!already_sent) frame_xfer -> %d\n", (int)result);
141176 }
@@ -152,7 +187,7 @@ STATIC void uvc_cb_fun(void *unused) {
152187 start_ms += interval_ms ;
153188
154189 convert_framebuffer ();
155- bool result = tud_video_n_frame_xfer (0 , 0 , (void * )frame_buffer_yuyv , FRAME_WIDTH * FRAME_HEIGHT * 16 / 8 );
190+ bool result = tud_video_n_frame_xfer (0 , 0 , (void * )frame_buffer_yuyv , uvc_frame_width * uvc_frame_height * 16 / 8 );
156191 (void )result ;
157192 // printf("frame_xfer -> %d\n", (int)result);
158193}
0 commit comments