From 5a1a88bf33c6cdd941268252a84eeba004cd2313 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sat, 28 Oct 2023 12:58:17 +0200 Subject: [PATCH] New support for AUDIO Allows looking for audio streams in media files (settings) and enabling / disabling audio for each MediaPlayer individually. Control of volume per media, saving in session file. --- src/ImGuiVisitor.cpp | 45 ++++++++++++++++++++++++++++++++++++ src/MediaPlayer.cpp | 43 ++++++++++++++++++++++++++++++++++ src/MediaPlayer.h | 13 +++++++++++ src/SessionCreator.cpp | 8 +++++++ src/SessionVisitor.cpp | 5 ++++ src/Settings.cpp | 2 ++ src/Settings.h | 4 ++++ src/SourceControlWindow.cpp | 12 +++++++++- src/UserInterfaceManager.cpp | 16 +++++++++---- 9 files changed, 142 insertions(+), 6 deletions(-) diff --git a/src/ImGuiVisitor.cpp b/src/ImGuiVisitor.cpp index b8ad1dd..5fa8551 100644 --- a/src/ImGuiVisitor.cpp +++ b/src/ImGuiVisitor.cpp @@ -743,9 +743,13 @@ void ImGuiVisitor::visit (MediaSource& s) { if (ImGui::Selectable( ICON_FA_MICROCHIP " / " ICON_FA_COGS " Auto select", &hwdec )) mp->setSoftwareDecodingForced(false); + if (ImGui::IsItemHovered() && !hwdec) + ImGuiToolkit::ToolTip( "Changing decoding will\nre-open the media" ); hwdec = mp->softwareDecodingForced(); if (ImGui::Selectable( ICON_FA_COGS " Software only", &hwdec )) mp->setSoftwareDecodingForced(true); + if (ImGui::IsItemHovered() && !hwdec) + ImGuiToolkit::ToolTip( "Changing decoding will\nre-open the media" ); ImGui::EndCombo(); } } @@ -756,6 +760,47 @@ void ImGuiVisitor::visit (MediaSource& s) ImGui::TextDisabled("Hardware decoding disabled"); } + // enable / disable audio if available + if (mp->audioAvailable()) { + + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + if (ImGui::BeginCombo("Audio", mp->audioEnabled() ? ICON_FA_VOLUME_UP " Enabled" : ICON_FA_VOLUME_MUTE " Disabled" ) ) + { + if (ImGui::Selectable( ICON_FA_VOLUME_UP " Enable", mp->audioEnabled() )) + mp->setAudioEnabled(true); + if (ImGui::IsItemHovered() && !mp->audioEnabled()) + ImGuiToolkit::ToolTip( "Changing audio will\nre-open the media" ); + + if (ImGui::Selectable( ICON_FA_VOLUME_MUTE " Disable", !mp->audioEnabled() )) + mp->setAudioEnabled(false); + if (ImGui::IsItemHovered() && mp->audioEnabled()) + ImGuiToolkit::ToolTip( "Changing audio will\nre-open the media" ); + ImGui::EndCombo(); + } + + if (mp->audioEnabled()) { + + ImGuiIO& io = ImGui::GetIO(); + /// + /// AUDIO VOLUME + /// + int vol = mp->audioVolume(); + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + if ( ImGui::SliderInt("##Volume", &vol, 0, 100, "%d%%") ) + mp->setAudioVolume(vol); + if (ImGui::IsItemHovered() && io.MouseWheel != 0.f ){ + vol = CLAMP(vol + int(10.f * io.MouseWheel), 0, 100); + mp->setAudioVolume(vol); + } + ImGui::SameLine(0, IMGUI_SAME_LINE); + if (ImGuiToolkit::TextButton("Volume")) { + mp->setAudioVolume(100); + } + } + + } + + } } diff --git a/src/MediaPlayer.cpp b/src/MediaPlayer.cpp index 38cf170..c5cf159 100644 --- a/src/MediaPlayer.cpp +++ b/src/MediaPlayer.cpp @@ -30,6 +30,7 @@ #include "BaseToolkit.h" #include "GstToolkit.h" #include "Metronome.h" +#include "Settings.h" #include "MediaPlayer.h" @@ -72,6 +73,10 @@ MediaPlayer::MediaPlayer() position_ = GST_CLOCK_TIME_NONE; loop_ = LoopMode::LOOP_REWIND; + // default audio disabled + audio_enabled_ = false; + audio_volume_ = 100; + // start index in frame_ stack write_index_ = 0; last_index_ = 0; @@ -241,6 +246,11 @@ MediaInfo MediaPlayer::UriDiscoverer(const std::string &uri) video_stream_info.log = "No video stream"; gst_discoverer_stream_info_list_free(streams); + + // test audio + GList *audios = gst_discoverer_info_get_audio_streams(info); + video_stream_info.hasaudio = g_list_length(audios) > 0 && Settings::application.accept_audio; + gst_discoverer_stream_info_list_free(audios); } if (info) @@ -347,6 +357,8 @@ void MediaPlayer::execute_open() gint flags; // ENABLE ONLY VIDEO, NOT AUDIO AND TEXT SUBTITLES flags = GST_PLAY_FLAG_VIDEO; + if (media_.hasaudio && audio_enabled_) + flags |= GST_PLAY_FLAG_AUDIO; // ENABLE DEINTERLACING if (media_.interlaced) flags |= GST_PLAY_FLAG_DEINTERLACE; @@ -467,6 +479,9 @@ void MediaPlayer::execute_open() Log::Info("MediaPlayer %s Timeline [%ld %ld] %ld frames, %d gaps", std::to_string(id_).c_str(), timeline_.begin(), timeline_.end(), timeline_.numFrames(), timeline_.numGaps()); + if (media_.hasaudio) + Log::Info("MediaPlayer %s Audio track %s", std::to_string(id_).c_str(), audio_enabled_ ? "enabled" : "disabled"); + opened_ = true; // register media player @@ -1660,6 +1675,34 @@ void MediaPlayer::TimeCounter::tic () } +void MediaPlayer::setAudioEnabled(bool on) +{ + // in case of change + if (audio_enabled_ != on) { + // toggle + audio_enabled_ = on; + + // if openned + if (media_.hasaudio ) { + // apply + reopen(); + // reset volume + setAudioVolume(audio_volume_); + } + } +} + +void MediaPlayer::setAudioVolume(int vol) +{ + // set value + audio_volume_ = CLAMP(vol, 0, 100); + + // apply value + if (pipeline_ && media_.hasaudio) + gst_stream_volume_set_volume (GST_STREAM_VOLUME (pipeline_), GST_STREAM_VOLUME_FORMAT_LINEAR, gdouble(audio_volume_) * 0.01); + +} + //static void audio_changed_callback (GstElement *pipeline, MediaPlayer *mp) //{ // gint n_audio; diff --git a/src/MediaPlayer.h b/src/MediaPlayer.h index f7e525c..d9e0f6d 100644 --- a/src/MediaPlayer.h +++ b/src/MediaPlayer.h @@ -37,6 +37,7 @@ struct MediaInfo { GstClockTime dt; GstClockTime end; std::string log; + bool hasaudio; MediaInfo() { width = par_width = 1; @@ -49,6 +50,7 @@ struct MediaInfo { interlaced = false; seekable = false; valid = false; + hasaudio = false; dt = GST_CLOCK_TIME_NONE; end = GST_CLOCK_TIME_NONE; log = ""; @@ -264,6 +266,15 @@ public: void setVideoEffect(const std::string &pipeline_element); inline std::string videoEffect() { return video_filter_; } inline bool videoEffectAvailable() { return video_filter_available_; } + /** + * Enables or disables audio + * NB: setAudioEnabled reopens the video + * */ + void setAudioEnabled(bool on); + void setAudioVolume(int vol); + inline bool audioEnabled() const { return audio_enabled_; } + inline int audioVolume() const { return audio_volume_; } + inline bool audioAvailable() const { return media_.hasaudio; } /** * Accept visitors * */ @@ -309,6 +320,8 @@ private: bool enabled_; bool rewind_on_disable_; bool force_software_decoding_; + bool audio_enabled_; + int audio_volume_; std::string decoder_name_; bool video_filter_available_; std::string video_filter_; diff --git a/src/SessionCreator.cpp b/src/SessionCreator.cpp index b88988c..3ebbbca 100644 --- a/src/SessionCreator.cpp +++ b/src/SessionCreator.cpp @@ -894,6 +894,14 @@ void SessionLoader::visit(MediaPlayer &n) n.setTimeline(tl); } + // audio + int audiovolume = 100; + mediaplayerNode->QueryIntAttribute("audio_volume", &audiovolume); + n.setAudioVolume(audiovolume); + bool audioenabled = false; + mediaplayerNode->QueryBoolAttribute("audio", &audioenabled); + n.setAudioEnabled(audioenabled); + // change play rate: will be activated in SessionLoader::visit (MediaSource& s) double speed = 1.0; mediaplayerNode->QueryDoubleAttribute("speed", &speed); diff --git a/src/SessionVisitor.cpp b/src/SessionVisitor.cpp index d5718a0..44a75d1 100644 --- a/src/SessionVisitor.cpp +++ b/src/SessionVisitor.cpp @@ -423,6 +423,11 @@ void SessionVisitor::visit(MediaPlayer &n) XMLElement *newelement = xmlDoc_->NewElement("MediaPlayer"); newelement->SetAttribute("id", n.id()); + if (n.audioAvailable()) { + newelement->SetAttribute("audio", n.audioEnabled()); + newelement->SetAttribute("audio_volume", n.audioVolume()); + } + if (!n.singleFrame()) { newelement->SetAttribute("loop", (int) n.loop()); newelement->SetAttribute("speed", n.playSpeed()); diff --git a/src/Settings.cpp b/src/Settings.cpp index 3840ead..fb38700 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -144,6 +144,7 @@ void Settings::Save(uint64_t runtime) applicationNode->SetAttribute("broadcast_port", application.broadcast_port); applicationNode->SetAttribute("loopback_camera", application.loopback_camera); applicationNode->SetAttribute("shm_socket_path", application.shm_socket_path.c_str()); + applicationNode->SetAttribute("accept_audio", application.accept_audio); pRoot->InsertEndChild(applicationNode); // Widgets @@ -437,6 +438,7 @@ void Settings::Load() applicationNode->QueryIntAttribute("stream_protocol", &application.stream_protocol); applicationNode->QueryIntAttribute("broadcast_port", &application.broadcast_port); applicationNode->QueryIntAttribute("loopback_camera", &application.loopback_camera); + applicationNode->QueryBoolAttribute("accept_audio", &application.accept_audio); // text attributes const char *tmpstr = applicationNode->Attribute("shm_socket_path"); diff --git a/src/Settings.h b/src/Settings.h index cf15898..c682db3 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -295,6 +295,9 @@ struct Application int shm_method; std::string shm_socket_path; + // audio + bool accept_audio; + // Settings of widgets WidgetsConfig widget; @@ -369,6 +372,7 @@ struct Application windows = std::vector(1+MAX_OUTPUT_WINDOW); windows[0].w = 1600; windows[0].h = 900; + accept_audio = true; } }; diff --git a/src/SourceControlWindow.cpp b/src/SourceControlWindow.cpp index db531fd..744376b 100644 --- a/src/SourceControlWindow.cpp +++ b/src/SourceControlWindow.cpp @@ -1511,7 +1511,11 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) ImGui::PopStyleColor(1); ImGui::SetCursorScreenPos(imgarea.GetTL() + ImVec2(h_space_, v_space_)); - ImGui::Text("%s", ms->initials()); + if ( mediaplayer_active_->audioEnabled()) + // Icon to inform audio decoding + ImGui::Text("%s " ICON_FA_VOLUME_UP, ms->initials()); + else + ImGui::Text("%s", ms->initials()); ImGui::PopFont(); } if (!magnifying_glass) { @@ -1532,6 +1536,12 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) ImGui::SetCursorScreenPos(imgarea.GetTL() + ImVec2(h_space_, v_space_)); ImGui::Text("%s", info_.str().c_str()); + // Icon to inform audio decoding + if ( mediaplayer_active_->audioEnabled()) { + ImGui::SetCursorScreenPos(imgarea.GetTL() + ImVec2( imgarea.GetWidth() - 2.f * ImGui::GetTextLineHeightWithSpacing(), 0.35f * tooltip_height)); + ImGui::Text(ICON_FA_VOLUME_UP); + } + // Icon to inform hardware decoding if ( mediaplayer_active_->decoderName().compare("software") != 0) { ImGui::SetCursorScreenPos(imgarea.GetTL() + ImVec2( imgarea.GetWidth() - ImGui::GetTextLineHeightWithSpacing(), 0.35f * tooltip_height)); diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index c01dd3d..8f5eba1 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -5330,15 +5330,13 @@ void Navigator::RenderMainPannelSettings() // System preferences // ImGuiToolkit::Spacing(); -// ImGuiToolkit::HelpMarker("If you encounter some rendering issues on your machine, " -// "you can try to disable some of the OpenGL optimizations below."); -// ImGui::SameLine(); ImGui::TextDisabled("System"); static bool need_restart = false; static bool vsync = (Settings::application.render.vsync > 0); static bool multi = (Settings::application.render.multisampling > 0); static bool gpu = Settings::application.render.gpu_decoding; + static bool audio = Settings::application.accept_audio; bool change = false; // hardware support deserves more explanation ImGuiToolkit::Indication("If enabled, tries to find a platform adapted hardware-accelerated " @@ -5349,14 +5347,21 @@ void Navigator::RenderMainPannelSettings() else ImGui::TextDisabled("Hardware en/decoding unavailable"); + // audio support deserves more explanation + ImGuiToolkit::Indication("If enabled, tries to find audio in openned videos " + "and allows enabling audio for each source.", ICON_FA_VOLUME_OFF); + ImGui::SameLine(0); + change |= ImGuiToolkit::ButtonSwitch( "Audio support", &audio); + #ifndef NDEBUG change |= ImGuiToolkit::ButtonSwitch( "Vertical synchronization", &vsync); change |= ImGuiToolkit::ButtonSwitch( "Multisample antialiasing", &multi); #endif if (change) { need_restart = ( vsync != (Settings::application.render.vsync > 0) || - multi != (Settings::application.render.multisampling > 0) || - gpu != Settings::application.render.gpu_decoding ); + multi != (Settings::application.render.multisampling > 0) || + gpu != Settings::application.render.gpu_decoding || + audio != Settings::application.accept_audio ); } if (need_restart) { ImGuiToolkit::Spacing(); @@ -5364,6 +5369,7 @@ void Navigator::RenderMainPannelSettings() Settings::application.render.vsync = vsync ? 1 : 0; Settings::application.render.multisampling = multi ? 3 : 0; Settings::application.render.gpu_decoding = gpu; + Settings::application.accept_audio = audio; if (UserInterface::manager().TryClose()) Rendering::manager().close(); }