X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=mixer.cpp;h=1a76ac39c5c4226bab61b22b7af0f0ae0f841c18;hb=ce9d5f1c2ef4b6535e5fb9ea0265ed2a948f15d6;hp=80426c2c8db8b62afaa03d8957102f14024016c6;hpb=dc508afd3b72b3c4212753c9a3e90f14d21f460e;p=nageru diff --git a/mixer.cpp b/mixer.cpp index 80426c2..1a76ac3 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" @@ -88,7 +89,7 @@ void ensure_texture_resolution(PBOFrameAllocator::Userdata *userdata, unsigned f case bmusb::PixelFormat_8BitYCbCr: first = userdata->tex_y[field] == 0 || userdata->tex_cbcr[field] == 0; break; - case bmusb::PixelFormat_8BitRGBA: + case bmusb::PixelFormat_8BitBGRA: first = userdata->tex_rgba[field] == 0; break; default: @@ -121,13 +122,13 @@ void ensure_texture_resolution(PBOFrameAllocator::Userdata *userdata, unsigned f check_error(); break; } - case bmusb::PixelFormat_8BitRGBA: + case bmusb::PixelFormat_8BitBGRA: glBindTexture(GL_TEXTURE_2D, userdata->tex_rgba[field]); check_error(); 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); + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); } check_error(); break; @@ -205,13 +206,16 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) mixer_surface(create_surface(format)), h264_encoder_surface(create_surface(format)), decklink_output_surface(create_surface(format)), + ycbcr_interpretation(global_flags.ycbcr_interpretation), audio_mixer(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"); + global_flags.can_disable_srgb_decoder = + epoxy_has_gl_extension("GL_EXT_texture_sRGB_decode") && + epoxy_has_gl_extension("GL_ARB_sampler_objects"); // Since we allow non-bouncing 4:2:2 YCbCrInputs, effective subpixel precision // will be halved when sampling them, and we need to compensate here. @@ -306,10 +310,23 @@ Mixer::Mixer(const QSurfaceFormat &format, unsigned num_cards) 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); } @@ -357,7 +374,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. @@ -387,7 +404,14 @@ void Mixer::configure_card(unsigned card_index, CaptureInterface *capture, CardT 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_8BitBGRA; + } 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) { @@ -639,9 +663,9 @@ void Mixer::bm_frame(unsigned card_index, uint16_t timecode, upload_texture(userdata->tex_cbcr[field], cbcr_width, video_format.height, cbcr_width * sizeof(uint16_t), interlaced_stride, GL_RG, GL_UNSIGNED_BYTE, field_cbcr_start); break; } - case bmusb::PixelFormat_8BitRGBA: { + case bmusb::PixelFormat_8BitBGRA: { size_t field_start = video_offset + video_format.stride * field_start_line; - upload_texture(userdata->tex_rgba[field], video_format.width, video_format.height, video_format.stride, interlaced_stride, GL_RGBA, GL_UNSIGNED_BYTE, field_start); + upload_texture(userdata->tex_rgba[field], video_format.width, video_format.height, video_format.stride, interlaced_stride, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, field_start); break; } default: @@ -704,7 +728,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(); } @@ -747,7 +771,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; } @@ -768,7 +792,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; @@ -935,7 +959,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, @@ -1058,12 +1082,29 @@ void Mixer::render_one_frame(int64_t duration) printf("Timecode: '%s'\n", timecode_text.c_str()); } + // Update Y'CbCr settings for all cards. + { + unique_lock lock(card_mutex); + for (unsigned card_index = 0; card_index < num_cards; ++card_index) { + YCbCrInterpretation *interpretation = &ycbcr_interpretation[card_index]; + input_state.ycbcr_coefficients_auto[card_index] = interpretation->ycbcr_coefficients_auto; + input_state.ycbcr_coefficients[card_index] = interpretation->ycbcr_coefficients; + input_state.full_range[card_index] = interpretation->full_range; + } + } + // Get the main chain from the theme, and set its state immediately. Theme::Chain theme_main_chain = theme->get_chain(0, pts(), global_flags.width, global_flags.height, input_state); EffectChain *chain = theme_main_chain.chain; 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; @@ -1253,6 +1294,18 @@ void Mixer::channel_clicked(int preview_num) theme->channel_clicked(preview_num); } +YCbCrInterpretation Mixer::get_input_ycbcr_interpretation(unsigned card_index) const +{ + unique_lock lock(card_mutex); + return ycbcr_interpretation[card_index]; +} + +void Mixer::set_input_ycbcr_interpretation(unsigned card_index, const YCbCrInterpretation &interpretation) +{ + unique_lock lock(card_mutex); + ycbcr_interpretation[card_index] = interpretation; +} + void Mixer::start_mode_scanning(unsigned card_index) { assert(card_index < num_cards); @@ -1298,10 +1351,12 @@ void Mixer::OutputChannel::output_frame(DisplayFrame frame) } ready_frame = frame; has_ready_frame = true; - } - if (new_frame_ready_callback) { - new_frame_ready_callback(); + // Call the callbacks under the mutex (they should be short), + // so that we don't race against a callback removal. + for (const auto &key_and_callback : new_frame_ready_callbacks) { + key_and_callback.second(); + } } // Reduce the number of callbacks by filtering duplicates. The reason @@ -1368,9 +1423,16 @@ bool Mixer::OutputChannel::get_display_frame(DisplayFrame *frame) return true; } -void Mixer::OutputChannel::set_frame_ready_callback(Mixer::new_frame_ready_callback_t callback) +void Mixer::OutputChannel::add_frame_ready_callback(void *key, Mixer::new_frame_ready_callback_t callback) +{ + unique_lock lock(frame_mutex); + new_frame_ready_callbacks[key] = callback; +} + +void Mixer::OutputChannel::remove_frame_ready_callback(void *key) { - new_frame_ready_callback = callback; + unique_lock lock(frame_mutex); + new_frame_ready_callbacks.erase(key); } void Mixer::OutputChannel::set_transition_names_updated_callback(Mixer::transition_names_updated_callback_t callback)