]> git.sesse.net Git - bmusb/blob - bmusb.cpp
Move to -Wall, and fix all -Wall warnings.
[bmusb] / bmusb.cpp
1 // Intensity Shuttle USB3 prototype capture driver, v0.3
2 // Can download 8-bit and 10-bit UYVY/v210 frames from HDMI, quite stable
3 // (can do captures for hours at a time with no drops), except during startup
4 // 576p60/720p60/1080i60 works, 1080p60 does not work (firmware limitation)
5 // Audio comes out as 8-channel 24-bit raw audio.
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <libusb.h>
10 #include <arpa/inet.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <fcntl.h>
14 #include <stdint.h>
15 #include <algorithm>
16 #include <functional>
17 #include <memory>
18 #include <deque>
19 #include <utility>
20 #include <mutex>
21 #include <condition_variable>
22 #include <thread>
23 #include <stack>
24 #include <atomic>
25 #include "bmusb.h"
26
27 using namespace std;
28
29 static int current_register = 0;
30
31 #define NUM_REGISTERS 60
32 uint8_t register_file[NUM_REGISTERS];
33
34 #define WIDTH 1280
35 #define HEIGHT 750  /* 30 lines ancillary data? */
36 //#define WIDTH 1920
37 //#define HEIGHT 1125  /* ??? lines ancillary data? */
38 #define HEADER_SIZE 44
39 //#define HEADER_SIZE 0
40 #define AUDIO_HEADER_SIZE 4
41
42 //#define FRAME_SIZE (WIDTH * HEIGHT * 2 + HEADER_SIZE)  // UYVY
43 //#define FRAME_SIZE (WIDTH * HEIGHT * 2 * 4 / 3 + HEADER_SIZE)  // v210
44 #define FRAME_SIZE (8 << 20)
45
46 FILE *audiofp;
47
48 FrameAllocator::Frame current_video_frame;
49 FrameAllocator::Frame current_audio_frame;
50
51 struct QueuedFrame {
52         uint16_t timecode;
53         uint16_t format;
54         FrameAllocator::Frame frame;
55 };
56
57 mutex queue_lock;
58 condition_variable queues_not_empty;
59 deque<QueuedFrame> pending_video_frames;
60 deque<QueuedFrame> pending_audio_frames;
61
62 thread usb_thread;
63 atomic<bool> should_quit;
64
65 FrameAllocator::~FrameAllocator() {}
66
67 #define NUM_QUEUED_FRAMES 8
68 class MallocFrameAllocator : public FrameAllocator {
69 public:
70         MallocFrameAllocator(size_t frame_size);
71         Frame alloc_frame() override;
72         void release_frame(Frame frame) override;
73
74 private:
75         size_t frame_size;
76
77         mutex freelist_mutex;
78         stack<unique_ptr<uint8_t[]>> freelist;  // All of size <frame_size>.
79 };
80
81 MallocFrameAllocator::MallocFrameAllocator(size_t frame_size)
82         : frame_size(frame_size)
83 {
84         for (int i = 0; i < NUM_QUEUED_FRAMES; ++i) {
85                 freelist.push(unique_ptr<uint8_t[]>(new uint8_t[frame_size]));
86         }
87 }
88
89 FrameAllocator::Frame MallocFrameAllocator::alloc_frame()
90 {
91         Frame vf;
92         vf.owner = this;
93
94         unique_lock<mutex> lock(freelist_mutex);  // Meh.
95         if (freelist.empty()) {
96                 printf("Frame overrun (no more spare frames of size %ld), dropping frame!\n",
97                         frame_size);
98         } else {
99                 vf.data = freelist.top().release();
100                 vf.size = frame_size;
101                 freelist.pop();  // Meh.
102         }
103         return vf;
104 }
105
106 void MallocFrameAllocator::release_frame(Frame frame)
107 {
108         unique_lock<mutex> lock(freelist_mutex);
109         freelist.push(unique_ptr<uint8_t[]>(frame.data));
110 }
111
112 FrameAllocator *video_frame_allocator = nullptr;
113 FrameAllocator *audio_frame_allocator = nullptr;
114 frame_callback_t frame_callback = nullptr;
115
116 bool uint16_less_than_with_wraparound(uint16_t a, uint16_t b)
117 {
118         if (a == b) {
119                 return false;
120         } else if (a < b) {
121                 return (b - a < 0x8000);
122         } else {
123                 int wrap_b = 0x10000 + int(b);
124                 return (wrap_b - a < 0x8000);
125         }
126 }
127
128 void queue_frame(uint16_t format, uint16_t timecode, FrameAllocator::Frame frame, deque<QueuedFrame> *q)
129 {
130         if (!q->empty() && !uint16_less_than_with_wraparound(q->back().timecode, timecode)) {
131                 printf("Blocks going backwards: prev=0x%04x, cur=0x%04x (dropped)\n",
132                         q->back().timecode, timecode);
133                 frame.owner->release_frame(frame);
134                 return;
135         }
136
137         QueuedFrame qf;
138         qf.format = format;
139         qf.timecode = timecode;
140         qf.frame = frame;
141
142         {
143                 unique_lock<mutex> lock(queue_lock);
144                 q->push_back(move(qf));
145         }
146         queues_not_empty.notify_one();  // might be spurious
147 }
148
149 void dump_frame(const char *filename, uint8_t *frame_start, size_t frame_len)
150 {
151         FILE *fp = fopen(filename, "wb");
152         if (fwrite(frame_start + HEADER_SIZE, frame_len - HEADER_SIZE, 1, fp) != 1) {
153                 printf("short write!\n");
154         }
155         fclose(fp);
156 }
157
158 void dump_audio_block(uint8_t *audio_start, size_t audio_len)
159 {
160         fwrite(audio_start + AUDIO_HEADER_SIZE, 1, audio_len - AUDIO_HEADER_SIZE, audiofp);
161 }
162
163 void dequeue_thread()
164 {
165         for ( ;; ) {
166                 unique_lock<mutex> lock(queue_lock);
167                 queues_not_empty.wait(lock, []{ return !pending_video_frames.empty() && !pending_audio_frames.empty(); });
168
169                 uint16_t video_timecode = pending_video_frames.front().timecode;
170                 uint16_t audio_timecode = pending_audio_frames.front().timecode;
171                 if (video_timecode < audio_timecode) {
172                         printf("Video block 0x%04x without corresponding audio block, dropping.\n",
173                                 video_timecode);
174                         video_frame_allocator->release_frame(pending_video_frames.front().frame);
175                         pending_video_frames.pop_front();
176                 } else if (audio_timecode < video_timecode) {
177                         printf("Audio block 0x%04x without corresponding video block, dropping.\n",
178                                 audio_timecode);
179                         audio_frame_allocator->release_frame(pending_audio_frames.front().frame);
180                         pending_audio_frames.pop_front();
181                 } else {
182                         QueuedFrame video_frame = pending_video_frames.front();
183                         QueuedFrame audio_frame = pending_audio_frames.front();
184                         pending_audio_frames.pop_front();
185                         pending_video_frames.pop_front();
186                         lock.unlock();
187
188 #if 0
189                         char filename[255];
190                         snprintf(filename, sizeof(filename), "%04x%04x.uyvy", video_frame.format, video_timecode);
191                         dump_frame(filename, video_frame.frame.data, video_frame.data_len);
192                         dump_audio_block(audio_frame.frame.data, audio_frame.data_len); 
193 #endif
194
195                         frame_callback(video_timecode,
196                                        video_frame.frame, HEADER_SIZE, video_frame.format,
197                                        audio_frame.frame, AUDIO_HEADER_SIZE, audio_frame.format);
198                 }
199         }
200 }
201
202 void start_new_frame(const uint8_t *start)
203 {
204         uint16_t format = (start[3] << 8) | start[2];
205         uint16_t timecode = (start[1] << 8) | start[0];
206
207         if (current_video_frame.len > 0) {
208                 //dump_frame();
209                 queue_frame(format, timecode, current_video_frame, &pending_video_frames);
210         }
211         //printf("Found frame start, format 0x%04x timecode 0x%04x, previous frame length was %d/%d\n",
212         //      format, timecode,
213         //      //start[7], start[6], start[5], start[4],
214         //      read_current_frame, FRAME_SIZE);
215
216         current_video_frame = video_frame_allocator->alloc_frame();
217         //if (current_video_frame.data == nullptr) {
218         //      read_current_frame = -1;
219         //} else {
220         //      read_current_frame = 0;
221         //}
222 }
223
224 void start_new_audio_block(const uint8_t *start)
225 {
226         uint16_t format = (start[3] << 8) | start[2];
227         uint16_t timecode = (start[1] << 8) | start[0];
228         if (current_audio_frame.len > 0) {
229                 //dump_audio_block();
230                 queue_frame(format, timecode, current_audio_frame, &pending_audio_frames);
231         }
232         //printf("Found audio block start, format 0x%04x timecode 0x%04x, previous block length was %d\n",
233         //      format, timecode, read_current_audio_block);
234         current_audio_frame = audio_frame_allocator->alloc_frame();
235 }
236
237 #if 0
238 static void dump_pack(const libusb_transfer *xfr, int offset, const libusb_iso_packet_descriptor *pack)
239 {
240         //      printf("ISO pack%u length:%u, actual_length:%u, offset:%u\n", i, pack->length, pack->actual_length, offset);
241         for (unsigned j = 0; j < pack->actual_length; j++) {
242         //for (int j = 0; j < min(pack->actual_length, 16u); j++) {
243                 printf("%02x", xfr->buffer[j + offset]);
244                 if ((j % 16) == 15)
245                         printf("\n");
246                 else if ((j % 8) == 7)
247                         printf("  ");
248                 else
249                         printf(" ");
250         }
251 }
252 #endif
253
254 void add_to_frame(FrameAllocator::Frame *current_frame, const char *frame_type_name, const uint8_t *start, const uint8_t *end)
255 {
256         if (current_frame->data == nullptr ||
257             current_frame->len > current_video_frame.size ||
258             start == end) {
259                 return;
260         }
261
262         int bytes = end - start;
263         if (current_frame->len + bytes > current_frame->size) {
264                 printf("%d bytes overflow after last %s frame\n",
265                         int(current_frame->len + bytes - current_frame->size), frame_type_name);
266                 //dump_frame();
267         } else {
268                 memcpy(current_frame->data + current_frame->len, start, bytes);
269                 current_frame->len += bytes;
270         }
271 }
272
273 void decode_packs(const libusb_transfer *xfr,
274                   const char *sync_pattern,
275                   int sync_length,
276                   FrameAllocator::Frame *current_frame,
277                   const char *frame_type_name,
278                   function<void(const uint8_t *start)> start_callback)
279 {
280         int offset = 0;
281         for (int i = 0; i < xfr->num_iso_packets; i++) {
282                 const libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
283
284                 if (pack->status != LIBUSB_TRANSFER_COMPLETED) {
285                         fprintf(stderr, "Error: pack %u/%u status %d\n", i, xfr->num_iso_packets, pack->status);
286                         continue;
287 //exit(5);
288                 }
289
290                 const unsigned char *iso_start = xfr->buffer + offset;
291                 for (unsigned iso_offset = 0; iso_offset < pack->actual_length; ) {  // Usually runs only one iteration.
292                         const unsigned char* start_next_frame = (const unsigned char *)memmem(iso_start + iso_offset, pack->actual_length - iso_offset, sync_pattern, sync_length);
293                         if (start_next_frame == nullptr) {
294                                 // add the rest of the buffer
295                                 const uint8_t *start = iso_start + iso_offset;
296                                 const uint8_t *end = iso_start + pack->actual_length;
297                                 add_to_frame(current_frame, frame_type_name, start, end);
298                                 break;
299                         } else {
300                                 const uint8_t *start = iso_start + iso_offset;
301                                 const uint8_t *end = start_next_frame;
302                                 add_to_frame(current_frame, frame_type_name, start, end);
303                                 start_callback(start_next_frame + sync_length);
304
305                                 int suboffset = start_next_frame - iso_start;
306                                 iso_offset = suboffset + sync_length;  // skip sync
307                         }
308                 }
309 #if 0
310                 dump_pack(xfr, offset, pack);
311 #endif
312                 offset += pack->length;
313         }
314 }
315
316 static void cb_xfr(struct libusb_transfer *xfr)
317 {
318         if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
319                 fprintf(stderr, "transfer status %d\n", xfr->status);
320                 libusb_free_transfer(xfr);
321                 exit(3);
322         }
323
324         if (xfr->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
325                 if (xfr->endpoint == 0x84) {
326                         decode_packs(xfr, "DeckLinkAudioResyncT", 20, &current_audio_frame, "audio", start_new_audio_block);
327                 } else {
328                         decode_packs(xfr, "\x00\x00\xff\xff", 4, &current_video_frame, "video", start_new_frame);
329                 }
330         }
331         if (xfr->type == LIBUSB_TRANSFER_TYPE_CONTROL) {
332                 //const libusb_control_setup *setup = libusb_control_transfer_get_setup(xfr);
333                 uint8_t *buf = libusb_control_transfer_get_data(xfr);
334 #if 0
335                 if (setup->wIndex == 44) {
336                         printf("read timer register: 0x%02x%02x%02x%02x\n", buf[0], buf[1], buf[2], buf[3]);
337                 } else {
338                         printf("read register %2d:                      0x%02x%02x%02x%02x\n",
339                                 setup->wIndex, buf[0], buf[1], buf[2], buf[3]);
340                 }
341 #else
342                 memcpy(register_file + current_register, buf, 4);
343                 current_register = (current_register + 4) % NUM_REGISTERS;
344                 if (current_register == 0) {
345                         // read through all of them
346                         printf("register dump:");
347                         for (int i = 0; i < NUM_REGISTERS; i += 4) {
348                                 printf(" 0x%02x%02x%02x%02x", register_file[i], register_file[i + 1], register_file[i + 2], register_file[i + 3]);
349                         }
350                         printf("\n");
351                 }
352                 libusb_fill_control_setup(xfr->buffer,
353                     LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
354                         /*index=*/current_register, /*length=*/4);
355 #endif
356         }
357
358 #if 0
359         printf("length:%u, actual_length:%u\n", xfr->length, xfr->actual_length);
360         for (i = 0; i < xfr->actual_length; i++) {
361                 printf("%02x", xfr->buffer[i]);
362                 if (i % 16)
363                         printf("\n");
364                 else if (i % 8)
365                         printf("  ");
366                 else
367                         printf(" ");
368         }
369 #endif
370
371         if (libusb_submit_transfer(xfr) < 0) {
372                 fprintf(stderr, "error re-submitting URB\n");
373                 exit(1);
374         }
375 }
376
377 void usb_thread_func()
378 {
379         printf("usb thread started\n");
380
381         sched_param param;
382         memset(&param, 0, sizeof(param));
383         param.sched_priority = 1;
384         if (sched_setscheduler(0, SCHED_RR, &param) == -1) {
385                 printf("couldn't set realtime priority for USB thread: %s\n", strerror(errno));
386         }
387         while (!should_quit) {
388                 int rc = libusb_handle_events(nullptr);
389                 if (rc != LIBUSB_SUCCESS)
390                         break;
391         }
392 }
393
394 FrameAllocator *get_video_frame_allocator()
395 {
396         return video_frame_allocator;
397 }
398
399 void set_video_frame_allocator(FrameAllocator *allocator)
400 {
401         video_frame_allocator = allocator;
402 }
403
404 FrameAllocator *get_audio_frame_allocator()
405 {
406         return audio_frame_allocator;
407 }
408
409 void set_audio_frame_allocator(FrameAllocator *allocator)
410 {
411         audio_frame_allocator = allocator;
412 }
413
414 void set_frame_callback(frame_callback_t callback)
415 {
416         frame_callback = callback;
417 }
418
419 void start_bm_capture()
420 {
421         if (video_frame_allocator == nullptr) {
422                 set_video_frame_allocator(new MallocFrameAllocator(FRAME_SIZE));  // FIXME: leak.
423         }
424         if (audio_frame_allocator == nullptr) {
425                 set_audio_frame_allocator(new MallocFrameAllocator(65536));  // FIXME: leak.
426         }
427         thread(dequeue_thread).detach();
428
429         int rc;
430         struct libusb_transfer *xfr;
431         vector<libusb_transfer *> iso_xfrs;
432
433         rc = libusb_init(nullptr);
434         if (rc < 0) {
435                 fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc));
436                 exit(1);
437         }
438
439         struct libusb_device_handle *devh = libusb_open_device_with_vid_pid(nullptr, 0x1edb, 0xbd3b);
440         if (!devh) {
441                 fprintf(stderr, "Error finding USB device\n");
442                 exit(1);
443         }
444
445         libusb_config_descriptor *config;
446         rc = libusb_get_config_descriptor(libusb_get_device(devh), /*config_index=*/0, &config);
447         if (rc < 0) {
448                 fprintf(stderr, "Error getting configuration: %s\n", libusb_error_name(rc));
449                 exit(1);
450         }
451         printf("%d interface\n", config->bNumInterfaces);
452         for (int interface_number = 0; interface_number < config->bNumInterfaces; ++interface_number) {
453                 printf("  interface %d\n", interface_number);
454                 const libusb_interface *interface = &config->interface[interface_number];
455                 for (int altsetting = 0; altsetting < interface->num_altsetting; ++altsetting) {
456                         printf("    alternate setting %d\n", altsetting);
457                         const libusb_interface_descriptor *interface_desc = &interface->altsetting[altsetting];
458                         for (int endpoint_number = 0; endpoint_number < interface_desc->bNumEndpoints; ++endpoint_number) {
459                                 const libusb_endpoint_descriptor *endpoint = &interface_desc->endpoint[endpoint_number];
460                                 printf("        endpoint address 0x%02x\n", endpoint->bEndpointAddress);
461                         }
462                 }
463         }
464
465         rc = libusb_set_configuration(devh, /*configuration=*/1);
466         if (rc < 0) {
467                 fprintf(stderr, "Error setting configuration 1: %s\n", libusb_error_name(rc));
468                 exit(1);
469         }
470
471         rc = libusb_claim_interface(devh, 0);
472         if (rc < 0) {
473                 fprintf(stderr, "Error claiming interface 0: %s\n", libusb_error_name(rc));
474                 exit(1);
475         }
476
477         // Alternate setting 1 is output, alternate setting 2 is input.
478         // Card is reset when switching alternates, so the driver uses
479         // this “double switch” when it wants to reset.
480         rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/1);
481         if (rc < 0) {
482                 fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc));
483                 exit(1);
484         }
485         rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/2);
486         if (rc < 0) {
487                 fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc));
488                 exit(1);
489         }
490 #if 0
491         rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/1);
492         if (rc < 0) {
493                 fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc));
494                 exit(1);
495         }
496 #endif
497
498 #if 0
499         rc = libusb_claim_interface(devh, 3);
500         if (rc < 0) {
501                 fprintf(stderr, "Error claiming interface 3: %s\n", libusb_error_name(rc));
502                 exit(1);
503         }
504 #endif
505
506         // theories:
507         //   44 is some kind of timer register (first 16 bits count upwards)
508         //   24 is some sort of watchdog?
509         //      you can seemingly set it to 0x73c60001 and that bit will eventually disappear
510         //      (or will go to 0x73c60010?), also seen 0x73c60100
511         //   12 also changes all the time, unclear why  
512         //   16 seems to be autodetected mode somehow
513         //      --    this is e00115e0 after reset?
514         //                    ed0115e0 after mode change [to output?]
515         //                    2d0015e0 after more mode change [to input]
516         //                    ed0115e0 after more mode change
517         //                    2d0015e0 after more mode change
518         //
519         //                    390115e0 seems to indicate we have signal
520         //         changes to 200115e0 when resolution changes/we lose signal, driver resets after a while
521         //
522         //                    200015e0 on startup
523         //         changes to 250115e0 when we sync to the signal
524         //
525         //    so only first 16 bits count, and 0x0100 is a mask for ok/stable signal?
526         //
527         //    28 and 32 seems to be analog audio input levels (one byte for each of the eight channels).
528         //    however, if setting 32 with HDMI embedded audio, it is immediately overwritten back (to 0xe137002a).
529         //
530         //    4, 8, 20 are unclear. seem to be some sort of bitmask, but we can set them to 0 with no apparent effect.
531         //    perhaps some of them are related to analog output?
532         //
533         //    36 can be set to 0 with no apparent effect (all of this tested on both video and audio),
534         //    but the driver sets it to 0x8036802a at some point.
535         //
536         // register 16:
537         // first byte is 0x39 for a stable 576p60 signal, 0x2d for a stable 720p60 signal, 0x20 for no signal
538         //
539         // theories:
540         //   0x01 - stable signal
541         //   0x04 - deep color
542         //   0x08 - unknown (audio??)
543         //   0x20 - 720p??
544         //   0x30 - 576p??
545
546         struct ctrl {
547                 int endpoint;
548                 int request;
549                 int index;
550                 uint32_t data;
551         };
552         static const ctrl ctrls[] = {
553                 { LIBUSB_ENDPOINT_IN,  214, 16, 0 },
554                 { LIBUSB_ENDPOINT_IN,  214,  0, 0 },
555                 { LIBUSB_ENDPOINT_IN,  214,  0, 0 },
556                 { LIBUSB_ENDPOINT_IN,  214,  4, 0 },
557                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
558                 { LIBUSB_ENDPOINT_IN,  214, 16, 0 },
559                 { LIBUSB_ENDPOINT_IN,  214, 20, 0 },
560                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
561                 { LIBUSB_ENDPOINT_IN,  214, 28, 0 },
562                 { LIBUSB_ENDPOINT_IN,  215, 32, 0 },
563                 { LIBUSB_ENDPOINT_IN,  214, 36, 0 },
564                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
565                 { LIBUSB_ENDPOINT_IN,  216, 44, 0 },
566                 { LIBUSB_ENDPOINT_IN,  214, 48, 0 },
567                 { LIBUSB_ENDPOINT_IN,  214, 52, 0 },
568                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
569                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
570                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
571                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
572                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
573                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
574                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
575                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
576                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
577                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
578                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
579                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
580                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
581                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
582                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
583                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
584                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
585                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
586                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
587                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
588                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
589                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
590                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
591                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
592                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
593                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
594                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
595                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
596                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
597                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
598                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
599                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
600                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },  // packet 354
601                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
602                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
603                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
604                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
605                 // more...
606                 //{ LIBUSB_ENDPOINT_OUT, 215,  0, 0x80000100 },
607                 //{ LIBUSB_ENDPOINT_OUT, 215,  0, 0x09000000 },  // wow, some kind of mode
608
609                 // seems to capture on HDMI, clearing the 0x20000000 bit seems to activate 10-bit
610                 // capture (v210).
611                 // clearing the 0x08000000 bit seems to change the capture format (other source?)
612                 // 0x10000000 = analog audio instead of embedded audio, it seems
613                 // 0x3a000000 = component video? (analog audio)
614                 // 0x3c000000 = composite video? (analog audio)
615                 // 0x3e000000 = s-video? (analog audio)
616                 { LIBUSB_ENDPOINT_OUT, 215,  0, 0x29000000 },
617                 //{ LIBUSB_ENDPOINT_OUT, 215,  0, 0x09000000 },
618
619                 //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0xffffffff },
620                 //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0xffffffff },
621                 //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0x40404040 },
622                 //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0x40404040 },
623                 //{ LIBUSB_ENDPOINT_OUT, 215, 36, 0x8036802a },
624                 { LIBUSB_ENDPOINT_OUT, 215, 24, 0x73c60001 },  // latch for frame start?
625                 //{ LIBUSB_ENDPOINT_OUT, 215, 24, 0x13370001 },  // latch for frame start?
626                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },  // 
627                 //{ LIBUSB_ENDPOINT_OUT, 215,  4, 0x00000000 },  // appears to have no e fect
628                 //{ LIBUSB_ENDPOINT_OUT, 215,  8, 0x00000000 },  // appears to have no effect
629                 //{ LIBUSB_ENDPOINT_OUT, 215, 20, 0x00000000 },  // appears to have no effect
630                 //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0x00000000 },  // appears to have no effect
631                 //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0x00000000 },  // appears to have no effect
632                 //{ LIBUSB_ENDPOINT_OUT, 215, 36, 0x00000000 },  // appears to have no effect
633 #if 0
634                 { LIBUSB_ENDPOINT_OUT, 215,  0 },
635                 { LIBUSB_ENDPOINT_OUT, 215,  0 },
636                 { LIBUSB_ENDPOINT_OUT, 215, 28 },
637                 { LIBUSB_ENDPOINT_OUT, 215, 32 },
638                 { LIBUSB_ENDPOINT_OUT, 215, 36 },
639                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
640                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
641                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
642                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
643                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
644                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
645                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
646                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
647                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
648                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
649                 { LIBUSB_ENDPOINT_OUT, 215,  0 },
650                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
651                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
652                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
653                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
654                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
655                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
656 #endif
657         };
658
659         for (unsigned req = 0; req < sizeof(ctrls) / sizeof(ctrls[0]); ++req) {
660                 uint32_t flipped = htonl(ctrls[req].data);
661                 static uint8_t value[4];
662                 memcpy(value, &flipped, sizeof(flipped));
663                 int size = sizeof(value);
664                 //if (ctrls[req].request == 215) size = 0;
665                 rc = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | ctrls[req].endpoint,
666                         /*request=*/ctrls[req].request, /*value=*/0, /*index=*/ctrls[req].index, value, size, /*timeout=*/0);
667                 if (rc < 0) {
668                         fprintf(stderr, "Error on control %d: %s\n", ctrls[req].index, libusb_error_name(rc));
669                         exit(1);
670                 }
671                 
672                 printf("rc=%d: ep=%d@%d %d -> 0x", rc, ctrls[req].endpoint, ctrls[req].request, ctrls[req].index);
673                 for (int i = 0; i < rc; ++i) {
674                         printf("%02x", value[i]);
675                 }
676                 printf("\n");
677         }
678
679 #if 0
680         // DEBUG
681         for ( ;; ) {
682                 static int my_index = 0;
683                 static uint8_t value[4];
684                 int size = sizeof(value);
685                 rc = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN,
686                         /*request=*/214, /*value=*/0, /*index=*/my_index, value, size, /*timeout=*/0);
687                 if (rc < 0) {
688                         fprintf(stderr, "Error on control\n");
689                         exit(1);
690                 }
691                 printf("rc=%d index=%d: 0x", rc, my_index);
692                 for (int i = 0; i < rc; ++i) {
693                         printf("%02x", value[i]);
694                 }
695                 printf("\n");
696         }
697 #endif
698
699 #if 0
700         // set up an asynchronous transfer of the timer register
701         static uint8_t cmdbuf[LIBUSB_CONTROL_SETUP_SIZE + 4];
702         static int completed = 0;
703
704         xfr = libusb_alloc_transfer(0);
705         libusb_fill_control_setup(cmdbuf,
706             LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
707                 /*index=*/44, /*length=*/4);
708         libusb_fill_control_transfer(xfr, devh, cmdbuf, cb_xfr, &completed, 0);
709         libusb_submit_transfer(xfr);
710
711         // set up an asynchronous transfer of register 24
712         static uint8_t cmdbuf2[LIBUSB_CONTROL_SETUP_SIZE + 4];
713         static int completed2 = 0;
714
715         xfr = libusb_alloc_transfer(0);
716         libusb_fill_control_setup(cmdbuf2,
717             LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
718                 /*index=*/24, /*length=*/4);
719         libusb_fill_control_transfer(xfr, devh, cmdbuf2, cb_xfr, &completed2, 0);
720         libusb_submit_transfer(xfr);
721 #endif
722
723         // set up an asynchronous transfer of the register dump
724         static uint8_t cmdbuf3[LIBUSB_CONTROL_SETUP_SIZE + 4];
725         static int completed3 = 0;
726
727         xfr = libusb_alloc_transfer(0);
728         libusb_fill_control_setup(cmdbuf3,
729             LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
730                 /*index=*/current_register, /*length=*/4);
731         libusb_fill_control_transfer(xfr, devh, cmdbuf3, cb_xfr, &completed3, 0);
732         //libusb_submit_transfer(xfr);
733
734         audiofp = fopen("audio.raw", "wb");
735
736         // set up isochronous transfers for audio and video
737         for (int e = 3; e <= 4; ++e) {
738                 //int num_transfers = (e == 3) ? 6 : 6;
739                 int num_transfers = 6;
740                 for (int i = 0; i < num_transfers; ++i) {
741                         int num_iso_pack, size;
742                         if (e == 3) {
743                                 // Video seems to require isochronous packets scaled with the width; 
744                                 // seemingly six lines is about right, rounded up to the required 1kB
745                                 // multiple.
746                                 size = WIDTH * 2 * 6;
747                                 // Note that for 10-bit input, you'll need to increase size accordingly.
748                                 //size = size * 4 / 3;
749                                 if (size % 1024 != 0) {
750                                         size &= ~1023;
751                                         size += 1024;
752                                 }
753                                 num_iso_pack = (2 << 20) / size;  // 2 MB.
754                                 printf("Picking %d packets of 0x%x bytes each\n", num_iso_pack, size);
755                         } else {
756                                 size = 0xc0;
757                                 num_iso_pack = 80;
758                         }
759                         int num_bytes = num_iso_pack * size;
760                         uint8_t *buf = new uint8_t[num_bytes];
761
762                         xfr = libusb_alloc_transfer(num_iso_pack);
763                         if (!xfr) {
764                                 fprintf(stderr, "oom\n");
765                                 exit(1);
766                         }
767
768                         int ep = LIBUSB_ENDPOINT_IN | e;
769                         libusb_fill_iso_transfer(xfr, devh, ep, buf, num_bytes,
770                                 num_iso_pack, cb_xfr, nullptr, 0);
771                         libusb_set_iso_packet_lengths(xfr, size);
772                         iso_xfrs.push_back(xfr);
773                 }
774         }
775
776         {
777                 int i = 0;
778                 for (libusb_transfer *xfr : iso_xfrs) {
779                         rc = libusb_submit_transfer(xfr);
780                         ++i;
781                         if (rc < 0) {
782                                 //printf("num_bytes=%d\n", num_bytes);
783                                 fprintf(stderr, "Error submitting iso to endpoint 0x%02x, number %d: %s\n",
784                                         xfr->endpoint, i, libusb_error_name(rc));
785                                 exit(1);
786                         }
787                 }
788         }
789
790         usb_thread = thread(usb_thread_func);
791
792
793 #if 0
794         libusb_release_interface(devh, 0);
795 out:
796         if (devh)
797                 libusb_close(devh);
798         libusb_exit(nullptr);
799         return rc;
800 #endif
801 }
802
803 void stop_bm_capture()
804 {
805         should_quit = true;
806         usb_thread.join();
807 }