X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=mixer.cpp;h=6bc878074f2fa77e9ef9ef6da6157d54dc202eaa;hb=08a992d58c34da8d8bbd70226f7e85c9f30d9514;hp=6276e2cb20c15b92aaaa4c70dfd00afdf0360322;hpb=78e28934f9aecfb4dc9cd573b0c9b4e0bd012729;p=nageru diff --git a/mixer.cpp b/mixer.cpp index 6276e2c..6bc8780 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include "bmusb/bmusb.h" #include "context.h" @@ -44,8 +46,10 @@ class QOpenGLContext; using namespace movit; using namespace std; using namespace std::placeholders; +using namespace bmusb; Mixer *global_mixer = nullptr; +bool uses_mlock = false; namespace { @@ -174,7 +178,7 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) assert(num_fake_cards <= num_cards); // Enforced in flags.cpp. for ( ; card_index < num_fake_cards; ++card_index) { - configure_card(card_index, format, new FakeCapture(card_index)); + configure_card(card_index, new FakeCapture(card_index), /*is_fake_capture=*/true); } if (global_flags.num_fake_cards > 0) { @@ -190,7 +194,7 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) break; } - configure_card(card_index, format, new DeckLinkCapture(decklink, card_index - num_fake_cards)); + configure_card(card_index, new DeckLinkCapture(decklink, card_index - num_fake_cards), /*is_fake_capture=*/false); ++num_pci_devices; } decklink_iterator->Release(); @@ -200,11 +204,15 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) } } for ( ; card_index < num_cards; ++card_index) { - configure_card(card_index, format, new BMUSBCapture(card_index - num_pci_devices - num_fake_cards)); + BMUSBCapture *capture = new BMUSBCapture(card_index - num_pci_devices - num_fake_cards); + capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, card_index)); + configure_card(card_index, capture, /*is_fake_capture=*/false); ++num_usb_devices; } if (num_usb_devices > 0) { + has_bmusb_thread = true; + BMUSBCapture::set_card_connected_callback(bind(&Mixer::bm_hotplug_add, this, _1)); BMUSBCapture::start_bm_thread(); } @@ -261,14 +269,12 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) locut.init(FILTER_HPF, 2); - // If --flat-audio is given, turn off everything that messes with the sound, - // except the final makeup gain. - if (global_flags.flat_audio) { - set_locut_enabled(false); - set_gain_staging_auto(false); - set_limiter_enabled(false); - set_compressor_enabled(false); - } + set_locut_enabled(global_flags.locut_enabled); + set_gain_staging_db(global_flags.initial_gain_staging_db); + set_gain_staging_auto(global_flags.gain_staging_auto); + set_compressor_enabled(global_flags.compressor_enabled); + set_limiter_enabled(global_flags.limiter_enabled); + set_final_makeup_gain_auto(global_flags.final_makeup_gain_auto); // hlen=16 is pretty low quality, but we use quite a bit of CPU otherwise, // and there's a limit to how important the peak meter is. @@ -283,7 +289,9 @@ Mixer::~Mixer() { resource_pool->release_glsl_program(cbcr_program_num); glDeleteBuffers(1, &cbcr_vbo); - BMUSBCapture::stop_bm_thread(); + if (has_bmusb_thread) { + BMUSBCapture::stop_bm_thread(); + } for (unsigned card_index = 0; card_index < num_cards; ++card_index) { { @@ -297,17 +305,33 @@ Mixer::~Mixer() video_encoder.reset(nullptr); } -void Mixer::configure_card(unsigned card_index, const QSurfaceFormat &format, CaptureInterface *capture) +void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, bool is_fake_capture) { printf("Configuring card %d...\n", card_index); CaptureCard *card = &cards[card_index]; + if (card->capture != nullptr) { + card->capture->stop_dequeue_thread(); + delete card->capture; + } card->capture = capture; + card->is_fake_capture = is_fake_capture; card->capture->set_frame_callback(bind(&Mixer::bm_frame, this, card_index, _1, _2, _3, _4, _5, _6, _7)); - card->frame_allocator.reset(new PBOFrameAllocator(8 << 20, WIDTH, HEIGHT)); // 8 MB. + if (card->frame_allocator == nullptr) { + card->frame_allocator.reset(new PBOFrameAllocator(8 << 20, WIDTH, HEIGHT)); // 8 MB. + } card->capture->set_video_frame_allocator(card->frame_allocator.get()); - card->surface = create_surface(format); - card->resampling_queue.reset(new ResamplingQueue(card_index, OUTPUT_FREQUENCY, OUTPUT_FREQUENCY, 2)); + if (card->surface == nullptr) { + card->surface = create_surface_with_same_format(mixer_surface); + } + { + unique_lock lock(cards[card_index].audio_mutex); + card->resampling_queue.reset(new ResamplingQueue(card_index, OUTPUT_FREQUENCY, OUTPUT_FREQUENCY, 2)); + } + while (!card->new_frames.empty()) card->new_frames.pop(); + card->fractional_samples = 0; + card->last_timecode = -1; + card->next_local_pts = 0; card->capture->configure_card(); } @@ -500,6 +524,7 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, } userdata->last_interlaced = video_format.interlaced; userdata->last_has_signal = video_format.has_signal; + userdata->last_is_connected = video_format.is_connected; userdata->last_frame_rate_nom = video_format.frame_rate_nom; userdata->last_frame_rate_den = video_format.frame_rate_den; RefCountedFrame frame(video_frame); @@ -603,6 +628,17 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, } } +void Mixer::bm_hotplug_add(libusb_device *dev) +{ + lock_guard lock(hotplug_mutex); + hotplugged_cards.push_back(dev); +} + +void Mixer::bm_hotplug_remove(unsigned card_index) +{ + cards[card_index].new_frames_changed.notify_all(); +} + void Mixer::thread_func() { eglBindAPI(EGL_OPENGL_API); @@ -623,7 +659,6 @@ void Mixer::thread_func() bool has_new_frame[MAX_CARDS] = { false }; int num_samples[MAX_CARDS] = { 0 }; - // TODO: Add a timeout. unsigned master_card_index = theme->map_signal(master_clock_channel); assert(master_card_index < num_cards); @@ -632,6 +667,8 @@ void Mixer::thread_func() stats_dropped_frames += new_frames[master_card_index].dropped_frames; send_audio_level_callback(); + handle_hotplugged_cards(); + for (unsigned card_index = 0; card_index < num_cards; ++card_index) { if (card_index == master_card_index || !has_new_frame[card_index]) { continue; @@ -678,12 +715,39 @@ void Mixer::thread_func() double elapsed = now.tv_sec - start.tv_sec + 1e-9 * (now.tv_nsec - start.tv_nsec); if (frame % 100 == 0) { - printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)\n", + printf("%d frames (%d dropped) in %.3f seconds = %.1f fps (%.1f ms/frame)", frame, stats_dropped_frames, elapsed, frame / elapsed, 1e3 * elapsed / frame); // chain->print_phase_timing(); + + // Check our memory usage, to see if we are close to our mlockall() + // limit (if at all set). + rusage used; + if (getrusage(RUSAGE_SELF, &used) == -1) { + perror("getrusage(RUSAGE_SELF)"); + assert(false); + } + + if (uses_mlock) { + rlimit limit; + if (getrlimit(RLIMIT_MEMLOCK, &limit) == -1) { + perror("getrlimit(RLIMIT_MEMLOCK)"); + assert(false); + } + + printf(", using %ld / %ld MB lockable memory (%.1f%%)", + long(used.ru_maxrss / 1024), + long(limit.rlim_cur / 1048576), + float(100.0 * (used.ru_maxrss * 1024.0) / limit.rlim_cur)); + } else { + printf(", using %ld MB memory (not locked)", + long(used.ru_maxrss / 1024)); + } + + printf("\n"); } + if (should_cut.exchange(false)) { // Test and clear. video_encoder->do_cut(frame); } @@ -706,9 +770,19 @@ void Mixer::thread_func() void Mixer::get_one_frame_from_each_card(unsigned master_card_index, CaptureCard::NewFrame new_frames[MAX_CARDS], bool has_new_frame[MAX_CARDS], int num_samples[MAX_CARDS]) { +start: // The first card is the master timer, so wait for it to have a new frame. + // TODO: Add a timeout. unique_lock lock(bmusb_mutex); - cards[master_card_index].new_frames_changed.wait(lock, [this, master_card_index]{ return !cards[master_card_index].new_frames.empty(); }); + cards[master_card_index].new_frames_changed.wait(lock, [this, master_card_index]{ return !cards[master_card_index].new_frames.empty() || cards[master_card_index].capture->get_disconnected(); }); + + if (cards[master_card_index].new_frames.empty()) { + // We were woken up, but not due to a new frame. Deal with it + // and then restart. + assert(cards[master_card_index].capture->get_disconnected()); + handle_hotplugged_cards(); + goto start; + } for (unsigned card_index = 0; card_index < num_cards; ++card_index) { CaptureCard *card = &cards[card_index]; @@ -743,6 +817,52 @@ void Mixer::get_one_frame_from_each_card(unsigned master_card_index, CaptureCard } } +void Mixer::handle_hotplugged_cards() +{ + // Check for cards that have been disconnected since last frame. + for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + CaptureCard *card = &cards[card_index]; + if (card->capture->get_disconnected()) { + fprintf(stderr, "Card %u went away, replacing with a fake card.\n", card_index); + configure_card(card_index, new FakeCapture(card_index), /*is_fake_capture=*/true); + card->queue_length_policy.reset(card_index); + card->capture->start_bm_capture(); + } + } + + // Check for cards that have been connected since last frame. + vector hotplugged_cards_copy; + { + lock_guard lock(hotplug_mutex); + swap(hotplugged_cards, hotplugged_cards_copy); + } + for (libusb_device *new_dev : hotplugged_cards_copy) { + // Look for a fake capture card where we can stick this in. + int free_card_index = -1; + for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + if (cards[card_index].is_fake_capture) { + free_card_index = int(card_index); + break; + } + } + + if (free_card_index == -1) { + fprintf(stderr, "New card plugged in, but no free slots -- ignoring.\n"); + libusb_unref_device(new_dev); + } else { + // BMUSBCapture takes ownership. + fprintf(stderr, "New card plugged in, choosing slot %d.\n", free_card_index); + CaptureCard *card = &cards[free_card_index]; + BMUSBCapture *capture = new BMUSBCapture(free_card_index, new_dev); + configure_card(free_card_index, capture, /*is_fake_capture=*/false); + card->queue_length_policy.reset(free_card_index); + capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, free_card_index)); + capture->start_bm_capture(); + } + } +} + + void Mixer::schedule_audio_resampling_tasks(unsigned dropped_frames, int num_samples_per_frame, int length_per_frame) { // Resample the audio as needed, including from previously dropped frames.