7 #include "packager/mpd/base/period.h"
9 #include "packager/base/stl_util.h"
10 #include "packager/mpd/base/adaptation_set.h"
11 #include "packager/mpd/base/mpd_options.h"
12 #include "packager/mpd/base/mpd_utils.h"
13 #include "packager/mpd/base/xml/xml_node.h"
20 bool ProtectedContentEq(
21 const MediaInfo::ProtectedContent& content_protection1,
22 const MediaInfo::ProtectedContent& content_protection2) {
23 return content_protection1.SerializeAsString() ==
24 content_protection2.SerializeAsString();
27 std::set<std::string> GetUUIDs(
28 const MediaInfo::ProtectedContent& protected_content) {
29 std::set<std::string> uuids;
30 for (
const auto& entry : protected_content.content_protection_entry())
31 uuids.insert(entry.uuid());
35 const std::string& GetDefaultAudioLanguage(
const MpdOptions& mpd_options) {
36 return mpd_options.mpd_params.default_language;
39 const std::string& GetDefaultTextLanguage(
const MpdOptions& mpd_options) {
40 return mpd_options.mpd_params.default_text_language.empty()
41 ? mpd_options.mpd_params.default_language
42 : mpd_options.mpd_params.default_text_language;
45 AdaptationSet::Role RoleFromString(
const std::string& role_str) {
46 if (role_str ==
"caption")
47 return AdaptationSet::Role::kRoleCaption;
48 if (role_str ==
"subtitle")
49 return AdaptationSet::Role::kRoleSubtitle;
50 if (role_str ==
"main")
51 return AdaptationSet::Role::kRoleMain;
52 if (role_str ==
"alternate")
53 return AdaptationSet::Role::kRoleAlternate;
54 if (role_str ==
"supplementary")
55 return AdaptationSet::Role::kRoleSupplementary;
56 if (role_str ==
"commentary")
57 return AdaptationSet::Role::kRoleCommentary;
58 if (role_str ==
"dub")
59 return AdaptationSet::Role::kRoleDub;
60 return AdaptationSet::Role::kRoleUnknown;
66 double start_time_in_seconds,
68 uint32_t* representation_counter)
70 start_time_in_seconds_(start_time_in_seconds),
71 mpd_options_(mpd_options),
72 representation_counter_(representation_counter) {}
75 const MediaInfo& media_info,
76 bool content_protection_in_adaptation_set) {
79 if (duration_seconds_ == 0)
80 duration_seconds_ = media_info.media_duration_seconds();
82 const std::string key = GetAdaptationSetKey(
85 std::list<AdaptationSet*>& adaptation_sets = adaptation_set_list_map_[key];
88 if (protected_adaptation_set_map_.Match(
89 *adaptation_set, media_info, content_protection_in_adaptation_set))
90 return adaptation_set;
95 const std::string language = GetLanguage(media_info);
96 std::unique_ptr<AdaptationSet> new_adaptation_set =
97 NewAdaptationSet(language, mpd_options_, representation_counter_);
98 if (!SetNewAdaptationSetAttributes(language, media_info, adaptation_sets,
99 content_protection_in_adaptation_set,
100 new_adaptation_set.get())) {
104 if (content_protection_in_adaptation_set &&
105 media_info.has_protected_content()) {
106 protected_adaptation_set_map_.Register(*new_adaptation_set, media_info);
110 if (protected_adaptation_set_map_.Switchable(*adaptation_set,
111 *new_adaptation_set)) {
112 adaptation_set->AddAdaptationSetSwitching(new_adaptation_set.get());
113 new_adaptation_set->AddAdaptationSetSwitching(adaptation_set);
117 AdaptationSet* adaptation_set_ptr = new_adaptation_set.get();
118 adaptation_sets.push_back(adaptation_set_ptr);
119 adaptation_sets_.emplace_back(std::move(new_adaptation_set));
120 return adaptation_set_ptr;
124 adaptation_sets_.sort(
125 [](
const std::unique_ptr<AdaptationSet>& adaptation_set_a,
126 const std::unique_ptr<AdaptationSet>& adaptation_set_b) {
127 if (!adaptation_set_a->has_id())
129 if (!adaptation_set_b->has_id())
131 return adaptation_set_a->id() < adaptation_set_b->id();
137 if (!period.
SetId(id_))
138 return base::nullopt;
140 for (
const auto& adaptation_set : adaptation_sets_) {
141 auto child = adaptation_set->GetXml();
142 if (!child || !period.
AddChild(std::move(*child)))
143 return base::nullopt;
146 if (output_period_duration) {
148 SecondsToXmlDuration(duration_seconds_))) {
149 return base::nullopt;
151 }
else if (mpd_options_.mpd_type == MpdType::kDynamic) {
153 "start", SecondsToXmlDuration(start_time_in_seconds_))) {
154 return base::nullopt;
161 std::list<AdaptationSet*> adaptation_sets;
162 for (
const auto& adaptation_set : adaptation_sets_) {
163 adaptation_sets.push_back(adaptation_set.get());
165 return adaptation_sets;
168 std::unique_ptr<AdaptationSet> Period::NewAdaptationSet(
169 const std::string& language,
171 uint32_t* representation_counter) {
172 return std::unique_ptr<AdaptationSet>(
173 new AdaptationSet(language, options, representation_counter));
176 bool Period::SetNewAdaptationSetAttributes(
177 const std::string& language,
178 const MediaInfo& media_info,
179 const std::list<AdaptationSet*>& adaptation_sets,
180 bool content_protection_in_adaptation_set,
181 AdaptationSet* new_adaptation_set) {
182 if (!media_info.dash_roles().empty()) {
183 for (
const std::string& role_str : media_info.dash_roles()) {
184 AdaptationSet::Role role = RoleFromString(role_str);
185 if (role == AdaptationSet::kRoleUnknown) {
186 LOG(ERROR) <<
"Unrecognized role '" << role_str <<
"'.";
189 new_adaptation_set->AddRole(role);
191 }
else if (!language.empty()) {
192 const bool is_main_role =
193 language == (media_info.has_audio_info()
194 ? GetDefaultAudioLanguage(mpd_options_)
195 : GetDefaultTextLanguage(mpd_options_));
197 new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
199 for (
const std::string& accessibility : media_info.dash_accessibilities()) {
200 size_t pos = accessibility.find(
'=');
201 if (pos == std::string::npos) {
203 <<
"Accessibility should be in scheme=value format, but seeing "
207 new_adaptation_set->AddAccessibility(accessibility.substr(0, pos),
208 accessibility.substr(pos + 1));
211 new_adaptation_set->set_codec(GetBaseCodec(media_info));
213 if (media_info.has_video_info()) {
216 if (adaptation_sets.size() > 1) {
217 new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
218 }
else if (adaptation_sets.size() == 1) {
219 (*adaptation_sets.begin())->AddRole(AdaptationSet::kRoleMain);
220 new_adaptation_set->AddRole(AdaptationSet::kRoleMain);
223 if (media_info.video_info().has_playback_rate()) {
224 std::string trick_play_reference_adaptation_set_key;
225 AdaptationSet* trick_play_reference_adaptation_set =
226 FindMatchingAdaptationSetForTrickPlay(
227 media_info, content_protection_in_adaptation_set,
228 &trick_play_reference_adaptation_set_key);
229 if (trick_play_reference_adaptation_set) {
230 new_adaptation_set->AddTrickPlayReference(
231 trick_play_reference_adaptation_set);
233 trickplay_cache_[trick_play_reference_adaptation_set_key].push_back(
237 std::string trick_play_adaptation_set_key;
238 AdaptationSet* trickplay_adaptation_set =
239 FindMatchingAdaptationSetForTrickPlay(
240 media_info, content_protection_in_adaptation_set,
241 &trick_play_adaptation_set_key);
242 if (trickplay_adaptation_set) {
243 trickplay_adaptation_set->AddTrickPlayReference(new_adaptation_set);
244 trickplay_cache_.erase(trick_play_adaptation_set_key);
248 }
else if (media_info.has_text_info()) {
252 new_adaptation_set->ForceSetSegmentAlignment(
true);
257 AdaptationSet* Period::FindMatchingAdaptationSetForTrickPlay(
258 const MediaInfo& media_info,
259 bool content_protection_in_adaptation_set,
260 std::string* adaptation_set_key) {
261 std::list<AdaptationSet*>* adaptation_sets =
nullptr;
262 const bool is_trickplay_adaptation_set =
263 media_info.video_info().has_playback_rate();
264 if (is_trickplay_adaptation_set) {
265 *adaptation_set_key = GetAdaptationSetKeyForTrickPlay(media_info);
266 if (adaptation_set_list_map_.find(*adaptation_set_key) ==
267 adaptation_set_list_map_.end())
269 adaptation_sets = &adaptation_set_list_map_[*adaptation_set_key];
271 *adaptation_set_key = GetAdaptationSetKey(
273 if (trickplay_cache_.find(*adaptation_set_key) == trickplay_cache_.end())
275 adaptation_sets = &trickplay_cache_[*adaptation_set_key];
277 for (AdaptationSet* adaptation_set : *adaptation_sets) {
278 if (protected_adaptation_set_map_.Match(
279 *adaptation_set, media_info,
280 content_protection_in_adaptation_set)) {
281 return adaptation_set;
288 std::string Period::GetAdaptationSetKeyForTrickPlay(
289 const MediaInfo& media_info) {
290 MediaInfo media_info_no_trickplay = media_info;
291 media_info_no_trickplay.mutable_video_info()->clear_playback_rate();
292 return GetAdaptationSetKey(media_info_no_trickplay,
296 void Period::ProtectedAdaptationSetMap::Register(
297 const AdaptationSet& adaptation_set,
298 const MediaInfo& media_info) {
299 DCHECK(!ContainsKey(protected_content_map_, &adaptation_set));
300 protected_content_map_[&adaptation_set] = media_info.protected_content();
303 bool Period::ProtectedAdaptationSetMap::Match(
304 const AdaptationSet& adaptation_set,
305 const MediaInfo& media_info,
306 bool content_protection_in_adaptation_set) {
307 if (adaptation_set.codec() != GetBaseCodec(media_info))
310 if (!content_protection_in_adaptation_set)
313 const auto protected_content_it =
314 protected_content_map_.find(&adaptation_set);
317 if (protected_content_it == protected_content_map_.end())
318 return !media_info.has_protected_content();
319 if (!media_info.has_protected_content())
322 return ProtectedContentEq(protected_content_it->second,
323 media_info.protected_content());
326 bool Period::ProtectedAdaptationSetMap::Switchable(
327 const AdaptationSet& adaptation_set_a,
328 const AdaptationSet& adaptation_set_b) {
329 const auto protected_content_it_a =
330 protected_content_map_.find(&adaptation_set_a);
331 const auto protected_content_it_b =
332 protected_content_map_.find(&adaptation_set_b);
334 if (protected_content_it_a == protected_content_map_.end())
335 return protected_content_it_b == protected_content_map_.end();
336 if (protected_content_it_b == protected_content_map_.end())
340 return GetUUIDs(protected_content_it_a->second) ==
341 GetUUIDs(protected_content_it_b->second);
345 if (!trickplay_cache_.empty()) {
346 LOG(WARNING) <<
"Trickplay adaptation set did not get a valid adaptation "
347 "set match. Please check the command line options.";