From e756132309a6822fe76d18c22f7c3ed4640fd02e Mon Sep 17 00:00:00 2001 From: Dimiden Date: Mon, 20 Jan 2025 13:53:30 +0900 Subject: [PATCH] Added default playlist to the stream info API --- .../apps/streams/streams_controller.cpp | 53 ++++++++----------- src/projects/base/info/playlist.h | 16 +++++- src/projects/base/publisher/stream.cpp | 12 +++++ src/projects/base/publisher/stream.h | 38 ++++++++++++- .../output_profiles/playlist/playlist.h | 2 +- .../providers/multiplex/multiplex_profile.cpp | 4 +- src/projects/providers/ovt/ovt_stream.cpp | 2 +- src/projects/publishers/hls/hls_stream.cpp | 26 +++++++-- src/projects/publishers/hls/hls_stream.h | 8 ++- .../publishers/llhls/llhls_stream.cpp | 33 ++++++++---- src/projects/publishers/llhls/llhls_stream.h | 9 ++-- src/projects/publishers/srt/srt_publisher.cpp | 3 +- src/projects/publishers/srt/srt_stream.cpp | 22 ++++++-- src/projects/publishers/srt/srt_stream.h | 8 ++- src/projects/publishers/webrtc/rtc_stream.cpp | 17 +++++- src/projects/publishers/webrtc/rtc_stream.h | 6 +++ 16 files changed, 194 insertions(+), 65 deletions(-) diff --git a/src/projects/api_server/controllers/v1/vhosts/apps/streams/streams_controller.cpp b/src/projects/api_server/controllers/v1/vhosts/apps/streams/streams_controller.cpp index 01b7ed76c..e5c05f246 100644 --- a/src/projects/api_server/controllers/v1/vhosts/apps/streams/streams_controller.cpp +++ b/src/projects/api_server/controllers/v1/vhosts/apps/streams/streams_controller.cpp @@ -8,12 +8,13 @@ //============================================================================== #include "streams_controller.h" -#include #include +#include + #include -#include "stream_actions_controller.h" #include "../../../../../api_private.h" +#include "stream_actions_controller.h" namespace api { @@ -100,7 +101,7 @@ namespace api properties->EnableIgnoreRtcpSRTimestamp(jv_properties["ignoreRtcpSRTimestamp"].asBool()); } } - + logti("Request to pull stream: %s/%s - persistent(%s) noInputFailoverTimeoutMs(%d) unusedStreamDeletionTimeoutMs(%d) ignoreRtcpSRTimestamp(%s)", app->GetVHostAppName().CStr(), stream_name.CStr(), properties->IsPersistent() ? "true" : "false", properties->GetNoInputFailoverTimeout(), properties->GetUnusedStreamDeletionTimeout(), properties->IsRtcpSRTimestampIgnored() ? "true" : "false"); for (auto &url : request_urls) { @@ -164,42 +165,32 @@ namespace api const std::shared_ptr &app, const std::shared_ptr &stream, const std::vector> &output_streams) { - // Update llhls playlist from the LLHLS stream auto orchestrator = ocst::Orchestrator::GetInstance(); auto app_name = app->GetVHostAppName(); auto stream_name = stream->GetName(); - auto publisher = orchestrator->GetPublisherFromType(PublisherType::LLHls); - if (publisher) + // Get default playlist from the publishers + for ( + auto publisher_type = PublisherType::Webrtc; + publisher_type < PublisherType::NumberOfPublishers; + publisher_type = static_cast(ov::ToUnderlyingType(publisher_type) + 1)) { - - for (auto &output_stream : output_streams) - { - auto llhls_stream = publisher->GetStream(app->GetId(), output_stream->GetId()); - if (llhls_stream) - { - auto llhls_playlist = llhls_stream->GetPlaylist("llhls"); - if (llhls_playlist) - { - output_stream->AddPlaylist(std::make_shared(*llhls_playlist)); - } - } - } - } + auto publisher = orchestrator->GetPublisherFromType(publisher_type); - // update webrtc_default playlist from the WebRTC stream - publisher = orchestrator->GetPublisherFromType(PublisherType::Webrtc); - if (publisher) - { - for (auto &output_stream : output_streams) + if (publisher != nullptr) { - auto webrtc_stream = publisher->GetStream(app->GetId(), output_stream->GetId()); - if (webrtc_stream) + for (auto &output_stream : output_streams) { - auto webrtc_playlist = webrtc_stream->GetPlaylist("webrtc_default"); - if (webrtc_playlist) + auto stream = publisher->GetStream(app->GetId(), output_stream->GetId()); + + if (stream != nullptr) { - output_stream->AddPlaylist(std::make_shared(*webrtc_playlist)); + auto playlist = stream->GetDefaultPlaylist(); + + if (playlist != nullptr) + { + output_stream->AddPlaylist(std::make_shared(*playlist)); + } } } } @@ -221,7 +212,7 @@ namespace api auto code = orchestrator->TerminateStream(app_name, stream_name); auto http_code = http::StatusCodeFromCommonError(code); if (http_code != http::StatusCode::OK) - { + { throw http::HttpError(http_code, "Could not terminate the stream"); } diff --git a/src/projects/base/info/playlist.h b/src/projects/base/info/playlist.h index 670d00c94..625cee73a 100644 --- a/src/projects/base/info/playlist.h +++ b/src/projects/base/info/playlist.h @@ -74,10 +74,11 @@ namespace info class Playlist { public: - Playlist(const ov::String &name, const ov::String &file_name) + Playlist(const ov::String &name, const ov::String &file_name, bool is_default) { _name = name; _file_name = file_name; + _is_default = is_default; } ~Playlist() = default; @@ -86,6 +87,7 @@ namespace info { _name = other._name; _file_name = other._file_name; + _is_default = other._is_default; _webrtc_auto_abr = other._webrtc_auto_abr; _hls_chunklist_path_depth = other._hls_chunklist_path_depth; _enable_ts_packaging = other._enable_ts_packaging; @@ -143,6 +145,11 @@ namespace info return _file_name; } + bool IsDefault() const + { + return _is_default; + } + // Get Rendition List const std::vector> &GetRenditionList() const { @@ -162,6 +169,11 @@ namespace info return false; } + if (_is_default != rhs._is_default) + { + return false; + } + if (_webrtc_auto_abr != rhs._webrtc_auto_abr) { return false; @@ -203,6 +215,8 @@ namespace info ov::String _name; ov::String _file_name; + bool _is_default = false; + bool _webrtc_auto_abr = false; int _hls_chunklist_path_depth = -1; bool _enable_ts_packaging = false; diff --git a/src/projects/base/publisher/stream.cpp b/src/projects/base/publisher/stream.cpp index 5af1119e3..d488f1fc3 100644 --- a/src/projects/base/publisher/stream.cpp +++ b/src/projects/base/publisher/stream.cpp @@ -202,6 +202,18 @@ namespace pub { } + std::shared_ptr Stream::GetDefaultPlaylist() const + { + auto info = GetDefaultPlaylistInfo(); + + if (info != nullptr) + { + return GetPlaylist(info->file_name); + } + + return nullptr; + } + bool Stream::Start() { if (_state != State::CREATED) diff --git a/src/projects/base/publisher/stream.h b/src/projects/base/publisher/stream.h index 932bbed53..eb40a6d55 100644 --- a/src/projects/base/publisher/stream.h +++ b/src/projects/base/publisher/stream.h @@ -68,7 +68,6 @@ namespace pub class Stream : public info::Stream, public ov::EnableSharedFromThis { public: - // Create stream --> Start stream --> Stop stream --> Delete stream enum class State : uint8_t { @@ -78,6 +77,43 @@ namespace pub ERROR, }; + struct DefaultPlaylistInfo + { + // Playlist name + // + // For example, in LL-HLS, the value is "llhls_default" + ov::String name; + + // Playlist file name, used to retrieve a playlist from the Stream, such as with GetPlaylist() + // + // For example, in LL-HLS, the value is "llhls" + ov::String file_name; + + // Used internally by the stream of publisher to distinguish playlists based on file names + // (e.g., when caching the master playlist in LLHlsStream) + // + // For example, in LL-HLS, the value is "llhls.m3u8" + ov::String internal_file_name; + + DefaultPlaylistInfo( + const ov::String &name, + const ov::String &file_name, + const ov::String &internal_file_name) + : name(name), + file_name(file_name), + internal_file_name(internal_file_name) + { + } + }; + + public: + virtual std::shared_ptr GetDefaultPlaylistInfo() const + { + return nullptr; + } + + std::shared_ptr GetDefaultPlaylist() const; + // Session을 추가한다. bool AddSession(std::shared_ptr session); bool RemoveSession(session_id_t id); diff --git a/src/projects/config/items/virtual_hosts/applications/output_profiles/playlist/playlist.h b/src/projects/config/items/virtual_hosts/applications/output_profiles/playlist/playlist.h index 60272b963..cc107775f 100644 --- a/src/projects/config/items/virtual_hosts/applications/output_profiles/playlist/playlist.h +++ b/src/projects/config/items/virtual_hosts/applications/output_profiles/playlist/playlist.h @@ -38,7 +38,7 @@ namespace cfg // Copy cfg to info std::shared_ptr GetPlaylistInfo() const { - auto playlist = std::make_shared(GetName(), GetFileName()); + auto playlist = std::make_shared(GetName(), GetFileName(), false); playlist->SetHlsChunklistPathDepth(_options.GetHlsChunklistPathDepth()); playlist->SetWebRtcAutoAbr(_options.IsWebRtcAutoAbr()); playlist->EnableTsPackaging(_options.IsTsPackagingEnabled()); diff --git a/src/projects/providers/multiplex/multiplex_profile.cpp b/src/projects/providers/multiplex/multiplex_profile.cpp index 139eebc32..4aff0ab10 100644 --- a/src/projects/providers/multiplex/multiplex_profile.cpp +++ b/src/projects/providers/multiplex/multiplex_profile.cpp @@ -243,7 +243,7 @@ namespace pvd return false; } - auto playlist = std::make_shared(name_object.asString().c_str(), file_name_object.asString().c_str()); + auto playlist = std::make_shared(name_object.asString().c_str(), file_name_object.asString().c_str(), false); // Options auto options_object = playlist_object["options"]; @@ -406,7 +406,7 @@ namespace pvd return false; } - auto playlist = std::make_shared(playlist_name_node.text().as_string(), file_name_node.text().as_string()); + auto playlist = std::make_shared(playlist_name_node.text().as_string(), file_name_node.text().as_string(), false); // Options auto options_node = playlist_node.child("Options"); diff --git a/src/projects/providers/ovt/ovt_stream.cpp b/src/projects/providers/ovt/ovt_stream.cpp index cd7a1d417..316dc4a02 100644 --- a/src/projects/providers/ovt/ovt_stream.cpp +++ b/src/projects/providers/ovt/ovt_stream.cpp @@ -353,7 +353,7 @@ namespace pvd ov::String playlist_name = json_playlist["name"].asString().c_str(); ov::String playlist_file_name = json_playlist["fileName"].asString().c_str(); - auto playlist = std::make_shared(playlist_name, playlist_file_name); + auto playlist = std::make_shared(playlist_name, playlist_file_name, false); // Options auto json_options = json_playlist["options"]; diff --git a/src/projects/publishers/hls/hls_stream.cpp b/src/projects/publishers/hls/hls_stream.cpp index bd1c7a4d4..9fcc87cf4 100755 --- a/src/projects/publishers/hls/hls_stream.cpp +++ b/src/projects/publishers/hls/hls_stream.cpp @@ -66,6 +66,23 @@ HlsStream::~HlsStream() logtd("TsStream(%s/%s) has been terminated finally", GetApplicationName(), GetName().CStr()); } +std::shared_ptr HlsStream::GetDefaultPlaylistInfo() const +{ + // Since the same value is always stored in info, it is not an issue + // even if multiple instances of info are created due to a race conditio in multi-threading + static auto info = []() -> std::shared_ptr { + ov::String file_name = "playlist.m3u8"; + auto file_name_without_ext = file_name.Substring(0, file_name.IndexOfRev('.')); + + return std::make_shared( + "hls_default", + file_name_without_ext, + file_name); + }(); + + return info; +} + ov::String HlsStream::GetStreamId() const { return ov::String::FormatString("hlsv3/%s", GetUri().CStr()); @@ -197,12 +214,13 @@ bool HlsStream::CreateDefaultPlaylist() } // Create default playlist - ov::String default_playlist_name = TS_HLS_DEFAULT_PLAYLIST_NAME; - auto default_playlist_name_without_ext = default_playlist_name.Substring(0, default_playlist_name.IndexOfRev('.')); - auto default_playlist = Stream::GetPlaylist(default_playlist_name_without_ext); + auto default_playlist_info = GetDefaultPlaylistInfo(); + OV_ASSERT2(default_playlist_info != nullptr); + + auto default_playlist = Stream::GetPlaylist(default_playlist_info->file_name); if (default_playlist == nullptr) { - auto playlist = std::make_shared("hls_default", default_playlist_name_without_ext); + auto playlist = std::make_shared(default_playlist_info->name, default_playlist_info->file_name, true); auto rendition = std::make_shared("default", first_video_track ? first_video_track->GetVariantName() : "", first_audio_track ? first_audio_track->GetVariantName() : ""); playlist->AddRendition(rendition); diff --git a/src/projects/publishers/hls/hls_stream.h b/src/projects/publishers/hls/hls_stream.h index 49f2906e4..8b7c30cc1 100755 --- a/src/projects/publishers/hls/hls_stream.h +++ b/src/projects/publishers/hls/hls_stream.h @@ -19,8 +19,6 @@ #include "hls_master_playlist.h" #include "hls_media_playlist.h" -#define TS_HLS_DEFAULT_PLAYLIST_NAME "playlist.m3u8" - // max initial media packet buffer size, for OOM protection #define MAX_INITIAL_MEDIA_PACKET_BUFFER_SIZE 10000 @@ -34,6 +32,12 @@ class HlsStream final : public pub::Stream, public mpegts::PackagerSink explicit HlsStream(const std::shared_ptr application, const info::Stream &info, uint32_t worker_count); ~HlsStream() final; + //-------------------------------------------------------------------- + // Implementation of info::Stream + //-------------------------------------------------------------------- + std::shared_ptr GetDefaultPlaylistInfo() const override; + //-------------------------------------------------------------------- + void SendVideoFrame(const std::shared_ptr &media_packet) override; void SendAudioFrame(const std::shared_ptr &media_packet) override; void SendDataFrame(const std::shared_ptr &media_packet) override; diff --git a/src/projects/publishers/llhls/llhls_stream.cpp b/src/projects/publishers/llhls/llhls_stream.cpp index 9c7586e22..6b1ce0eed 100755 --- a/src/projects/publishers/llhls/llhls_stream.cpp +++ b/src/projects/publishers/llhls/llhls_stream.cpp @@ -9,16 +9,15 @@ #include "llhls_stream.h" #include +#include +#include #include #include -#include -#include - #include "llhls_application.h" -#include "llhls_session.h" #include "llhls_private.h" +#include "llhls_session.h" std::shared_ptr LLHlsStream::Create(const std::shared_ptr application, const info::Stream &info, uint32_t worker_count) { @@ -41,6 +40,21 @@ ov::String LLHlsStream::GetStreamId() const return ov::String::FormatString("llhls/%s", GetUri().CStr()); } +std::shared_ptr LLHlsStream::GetDefaultPlaylistInfo() const +{ + static auto info = []() -> std::shared_ptr { + ov::String file_name = "llhls.m3u8"; + auto file_name_without_ext = file_name.Substring(0, file_name.IndexOfRev('.')); + + return std::make_shared( + "llhls_default", + file_name_without_ext, + file_name); + }(); + + return info; +} + bool LLHlsStream::Start() { if (GetState() != State::CREATED) @@ -150,12 +164,13 @@ bool LLHlsStream::Start() { // If there is no default playlist, make default playlist // Default playlist is consist of first compatible video and audio track among all tracks - ov::String default_playlist_name = DEFAULT_PLAYLIST_NAME; - auto default_playlist_name_without_ext = default_playlist_name.Substring(0, default_playlist_name.IndexOfRev('.')); - auto default_playlist = Stream::GetPlaylist(default_playlist_name_without_ext); + auto default_playlist_info = GetDefaultPlaylistInfo(); + OV_ASSERT2(default_playlist_info != nullptr); + + auto default_playlist = Stream::GetPlaylist(default_playlist_info->file_name); if (default_playlist == nullptr) { - auto playlist = std::make_shared("llhls_default", default_playlist_name_without_ext); + auto playlist = std::make_shared(default_playlist_info->name, default_playlist_info->file_name, true); auto rendition = std::make_shared("default", first_video_track ? first_video_track->GetVariantName() : "", first_audio_track ? first_audio_track->GetVariantName() : ""); playlist->AddRendition(rendition); @@ -165,7 +180,7 @@ bool LLHlsStream::Start() auto master_playlist = CreateMasterPlaylist(playlist); std::lock_guard guard(_master_playlists_lock); - _master_playlists[default_playlist_name] = master_playlist; + _master_playlists[default_playlist_info->internal_file_name] = master_playlist; } } else diff --git a/src/projects/publishers/llhls/llhls_stream.h b/src/projects/publishers/llhls/llhls_stream.h index 85ec2a64e..94661b748 100755 --- a/src/projects/publishers/llhls/llhls_stream.h +++ b/src/projects/publishers/llhls/llhls_stream.h @@ -19,9 +19,6 @@ #include "llhls_master_playlist.h" #include "llhls_chunklist.h" -#define DEFAULT_PLAYLIST_NAME "llhls.m3u8" - - // max initial media packet buffer size, for OOM protection #define MAX_INITIAL_MEDIA_PACKET_BUFFER_SIZE 10000 @@ -37,6 +34,12 @@ class LLHlsStream final : public pub::Stream, public bmff::FMp4StorageObserver ov::String GetStreamId() const; + //-------------------------------------------------------------------- + // Implementation of info::Stream + //-------------------------------------------------------------------- + std::shared_ptr GetDefaultPlaylistInfo() const override; + //-------------------------------------------------------------------- + void SendVideoFrame(const std::shared_ptr &media_packet) override; void SendAudioFrame(const std::shared_ptr &media_packet) override; void SendDataFrame(const std::shared_ptr &media_packet) override; diff --git a/src/projects/publishers/srt/srt_publisher.cpp b/src/projects/publishers/srt/srt_publisher.cpp index 66339227d..a9ee35703 100644 --- a/src/projects/publishers/srt/srt_publisher.cpp +++ b/src/projects/publishers/srt/srt_publisher.cpp @@ -428,7 +428,8 @@ namespace pub if (playlist_name.IsEmpty()) { - playlist_name = DEFAULT_SRT_PLAYLIST_NAME; + // Use default playlist + playlist_name = stream->GetDefaultPlaylistInfo()->file_name; } srt_playlist = stream->GetSrtPlaylist(playlist_name); diff --git a/src/projects/publishers/srt/srt_stream.cpp b/src/projects/publishers/srt/srt_stream.cpp index 1052671f9..81b1118ac 100644 --- a/src/projects/publishers/srt/srt_stream.cpp +++ b/src/projects/publishers/srt/srt_stream.cpp @@ -49,15 +49,27 @@ namespace pub logad("SrtStream has been terminated finally"); } + std::shared_ptr SrtStream::GetDefaultPlaylistInfo() const + { + static auto info = std::make_shared( + "srt_default", + "srt_default", + "srt_default"); + + return info; + } + std::shared_ptr SrtStream::PrepareDefaultPlaylist() { - auto playlist_name = DEFAULT_SRT_PLAYLIST_NAME; - auto playlist = Stream::GetPlaylist(playlist_name); + auto default_playlist_info = GetDefaultPlaylistInfo(); + OV_ASSERT2(default_playlist_info != nullptr); + + auto playlist = Stream::GetPlaylist(default_playlist_info->file_name); if (playlist != nullptr) { // The playlist is already created - logaw("The playlist %s is already created", playlist_name); + logaw("The playlist %s is already created", default_playlist_info->file_name.CStr()); OV_ASSERT2(false); return playlist; } @@ -97,7 +109,7 @@ namespace pub return nullptr; } - auto new_playlist = std::make_shared(playlist_name, playlist_name); + auto new_playlist = std::make_shared(default_playlist_info->name, default_playlist_info->file_name, true); auto rendition = std::make_shared( "default", (first_video_track != nullptr) ? first_video_track->GetVariantName() : "", @@ -250,7 +262,7 @@ namespace pub for (const auto &[file_name, playlist] : GetPlaylists()) { - bool is_default_playlist = (file_name == DEFAULT_SRT_PLAYLIST_NAME); + bool is_default_playlist = playlist->IsDefault(); auto &rendition_list = playlist->GetRenditionList(); auto srt_playlist = GetSrtPlaylistInternal(file_name); diff --git a/src/projects/publishers/srt/srt_stream.h b/src/projects/publishers/srt/srt_stream.h index ce9db2faa..2de3272c4 100644 --- a/src/projects/publishers/srt/srt_stream.h +++ b/src/projects/publishers/srt/srt_stream.h @@ -14,8 +14,6 @@ #include "./srt_playlist.h" #include "monitoring/monitoring.h" -#define DEFAULT_SRT_PLAYLIST_NAME "playlist" - namespace pub { class SrtStream final : public Stream, public SrtPlaylistSink @@ -31,6 +29,12 @@ namespace pub std::shared_ptr GetSrtPlaylist(const ov::String &file_name); + //-------------------------------------------------------------------- + // Implementation of info::Stream + //-------------------------------------------------------------------- + std::shared_ptr GetDefaultPlaylistInfo() const override; + //-------------------------------------------------------------------- + //-------------------------------------------------------------------- // Overriding of Stream //-------------------------------------------------------------------- diff --git a/src/projects/publishers/webrtc/rtc_stream.cpp b/src/projects/publishers/webrtc/rtc_stream.cpp index b5761739e..7149b3770 100644 --- a/src/projects/publishers/webrtc/rtc_stream.cpp +++ b/src/projects/publishers/webrtc/rtc_stream.cpp @@ -107,6 +107,16 @@ RtcStream::~RtcStream() Stop(); } +std::shared_ptr RtcStream::GetDefaultPlaylistInfo() const +{ + static auto info = std::make_shared( + "webrtc_default", + "webrtc_default", + "webrtc_default"); + + return info; +} + bool RtcStream::Start() { if(GetState() != State::CREATED) @@ -190,12 +200,15 @@ bool RtcStream::Start() if (webrtc_config.ShouldCreateDefaultPlaylist() == true) { // Create Default Playlist for no file name (ws://domain/app/stream) - _default_playlist_name = "webrtc_default"; + auto default_playlist_info = GetDefaultPlaylistInfo(); + OV_ASSERT2(default_playlist_info != nullptr); + + _default_playlist_name = default_playlist_info->file_name; auto default_playlist = GetPlaylist(_default_playlist_name); if (default_playlist == nullptr) { - auto playlist = std::make_shared("webrtc_default", _default_playlist_name); + auto playlist = std::make_shared(default_playlist_info->name, _default_playlist_name, true); auto rendition = std::make_shared("default", _first_video_track ? _first_video_track->GetVariantName() : "", _first_audio_track ? _first_audio_track->GetVariantName() : ""); playlist->AddRendition(rendition); diff --git a/src/projects/publishers/webrtc/rtc_stream.h b/src/projects/publishers/webrtc/rtc_stream.h index 2d8e837d8..d031994f2 100644 --- a/src/projects/publishers/webrtc/rtc_stream.h +++ b/src/projects/publishers/webrtc/rtc_stream.h @@ -33,6 +33,12 @@ class RtcStream final : public pub::Stream, public RtpPacketizerInterface uint32_t worker_count); ~RtcStream() final; + //-------------------------------------------------------------------- + // Implementation of info::Stream + //-------------------------------------------------------------------- + std::shared_ptr GetDefaultPlaylistInfo() const override; + //-------------------------------------------------------------------- + std::shared_ptr GetSessionDescription(const ov::String &file_name); std::shared_ptr GetRtcPlaylist(const ov::String &file_name, cmn::MediaCodecId video_codec_id, cmn::MediaCodecId audio_codec_id);