7 #include <packager/mpd/base/period.h>
9 #include <absl/log/check.h>
10 #include <absl/log/log.h>
12 #include <packager/mpd/base/adaptation_set.h>
13 #include <packager/mpd/base/mpd_options.h>
14 #include <packager/mpd/base/mpd_utils.h>
15 #include <packager/mpd/base/xml/xml_node.h>
20 const std::string& GetDefaultAudioLanguage(
const MpdOptions& mpd_options) {
21 return mpd_options.mpd_params.default_language;
24 const std::string& GetDefaultTextLanguage(
const MpdOptions& mpd_options) {
25 return mpd_options.mpd_params.default_text_language.empty()
26 ? mpd_options.mpd_params.default_language
27 : mpd_options.mpd_params.default_text_language;
30 AdaptationSet::Role RoleFromString(
const std::string& role_str) {
31 if (role_str ==
"caption")
32 return AdaptationSet::Role::kRoleCaption;
33 if (role_str ==
"subtitle")
34 return AdaptationSet::Role::kRoleSubtitle;
35 if (role_str ==
"main")
36 return AdaptationSet::Role::kRoleMain;
37 if (role_str ==
"alternate")
38 return AdaptationSet::Role::kRoleAlternate;
39 if (role_str ==
"supplementary")
40 return AdaptationSet::Role::kRoleSupplementary;
41 if (role_str ==
"commentary")
42 return AdaptationSet::Role::kRoleCommentary;
43 if (role_str ==
"dub")
44 return AdaptationSet::Role::kRoleDub;
45 if (role_str ==
"forced-subtitle")
46 return AdaptationSet::Role::kRoleForcedSubtitle;
47 if (role_str ==
"karaoke")
48 return AdaptationSet::Role::kRoleKaraoke;
49 if (role_str ==
"sign")
50 return AdaptationSet::Role::kRoleSign;
51 if (role_str ==
"metadata")
52 return AdaptationSet::Role::kRoleMetadata;
53 if (role_str ==
"enhanced-audio-intelligibility")
54 return AdaptationSet::Role::kRoleEnhancedAudioIntelligibility;
55 if (role_str ==
"emergency")
56 return AdaptationSet::Role::kRoleEmergency;
57 if (role_str ==
"easyreader")
58 return AdaptationSet::Role::kRoleEasyreader;
59 if (role_str ==
"description")
60 return AdaptationSet::Role::kRoleDescription;
61 return AdaptationSet::Role::kRoleUnknown;
67 double start_time_in_seconds,
69 uint32_t* representation_counter)
71 start_time_in_seconds_(start_time_in_seconds),
72 mpd_options_(mpd_options),
73 representation_counter_(representation_counter) {}
76 const MediaInfo& media_info,
77 bool content_protection_in_adaptation_set) {
80 if (duration_seconds_ == 0)
81 duration_seconds_ = media_info.media_duration_seconds();
83 const std::string key = GetAdaptationSetKey(
84 media_info, mpd_options_.mpd_params.allow_codec_switching);
86 std::list<AdaptationSet*>& adaptation_sets = adaptation_set_list_map_[key];
89 if (adaptation_set->MatchAdaptationSet(
90 media_info, content_protection_in_adaptation_set))
91 return adaptation_set;
96 const std::string language = GetLanguage(media_info);
97 std::unique_ptr<AdaptationSet> new_adaptation_set =
98 NewAdaptationSet(language, mpd_options_, representation_counter_);
99 if (!SetNewAdaptationSetAttributes(language, media_info, adaptation_sets,
100 content_protection_in_adaptation_set,
101 new_adaptation_set.get())) {
106 if (adaptation_set->SwitchableAdaptationSet(*new_adaptation_set)) {
107 adaptation_set->AddAdaptationSetSwitching(new_adaptation_set.get());
108 new_adaptation_set->AddAdaptationSetSwitching(adaptation_set);
112 AdaptationSet* adaptation_set_ptr = new_adaptation_set.get();
113 adaptation_sets.push_back(adaptation_set_ptr);
114 adaptation_sets_.emplace_back(std::move(new_adaptation_set));
115 return adaptation_set_ptr;
119 adaptation_sets_.sort(
120 [](
const std::unique_ptr<AdaptationSet>& adaptation_set_a,
121 const std::unique_ptr<AdaptationSet>& adaptation_set_b) {
122 auto index_a = adaptation_set_a->SortIndex();
123 auto index_b = adaptation_set_b->SortIndex();
129 return index_a < index_b;
135 if (!period.
SetId(id_))
139 if (mpd_options_.mpd_params.low_latency_dash_mode) {
141 xml::XmlNode service_description_node(
"ServiceDescription");
147 uint64_t target_latency_ms =
148 mpd_options_.mpd_params.target_latency_seconds * 1000;
151 if (!service_description_node.
AddChild(std::move(latency_node)))
155 if (!period.
AddChild(std::move(service_description_node)))
163 for (
const auto& adaptation_set : adaptation_sets_) {
164 auto child = adaptation_set->GetXml();
165 if (!child || !child->SetId(idx++) || !period.
AddChild(std::move(*child)))
169 if (output_period_duration) {
171 SecondsToXmlDuration(duration_seconds_))) {
174 }
else if (mpd_options_.mpd_type == MpdType::kDynamic) {
176 "start", SecondsToXmlDuration(start_time_in_seconds_))) {
184 std::list<AdaptationSet*> adaptation_sets;
185 for (
const auto& adaptation_set : adaptation_sets_) {
186 adaptation_sets.push_back(adaptation_set.get());
188 return adaptation_sets;
191 std::unique_ptr<AdaptationSet> Period::NewAdaptationSet(
192 const std::string& language,
194 uint32_t* representation_counter) {
195 return std::unique_ptr<AdaptationSet>(
196 new AdaptationSet(language, options, representation_counter));
199 bool Period::SetNewAdaptationSetAttributes(
200 const std::string& language,
201 const MediaInfo& media_info,
202 const std::list<AdaptationSet*>& adaptation_sets,
203 bool content_protection_in_adaptation_set,
204 AdaptationSet* new_adaptation_set) {
205 if (!media_info.dash_roles().empty()) {
206 for (
const std::string& role_str : media_info.dash_roles()) {
207 AdaptationSet::Role role = RoleFromString(role_str);
208 if (role == AdaptationSet::kRoleUnknown) {
209 LOG(ERROR) <<
"Unrecognized role '" << role_str <<
"'.";
212 new_adaptation_set->AddRole(role);
214 }
else if (!language.empty()) {
215 const bool is_main_role =
216 language == (media_info.has_audio_info()
217 ? GetDefaultAudioLanguage(mpd_options_)
218 : GetDefaultTextLanguage(mpd_options_));
220 new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
222 for (
const std::string& accessibility : media_info.dash_accessibilities()) {
223 size_t pos = accessibility.find(
'=');
224 if (pos == std::string::npos) {
226 <<
"Accessibility should be in scheme=value format, but seeing "
230 new_adaptation_set->AddAccessibility(accessibility.substr(0, pos),
231 accessibility.substr(pos + 1));
234 const std::string& codec = GetBaseCodec(media_info);
235 new_adaptation_set->set_codec(codec);
237 if (media_info.has_video_info()) {
240 if (adaptation_sets.size() > 1) {
241 new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
242 }
else if (adaptation_sets.size() == 1) {
243 (*adaptation_sets.begin())->AddRole(AdaptationSet::kRoleMain);
244 new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
247 if (media_info.video_info().has_playback_rate()) {
248 std::string trick_play_reference_adaptation_set_key;
249 AdaptationSet* trick_play_reference_adaptation_set =
250 FindMatchingAdaptationSetForTrickPlay(
251 media_info, content_protection_in_adaptation_set,
252 &trick_play_reference_adaptation_set_key);
253 if (trick_play_reference_adaptation_set) {
254 new_adaptation_set->AddTrickPlayReference(
255 trick_play_reference_adaptation_set);
257 trickplay_cache_[trick_play_reference_adaptation_set_key].push_back(
261 std::string trick_play_adaptation_set_key;
262 AdaptationSet* trickplay_adaptation_set =
263 FindMatchingAdaptationSetForTrickPlay(
264 media_info, content_protection_in_adaptation_set,
265 &trick_play_adaptation_set_key);
266 if (trickplay_adaptation_set) {
267 trickplay_adaptation_set->AddTrickPlayReference(new_adaptation_set);
268 trickplay_cache_.erase(trick_play_adaptation_set_key);
281 if (new_adaptation_set->codec().find(
"dvh") == 0) {
282 new_adaptation_set->set_transfer_characteristics(kTransferFunctionPQ);
283 }
else if (media_info.video_info().has_transfer_characteristics()) {
284 new_adaptation_set->set_transfer_characteristics(
285 media_info.video_info().transfer_characteristics());
288 new_adaptation_set->set_matrix_coefficients(
289 media_info.video_info().matrix_coefficients());
290 new_adaptation_set->set_color_primaries(
291 media_info.video_info().color_primaries());
292 }
else if (media_info.has_audio_info()) {
293 if (codec ==
"mp4a" || codec ==
"ac-3" || codec ==
"ec-3" ||
295 if (mpd_options_.dash_profile == DashProfile::kLive) {
296 new_adaptation_set->ForceStartwithSAP(1);
297 }
else if (mpd_options_.dash_profile == DashProfile::kOnDemand) {
298 new_adaptation_set->ForceSubsegmentStartswithSAP(1);
301 }
else if (media_info.has_text_info()) {
305 new_adaptation_set->ForceSetSegmentAlignment(
true);
308 if (content_protection_in_adaptation_set &&
309 media_info.has_protected_content()) {
310 new_adaptation_set->set_protected_content(media_info);
317 AdaptationSet* Period::FindMatchingAdaptationSetForTrickPlay(
318 const MediaInfo& media_info,
319 bool content_protection_in_adaptation_set,
320 std::string* adaptation_set_key) {
321 std::list<AdaptationSet*>* adaptation_sets =
nullptr;
322 const bool is_trickplay_adaptation_set =
323 media_info.video_info().has_playback_rate();
324 if (is_trickplay_adaptation_set) {
325 *adaptation_set_key = GetAdaptationSetKeyForTrickPlay(media_info);
326 if (adaptation_set_list_map_.find(*adaptation_set_key) ==
327 adaptation_set_list_map_.end())
329 adaptation_sets = &adaptation_set_list_map_[*adaptation_set_key];
331 *adaptation_set_key = GetAdaptationSetKey(
332 media_info, mpd_options_.mpd_params.allow_codec_switching);
333 if (trickplay_cache_.find(*adaptation_set_key) == trickplay_cache_.end())
335 adaptation_sets = &trickplay_cache_[*adaptation_set_key];
337 for (AdaptationSet* adaptation_set : *adaptation_sets) {
338 if (adaptation_set->MatchAdaptationSet(
339 media_info, content_protection_in_adaptation_set))
340 return adaptation_set;
346 std::string Period::GetAdaptationSetKeyForTrickPlay(
347 const MediaInfo& media_info) {
348 MediaInfo media_info_no_trickplay = media_info;
349 media_info_no_trickplay.mutable_video_info()->clear_playback_rate();
350 return GetAdaptationSetKey(media_info_no_trickplay,
351 mpd_options_.mpd_params.allow_codec_switching);
355 if (!trickplay_cache_.empty()) {
356 LOG(WARNING) <<
"Trickplay adaptation set did not get a valid adaptation "
357 "set match. Please check the command line options.";
virtual AdaptationSet * GetOrCreateAdaptationSet(const MediaInfo &media_info, bool content_protection_in_adaptation_set)
std::optional< xml::XmlNode > GetXml(bool output_period_duration)
Period(uint32_t period_id, double start_time_in_seconds, const MpdOptions &mpd_options, uint32_t *representation_counter)
const std::list< AdaptationSet * > GetAdaptationSets() const
bool AddChild(XmlNode child)
bool SetStringAttribute(const std::string &attribute_name, const std::string &attribute)
bool SetIntegerAttribute(const std::string &attribute_name, uint64_t number)
All the methods that are virtual are virtual for mocking.
void AddContentProtectionElements(const MediaInfo &media_info, Representation *parent)