X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=bmusb.cpp;h=fbf31dd95177c8480dca45c6579ae696b126b7d2;hb=6fd0f2d883caf80001dc04e324ac4036ae396e06;hp=8d0918c2215bd6b750ba015b421d50b4c3f38c72;hpb=91ded9e745544b6a6aa813f289113305674cdbd9;p=bmusb diff --git a/bmusb.cpp b/bmusb.cpp index 8d0918c..fbf31dd 100644 --- a/bmusb.cpp +++ b/bmusb.cpp @@ -4,29 +4,31 @@ // 576p60/720p60/1080i60 works, 1080p60 does not work (firmware limitation) // Audio comes out as 8-channel 24-bit raw audio. +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include #include -#include -#include -#include -#ifdef __SSE2__ +#ifdef __SSE4_1__ #include #endif +#include "bmusb.h" + #include +#include +#include +#include +#include +#include #include #include -#include -#include #include -#include -#include #include -#include -#include "bmusb.h" +#include using namespace std; using namespace std::placeholders; @@ -45,9 +47,12 @@ using namespace std::placeholders; FILE *audiofp; +thread usb_thread; +atomic should_quit; + FrameAllocator::~FrameAllocator() {} -#define NUM_QUEUED_FRAMES 8 +#define NUM_QUEUED_FRAMES 16 class MallocFrameAllocator : public FrameAllocator { public: MallocFrameAllocator(size_t frame_size); @@ -88,6 +93,9 @@ FrameAllocator::Frame MallocFrameAllocator::alloc_frame() void MallocFrameAllocator::release_frame(Frame frame) { + if (frame.overflow > 0) { + printf("%d bytes overflow after last (malloc) frame\n", int(frame.overflow)); + } unique_lock lock(freelist_mutex); freelist.push(unique_ptr(frame.data)); } @@ -139,11 +147,14 @@ void dump_audio_block(uint8_t *audio_start, size_t audio_len) fwrite(audio_start + AUDIO_HEADER_SIZE, 1, audio_len - AUDIO_HEADER_SIZE, audiofp); } -void BMUSBCapture::dequeue_thread() +void BMUSBCapture::dequeue_thread_func() { - for ( ;; ) { + if (has_dequeue_callbacks) { + dequeue_init_callback(); + } + while (!dequeue_thread_should_quit) { unique_lock lock(queue_lock); - queues_not_empty.wait(lock, [this]{ return !pending_video_frames.empty() && !pending_audio_frames.empty(); }); + queues_not_empty.wait(lock, [this]{ return dequeue_thread_should_quit || (!pending_video_frames.empty() && !pending_audio_frames.empty()); }); uint16_t video_timecode = pending_video_frames.front().timecode; uint16_t audio_timecode = pending_audio_frames.front().timecode; @@ -176,6 +187,9 @@ void BMUSBCapture::dequeue_thread() audio_frame.frame, AUDIO_HEADER_SIZE, audio_frame.format); } } + if (has_dequeue_callbacks) { + dequeue_cleanup_callback(); + } } void BMUSBCapture::start_new_frame(const uint8_t *start) @@ -184,6 +198,21 @@ void BMUSBCapture::start_new_frame(const uint8_t *start) uint16_t timecode = (start[1] << 8) | start[0]; if (current_video_frame.len > 0) { + // If format is 0x0800 (no signal), add a fake (empty) audio + // frame to get it out of the queue. + // TODO: Figure out if there are other formats that come with + // no audio, and treat them the same. + if (format == 0x0800) { + FrameAllocator::Frame fake_audio_frame = audio_frame_allocator->alloc_frame(); + if (fake_audio_frame.data == nullptr) { + // Oh well, it's just a no-signal frame anyway. + printf("Couldn't allocate fake audio frame, also dropping no-signal video frame.\n"); + current_video_frame.owner->release_frame(current_video_frame); + current_video_frame = video_frame_allocator->alloc_frame(); + return; + } + queue_frame(format, timecode, fake_audio_frame, &pending_audio_frames); + } //dump_frame(); queue_frame(format, timecode, current_video_frame, &pending_video_frames); } @@ -252,8 +281,13 @@ void add_to_frame(FrameAllocator::Frame *current_frame, const char *frame_type_n int bytes = end - start; if (current_frame->len + bytes > current_frame->size) { - printf("%d bytes overflow after last %s frame\n", - int(current_frame->len + bytes - current_frame->size), frame_type_name); + current_frame->overflow = current_frame->len + bytes - current_frame->size; + current_frame->len = current_frame->size; + if (current_frame->overflow > 1048576) { + printf("%d bytes overflow after last %s frame\n", + int(current_frame->overflow), frame_type_name); + current_frame->overflow = 0; + } //dump_frame(); } else { if (current_frame->interleaved) { @@ -278,7 +312,50 @@ void add_to_frame(FrameAllocator::Frame *current_frame, const char *frame_type_n } } -#ifdef __SSE2__ +#ifdef __SSE4_1__ + +#if 0 +void avx2_dump(const char *name, __m256i n) +{ + printf("%-10s:", name); + printf(" %02x", _mm256_extract_epi8(n, 0)); + printf(" %02x", _mm256_extract_epi8(n, 1)); + printf(" %02x", _mm256_extract_epi8(n, 2)); + printf(" %02x", _mm256_extract_epi8(n, 3)); + printf(" %02x", _mm256_extract_epi8(n, 4)); + printf(" %02x", _mm256_extract_epi8(n, 5)); + printf(" %02x", _mm256_extract_epi8(n, 6)); + printf(" %02x", _mm256_extract_epi8(n, 7)); + printf(" "); + printf(" %02x", _mm256_extract_epi8(n, 8)); + printf(" %02x", _mm256_extract_epi8(n, 9)); + printf(" %02x", _mm256_extract_epi8(n, 10)); + printf(" %02x", _mm256_extract_epi8(n, 11)); + printf(" %02x", _mm256_extract_epi8(n, 12)); + printf(" %02x", _mm256_extract_epi8(n, 13)); + printf(" %02x", _mm256_extract_epi8(n, 14)); + printf(" %02x", _mm256_extract_epi8(n, 15)); + printf(" "); + printf(" %02x", _mm256_extract_epi8(n, 16)); + printf(" %02x", _mm256_extract_epi8(n, 17)); + printf(" %02x", _mm256_extract_epi8(n, 18)); + printf(" %02x", _mm256_extract_epi8(n, 19)); + printf(" %02x", _mm256_extract_epi8(n, 20)); + printf(" %02x", _mm256_extract_epi8(n, 21)); + printf(" %02x", _mm256_extract_epi8(n, 22)); + printf(" %02x", _mm256_extract_epi8(n, 23)); + printf(" "); + printf(" %02x", _mm256_extract_epi8(n, 24)); + printf(" %02x", _mm256_extract_epi8(n, 25)); + printf(" %02x", _mm256_extract_epi8(n, 26)); + printf(" %02x", _mm256_extract_epi8(n, 27)); + printf(" %02x", _mm256_extract_epi8(n, 28)); + printf(" %02x", _mm256_extract_epi8(n, 29)); + printf(" %02x", _mm256_extract_epi8(n, 30)); + printf(" %02x", _mm256_extract_epi8(n, 31)); + printf("\n"); +} +#endif // Does a memcpy and memchr in one to reduce processing time. // Note that the benefit is somewhat limited if your L3 cache is small, @@ -335,32 +412,39 @@ const uint8_t *add_to_frame_fastpath(FrameAllocator::Frame *current_frame, const #if __AVX2__ const __m256i needle = _mm256_set1_epi8(sync_char); - const __m256i *in = (const __m256i *)aligned_start; + const __restrict __m256i *in = (const __m256i *)aligned_start; if (current_frame->interleaved) { - __m256i *out1 = (__m256i *)(current_frame->data + (current_frame->len + 1) / 2); - __m256i *out2 = (__m256i *)(current_frame->data2 + current_frame->len / 2); + __restrict __m256i *out1 = (__m256i *)(current_frame->data + (current_frame->len + 1) / 2); + __restrict __m256i *out2 = (__m256i *)(current_frame->data2 + current_frame->len / 2); if (current_frame->len % 2 == 1) { swap(out1, out2); } - __m256i mask_lower_byte = _mm256_set1_epi16(0x00ff); + __m256i shuffle_cw = _mm256_set_epi8( + 15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0, + 15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0); while (in < (const __m256i *)limit) { - __m256i data1 = _mm256_load_si256(in); - __m256i data2 = _mm256_load_si256(in + 1); - __m256i data1_lo = _mm256_and_si256(data1, mask_lower_byte); - __m256i data2_lo = _mm256_and_si256(data2, mask_lower_byte); - __m256i data1_hi = _mm256_srli_epi16(data1, 8); - __m256i data2_hi = _mm256_srli_epi16(data2, 8); - __m256i lo = _mm256_packus_epi16(data1_lo, data2_lo); - lo = _mm256_permute4x64_epi64(lo, 0b11011000); - _mm256_storeu_si256(out1, lo); // Store as early as possible, even if the data isn't used. - __m256i hi = _mm256_packus_epi16(data1_hi, data2_hi); - hi = _mm256_permute4x64_epi64(hi, 0b11011000); - _mm256_storeu_si256(out2, hi); + // Note: For brevity, comments show lanes as if they were 2x64-bit (they're actually 2x128). + __m256i data1 = _mm256_stream_load_si256(in); // AaBbCcDd EeFfGgHh + __m256i data2 = _mm256_stream_load_si256(in + 1); // IiJjKkLl MmNnOoPp + __m256i found1 = _mm256_cmpeq_epi8(data1, needle); __m256i found2 = _mm256_cmpeq_epi8(data2, needle); - if (!_mm256_testz_si256(found1, found1) || - !_mm256_testz_si256(found2, found2)) { + __m256i found = _mm256_or_si256(found1, found2); + + data1 = _mm256_shuffle_epi8(data1, shuffle_cw); // ABCDabcd EFGHefgh + data2 = _mm256_shuffle_epi8(data2, shuffle_cw); // IJKLijkl MNOPmnop + + data1 = _mm256_permute4x64_epi64(data1, 0b11011000); // ABCDEFGH abcdefgh + data2 = _mm256_permute4x64_epi64(data2, 0b11011000); // IJKLMNOP ijklmnop + + __m256i lo = _mm256_permute2x128_si256(data1, data2, 0b00100000); + __m256i hi = _mm256_permute2x128_si256(data1, data2, 0b00110001); + + _mm256_storeu_si256(out1, lo); // Store as early as possible, even if the data isn't used. + _mm256_storeu_si256(out2, hi); + + if (!_mm256_testz_si256(found, found)) { break; } @@ -462,7 +546,7 @@ void decode_packs(const libusb_transfer *xfr, const uint8_t *start = xfr->buffer + offset; const uint8_t *limit = start + pack->actual_length; while (start < limit) { // Usually runs only one iteration. -#ifdef __SSE2__ +#ifdef __SSE4_1__ start = add_to_frame_fastpath(current_frame, start, limit, sync_pattern[0]); if (start == limit) break; assert(start < limit); @@ -552,8 +636,6 @@ void BMUSBCapture::cb_xfr(struct libusb_transfer *xfr) void BMUSBCapture::usb_thread_func() { - printf("usb thread started\n"); - sched_param param; memset(¶m, 0, sizeof(param)); param.sched_priority = 1; @@ -567,7 +649,7 @@ void BMUSBCapture::usb_thread_func() } } -void BMUSBCapture::start_bm_capture() +void BMUSBCapture::configure_card() { if (video_frame_allocator == nullptr) { set_video_frame_allocator(new MallocFrameAllocator(FRAME_SIZE)); // FIXME: leak. @@ -575,11 +657,11 @@ void BMUSBCapture::start_bm_capture() if (audio_frame_allocator == nullptr) { set_audio_frame_allocator(new MallocFrameAllocator(65536)); // FIXME: leak. } - thread(&BMUSBCapture::dequeue_thread, this).detach(); + dequeue_thread_should_quit = false; + dequeue_thread = thread(&BMUSBCapture::dequeue_thread_func, this); int rc; struct libusb_transfer *xfr; - vector iso_xfrs; rc = libusb_init(nullptr); if (rc < 0) { @@ -587,7 +669,9 @@ void BMUSBCapture::start_bm_capture() exit(1); } - struct libusb_device_handle *devh = libusb_open_device_with_vid_pid(nullptr, 0x1edb, 0xbd3b); + //struct libusb_device_handle *devh = libusb_open_device_with_vid_pid(nullptr, 0x1edb, 0xbd3b); + //struct libusb_device_handle *devh = libusb_open_device_with_vid_pid(nullptr, 0x1edb, 0xbd4f); + struct libusb_device_handle *devh = libusb_open_device_with_vid_pid(nullptr, vid, pid); if (!devh) { fprintf(stderr, "Error finding USB device\n"); exit(1); @@ -604,8 +688,8 @@ void BMUSBCapture::start_bm_capture() printf(" interface %d\n", interface_number); const libusb_interface *interface = &config->interface[interface_number]; for (int altsetting = 0; altsetting < interface->num_altsetting; ++altsetting) { - printf(" alternate setting %d\n", altsetting); const libusb_interface_descriptor *interface_desc = &interface->altsetting[altsetting]; + printf(" alternate setting %d\n", interface_desc->bAlternateSetting); for (int endpoint_number = 0; endpoint_number < interface_desc->bNumEndpoints; ++endpoint_number) { const libusb_endpoint_descriptor *endpoint = &interface_desc->endpoint[endpoint_number]; printf(" endpoint address 0x%02x\n", endpoint->bEndpointAddress); @@ -628,6 +712,9 @@ void BMUSBCapture::start_bm_capture() // Alternate setting 1 is output, alternate setting 2 is input. // Card is reset when switching alternates, so the driver uses // this “double switch” when it wants to reset. + // + // There's also alternate settings 3 and 4, which seem to be + // like 1 and 2 except they advertise less bandwidth needed. rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/1); if (rc < 0) { fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc)); @@ -675,6 +762,8 @@ void BMUSBCapture::start_bm_capture() // // so only first 16 bits count, and 0x0100 is a mask for ok/stable signal? // + // Bottom 16 bits of this register seem to be firmware version number (possibly not all all of them). + // // 28 and 32 seems to be analog audio input levels (one byte for each of the eight channels). // however, if setting 32 with HDMI embedded audio, it is immediately overwritten back (to 0xe137002a). // @@ -684,6 +773,11 @@ void BMUSBCapture::start_bm_capture() // 36 can be set to 0 with no apparent effect (all of this tested on both video and audio), // but the driver sets it to 0x8036802a at some point. // + // all of this is on request 214/215. other requests (192, 219, + // 222, 223, 224) are used for firmware upgrade. Probably best to + // stay out of it unless you know what you're doing. + // + // // register 16: // first byte is 0x39 for a stable 576p60 signal, 0x2d for a stable 720p60 signal, 0x20 for no signal // @@ -703,59 +797,6 @@ void BMUSBCapture::start_bm_capture() static const ctrl ctrls[] = { { LIBUSB_ENDPOINT_IN, 214, 16, 0 }, { LIBUSB_ENDPOINT_IN, 214, 0, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 0, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 4, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 16, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 20, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 24, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 28, 0 }, - { LIBUSB_ENDPOINT_IN, 215, 32, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 36, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 216, 44, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 48, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 52, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 24, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 24, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, // packet 354 - { LIBUSB_ENDPOINT_IN, 214, 24, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 12, 0 }, - { LIBUSB_ENDPOINT_IN, 214, 40, 0 }, - // more... - //{ LIBUSB_ENDPOINT_OUT, 215, 0, 0x80000100 }, - //{ LIBUSB_ENDPOINT_OUT, 215, 0, 0x09000000 }, // wow, some kind of mode // seems to capture on HDMI, clearing the 0x20000000 bit seems to activate 10-bit // capture (v210). @@ -765,46 +806,10 @@ void BMUSBCapture::start_bm_capture() // 0x3c000000 = composite video? (analog audio) // 0x3e000000 = s-video? (analog audio) { LIBUSB_ENDPOINT_OUT, 215, 0, 0x29000000 }, + //{ LIBUSB_ENDPOINT_OUT, 215, 0, 0x80000100 }, //{ LIBUSB_ENDPOINT_OUT, 215, 0, 0x09000000 }, - - //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0xffffffff }, - //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0xffffffff }, - //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0x40404040 }, - //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0x40404040 }, - //{ LIBUSB_ENDPOINT_OUT, 215, 36, 0x8036802a }, { LIBUSB_ENDPOINT_OUT, 215, 24, 0x73c60001 }, // latch for frame start? - //{ LIBUSB_ENDPOINT_OUT, 215, 24, 0x13370001 }, // latch for frame start? { LIBUSB_ENDPOINT_IN, 214, 24, 0 }, // - //{ LIBUSB_ENDPOINT_OUT, 215, 4, 0x00000000 }, // appears to have no e fect - //{ LIBUSB_ENDPOINT_OUT, 215, 8, 0x00000000 }, // appears to have no effect - //{ LIBUSB_ENDPOINT_OUT, 215, 20, 0x00000000 }, // appears to have no effect - //{ LIBUSB_ENDPOINT_OUT, 215, 28, 0x00000000 }, // appears to have no effect - //{ LIBUSB_ENDPOINT_OUT, 215, 32, 0x00000000 }, // appears to have no effect - //{ LIBUSB_ENDPOINT_OUT, 215, 36, 0x00000000 }, // appears to have no effect -#if 0 - { LIBUSB_ENDPOINT_OUT, 215, 0 }, - { LIBUSB_ENDPOINT_OUT, 215, 0 }, - { LIBUSB_ENDPOINT_OUT, 215, 28 }, - { LIBUSB_ENDPOINT_OUT, 215, 32 }, - { LIBUSB_ENDPOINT_OUT, 215, 36 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 0 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, - { LIBUSB_ENDPOINT_OUT, 215, 24 }, -#endif }; for (unsigned req = 0; req < sizeof(ctrls) / sizeof(ctrls[0]); ++req) { @@ -904,7 +909,7 @@ void BMUSBCapture::start_bm_capture() size &= ~1023; size += 1024; } - num_iso_pack = (2 << 20) / size; // 2 MB. + num_iso_pack = (2 << 18) / size; // 512 kB. printf("Picking %d packets of 0x%x bytes each\n", num_iso_pack, size); } else { size = 0xc0; @@ -927,24 +932,24 @@ void BMUSBCapture::start_bm_capture() iso_xfrs.push_back(xfr); } } +} - { - int i = 0; - for (libusb_transfer *xfr : iso_xfrs) { - rc = libusb_submit_transfer(xfr); - ++i; - if (rc < 0) { - //printf("num_bytes=%d\n", num_bytes); - fprintf(stderr, "Error submitting iso to endpoint 0x%02x, number %d: %s\n", - xfr->endpoint, i, libusb_error_name(rc)); - exit(1); - } +void BMUSBCapture::start_bm_capture() +{ + printf("starting capture\n"); + int i = 0; + for (libusb_transfer *xfr : iso_xfrs) { + printf("submitting transfer...\n"); + int rc = libusb_submit_transfer(xfr); + ++i; + if (rc < 0) { + //printf("num_bytes=%d\n", num_bytes); + fprintf(stderr, "Error submitting iso to endpoint 0x%02x, number %d: %s\n", + xfr->endpoint, i, libusb_error_name(rc)); + exit(1); } } - should_quit = false; - usb_thread = thread(&BMUSBCapture::usb_thread_func, this); - #if 0 libusb_release_interface(devh, 0); @@ -956,7 +961,20 @@ out: #endif } -void BMUSBCapture::stop_bm_capture() +void BMUSBCapture::stop_dequeue_thread() +{ + dequeue_thread_should_quit = true; + queues_not_empty.notify_all(); + dequeue_thread.join(); +} + +void BMUSBCapture::start_bm_thread() +{ + should_quit = false; + usb_thread = thread(&BMUSBCapture::usb_thread_func); +} + +void BMUSBCapture::stop_bm_thread() { should_quit = true; usb_thread.join();