From c111b50bf5c9419e9d8abccf10f9e4066f9e6fa5 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Tue, 17 Nov 2015 01:20:30 +0100 Subject: [PATCH] Auto-tune the USB buffers based on the previous frame size instead of having them as a #define. --- bmusb.cpp | 85 +++++++++++++++++++++++++++++++++++++++++-------------- bmusb.h | 1 + 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/bmusb.cpp b/bmusb.cpp index 74adca8..9c0d1d1 100644 --- a/bmusb.cpp +++ b/bmusb.cpp @@ -33,23 +33,50 @@ using namespace std; using namespace std::placeholders; -#define WIDTH 1280 -#define HEIGHT 750 /* 30 lines ancillary data? */ -//#define WIDTH 1920 -//#define HEIGHT 1125 /* ??? lines ancillary data? */ +#define MIN_WIDTH 640 #define HEADER_SIZE 44 //#define HEADER_SIZE 0 #define AUDIO_HEADER_SIZE 4 -//#define FRAME_SIZE (WIDTH * HEIGHT * 2 + HEADER_SIZE) // UYVY -//#define FRAME_SIZE (WIDTH * HEIGHT * 2 * 4 / 3 + HEADER_SIZE) // v210 -#define FRAME_SIZE (8 << 20) +#define FRAME_SIZE (8 << 20) // 8 MB. +#define USB_VIDEO_TRANSFER_SIZE (128 << 10) // 128 kB. + +namespace { FILE *audiofp; thread usb_thread; atomic should_quit; +int find_xfer_size_for_width(int width) +{ + // Video seems to require isochronous packets scaled with the width; + // seemingly six lines is about right, rounded up to the required 1kB + // multiple. + int size = width * 2 * 6; + // Note that for 10-bit input, you'll need to increase size accordingly. + //size = size * 4 / 3; + if (size % 1024 != 0) { + size &= ~1023; + size += 1024; + } + return size; +} + +void change_xfer_size_for_width(int width, libusb_transfer *xfr) +{ + assert(width >= MIN_WIDTH); + size_t size = find_xfer_size_for_width(width); + int num_iso_pack = xfr->length / size; + if (num_iso_pack != xfr->num_iso_packets || + size != xfr->iso_packet_desc[0].length) { + xfr->num_iso_packets = num_iso_pack; + libusb_set_iso_packet_lengths(xfr, size); + } +} + +} // namespace + FrameAllocator::~FrameAllocator() {} // Audio is more important than video, and also much cheaper. @@ -226,6 +253,14 @@ void BMUSBCapture::start_new_frame(const uint8_t *start) } //dump_frame(); queue_frame(format, timecode, current_video_frame, &pending_video_frames); + + // Update the assumed frame width. We might be one frame too late on format changes, + // but it's much better than asking the user to choose manually. + int width, height, frame_rate_nom, frame_rate_den; + bool interlaced; + if (decode_video_format(format, &width, &height, &frame_rate_nom, &frame_rate_den, &interlaced)) { + assumed_frame_width = width; + } } //printf("Found frame start, format 0x%04x timecode 0x%04x, previous frame length was %d/%d\n", // format, timecode, @@ -597,6 +632,9 @@ void BMUSBCapture::cb_xfr(struct libusb_transfer *xfr) decode_packs(xfr, "DeckLinkAudioResyncT", 20, &usb->current_audio_frame, "audio", bind(&BMUSBCapture::start_new_audio_block, usb, _1)); } else { decode_packs(xfr, "\x00\x00\xff\xff", 4, &usb->current_video_frame, "video", bind(&BMUSBCapture::start_new_frame, usb, _1)); + + // Update the transfer with the new assumed width, if we're in the process of changing formats. + change_xfer_size_for_width(usb->assumed_frame_width, xfr); } } if (xfr->type == LIBUSB_TRANSFER_TYPE_CONTROL) { @@ -981,26 +1019,22 @@ void BMUSBCapture::configure_card() //int num_transfers = (e == 3) ? 6 : 6; int num_transfers = 10; for (int i = 0; i < num_transfers; ++i) { + size_t buf_size; int num_iso_pack, size; if (e == 3) { - // Video seems to require isochronous packets scaled with the width; - // seemingly six lines is about right, rounded up to the required 1kB - // multiple. - size = WIDTH * 2 * 6; - // Note that for 10-bit input, you'll need to increase size accordingly. - //size = size * 4 / 3; - if (size % 1024 != 0) { - size &= ~1023; - size += 1024; - } - num_iso_pack = (2 << 16) / size; // 128 kB. - printf("Picking %d packets of 0x%x bytes each\n", num_iso_pack, size); + // Allocate for minimum width (because that will give us the most + // number of packets, so we don't need to reallocated, but we'll + // default to 720p for the first frame. + size = find_xfer_size_for_width(MIN_WIDTH); + num_iso_pack = USB_VIDEO_TRANSFER_SIZE / size; + buf_size = USB_VIDEO_TRANSFER_SIZE; } else { size = 0xc0; num_iso_pack = 80; + buf_size = num_iso_pack * size; } - int num_bytes = num_iso_pack * size; - uint8_t *buf = new uint8_t[num_bytes]; + assert(size_t(num_iso_pack * size) <= buf_size); + uint8_t *buf = new uint8_t[buf_size]; xfr = libusb_alloc_transfer(num_iso_pack); if (!xfr) { @@ -1009,10 +1043,15 @@ void BMUSBCapture::configure_card() } int ep = LIBUSB_ENDPOINT_IN | e; - libusb_fill_iso_transfer(xfr, devh, ep, buf, num_bytes, + libusb_fill_iso_transfer(xfr, devh, ep, buf, buf_size, num_iso_pack, cb_xfr, nullptr, 0); libusb_set_iso_packet_lengths(xfr, size); xfr->user_data = this; + + if (e == 3) { + change_xfer_size_for_width(assumed_frame_width, xfr); + } + iso_xfrs.push_back(xfr); } } @@ -1079,6 +1118,8 @@ bool decode_video_format(uint16_t video_format, int *width, int *height, int *fr // No video signal. These green pseudo-frames seem to come at about 30.13 Hz. // It's a strange thing, but what can you do. // FIXME: find the dimensions. + *width = 720; + *height = 576; *frame_rate_nom = 3013; *frame_rate_den = 100; return true; diff --git a/bmusb.h b/bmusb.h index 9eb3ff4..5ce73cc 100644 --- a/bmusb.h +++ b/bmusb.h @@ -152,6 +152,7 @@ class BMUSBCapture { int card_index; std::vector iso_xfrs; + int assumed_frame_width = 1280; }; // Get details for the given video format; returns false if detection was incomplete. -- 2.39.2