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