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