X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=mixer.cpp;h=84419f41518fc4c092df7c6afc527e23c83c5121;hb=bcdbb7bdfcfc45b34ccdf36d3950e7e0af6444bd;hp=317c762a70575e9307b4c6a52c1e55aa59cb4304;hpb=25f6b207f157c081495d7abd4ea314b5ef760322;p=nageru diff --git a/mixer.cpp b/mixer.cpp index 317c762..84419f4 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -39,6 +39,7 @@ #include "decklink_output.h" #include "defs.h" #include "disk_space_estimator.h" +#include "ffmpeg_capture.h" #include "flags.h" #include "input_mapping.h" #include "pbo_frame_allocator.h" @@ -124,7 +125,11 @@ void ensure_texture_resolution(PBOFrameAllocator::Userdata *userdata, unsigned f case bmusb::PixelFormat_8BitRGBA: glBindTexture(GL_TEXTURE_2D, userdata->tex_rgba[field]); check_error(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + if (global_flags.can_disable_srgb_decoder) { // See the comments in tweaked_inputs.h. + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } else { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + } check_error(); break; } @@ -206,6 +211,9 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) CHECK(init_movit(MOVIT_SHADER_DIR, MOVIT_DEBUG_OFF)); check_error(); + // This nearly always should be true. + global_flags.can_disable_srgb_decoder = epoxy_has_gl_extension("GL_EXT_texture_sRGB_decode"); + // Since we allow non-bouncing 4:2:2 YCbCrInputs, effective subpixel precision // will be halved when sampling them, and we need to compensate here. movit_texel_subpixel_precision /= 2.0; @@ -271,7 +279,7 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) DeckLinkCapture *capture = new DeckLinkCapture(decklink, card_index); DeckLinkOutput *output = new DeckLinkOutput(resource_pool.get(), decklink_output_surface, global_flags.width, global_flags.height, card_index); output->set_device(decklink); - configure_card(card_index, capture, /*is_fake_capture=*/false, output); + configure_card(card_index, capture, CardType::LIVE_CARD, output); ++num_pci_devices; } decklink_iterator->Release(); @@ -285,24 +293,37 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) for (unsigned usb_card_index = 0; usb_card_index < num_usb_devices && card_index < num_cards; ++usb_card_index, ++card_index) { BMUSBCapture *capture = new BMUSBCapture(usb_card_index); capture->set_card_disconnected_callback(bind(&Mixer::bm_hotplug_remove, this, card_index)); - configure_card(card_index, capture, /*is_fake_capture=*/false, /*output=*/nullptr); + configure_card(card_index, capture, CardType::LIVE_CARD, /*output=*/nullptr); } fprintf(stderr, "Found %u USB card(s).\n", num_usb_devices); unsigned num_fake_cards = 0; for ( ; card_index < num_cards; ++card_index, ++num_fake_cards) { FakeCapture *capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio); - configure_card(card_index, capture, /*is_fake_capture=*/true, /*output=*/nullptr); + configure_card(card_index, capture, CardType::FAKE_CAPTURE, /*output=*/nullptr); } if (num_fake_cards > 0) { fprintf(stderr, "Initialized %u fake cards.\n", num_fake_cards); } + // Initialize all video inputs the theme asked for. Note that these are + // all put _after_ the regular cards, which stop at - 1. + std::vector video_inputs = theme->get_video_inputs(); + for (unsigned video_card_index = 0; video_card_index < video_inputs.size(); ++card_index, ++video_card_index) { + if (card_index >= MAX_VIDEO_CARDS) { + fprintf(stderr, "ERROR: Not enough card slots available for the videos the theme requested.\n"); + exit(1); + } + configure_card(card_index, video_inputs[video_card_index], CardType::FFMPEG_INPUT, /*output=*/nullptr); + video_inputs[video_card_index]->set_card_index(card_index); + } + num_video_inputs = video_inputs.size(); + BMUSBCapture::set_card_connected_callback(bind(&Mixer::bm_hotplug_add, this, _1)); BMUSBCapture::start_bm_thread(); - for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + for (unsigned card_index = 0; card_index < num_cards + num_video_inputs; ++card_index) { cards[card_index].queue_length_policy.reset(card_index); } @@ -350,7 +371,7 @@ Mixer::~Mixer() { BMUSBCapture::stop_bm_thread(); - for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + for (unsigned card_index = 0; card_index < num_cards + num_video_inputs; ++card_index) { { unique_lock lock(card_mutex); cards[card_index].should_quit = true; // Unblock thread. @@ -366,7 +387,7 @@ Mixer::~Mixer() video_encoder.reset(nullptr); } -void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, bool is_fake_capture, DeckLinkOutput *output) +void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, CardType card_type, DeckLinkOutput *output) { printf("Configuring card %d...\n", card_index); @@ -375,12 +396,19 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, bool card->capture->stop_dequeue_thread(); } card->capture.reset(capture); - card->is_fake_capture = is_fake_capture; + card->is_fake_capture = (card_type == CardType::FAKE_CAPTURE); if (card->output.get() != output) { card->output.reset(output); } - bmusb::PixelFormat pixel_format = global_flags.ten_bit_input ? PixelFormat_10BitYCbCr : PixelFormat_8BitYCbCr; + bmusb::PixelFormat pixel_format; + if (card_type == CardType::FFMPEG_INPUT) { + pixel_format = bmusb::PixelFormat_8BitRGBA; + } else if (global_flags.ten_bit_input) { + pixel_format = PixelFormat_10BitYCbCr; + } else { + pixel_format = PixelFormat_8BitYCbCr; + } card->capture->set_frame_callback(bind(&Mixer::bm_frame, this, card_index, _1, _2, _3, _4, _5, _6, _7)); if (card->frame_allocator == nullptr) { @@ -436,7 +464,7 @@ void Mixer::set_output_card_internal(int card_index) lock.lock(); card->parked_capture = move(card->capture); bmusb::CaptureInterface *fake_capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio); - configure_card(card_index, fake_capture, /*is_fake_capture=*/true, card->output.release()); + configure_card(card_index, fake_capture, CardType::FAKE_CAPTURE, card->output.release()); card->queue_length_policy.reset(card_index); card->capture->start_bm_capture(); desired_output_video_mode = output_video_mode = card->output->pick_video_mode(desired_output_video_mode); @@ -697,7 +725,7 @@ void Mixer::thread_func() // Start the actual capture. (We don't want to do it before we're actually ready // to process output frames.) - for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + for (unsigned card_index = 0; card_index < num_cards + num_video_inputs; ++card_index) { if (int(card_index) != output_card_index) { cards[card_index].capture->start_bm_capture(); } @@ -740,7 +768,7 @@ void Mixer::thread_func() handle_hotplugged_cards(); - for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + for (unsigned card_index = 0; card_index < num_cards + num_video_inputs; ++card_index) { if (card_index == master_card_index || !has_new_frame[card_index]) { continue; } @@ -761,7 +789,7 @@ void Mixer::thread_func() continue; } - for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + for (unsigned card_index = 0; card_index < num_cards + num_video_inputs; ++card_index) { if (!has_new_frame[card_index] || new_frames[card_index].frame->len == 0) continue; @@ -928,7 +956,7 @@ start: cards[master_card_index].new_frames.front().received_timestamp; } - for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + for (unsigned card_index = 0; card_index < num_cards + num_video_inputs; ++card_index) { CaptureCard *card = &cards[card_index]; if (input_card_is_master_clock(card_index, master_card_index)) { // We don't use the queue length policy for the master card, @@ -970,7 +998,7 @@ void Mixer::handle_hotplugged_cards() if (card->capture->get_disconnected()) { fprintf(stderr, "Card %u went away, replacing with a fake card.\n", card_index); FakeCapture *capture = new FakeCapture(global_flags.width, global_flags.height, FAKE_FPS, OUTPUT_FREQUENCY, card_index, global_flags.fake_cards_audio); - configure_card(card_index, capture, /*is_fake_capture=*/true, /*output=*/nullptr); + configure_card(card_index, capture, CardType::FAKE_CAPTURE, /*output=*/nullptr); card->queue_length_policy.reset(card_index); card->capture->start_bm_capture(); } @@ -1000,7 +1028,7 @@ void Mixer::handle_hotplugged_cards() 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, /*output=*/nullptr); + configure_card(free_card_index, capture, CardType::LIVE_CARD, /*output=*/nullptr); 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(); @@ -1057,6 +1085,12 @@ void Mixer::render_one_frame(int64_t duration) theme_main_chain.setup_chain(); //theme_main_chain.chain->enable_phase_timing(true); + // The theme can't (or at least shouldn't!) call connect_signal() on + // each FFmpeg input, so we'll do it here. + for (const pair &conn : theme->get_signal_connections()) { + conn.first->connect_signal_raw(conn.second->get_card_index()); + } + // If HDMI/SDI output is active and the user has requested auto mode, // its mode overrides the existing Y'CbCr setting for the chain. YCbCrLumaCoefficients ycbcr_output_coefficients;