X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=bmusb.cpp;h=4621e4b82880ea50189ecbcb07127205683d417f;hb=73f15e0a1d8f66bf3fe4947894ed7c7f00acb6fb;hp=387e042900ea82bc10d3f7775e1915c7f8113c1e;hpb=e6f8f805a7224fedf90ba90ef5a3ca83d21b6c6b;p=bmusb diff --git a/bmusb.cpp b/bmusb.cpp index 387e042..4621e4b 100644 --- a/bmusb.cpp +++ b/bmusb.cpp @@ -52,10 +52,16 @@ atomic should_quit; FrameAllocator::~FrameAllocator() {} -#define NUM_QUEUED_FRAMES 16 +// Audio is more important than video, and also much cheaper. +// By having many more audio frames available, hopefully if something +// starts to drop, we'll have CPU load go down (from not having to +// process as much video) before we have to drop audio. +#define NUM_QUEUED_VIDEO_FRAMES 16 +#define NUM_QUEUED_AUDIO_FRAMES 64 + class MallocFrameAllocator : public FrameAllocator { public: - MallocFrameAllocator(size_t frame_size); + MallocFrameAllocator(size_t frame_size, size_t num_queued_frames); Frame alloc_frame() override; void release_frame(Frame frame) override; @@ -66,10 +72,10 @@ private: stack> freelist; // All of size . }; -MallocFrameAllocator::MallocFrameAllocator(size_t frame_size) +MallocFrameAllocator::MallocFrameAllocator(size_t frame_size, size_t num_queued_frames) : frame_size(frame_size) { - for (int i = 0; i < NUM_QUEUED_FRAMES; ++i) { + for (size_t i = 0; i < num_queued_frames; ++i) { freelist.push(unique_ptr(new uint8_t[frame_size])); } } @@ -114,6 +120,7 @@ bool uint16_less_than_with_wraparound(uint16_t a, uint16_t b) void BMUSBCapture::queue_frame(uint16_t format, uint16_t timecode, FrameAllocator::Frame frame, deque *q) { + unique_lock lock(queue_lock); if (!q->empty() && !uint16_less_than_with_wraparound(q->back().timecode, timecode)) { printf("Blocks going backwards: prev=0x%04x, cur=0x%04x (dropped)\n", q->back().timecode, timecode); @@ -125,11 +132,7 @@ void BMUSBCapture::queue_frame(uint16_t format, uint16_t timecode, FrameAllocato qf.format = format; qf.timecode = timecode; qf.frame = frame; - - { - unique_lock lock(queue_lock); - q->push_back(move(qf)); - } + q->push_back(move(qf)); queues_not_empty.notify_one(); // might be spurious } @@ -156,13 +159,17 @@ void BMUSBCapture::dequeue_thread_func() unique_lock lock(queue_lock); queues_not_empty.wait(lock, [this]{ return dequeue_thread_should_quit || (!pending_video_frames.empty() && !pending_audio_frames.empty()); }); + if (dequeue_thread_should_quit) break; + uint16_t video_timecode = pending_video_frames.front().timecode; uint16_t audio_timecode = pending_audio_frames.front().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); + QueuedFrame video_frame = pending_video_frames.front(); pending_video_frames.pop_front(); + lock.unlock(); + video_frame_allocator->release_frame(video_frame.frame); } 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); @@ -632,8 +639,9 @@ void BMUSBCapture::cb_xfr(struct libusb_transfer *xfr) } #endif - if (libusb_submit_transfer(xfr) < 0) { - fprintf(stderr, "error re-submitting URB\n"); + int rc = libusb_submit_transfer(xfr); + if (rc < 0) { + fprintf(stderr, "error re-submitting URB: %s\n", libusb_error_name(rc)); exit(1); } } @@ -653,13 +661,87 @@ void BMUSBCapture::usb_thread_func() } } +struct USBCardDevice { + uint16_t product; + uint8_t bus, port; + libusb_device *device; +}; + +libusb_device_handle *open_card(int card_index) +{ + libusb_device **devices; + ssize_t num_devices = libusb_get_device_list(nullptr, &devices); + if (num_devices == -1) { + fprintf(stderr, "Error finding USB devices\n"); + exit(1); + } + vector found_cards; + for (ssize_t i = 0; i < num_devices; ++i) { + libusb_device_descriptor desc; + if (libusb_get_device_descriptor(devices[i], &desc) < 0) { + fprintf(stderr, "Error getting device descriptor for device %d\n", int(i)); + exit(1); + } + + uint8_t bus = libusb_get_bus_number(devices[i]); + uint8_t port = libusb_get_port_number(devices[i]); + + if (!(desc.idVendor == 0x1edb && desc.idProduct == 0xbd3b) && + !(desc.idVendor == 0x1edb && desc.idProduct == 0xbd4f)) { + libusb_unref_device(devices[i]); + continue; + } + + found_cards.push_back({ desc.idProduct, bus, port, devices[i] }); + } + libusb_free_device_list(devices, 0); + + // Sort the devices to get a consistent ordering. + sort(found_cards.begin(), found_cards.end(), [](const USBCardDevice &a, const USBCardDevice &b) { + if (a.product != b.product) + return a.product < b.product; + if (a.bus != b.bus) + return a.bus < b.bus; + return a.port < b.port; + }); + + for (size_t i = 0; i < found_cards.size(); ++i) { + fprintf(stderr, "Card %d: Bus %03u Device %03u ", int(i), found_cards[i].bus, found_cards[i].port); + if (found_cards[i].product == 0xbd3b) { + fprintf(stderr, "Intensity Shuttle\n"); + } else if (found_cards[i].product == 0xbd4f) { + fprintf(stderr, "UltraStudio SDI\n"); + } else { + assert(false); + } + } + + if (size_t(card_index) >= found_cards.size()) { + fprintf(stderr, "Could not open card %d (only %d found)\n", card_index, int(found_cards.size())); + exit(1); + } + + libusb_device_handle *devh; + int rc = libusb_open(found_cards[card_index].device, &devh); + if (rc < 0) { + fprintf(stderr, "Error opening card %d: %s\n", card_index, libusb_error_name(rc)); + exit(1); + } + + for (size_t i = 0; i < found_cards.size(); ++i) { + libusb_unref_device(found_cards[i].device); + } + + return devh; +} + void BMUSBCapture::configure_card() { if (video_frame_allocator == nullptr) { - set_video_frame_allocator(new MallocFrameAllocator(FRAME_SIZE)); // FIXME: leak. + set_video_frame_allocator(new MallocFrameAllocator(FRAME_SIZE, NUM_QUEUED_VIDEO_FRAMES)); // FIXME: leak. } if (audio_frame_allocator == nullptr) { - set_audio_frame_allocator(new MallocFrameAllocator(65536)); // FIXME: leak. + set_audio_frame_allocator(new MallocFrameAllocator(65536, NUM_QUEUED_AUDIO_FRAMES)); // FIXME: leak. } dequeue_thread_should_quit = false; dequeue_thread = thread(&BMUSBCapture::dequeue_thread_func, this); @@ -673,9 +755,7 @@ void BMUSBCapture::configure_card() 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, 0xbd4f); - struct libusb_device_handle *devh = libusb_open_device_with_vid_pid(nullptr, vid, pid); + libusb_device_handle *devh = open_card(card_index); if (!devh) { fprintf(stderr, "Error finding USB device\n"); exit(1); @@ -726,7 +806,7 @@ void BMUSBCapture::configure_card() } rc = libusb_set_interface_alt_setting(devh, /*interface=*/0, /*alternate_setting=*/2); if (rc < 0) { - fprintf(stderr, "Error setting alternate 1: %s\n", libusb_error_name(rc)); + fprintf(stderr, "Error setting alternate 2: %s\n", libusb_error_name(rc)); exit(1); } #if 0 @@ -899,7 +979,7 @@ void BMUSBCapture::configure_card() // set up isochronous transfers for audio and video for (int e = 3; e <= 4; ++e) { //int num_transfers = (e == 3) ? 6 : 6; - int num_transfers = 6; + int num_transfers = 10; for (int i = 0; i < num_transfers; ++i) { int num_iso_pack, size; if (e == 3) { @@ -913,7 +993,7 @@ void BMUSBCapture::configure_card() size &= ~1023; size += 1024; } - num_iso_pack = (2 << 18) / size; // 512 kB. + num_iso_pack = (2 << 16) / size; // 128 kB. printf("Picking %d packets of 0x%x bytes each\n", num_iso_pack, size); } else { size = 0xc0;