// 576p60/720p60/1080i60 works, 1080p60 does not work (firmware limitation)
// Audio comes out as 8-channel 24-bit raw audio.
+#include <assert.h>
+#include <errno.h>
+#include <libusb.h>
+#include <netinet/in.h>
+#include <sched.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
-#include <libusb.h>
-#include <arpa/inet.h>
-#include <unistd.h>
#include <string.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <assert.h>
-#ifdef __SSE2__
+#ifdef __SSE4_1__
#include <immintrin.h>
#endif
+#include "bmusb.h"
+
#include <algorithm>
+#include <atomic>
+#include <condition_variable>
+#include <cstddef>
+#include <cstdint>
+#include <deque>
#include <functional>
#include <memory>
-#include <deque>
-#include <utility>
#include <mutex>
-#include <condition_variable>
-#include <thread>
#include <stack>
-#include <atomic>
-#include "bmusb.h"
+#include <thread>
using namespace std;
using namespace std::placeholders;
FrameAllocator::~FrameAllocator() {}
-#define NUM_QUEUED_FRAMES 8
+#define NUM_QUEUED_FRAMES 16
class MallocFrameAllocator : public FrameAllocator {
public:
MallocFrameAllocator(size_t frame_size);
void MallocFrameAllocator::release_frame(Frame frame)
{
+ if (frame.overflow > 0) {
+ printf("%d bytes overflow after last (malloc) frame\n", int(frame.overflow));
+ }
unique_lock<mutex> lock(freelist_mutex);
freelist.push(unique_ptr<uint8_t[]>(frame.data));
}
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<mutex> 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;
- if (video_timecode < audio_timecode) {
+ if (uint16_less_than_with_wraparound(video_timecode, audio_timecode)) {
printf("Video block 0x%04x without corresponding audio block, dropping.\n",
video_timecode);
video_frame_allocator->release_frame(pending_video_frames.front().frame);
pending_video_frames.pop_front();
- } else if (audio_timecode < video_timecode) {
- printf("Audio block 0x%04x without corresponding video block, dropping.\n",
+ } else if (uint16_less_than_with_wraparound(audio_timecode, video_timecode)) {
+ printf("Audio block 0x%04x without corresponding video block, sending blank frame.\n",
audio_timecode);
- audio_frame_allocator->release_frame(pending_audio_frames.front().frame);
+ QueuedFrame audio_frame = pending_audio_frames.front();
pending_audio_frames.pop_front();
+ lock.unlock();
+ frame_callback(audio_timecode,
+ FrameAllocator::Frame(), 0, 0x0000,
+ audio_frame.frame, AUDIO_HEADER_SIZE, audio_frame.format);
} else {
QueuedFrame video_frame = pending_video_frames.front();
QueuedFrame audio_frame = pending_audio_frames.front();
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)
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);
}
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) {
}
}
-#ifdef __SSE2__
+#ifdef __SSE4_1__
#if 0
void avx2_dump(const char *name, __m256i n)
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);
void BMUSBCapture::usb_thread_func()
{
- printf("usb thread started\n");
-
sched_param param;
memset(¶m, 0, sizeof(param));
param.sched_priority = 1;
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;
// 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));
#endif
}
+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;