]> git.sesse.net Git - bmusb/blob - bmusb.cpp
Add very rudimentary stopping support.
[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 add_current_frame(const uint8_t *start, const uint8_t *end)
203 {
204         if (current_video_frame.data == nullptr ||
205             current_video_frame.len > current_video_frame.size) return;
206         if (start == end) return;
207
208         int bytes = end - start;
209         if (current_video_frame.len + bytes > current_video_frame.size) {
210                 printf("%d bytes overflow after last video frame\n", current_video_frame.len + bytes - current_video_frame.size);
211                 //dump_frame();
212         } else {
213                 memcpy(current_video_frame.data + current_video_frame.len, start, bytes);
214                 current_video_frame.len += bytes;
215         }
216 }
217
218 void start_new_frame(const uint8_t *start)
219 {
220         uint16_t format = (start[3] << 8) | start[2];
221         uint16_t timecode = (start[1] << 8) | start[0];
222
223         if (current_video_frame.len > 0) {
224                 //dump_frame();
225                 queue_frame(format, timecode, current_video_frame, &pending_video_frames);
226         }
227         //printf("Found frame start, format 0x%04x timecode 0x%04x, previous frame length was %d/%d\n",
228         //      format, timecode,
229         //      //start[7], start[6], start[5], start[4],
230         //      read_current_frame, FRAME_SIZE);
231
232         current_video_frame = video_frame_allocator->alloc_frame();
233         //if (current_video_frame.data == nullptr) {
234         //      read_current_frame = -1;
235         //} else {
236         //      read_current_frame = 0;
237         //}
238 }
239
240 void add_current_audio(const uint8_t *start, const uint8_t *end)
241 {
242         if (current_audio_frame.data == nullptr ||
243             current_audio_frame.len > current_audio_frame.size) return;
244         if (start == end) return;
245
246         int bytes = end - start;
247         if (current_audio_frame.len + bytes > current_audio_frame.size) {
248                 printf("%d bytes overflow after last audio block\n", current_audio_frame.len + bytes - current_audio_frame.size);
249                 //dump_audio_block();
250         } else {
251                 memcpy(current_audio_frame.data + current_audio_frame.len, start, bytes);
252                 current_audio_frame.len += bytes;
253         }
254 }
255
256 void start_new_audio_block(const uint8_t *start)
257 {
258         uint16_t format = (start[3] << 8) | start[2];
259         uint16_t timecode = (start[1] << 8) | start[0];
260         if (current_audio_frame.len > 0) {
261                 //dump_audio_block();
262                 queue_frame(format, timecode, current_audio_frame, &pending_audio_frames);
263         }
264         //printf("Found audio block start, format 0x%04x timecode 0x%04x, previous block length was %d\n",
265         //      format, timecode, read_current_audio_block);
266         current_audio_frame = audio_frame_allocator->alloc_frame();
267 }
268
269 static void dump_pack(const libusb_transfer *xfr, int offset, const libusb_iso_packet_descriptor *pack)
270 {
271         //      printf("ISO pack%u length:%u, actual_length:%u, offset:%u\n", i, pack->length, pack->actual_length, offset);
272         for (int j = 0; j < pack->actual_length; j++) {
273         //for (int j = 0; j < min(pack->actual_length, 16u); j++) {
274                 printf("%02x", xfr->buffer[j + offset]);
275                 if ((j % 16) == 15)
276                         printf("\n");
277                 else if ((j % 8) == 7)
278                         printf("  ");
279                 else
280                         printf(" ");
281         }
282 }
283
284 void decode_packs(const libusb_transfer *xfr, const char *sync_pattern, int sync_length, function<void(const uint8_t *start, const uint8_t *end)> add_callback, function<void(const uint8_t *start)> start_callback)
285 {
286         int offset = 0;
287         for (unsigned i = 0; i < xfr->num_iso_packets; i++) {
288                 const libusb_iso_packet_descriptor *pack = &xfr->iso_packet_desc[i];
289
290                 if (pack->status != LIBUSB_TRANSFER_COMPLETED) {
291                         fprintf(stderr, "Error: pack %u/%u status %d\n", i, xfr->num_iso_packets, pack->status);
292                         continue;
293 //exit(5);
294                 }
295
296                 const unsigned char *iso_start = xfr->buffer + offset;
297                 for (int iso_offset = 0; iso_offset < pack->actual_length; ) {  // Usually runs only one iteration.
298                         const unsigned char* start_next_frame = (const unsigned char *)memmem(iso_start + iso_offset, pack->actual_length - iso_offset, sync_pattern, sync_length);
299                         if (start_next_frame == nullptr) {
300                                 // add the rest of the buffer
301                                 add_callback(iso_start + iso_offset, iso_start + pack->actual_length);
302                                 break;
303                         } else {
304                                 add_callback(iso_start + iso_offset, start_next_frame);
305                                 start_callback(start_next_frame + sync_length);
306
307                                 int suboffset = start_next_frame - iso_start;
308                                 iso_offset = suboffset + sync_length;  // skip sync
309                         }
310                 }
311 #if 0
312                 dump_pack(xfr, offset, pack);
313 #endif
314                 offset += pack->length;
315         }
316 }
317
318 static void cb_xfr(struct libusb_transfer *xfr)
319 {
320         if (xfr->status != LIBUSB_TRANSFER_COMPLETED) {
321                 fprintf(stderr, "transfer status %d\n", xfr->status);
322                 libusb_free_transfer(xfr);
323                 exit(3);
324         }
325
326         if (xfr->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
327                 if (xfr->endpoint == 0x84) {
328                         decode_packs(xfr, "DeckLinkAudioResyncT", 20, add_current_audio, start_new_audio_block);
329                 } else {
330                         decode_packs(xfr, "\x00\x00\xff\xff", 4, add_current_frame, start_new_frame);
331                 }
332         }
333         if (xfr->type == LIBUSB_TRANSFER_TYPE_CONTROL) {
334                 const libusb_control_setup *setup = libusb_control_transfer_get_setup(xfr);
335                 uint8_t *buf = libusb_control_transfer_get_data(xfr);
336 #if 0
337                 if (setup->wIndex == 44) {
338                         printf("read timer register: 0x%02x%02x%02x%02x\n", buf[0], buf[1], buf[2], buf[3]);
339                 } else {
340                         printf("read register %2d:                      0x%02x%02x%02x%02x\n",
341                                 setup->wIndex, buf[0], buf[1], buf[2], buf[3]);
342                 }
343 #else
344                 memcpy(register_file + current_register, buf, 4);
345                 current_register = (current_register + 4) % NUM_REGISTERS;
346                 if (current_register == 0) {
347                         // read through all of them
348                         printf("register dump:");
349                         for (int i = 0; i < NUM_REGISTERS; i += 4) {
350                                 printf(" 0x%02x%02x%02x%02x", register_file[i], register_file[i + 1], register_file[i + 2], register_file[i + 3]);
351                         }
352                         printf("\n");
353                 }
354                 libusb_fill_control_setup(xfr->buffer,
355                     LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
356                         /*index=*/current_register, /*length=*/4);
357 #endif
358         }
359
360 #if 0
361         printf("length:%u, actual_length:%u\n", xfr->length, xfr->actual_length);
362         for (i = 0; i < xfr->actual_length; i++) {
363                 printf("%02x", xfr->buffer[i]);
364                 if (i % 16)
365                         printf("\n");
366                 else if (i % 8)
367                         printf("  ");
368                 else
369                         printf(" ");
370         }
371 #endif
372
373 end:
374         if (libusb_submit_transfer(xfr) < 0) {
375                 fprintf(stderr, "error re-submitting URB\n");
376                 exit(1);
377         }
378 }
379
380 void usb_thread_func()
381 {
382         printf("usb thread started\n");
383
384         sched_param param;
385         memset(&param, 0, sizeof(param));
386         param.sched_priority = 1;
387         if (sched_setscheduler(0, SCHED_RR, &param) == -1) {
388                 printf("couldn't set realtime priority for USB thread: %s\n", strerror(errno));
389         }
390         while (!should_quit) {
391                 int rc = libusb_handle_events(nullptr);
392                 if (rc != LIBUSB_SUCCESS)
393                         break;
394         }
395 }
396
397 FrameAllocator *get_video_frame_allocator()
398 {
399         return video_frame_allocator;
400 }
401
402 void set_video_frame_allocator(FrameAllocator *allocator)
403 {
404         video_frame_allocator = allocator;
405 }
406
407 FrameAllocator *get_audio_frame_allocator()
408 {
409         return audio_frame_allocator;
410 }
411
412 void set_audio_frame_allocator(FrameAllocator *allocator)
413 {
414         audio_frame_allocator = allocator;
415 }
416
417 void set_frame_callback(frame_callback_t callback)
418 {
419         frame_callback = callback;
420 }
421
422 void start_bm_capture()
423 {
424         if (video_frame_allocator == nullptr) {
425                 set_video_frame_allocator(new MallocFrameAllocator(FRAME_SIZE));  // FIXME: leak.
426         }
427         if (audio_frame_allocator == nullptr) {
428                 set_audio_frame_allocator(new MallocFrameAllocator(65536));  // FIXME: leak.
429         }
430         thread(dequeue_thread).detach();
431
432         int rc;
433         struct libusb_transfer *xfr;
434         vector<libusb_transfer *> iso_xfrs;
435
436         rc = libusb_init(nullptr);
437         if (rc < 0) {
438                 fprintf(stderr, "Error initializing libusb: %s\n", libusb_error_name(rc));
439                 exit(1);
440         }
441
442         struct libusb_device_handle *devh = libusb_open_device_with_vid_pid(nullptr, 0x1edb, 0xbd3b);
443         if (!devh) {
444                 fprintf(stderr, "Error finding USB device\n");
445                 exit(1);
446         }
447
448         libusb_config_descriptor *config;
449         rc = libusb_get_config_descriptor(libusb_get_device(devh), /*config_index=*/0, &config);
450         if (rc < 0) {
451                 fprintf(stderr, "Error getting configuration: %s\n", libusb_error_name(rc));
452                 exit(1);
453         }
454         printf("%d interface\n", config->bNumInterfaces);
455         for (int interface_number = 0; interface_number < config->bNumInterfaces; ++interface_number) {
456                 printf("  interface %d\n", interface_number);
457                 const libusb_interface *interface = &config->interface[interface_number];
458                 for (int altsetting = 0; altsetting < interface->num_altsetting; ++altsetting) {
459                         printf("    alternate setting %d\n", altsetting);
460                         const libusb_interface_descriptor *interface_desc = &interface->altsetting[altsetting];
461                         for (int endpoint_number = 0; endpoint_number < interface_desc->bNumEndpoints; ++endpoint_number) {
462                                 const libusb_endpoint_descriptor *endpoint = &interface_desc->endpoint[endpoint_number];
463                                 printf("        endpoint address 0x%02x\n", endpoint->bEndpointAddress);
464                         }
465                 }
466         }
467
468         rc = libusb_set_configuration(devh, /*configuration=*/1);
469         if (rc < 0) {
470                 fprintf(stderr, "Error setting configuration 1: %s\n", libusb_error_name(rc));
471                 exit(1);
472         }
473
474         rc = libusb_claim_interface(devh, 0);
475         if (rc < 0) {
476                 fprintf(stderr, "Error claiming interface 0: %s\n", libusb_error_name(rc));
477                 exit(1);
478         }
479
480         // Alternate setting 1 is output, alternate setting 2 is input.
481         // Card is reset when switching alternates, so the driver uses
482         // this “double switch” when it wants to reset.
483         rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/1);
484         if (rc < 0) {
485                 fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc));
486                 exit(1);
487         }
488         rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/2);
489         if (rc < 0) {
490                 fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc));
491                 exit(1);
492         }
493 #if 0
494         rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/1);
495         if (rc < 0) {
496                 fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc));
497                 exit(1);
498         }
499 #endif
500
501 #if 0
502         rc = libusb_claim_interface(devh, 3);
503         if (rc < 0) {
504                 fprintf(stderr, "Error claiming interface 3: %s\n", libusb_error_name(rc));
505                 exit(1);
506         }
507 #endif
508
509         // theories:
510         //   44 is some kind of timer register (first 16 bits count upwards)
511         //   24 is some sort of watchdog?
512         //      you can seemingly set it to 0x73c60001 and that bit will eventually disappear
513         //      (or will go to 0x73c60010?), also seen 0x73c60100
514         //   12 also changes all the time, unclear why  
515         //   16 seems to be autodetected mode somehow
516         //      --    this is e00115e0 after reset?
517         //                    ed0115e0 after mode change [to output?]
518         //                    2d0015e0 after more mode change [to input]
519         //                    ed0115e0 after more mode change
520         //                    2d0015e0 after more mode change
521         //
522         //                    390115e0 seems to indicate we have signal
523         //         changes to 200115e0 when resolution changes/we lose signal, driver resets after a while
524         //
525         //                    200015e0 on startup
526         //         changes to 250115e0 when we sync to the signal
527         //
528         //    so only first 16 bits count, and 0x0100 is a mask for ok/stable signal?
529         //
530         //    28 and 32 seems to be analog audio input levels (one byte for each of the eight channels).
531         //    however, if setting 32 with HDMI embedded audio, it is immediately overwritten back (to 0xe137002a).
532         //
533         //    4, 8, 20 are unclear. seem to be some sort of bitmask, but we can set them to 0 with no apparent effect.
534         //    perhaps some of them are related to analog output?
535         //
536         //    36 can be set to 0 with no apparent effect (all of this tested on both video and audio),
537         //    but the driver sets it to 0x8036802a at some point.
538         //
539         // register 16:
540         // first byte is 0x39 for a stable 576p60 signal, 0x2d for a stable 720p60 signal, 0x20 for no signal
541         //
542         // theories:
543         //   0x01 - stable signal
544         //   0x04 - deep color
545         //   0x08 - unknown (audio??)
546         //   0x20 - 720p??
547         //   0x30 - 576p??
548
549         struct ctrl {
550                 int endpoint;
551                 int request;
552                 int index;
553                 uint32_t data;
554         };
555         static const ctrl ctrls[] = {
556                 { LIBUSB_ENDPOINT_IN,  214, 16, 0 },
557                 { LIBUSB_ENDPOINT_IN,  214,  0, 0 },
558                 { LIBUSB_ENDPOINT_IN,  214,  0, 0 },
559                 { LIBUSB_ENDPOINT_IN,  214,  4, 0 },
560                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
561                 { LIBUSB_ENDPOINT_IN,  214, 16, 0 },
562                 { LIBUSB_ENDPOINT_IN,  214, 20, 0 },
563                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
564                 { LIBUSB_ENDPOINT_IN,  214, 28, 0 },
565                 { LIBUSB_ENDPOINT_IN,  215, 32, 0 },
566                 { LIBUSB_ENDPOINT_IN,  214, 36, 0 },
567                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
568                 { LIBUSB_ENDPOINT_IN,  216, 44, 0 },
569                 { LIBUSB_ENDPOINT_IN,  214, 48, 0 },
570                 { LIBUSB_ENDPOINT_IN,  214, 52, 0 },
571                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
572                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
573                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
574                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
575                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
576                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
577                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
578                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
579                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
580                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
581                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
582                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
583                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
584                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
585                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
586                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
587                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
588                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
589                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
590                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
591                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
592                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
593                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
594                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
595                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
596                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
597                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
598                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
599                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
600                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
601                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
602                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
603                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },  // packet 354
604                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },
605                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
606                 { LIBUSB_ENDPOINT_IN,  214, 12, 0 },
607                 { LIBUSB_ENDPOINT_IN,  214, 40, 0 },
608                 // more...
609                 //{ LIBUSB_ENDPOINT_OUT, 215,  0, 0x80000100 },
610                 //{ LIBUSB_ENDPOINT_OUT, 215,  0, 0x09000000 },  // wow, some kind of mode
611
612                 // seems to capture on HDMI, clearing the 0x20000000 bit seems to activate 10-bit
613                 // capture (v210).
614                 // clearing the 0x08000000 bit seems to change the capture format (other source?)
615                 // 0x10000000 = analog audio instead of embedded audio, it seems
616                 // 0x3a000000 = component video? (analog audio)
617                 // 0x3c000000 = composite video? (analog audio)
618                 // 0x3e000000 = s-video? (analog audio)
619                 { LIBUSB_ENDPOINT_OUT, 215,  0, 0x29000000 },
620                 //{ LIBUSB_ENDPOINT_OUT, 215,  0, 0x09000000 },
621
622                 //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0xffffffff },
623                 //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0xffffffff },
624                 //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0x40404040 },
625                 //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0x40404040 },
626                 //{ LIBUSB_ENDPOINT_OUT, 215, 36, 0x8036802a },
627                 { LIBUSB_ENDPOINT_OUT, 215, 24, 0x73c60001 },  // latch for frame start?
628                 //{ LIBUSB_ENDPOINT_OUT, 215, 24, 0x13370001 },  // latch for frame start?
629                 { LIBUSB_ENDPOINT_IN,  214, 24, 0 },  // 
630                 //{ LIBUSB_ENDPOINT_OUT, 215,  4, 0x00000000 },  // appears to have no e fect
631                 //{ LIBUSB_ENDPOINT_OUT, 215,  8, 0x00000000 },  // appears to have no effect
632                 //{ LIBUSB_ENDPOINT_OUT, 215, 20, 0x00000000 },  // appears to have no effect
633                 //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0x00000000 },  // appears to have no effect
634                 //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0x00000000 },  // appears to have no effect
635                 //{ LIBUSB_ENDPOINT_OUT, 215, 36, 0x00000000 },  // appears to have no effect
636 #if 0
637                 { LIBUSB_ENDPOINT_OUT, 215,  0 },
638                 { LIBUSB_ENDPOINT_OUT, 215,  0 },
639                 { LIBUSB_ENDPOINT_OUT, 215, 28 },
640                 { LIBUSB_ENDPOINT_OUT, 215, 32 },
641                 { LIBUSB_ENDPOINT_OUT, 215, 36 },
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, 24 },
650                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
651                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
652                 { LIBUSB_ENDPOINT_OUT, 215,  0 },
653                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
654                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
655                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
656                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
657                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
658                 { LIBUSB_ENDPOINT_OUT, 215, 24 },
659 #endif
660         };
661
662         for (int req = 0; req < sizeof(ctrls) / sizeof(ctrls[0]); ++req) {
663                 uint32_t flipped = htonl(ctrls[req].data);
664                 static uint8_t value[4];
665                 memcpy(value, &flipped, sizeof(flipped));
666                 int size = sizeof(value);
667                 //if (ctrls[req].request == 215) size = 0;
668                 rc = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | ctrls[req].endpoint,
669                         /*request=*/ctrls[req].request, /*value=*/0, /*index=*/ctrls[req].index, value, size, /*timeout=*/0);
670                 if (rc < 0) {
671                         fprintf(stderr, "Error on control %d: %s\n", ctrls[req].index, libusb_error_name(rc));
672                         exit(1);
673                 }
674                 
675                 printf("rc=%d: ep=%d@%d %d -> 0x", rc, ctrls[req].endpoint, ctrls[req].request, ctrls[req].index);
676                 for (int i = 0; i < rc; ++i) {
677                         printf("%02x", value[i]);
678                 }
679                 printf("\n");
680         }
681
682 #if 0
683         // DEBUG
684         for ( ;; ) {
685                 static int my_index = 0;
686                 static uint8_t value[4];
687                 int size = sizeof(value);
688                 rc = libusb_control_transfer(devh, LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN,
689                         /*request=*/214, /*value=*/0, /*index=*/my_index, value, size, /*timeout=*/0);
690                 if (rc < 0) {
691                         fprintf(stderr, "Error on control\n");
692                         exit(1);
693                 }
694                 printf("rc=%d index=%d: 0x", rc, my_index);
695                 for (int i = 0; i < rc; ++i) {
696                         printf("%02x", value[i]);
697                 }
698                 printf("\n");
699         }
700 #endif
701
702 #if 0
703         // set up an asynchronous transfer of the timer register
704         static uint8_t cmdbuf[LIBUSB_CONTROL_SETUP_SIZE + 4];
705         static int completed = 0;
706
707         xfr = libusb_alloc_transfer(0);
708         libusb_fill_control_setup(cmdbuf,
709             LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
710                 /*index=*/44, /*length=*/4);
711         libusb_fill_control_transfer(xfr, devh, cmdbuf, cb_xfr, &completed, 0);
712         libusb_submit_transfer(xfr);
713
714         // set up an asynchronous transfer of register 24
715         static uint8_t cmdbuf2[LIBUSB_CONTROL_SETUP_SIZE + 4];
716         static int completed2 = 0;
717
718         xfr = libusb_alloc_transfer(0);
719         libusb_fill_control_setup(cmdbuf2,
720             LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
721                 /*index=*/24, /*length=*/4);
722         libusb_fill_control_transfer(xfr, devh, cmdbuf2, cb_xfr, &completed2, 0);
723         libusb_submit_transfer(xfr);
724 #endif
725
726         // set up an asynchronous transfer of the register dump
727         static uint8_t cmdbuf3[LIBUSB_CONTROL_SETUP_SIZE + 4];
728         static int completed3 = 0;
729
730         xfr = libusb_alloc_transfer(0);
731         libusb_fill_control_setup(cmdbuf3,
732             LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN, /*request=*/214, /*value=*/0,
733                 /*index=*/current_register, /*length=*/4);
734         libusb_fill_control_transfer(xfr, devh, cmdbuf3, cb_xfr, &completed3, 0);
735         //libusb_submit_transfer(xfr);
736
737         audiofp = fopen("audio.raw", "wb");
738
739         // set up isochronous transfers for audio and video
740         for (int e = 3; e <= 4; ++e) {
741                 //int num_transfers = (e == 3) ? 6 : 6;
742                 int num_transfers = 6;
743                 for (int i = 0; i < num_transfers; ++i) {
744                         int num_iso_pack, size;
745                         if (e == 3) {
746                                 // Video seems to require isochronous packets scaled with the width; 
747                                 // seemingly six lines is about right, rounded up to the required 1kB
748                                 // multiple.
749                                 size = WIDTH * 2 * 6;
750                                 // Note that for 10-bit input, you'll need to increase size accordingly.
751                                 //size = size * 4 / 3;
752                                 if (size % 1024 != 0) {
753                                         size &= ~1023;
754                                         size += 1024;
755                                 }
756                                 num_iso_pack = (2 << 20) / size;  // 2 MB.
757                                 printf("Picking %d packets of 0x%x bytes each\n", num_iso_pack, size);
758                         } else {
759                                 size = 0xc0;
760                                 num_iso_pack = 80;
761                         }
762                         int num_bytes = num_iso_pack * size;
763                         uint8_t *buf = new uint8_t[num_bytes];
764
765                         xfr = libusb_alloc_transfer(num_iso_pack);
766                         if (!xfr) {
767                                 fprintf(stderr, "oom\n");
768                                 exit(1);
769                         }
770
771                         int ep = LIBUSB_ENDPOINT_IN | e;
772                         libusb_fill_iso_transfer(xfr, devh, ep, buf, num_bytes,
773                                 num_iso_pack, cb_xfr, nullptr, 0);
774                         libusb_set_iso_packet_lengths(xfr, size);
775                         iso_xfrs.push_back(xfr);
776                 }
777         }
778
779         {
780                 int i = 0;
781                 for (libusb_transfer *xfr : iso_xfrs) {
782                         rc = libusb_submit_transfer(xfr);
783                         ++i;
784                         if (rc < 0) {
785                                 //printf("num_bytes=%d\n", num_bytes);
786                                 fprintf(stderr, "Error submitting iso to endpoint 0x%02x, number %d: %s\n",
787                                         xfr->endpoint, i, libusb_error_name(rc));
788                                 exit(1);
789                         }
790                 }
791         }
792
793         usb_thread = thread(usb_thread_func);
794
795
796 #if 0
797         libusb_release_interface(devh, 0);
798 out:
799         if (devh)
800                 libusb_close(devh);
801         libusb_exit(nullptr);
802         return rc;
803 #endif
804 }
805
806 void stop_bm_capture()
807 {
808         should_quit = true;
809         usb_thread.join();
810 }