Shaka Packager SDK
Loading...
Searching...
No Matches
adaptation_set.cc
1// Copyright 2017 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/mpd/base/adaptation_set.h>
8
9#include <algorithm>
10#include <cmath>
11#include <cstddef>
12#include <cstdint>
13#include <list>
14#include <memory>
15#include <optional>
16#include <set>
17#include <string>
18#include <utility>
19
20#include <absl/log/check.h>
21#include <absl/log/log.h>
22#include <absl/strings/str_format.h>
23
24#include <packager/macros/classes.h>
25#include <packager/macros/logging.h>
26#include <packager/mpd/base/media_info.pb.h>
27#include <packager/mpd/base/mpd_options.h>
28#include <packager/mpd/base/mpd_utils.h>
29#include <packager/mpd/base/representation.h>
30#include <packager/mpd/base/xml/xml_node.h>
31
32namespace shaka {
33namespace {
34
35AdaptationSet::Role MediaInfoTextTypeToRole(
36 MediaInfo::TextInfo::TextType type) {
37 switch (type) {
38 case MediaInfo::TextInfo::UNKNOWN:
39 LOG(WARNING) << "Unknown text type, assuming subtitle.";
40 return AdaptationSet::kRoleSubtitle;
41 case MediaInfo::TextInfo::CAPTION:
42 return AdaptationSet::kRoleCaption;
43 case MediaInfo::TextInfo::SUBTITLE:
44 return AdaptationSet::kRoleSubtitle;
45 default:
46 NOTIMPLEMENTED() << "Unknown MediaInfo TextType: " << type
47 << " assuming subtitle.";
48 return AdaptationSet::kRoleSubtitle;
49 }
50}
51
52std::string RoleToText(AdaptationSet::Role role) {
53 // Using switch so that the compiler can detect whether there is a case that's
54 // not being handled.
55 switch (role) {
56 case AdaptationSet::kRoleCaption:
57 return "caption";
58 case AdaptationSet::kRoleSubtitle:
59 return "subtitle";
60 case AdaptationSet::kRoleMain:
61 return "main";
62 case AdaptationSet::kRoleAlternate:
63 return "alternate";
64 case AdaptationSet::kRoleSupplementary:
65 return "supplementary";
66 case AdaptationSet::kRoleCommentary:
67 return "commentary";
68 case AdaptationSet::kRoleDub:
69 return "dub";
70 case AdaptationSet::kRoleDescription:
71 return "description";
72 case AdaptationSet::kRoleSign:
73 return "sign";
74 case AdaptationSet::kRoleMetadata:
75 return "metadata";
76 case AdaptationSet::kRoleEnhancedAudioIntelligibility:
77 return "enhanced-audio-intelligibility";
78 case AdaptationSet::kRoleEmergency:
79 return "emergency";
80 case AdaptationSet::kRoleForcedSubtitle:
81 return "forced-subtitle";
82 case AdaptationSet::kRoleEasyreader:
83 return "easyreader";
84 case AdaptationSet::kRoleKaraoke:
85 return "karaoke";
86 default:
87 return "unknown";
88 }
89}
90
91// Returns the picture aspect ratio string e.g. "16:9", "4:3".
92// "Reducing the quotient to minimal form" does not work well in practice as
93// there may be some rounding performed in the input, e.g. the resolution of
94// 480p is 854:480 for 16:9 aspect ratio, can only be reduced to 427:240.
95// The algorithm finds out the pair of integers, num and den, where num / den is
96// the closest ratio to scaled_width / scaled_height, by looping den through
97// common values.
98std::string GetPictureAspectRatio(uint32_t width,
99 uint32_t height,
100 uint32_t pixel_width,
101 uint32_t pixel_height) {
102 const uint32_t scaled_width = pixel_width * width;
103 const uint32_t scaled_height = pixel_height * height;
104 const double par = static_cast<double>(scaled_width) / scaled_height;
105
106 // Typical aspect ratios have par_y less than or equal to 19:
107 // https://en.wikipedia.org/wiki/List_of_common_resolutions
108 const uint32_t kLargestPossibleParY = 19;
109
110 uint32_t par_num = 0;
111 uint32_t par_den = 0;
112 double min_error = 1.0;
113 for (uint32_t den = 1; den <= kLargestPossibleParY; ++den) {
114 uint32_t num = par * den + 0.5;
115 double error = fabs(par - static_cast<double>(num) / den);
116 if (error < min_error) {
117 min_error = error;
118 par_num = num;
119 par_den = den;
120 if (error == 0)
121 break;
122 }
123 }
124 VLOG(2) << "width*pix_width : height*pixel_height (" << scaled_width << ":"
125 << scaled_height << ") reduced to " << par_num << ":" << par_den
126 << " with error " << min_error << ".";
127
128 return absl::StrFormat("%d:%d", par_num, par_den);
129}
130
131// Adds an entry to picture_aspect_ratio if the size of picture_aspect_ratio is
132// less than 2 and video_info has both pixel width and pixel height.
133void AddPictureAspectRatio(const MediaInfo::VideoInfo& video_info,
134 std::set<std::string>* picture_aspect_ratio) {
135 // If there are more than one entries in picture_aspect_ratio, the @par
136 // attribute cannot be set, so skip.
137 if (picture_aspect_ratio->size() > 1)
138 return;
139
140 if (video_info.width() == 0 || video_info.height() == 0 ||
141 video_info.pixel_width() == 0 || video_info.pixel_height() == 0) {
142 // If there is even one Representation without a @sar attribute, @par cannot
143 // be calculated.
144 // Just populate the set with at least 2 bogus strings so that further call
145 // to this function will bail out immediately.
146 picture_aspect_ratio->insert("bogus");
147 picture_aspect_ratio->insert("entries");
148 return;
149 }
150
151 const std::string par = GetPictureAspectRatio(
152 video_info.width(), video_info.height(), video_info.pixel_width(),
153 video_info.pixel_height());
154 DVLOG(1) << "Setting par as: " << par
155 << " for video with width: " << video_info.width()
156 << " height: " << video_info.height()
157 << " pixel_width: " << video_info.pixel_width() << " pixel_height; "
158 << video_info.pixel_height();
159 picture_aspect_ratio->insert(par);
160}
161
162class RepresentationStateChangeListenerImpl
163 : public RepresentationStateChangeListener {
164 public:
165 // |adaptation_set| is not owned by this class.
166 RepresentationStateChangeListenerImpl(uint32_t representation_id,
167 AdaptationSet* adaptation_set)
168 : representation_id_(representation_id), adaptation_set_(adaptation_set) {
169 DCHECK(adaptation_set_);
170 }
171 ~RepresentationStateChangeListenerImpl() override {}
172
173 // RepresentationStateChangeListener implementation.
174 void OnNewSegmentForRepresentation(int64_t start_time,
175 int64_t duration) override {
176 adaptation_set_->OnNewSegmentForRepresentation(representation_id_,
177 start_time, duration);
178 }
179
180 void OnSetFrameRateForRepresentation(int32_t frame_duration,
181 int32_t timescale) override {
182 adaptation_set_->OnSetFrameRateForRepresentation(representation_id_,
183 frame_duration, timescale);
184 }
185
186 private:
187 const uint32_t representation_id_;
188 AdaptationSet* const adaptation_set_;
189
190 DISALLOW_COPY_AND_ASSIGN(RepresentationStateChangeListenerImpl);
191};
192
193} // namespace
194
195AdaptationSet::AdaptationSet(const std::string& language,
196 const MpdOptions& mpd_options,
197 uint32_t* counter)
198 : representation_counter_(counter),
199 language_(language),
200 mpd_options_(mpd_options),
201 protected_content_(nullptr) {
202 DCHECK(counter);
203}
204
205AdaptationSet::~AdaptationSet() {
206 delete protected_content_;
207}
208
209void AdaptationSet::set_protected_content(const MediaInfo& media_info) {
210 DCHECK(!protected_content_);
211 protected_content_ =
212 new MediaInfo::ProtectedContent(media_info.protected_content());
213}
214
215// The easiest way to check whether two protobufs are equal, is to compare the
216// serialized version.
217bool ProtectedContentEq(
218 const MediaInfo::ProtectedContent& content_protection1,
219 const MediaInfo::ProtectedContent& content_protection2) {
220 return content_protection1.SerializeAsString() ==
221 content_protection2.SerializeAsString();
222}
223
225 const MediaInfo& media_info,
226 bool content_protection_in_adaptation_set) {
227 if (codec_ != GetBaseCodec(media_info))
228 return false;
229
230 if (!content_protection_in_adaptation_set)
231 return true;
232
233 if (!protected_content_)
234 return !media_info.has_protected_content();
235
236 if (!media_info.has_protected_content())
237 return false;
238
239 return ProtectedContentEq(*protected_content_,
240 media_info.protected_content());
241}
242
243std::set<std::string> GetUUIDs(
244 const MediaInfo::ProtectedContent* protected_content) {
245 std::set<std::string> uuids;
246 for (const auto& entry : protected_content->content_protection_entry())
247 uuids.insert(entry.uuid());
248 return uuids;
249}
250
252 const AdaptationSet& adaptation_set) {
253 // adaptation sets are switchable if both are not protected
254 if (!protected_content_ && !adaptation_set.protected_content()) {
255 return true;
256 }
257
258 // or if both are protected and have the same UUID
259 if (protected_content_ && adaptation_set.protected_content()) {
260 return GetUUIDs(protected_content_) ==
261 GetUUIDs(adaptation_set.protected_content());
262 }
263
264 return false;
265}
266
268 const uint32_t representation_id = media_info.has_index()
269 ? media_info.index()
270 : (*representation_counter_)++;
271
272 // Note that AdaptationSet outlive Representation, so this object
273 // will die before AdaptationSet.
274 std::unique_ptr<RepresentationStateChangeListener> listener(
275 new RepresentationStateChangeListenerImpl(representation_id, this));
276 std::unique_ptr<Representation> new_representation(new Representation(
277 media_info, mpd_options_, representation_id, std::move(listener)));
278
279 if (!new_representation->Init()) {
280 LOG(ERROR) << "Failed to initialize Representation.";
281 return NULL;
282 }
283 UpdateFromMediaInfo(media_info);
284 Representation* representation_ptr = new_representation.get();
285 representation_map_[representation_ptr->id()] = std::move(new_representation);
286 return representation_ptr;
287}
288
290 const Representation& representation) {
291 // Note that AdaptationSet outlive Representation, so this object
292 // will die before AdaptationSet.
293 std::unique_ptr<RepresentationStateChangeListener> listener(
294 new RepresentationStateChangeListenerImpl(representation.id(), this));
295 std::unique_ptr<Representation> new_representation(
296 new Representation(representation, std::move(listener)));
297
298 UpdateFromMediaInfo(new_representation->GetMediaInfo());
299 Representation* representation_ptr = new_representation.get();
300 representation_map_[representation_ptr->id()] = std::move(new_representation);
301 return representation_ptr;
302}
303
305 const ContentProtectionElement& content_protection_element) {
306 content_protection_elements_.push_back(content_protection_element);
307 RemoveDuplicateAttributes(&content_protection_elements_.back());
308}
309
310void AdaptationSet::UpdateContentProtectionPssh(const std::string& drm_uuid,
311 const std::string& pssh) {
312 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
313 &content_protection_elements_);
314}
315
316void AdaptationSet::AddAccessibility(const std::string& scheme,
317 const std::string& value) {
318 accessibilities_.push_back(Accessibility{scheme, value});
319}
320
321void AdaptationSet::AddRole(Role role) {
322 roles_.insert(role);
323}
324
325// Creates a copy of <AdaptationSet> xml element, iterate thru all the
326// <Representation> (child) elements and add them to the copy.
327// Set all the attributes first and then add the children elements so that flags
328// can be passed to Representation to avoid setting redundant attributes. For
329// example, if AdaptationSet@width is set, then Representation@width is
330// redundant and should not be set.
331std::optional<xml::XmlNode> AdaptationSet::GetXml() {
332 xml::AdaptationSetXmlNode adaptation_set;
333
334 bool suppress_representation_width = false;
335 bool suppress_representation_height = false;
336 bool suppress_representation_frame_rate = false;
337
338 if (id_ && !adaptation_set.SetId(id_.value()))
339 return std::nullopt;
340 if (!adaptation_set.SetStringAttribute("contentType", content_type_))
341 return std::nullopt;
342 if (!language_.empty() && language_ != "und" &&
343 !adaptation_set.SetStringAttribute("lang", language_)) {
344 return std::nullopt;
345 }
346
347 // Note that std::{set,map} are ordered, so the last element is the max value.
348 if (video_widths_.size() == 1) {
349 suppress_representation_width = true;
350 if (!adaptation_set.SetIntegerAttribute("width", *video_widths_.begin()))
351 return std::nullopt;
352 } else if (video_widths_.size() > 1) {
353 if (!adaptation_set.SetIntegerAttribute("maxWidth",
354 *video_widths_.rbegin())) {
355 return std::nullopt;
356 }
357 }
358
359 if (video_heights_.size() == 1) {
360 suppress_representation_height = true;
361 if (!adaptation_set.SetIntegerAttribute("height", *video_heights_.begin()))
362 return std::nullopt;
363 } else if (video_heights_.size() > 1) {
364 if (!adaptation_set.SetIntegerAttribute("maxHeight",
365 *video_heights_.rbegin())) {
366 return std::nullopt;
367 }
368 }
369
370 if (subsegment_start_with_sap_) {
371 if (!adaptation_set.SetIntegerAttribute("subsegmentStartsWithSAP",
372 subsegment_start_with_sap_))
373 return std::nullopt;
374 } else if (start_with_sap_) {
375 if (!adaptation_set.SetIntegerAttribute("startWithSAP", start_with_sap_))
376 return std::nullopt;
377 }
378
379 if (video_frame_rates_.size() == 1) {
380 suppress_representation_frame_rate = true;
381 if (!adaptation_set.SetStringAttribute(
382 "frameRate", video_frame_rates_.begin()->second)) {
383 return std::nullopt;
384 }
385 } else if (video_frame_rates_.size() > 1) {
386 if (!adaptation_set.SetStringAttribute(
387 "maxFrameRate", video_frame_rates_.rbegin()->second)) {
388 return std::nullopt;
389 }
390 }
391
392 // https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf - 4.2.5.1
393 if (IsVideo() && matrix_coefficients_ > 0 &&
394 !adaptation_set.AddSupplementalProperty(
395 "urn:mpeg:mpegB:cicp:MatrixCoefficients",
396 std::to_string(matrix_coefficients_))) {
397 return std::nullopt;
398 }
399
400 // https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf - 4.2.5.1
401 if (IsVideo() && color_primaries_ > 0 &&
402 !adaptation_set.AddSupplementalProperty(
403 "urn:mpeg:mpegB:cicp:ColourPrimaries",
404 std::to_string(color_primaries_))) {
405 return std::nullopt;
406 }
407
408 // https://dashif.org/docs/DASH-IF-IOP-v4.3.pdf - 4.2.5.1
409 if (IsVideo() && transfer_characteristics_ > 0 &&
410 !adaptation_set.AddSupplementalProperty(
411 "urn:mpeg:mpegB:cicp:TransferCharacteristics",
412 std::to_string(transfer_characteristics_))) {
413 return std::nullopt;
414 }
415
416 // Note: must be checked before checking segments_aligned_ (below). So that
417 // segments_aligned_ is set before checking below.
418 if (mpd_options_.mpd_type == MpdType::kStatic) {
419 CheckStaticSegmentAlignment();
420 }
421
422 if (segments_aligned_ == kSegmentAlignmentTrue) {
423 if (!adaptation_set.SetStringAttribute(
424 mpd_options_.dash_profile == DashProfile::kOnDemand
425 ? "subsegmentAlignment"
426 : "segmentAlignment",
427 "true")) {
428 return std::nullopt;
429 }
430 }
431
432 if (picture_aspect_ratio_.size() == 1 &&
433 !adaptation_set.SetStringAttribute("par",
434 *picture_aspect_ratio_.begin())) {
435 return std::nullopt;
436 }
437
438 if (!adaptation_set.AddContentProtectionElements(
439 content_protection_elements_)) {
440 return std::nullopt;
441 }
442
443 std::string trick_play_reference_ids;
444 for (const AdaptationSet* tp_adaptation_set : trick_play_references_) {
445 // Should be a whitespace-separated list, see DASH-IOP 3.2.9.
446 if (!trick_play_reference_ids.empty())
447 trick_play_reference_ids += ' ';
448 CHECK(tp_adaptation_set->has_id());
449 trick_play_reference_ids += std::to_string(tp_adaptation_set->id());
450 }
451 if (!trick_play_reference_ids.empty() &&
452 !adaptation_set.AddEssentialProperty(
453 "http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) {
454 return std::nullopt;
455 }
456
457 std::string switching_ids;
458 for (const AdaptationSet* s_adaptation_set : switchable_adaptation_sets_) {
459 // Should be a comma-separated list, see DASH-IOP 3.8.
460 if (!switching_ids.empty())
461 switching_ids += ',';
462 CHECK(s_adaptation_set->has_id());
463 switching_ids += std::to_string(s_adaptation_set->id());
464 }
465 if (!switching_ids.empty() &&
466 !adaptation_set.AddSupplementalProperty(
467 "urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) {
468 return std::nullopt;
469 }
470
471 for (const AdaptationSet::Accessibility& accessibility : accessibilities_) {
472 if (!adaptation_set.AddAccessibilityElement(accessibility.scheme,
473 accessibility.value)) {
474 return std::nullopt;
475 }
476 }
477
478 for (AdaptationSet::Role role : roles_) {
479 if (!adaptation_set.AddRoleElement("urn:mpeg:dash:role:2011",
480 RoleToText(role))) {
481 return std::nullopt;
482 }
483 }
484
485 if (!label_.empty() && !adaptation_set.AddLabelElement(label_))
486 return std::nullopt;
487
488 for (const auto& representation_pair : representation_map_) {
489 const auto& representation = representation_pair.second;
490 if (suppress_representation_width)
491 representation->SuppressOnce(Representation::kSuppressWidth);
492 if (suppress_representation_height)
493 representation->SuppressOnce(Representation::kSuppressHeight);
494 if (suppress_representation_frame_rate)
495 representation->SuppressOnce(Representation::kSuppressFrameRate);
496 auto child = representation->GetXml();
497 if (!child || !adaptation_set.AddChild(std::move(*child)))
498 return std::nullopt;
499 }
500
501 return adaptation_set;
502}
503
504void AdaptationSet::ForceSetSegmentAlignment(bool segment_alignment) {
505 segments_aligned_ =
506 segment_alignment ? kSegmentAlignmentTrue : kSegmentAlignmentFalse;
507 force_set_segment_alignment_ = true;
508}
509
511 const AdaptationSet* adaptation_set) {
512 switchable_adaptation_sets_.push_back(adaptation_set);
513}
514
516 subsegment_start_with_sap_ = sap_value;
517}
518
519void AdaptationSet::ForceStartwithSAP(uint32_t sap_value) {
520 start_with_sap_ = sap_value;
521}
522
523// For dynamic MPD, storing all start_time and duration will out-of-memory
524// because there's no way of knowing when it will end. Static MPD
525// subsegmentAlignment check is *not* done here because it is possible that some
526// Representations might not have been added yet (e.g. a thread is assigned per
527// muxer so one might run faster than others). To be clear, for dynamic MPD, all
528// Representations should be added before a segment is added.
529void AdaptationSet::OnNewSegmentForRepresentation(uint32_t representation_id,
530 int64_t start_time,
531 int64_t duration) {
532 if (mpd_options_.mpd_type == MpdType::kDynamic) {
533 CheckDynamicSegmentAlignment(representation_id, start_time, duration);
534 } else {
535 representation_segment_start_times_[representation_id].push_back(
536 start_time);
537 }
538}
539
541 int32_t frame_duration,
542 int32_t timescale) {
543 RecordFrameRate(frame_duration, timescale);
544}
545
547 trick_play_references_.push_back(adaptation_set);
548}
549
550const std::list<Representation*> AdaptationSet::GetRepresentations() const {
551 std::list<Representation*> representations;
552 for (const auto& representation_pair : representation_map_) {
553 representations.push_back(representation_pair.second.get());
554 }
555 return representations;
556}
557
559 return content_type_ == "video";
560}
561
562void AdaptationSet::UpdateFromMediaInfo(const MediaInfo& media_info) {
563 // For videos, record the width, height, and the frame rate to calculate the
564 // max {width,height,framerate} required for DASH IOP.
565 if (media_info.has_video_info()) {
566 const MediaInfo::VideoInfo& video_info = media_info.video_info();
567 DCHECK(video_info.has_width());
568 DCHECK(video_info.has_height());
569 video_widths_.insert(video_info.width());
570 video_heights_.insert(video_info.height());
571
572 if (video_info.has_time_scale() && video_info.has_frame_duration())
573 RecordFrameRate(video_info.frame_duration(), video_info.time_scale());
574
575 AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
576 }
577
578 // the command-line index for this AdaptationSet will be the
579 // minimum of the Representations in the set
580 if (media_info.has_index()) {
581 if (index_.has_value()) {
582 index_ = std::min(index_.value(), media_info.index());
583 } else {
584 index_ = media_info.index();
585 }
586 }
587
588 if (media_info.has_dash_label())
589 label_ = media_info.dash_label();
590
591 if (media_info.has_video_info()) {
592 content_type_ = "video";
593 } else if (media_info.has_audio_info()) {
594 content_type_ = "audio";
595 } else if (media_info.has_text_info()) {
596 content_type_ = "text";
597
598 if (media_info.text_info().has_type() &&
599 (media_info.text_info().type() != MediaInfo::TextInfo::UNKNOWN)) {
600 roles_.insert(MediaInfoTextTypeToRole(media_info.text_info().type()));
601 }
602 }
603}
604
605// This implementation assumes that each representations' segments' are
606// contiguous.
607// Also assumes that all Representations are added before this is called.
608// This checks whether the first elements of the lists in
609// representation_segment_start_times_ are aligned.
610// For example, suppose this method was just called with args rep_id=2
611// start_time=1.
612// 1 -> [1, 100, 200]
613// 2 -> [1]
614// The timestamps of the first elements match, so this flags
615// segments_aligned_=true.
616// Also since the first segment start times match, the first element of all the
617// lists are removed, so the map of lists becomes:
618// 1 -> [100, 200]
619// 2 -> []
620// Note that there could be false positives.
621// e.g. just got rep_id=3 start_time=1 duration=300, and the duration of the
622// whole AdaptationSet is 300.
623// 1 -> [1, 100, 200]
624// 2 -> [1, 90, 100]
625// 3 -> [1]
626// They are not aligned but this will be marked as aligned.
627// But since this is unlikely to happen in the packager (and to save
628// computation), this isn't handled at the moment.
629void AdaptationSet::CheckDynamicSegmentAlignment(uint32_t representation_id,
630 int64_t start_time,
631 int64_t /* duration */) {
632 if (segments_aligned_ == kSegmentAlignmentFalse ||
633 force_set_segment_alignment_) {
634 return;
635 }
636
637 std::list<int64_t>& current_representation_start_times =
638 representation_segment_start_times_[representation_id];
639 current_representation_start_times.push_back(start_time);
640 // There's no way to detemine whether the segments are aligned if some
641 // representations do not have any segments.
642 if (representation_segment_start_times_.size() != representation_map_.size())
643 return;
644
645 DCHECK(!current_representation_start_times.empty());
646 const int64_t expected_start_time =
647 current_representation_start_times.front();
648 for (const auto& key_value : representation_segment_start_times_) {
649 const std::list<int64_t>& representation_start_time = key_value.second;
650 // If there are no entries in a list, then there is no way for the
651 // segment alignment status to change.
652 // Note that it can be empty because entries get deleted below.
653 if (representation_start_time.empty())
654 return;
655
656 if (expected_start_time != representation_start_time.front()) {
657 VLOG(1) << "Seeing Misaligned segments with different start_times: "
658 << expected_start_time << " vs "
659 << representation_start_time.front();
660 // Flag as false and clear the start times data, no need to keep it
661 // around.
662 segments_aligned_ = kSegmentAlignmentFalse;
663 representation_segment_start_times_.clear();
664 return;
665 }
666 }
667 segments_aligned_ = kSegmentAlignmentTrue;
668
669 for (auto& key_value : representation_segment_start_times_) {
670 std::list<int64_t>& representation_start_time = key_value.second;
671 representation_start_time.pop_front();
672 }
673}
674
675// Make sure all segements start times match for all Representations.
676// This assumes that the segments are contiguous.
677void AdaptationSet::CheckStaticSegmentAlignment() {
678 if (segments_aligned_ == kSegmentAlignmentFalse ||
679 force_set_segment_alignment_) {
680 return;
681 }
682 if (representation_segment_start_times_.empty())
683 return;
684 if (representation_segment_start_times_.size() == 1) {
685 segments_aligned_ = kSegmentAlignmentTrue;
686 return;
687 }
688
689 // This is not the most efficient implementation to compare the values
690 // because expected_time_line is compared against all other time lines, but
691 // probably the most readable.
692 const std::list<int64_t>& expected_time_line =
693 representation_segment_start_times_.begin()->second;
694
695 bool all_segment_time_line_same_length = true;
696 // Note that the first entry is skipped because it is expected_time_line.
697 RepresentationTimeline::const_iterator it =
698 representation_segment_start_times_.begin();
699 for (++it; it != representation_segment_start_times_.end(); ++it) {
700 const std::list<int64_t>& other_time_line = it->second;
701 if (expected_time_line.size() != other_time_line.size()) {
702 all_segment_time_line_same_length = false;
703 }
704
705 const std::list<int64_t>* longer_list = &other_time_line;
706 const std::list<int64_t>* shorter_list = &expected_time_line;
707 if (expected_time_line.size() > other_time_line.size()) {
708 shorter_list = &other_time_line;
709 longer_list = &expected_time_line;
710 }
711
712 if (!std::equal(shorter_list->begin(), shorter_list->end(),
713 longer_list->begin())) {
714 // Some segments are definitely unaligned.
715 segments_aligned_ = kSegmentAlignmentFalse;
716 representation_segment_start_times_.clear();
717 return;
718 }
719 }
720
721 // TODO(rkuroiwa): The right way to do this is to also check the durations.
722 // For example:
723 // (a) 3 4 5
724 // (b) 3 4 5 6
725 // could be true or false depending on the length of the third segment of (a).
726 // i.e. if length of the third segment is 2, then this is not aligned.
727 if (!all_segment_time_line_same_length) {
728 segments_aligned_ = kSegmentAlignmentUnknown;
729 return;
730 }
731
732 segments_aligned_ = kSegmentAlignmentTrue;
733}
734
735// Since all AdaptationSet cares about is the maxFrameRate, representation_id
736// is not passed to this method.
737void AdaptationSet::RecordFrameRate(int32_t frame_duration, int32_t timescale) {
738 if (frame_duration == 0) {
739 LOG(ERROR) << "Frame duration is 0 and cannot be set.";
740 return;
741 }
742 video_frame_rates_[static_cast<double>(timescale) / frame_duration] =
743 absl::StrFormat("%d/%d", timescale, frame_duration);
744}
745
746} // namespace shaka
void OnNewSegmentForRepresentation(uint32_t representation_id, int64_t start_time, int64_t duration)
virtual Representation * AddRepresentation(const MediaInfo &media_info)
virtual void AddAccessibility(const std::string &scheme, const std::string &value)
virtual void AddContentProtectionElement(const ContentProtectionElement &element)
virtual void ForceStartwithSAP(uint32_t sap_value)
const MediaInfo::ProtectedContent * protected_content() const
Return ProtectedContent.
virtual void ForceSetSegmentAlignment(bool segment_alignment)
void OnSetFrameRateForRepresentation(uint32_t representation_id, int32_t frame_duration, int32_t timescale)
virtual Representation * CopyRepresentation(const Representation &representation)
virtual void ForceSubsegmentStartswithSAP(uint32_t sap_value)
bool MatchAdaptationSet(const MediaInfo &media_info, bool content_protection_in_adaptation_set)
virtual void AddTrickPlayReference(const AdaptationSet *adaptation_set)
virtual void AddAdaptationSetSwitching(const AdaptationSet *adaptation_set)
std::optional< xml::XmlNode > GetXml()
AdaptationSet(const std::string &language, const MpdOptions &mpd_options, uint32_t *representation_counter)
void set_protected_content(const MediaInfo &media_info)
bool SwitchableAdaptationSet(const AdaptationSet &adaptation_set)
virtual void UpdateContentProtectionPssh(const std::string &drm_uuid, const std::string &pssh)
virtual void AddRole(Role role)
uint32_t id() const
AdaptationSetType specified in MPD.
Definition xml_node.h:164
bool AddAccessibilityElement(const std::string &scheme_id_uri, const std::string &value)
Definition xml_node.cc:359
bool AddLabelElement(const std::string &value)
Definition xml_node.cc:370
bool AddRoleElement(const std::string &scheme_id_uri, const std::string &value)
Definition xml_node.cc:365
bool AddEssentialProperty(const std::string &scheme_id_uri, const std::string &value)
Definition xml_node.cc:317
bool AddSupplementalProperty(const std::string &scheme_id_uri, const std::string &value)
Definition xml_node.cc:311
bool AddChild(XmlNode child)
Definition xml_node.cc:168
bool SetStringAttribute(const std::string &attribute_name, const std::string &attribute)
Definition xml_node.cc:211
bool SetId(uint32_t id)
Definition xml_node.cc:233
bool SetIntegerAttribute(const std::string &attribute_name, uint64_t number)
Definition xml_node.cc:218
All the methods that are virtual are virtual for mocking.
Defines Mpd Options.
Definition mpd_options.h:24