From 9b641abb4dfc4494d02fdcb6d2c867148ab8f08f Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Fri, 1 Apr 2016 23:00:50 +0200 Subject: [PATCH] Allow setting the video and audio inputs runtime. --- bmusb | 2 +- decklink_capture.cpp | 77 +++++++++++++++++++++++++++++++++++++++----- decklink_capture.h | 16 +++++++++ glwidget.cpp | 50 ++++++++++++++++++++++++++-- mixer.h | 30 +++++++++++++++++ 5 files changed, 164 insertions(+), 11 deletions(-) diff --git a/bmusb b/bmusb index 5b54cc5..a5e46ba 160000 --- a/bmusb +++ b/bmusb @@ -1 +1 @@ -Subproject commit 5b54cc5acb28f5d2071c99ef7a715044aa30f10c +Subproject commit a5e46ba4b478f7fae63f3032e574208c0e69cc11 diff --git a/decklink_capture.cpp b/decklink_capture.cpp index 80aedc1..df8b668 100644 --- a/decklink_capture.cpp +++ b/decklink_capture.cpp @@ -152,23 +152,64 @@ DeckLinkCapture::DeckLinkCapture(IDeckLink *card, int card_index) exit(1); } - /* Set up the video and audio sources. */ - IDeckLinkConfiguration *config; - if (card->QueryInterface(IID_IDeckLinkConfiguration, (void**)&config) != S_OK) { - fprintf(stderr, "Failed to get configuration interface for card %d\n", card_index); + IDeckLinkAttributes *attr; + if (card->QueryInterface(IID_IDeckLinkAttributes, (void**)&attr) != S_OK) { + fprintf(stderr, "Card %d has no attributes\n", card_index); exit(1); } - if (config->SetInt(bmdDeckLinkConfigVideoInputConnection, bmdVideoConnectionHDMI) != S_OK) { - fprintf(stderr, "Failed to set video input connection for card %d\n", card_index); + // Get the list of available video inputs. + int64_t video_input_mask; + if (attr->GetInt(BMDDeckLinkVideoInputConnections, &video_input_mask) != S_OK) { + fprintf(stderr, "Failed to enumerate video inputs for card %d\n", card_index); exit(1); } + const vector> video_input_types = { + { bmdVideoConnectionSDI, "SDI" }, + { bmdVideoConnectionHDMI, "HDMI" }, + { bmdVideoConnectionOpticalSDI, "Optical SDI" }, + { bmdVideoConnectionComponent, "Component" }, + { bmdVideoConnectionComposite, "Composite" }, + { bmdVideoConnectionSVideo, "S-Video" } + }; + for (const auto &video_input : video_input_types) { + if (video_input_mask & video_input.first) { + video_inputs.emplace(video_input.first, video_input.second); + } + } - if (config->SetInt(bmdDeckLinkConfigAudioInputConnection, bmdAudioConnectionEmbedded) != S_OK) { - fprintf(stderr, "Failed to set video input connection for card %d\n", card_index); + // And then the available audio inputs. + int64_t audio_input_mask; + if (attr->GetInt(BMDDeckLinkAudioInputConnections, &audio_input_mask) != S_OK) { + fprintf(stderr, "Failed to enumerate audio inputs for card %d\n", card_index); + exit(1); + } + const vector> audio_input_types = { + { bmdAudioConnectionEmbedded, "Embedded" }, + { bmdAudioConnectionAESEBU, "AES/EBU" }, + { bmdAudioConnectionAnalog, "Analog" }, + { bmdAudioConnectionAnalogXLR, "Analog XLR" }, + { bmdAudioConnectionAnalogRCA, "Analog RCA" }, + { bmdAudioConnectionMicrophone, "Microphone" }, + { bmdAudioConnectionHeadphones, "Headphones" } + }; + for (const auto &audio_input : audio_input_types) { + if (audio_input_mask & audio_input.first) { + audio_inputs.emplace(audio_input.first, audio_input.second); + } + } + + attr->Release(); + + /* Set up the video and audio sources. */ + if (card->QueryInterface(IID_IDeckLinkConfiguration, (void**)&config) != S_OK) { + fprintf(stderr, "Failed to get configuration interface for card %d\n", card_index); exit(1); } + set_video_input(bmdVideoConnectionHDMI); + set_audio_input(bmdAudioConnectionEmbedded); + IDeckLinkDisplayModeIterator *mode_it; if (input->GetDisplayModeIterator(&mode_it) != S_OK) { fprintf(stderr, "Failed to enumerate display modes for card %d\n", card_index); @@ -404,3 +445,23 @@ void DeckLinkCapture::set_video_mode_no_restart(uint32_t video_mode_id) current_video_mode = video_mode_id; } + +void DeckLinkCapture::set_video_input(uint32_t video_input_id) +{ + if (config->SetInt(bmdDeckLinkConfigVideoInputConnection, video_input_id) != S_OK) { + fprintf(stderr, "Failed to set video input connection for card %d\n", card_index); + exit(1); + } + + current_video_input = video_input_id; +} + +void DeckLinkCapture::set_audio_input(uint32_t audio_input_id) +{ + if (config->SetInt(bmdDeckLinkConfigAudioInputConnection, audio_input_id) != S_OK) { + fprintf(stderr, "Failed to set audio input connection for card %d\n", card_index); + exit(1); + } + + current_audio_input = audio_input_id; +} diff --git a/decklink_capture.h b/decklink_capture.h index 322adb5..69ab08e 100644 --- a/decklink_capture.h +++ b/decklink_capture.h @@ -79,6 +79,14 @@ public: void set_video_mode(uint32_t video_mode_id) override; uint32_t get_current_video_mode() const override { return current_video_mode; } + std::map get_available_video_inputs() const override { return video_inputs; } + void set_video_input(uint32_t video_input_id) override; + uint32_t get_current_video_input() const override { return current_video_input; } + + std::map get_available_audio_inputs() const override { return audio_inputs; } + void set_audio_input(uint32_t audio_input_id) override; + uint32_t get_current_audio_input() const override { return current_audio_input; } + private: void set_video_mode_no_restart(uint32_t video_mode_id); @@ -96,12 +104,20 @@ private: FrameAllocator *audio_frame_allocator = nullptr; frame_callback_t frame_callback = nullptr; + IDeckLinkConfiguration *config = nullptr; + IDeckLinkInput *input = nullptr; BMDTimeValue frame_duration; BMDTimeScale time_scale; std::map video_modes; BMDDisplayMode current_video_mode; + + std::map video_inputs; + BMDVideoConnection current_video_input; + + std::map audio_inputs; + BMDAudioConnection current_audio_input; }; #endif // !defined(_DECKLINK_CAPTURE_H) diff --git a/glwidget.cpp b/glwidget.cpp index 5a13258..e8c2416 100644 --- a/glwidget.cpp +++ b/glwidget.cpp @@ -138,8 +138,47 @@ void GLWidget::show_context_menu(unsigned signal_num, const QPoint &pos) card_submenu.setTitle("Input source"); menu.addMenu(&card_submenu); - // Add a submenu for selecting resolution, with an action for each resolution. - // Note that the choice depends a lot on which card is active. + // --- The choices in the next few options depend a lot on which card is active --- + + // Add a submenu for selecting video input, with an action for each input. + QMenu video_input_submenu; + QActionGroup video_input_group(&video_input_submenu); + std::map video_inputs = global_mixer->get_available_video_inputs(current_card); + uint32_t current_video_input = global_mixer->get_current_video_input(current_card); + for (const auto &mode : video_inputs) { + QString description(QString::fromStdString(mode.second)); + QAction *action = new QAction(description, &video_input_group); + action->setCheckable(true); + if (mode.first == current_video_input) { + action->setChecked(true); + } + action->setData(QList{"video_input", mode.first}); + video_input_submenu.addAction(action); + } + + video_input_submenu.setTitle("Video input"); + menu.addMenu(&video_input_submenu); + + // The same for audio input. + QMenu audio_input_submenu; + QActionGroup audio_input_group(&audio_input_submenu); + std::map audio_inputs = global_mixer->get_available_audio_inputs(current_card); + uint32_t current_audio_input = global_mixer->get_current_audio_input(current_card); + for (const auto &mode : audio_inputs) { + QString description(QString::fromStdString(mode.second)); + QAction *action = new QAction(description, &audio_input_group); + action->setCheckable(true); + if (mode.first == current_audio_input) { + action->setChecked(true); + } + action->setData(QList{"audio_input", mode.first}); + audio_input_submenu.addAction(action); + } + + audio_input_submenu.setTitle("Audio input"); + menu.addMenu(&audio_input_submenu); + + // The same for resolution. QMenu mode_submenu; QActionGroup mode_group(&mode_submenu); std::map video_modes = global_mixer->get_available_video_modes(current_card); @@ -172,6 +211,7 @@ void GLWidget::show_context_menu(unsigned signal_num, const QPoint &pos) mode_submenu.setTitle("Input mode"); menu.addMenu(&mode_submenu); + // Add an audio source selector. QAction *audio_source_action = new QAction("Use as audio source", &menu); audio_source_action->setCheckable(true); @@ -194,6 +234,12 @@ void GLWidget::show_context_menu(unsigned signal_num, const QPoint &pos) } else { global_mixer->set_video_mode(current_card, mode); } + } else if (selected[0].toString() == "video_input") { + uint32_t input = selected[1].toUInt(nullptr); + global_mixer->set_video_input(current_card, input); + } else if (selected[0].toString() == "audio_input") { + uint32_t input = selected[1].toUInt(nullptr); + global_mixer->set_audio_input(current_card, input); } else if (selected[0].toString() == "card") { unsigned card_index = selected[1].toUInt(nullptr); global_mixer->set_signal_mapping(signal_num, card_index); diff --git a/mixer.h b/mixer.h index 061b382..836420d 100644 --- a/mixer.h +++ b/mixer.h @@ -258,6 +258,36 @@ public: void start_mode_scanning(unsigned card_index); + std::map get_available_video_inputs(unsigned card_index) const { + assert(card_index < num_cards); + return cards[card_index].capture->get_available_video_inputs(); + } + + uint32_t get_current_video_input(unsigned card_index) const { + assert(card_index < num_cards); + return cards[card_index].capture->get_current_video_input(); + } + + void set_video_input(unsigned card_index, uint32_t input) { + assert(card_index < num_cards); + cards[card_index].capture->set_video_input(input); + } + + std::map get_available_audio_inputs(unsigned card_index) const { + assert(card_index < num_cards); + return cards[card_index].capture->get_available_audio_inputs(); + } + + uint32_t get_current_audio_input(unsigned card_index) const { + assert(card_index < num_cards); + return cards[card_index].capture->get_current_audio_input(); + } + + void set_audio_input(unsigned card_index, uint32_t input) { + assert(card_index < num_cards); + cards[card_index].capture->set_audio_input(input); + } + private: void configure_card(unsigned card_index, const QSurfaceFormat &format, CaptureInterface *capture); void bm_frame(unsigned card_index, uint16_t timecode, -- 2.39.2