7 #include <packager/mpd/base/adaptation_set.h>
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>
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>
27 AdaptationSet::Role MediaInfoTextTypeToRole(
28 MediaInfo::TextInfo::TextType 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;
38 NOTIMPLEMENTED() <<
"Unknown MediaInfo TextType: " << type
39 <<
" assuming subtitle.";
40 return AdaptationSet::kRoleSubtitle;
44 std::string RoleToText(AdaptationSet::Role role) {
48 case AdaptationSet::kRoleCaption:
50 case AdaptationSet::kRoleSubtitle:
52 case AdaptationSet::kRoleMain:
54 case AdaptationSet::kRoleAlternate:
56 case AdaptationSet::kRoleSupplementary:
57 return "supplementary";
58 case AdaptationSet::kRoleCommentary:
60 case AdaptationSet::kRoleDub:
62 case AdaptationSet::kRoleDescription:
64 case AdaptationSet::kRoleSign:
66 case AdaptationSet::kRoleMetadata:
68 case AdaptationSet::kRoleEnhancedAudioIntelligibility:
69 return "enhanced-audio-intelligibility";
70 case AdaptationSet::kRoleEmergency:
72 case AdaptationSet::kRoleForcedSubtitle:
73 return "forced-subtitle";
74 case AdaptationSet::kRoleEasyreader:
76 case AdaptationSet::kRoleKaraoke:
90 std::string GetPictureAspectRatio(uint32_t 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;
100 const uint32_t kLargestPossibleParY = 19;
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) {
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 <<
".";
120 return absl::StrFormat(
"%d:%d", par_num, par_den);
125 void AddPictureAspectRatio(
const MediaInfo::VideoInfo& video_info,
126 std::set<std::string>* picture_aspect_ratio) {
129 if (picture_aspect_ratio->size() > 1)
132 if (video_info.width() == 0 || video_info.height() == 0 ||
133 video_info.pixel_width() == 0 || video_info.pixel_height() == 0) {
138 picture_aspect_ratio->insert(
"bogus");
139 picture_aspect_ratio->insert(
"entries");
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);
154 class RepresentationStateChangeListenerImpl
155 :
public RepresentationStateChangeListener {
158 RepresentationStateChangeListenerImpl(uint32_t representation_id,
159 AdaptationSet* adaptation_set)
160 : representation_id_(representation_id), adaptation_set_(adaptation_set) {
161 DCHECK(adaptation_set_);
163 ~RepresentationStateChangeListenerImpl()
override {}
166 void OnNewSegmentForRepresentation(int64_t start_time,
167 int64_t duration)
override {
168 adaptation_set_->OnNewSegmentForRepresentation(representation_id_,
169 start_time, duration);
172 void OnSetFrameRateForRepresentation(int32_t frame_duration,
173 int32_t timescale)
override {
174 adaptation_set_->OnSetFrameRateForRepresentation(representation_id_,
175 frame_duration, timescale);
179 const uint32_t representation_id_;
180 AdaptationSet*
const adaptation_set_;
182 DISALLOW_COPY_AND_ASSIGN(RepresentationStateChangeListenerImpl);
190 : representation_counter_(counter),
192 mpd_options_(mpd_options),
193 protected_content_(nullptr) {
197 AdaptationSet::~AdaptationSet() {
198 delete protected_content_;
202 DCHECK(!protected_content_);
204 new MediaInfo::ProtectedContent(media_info.protected_content());
209 bool ProtectedContentEq(
210 const MediaInfo::ProtectedContent& content_protection1,
211 const MediaInfo::ProtectedContent& content_protection2) {
212 return content_protection1.SerializeAsString() ==
213 content_protection2.SerializeAsString();
217 const MediaInfo& media_info,
218 bool content_protection_in_adaptation_set) {
219 if (codec_ != GetBaseCodec(media_info))
222 if (!content_protection_in_adaptation_set)
225 if (!protected_content_)
226 return !media_info.has_protected_content();
228 if (!media_info.has_protected_content())
231 return ProtectedContentEq(*protected_content_,
232 media_info.protected_content());
235 std::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());
253 return GetUUIDs(protected_content_) ==
261 const uint32_t representation_id = media_info.has_index()
263 : (*representation_counter_)++;
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)));
272 if (!new_representation->Init()) {
273 LOG(ERROR) <<
"Failed to initialize Representation.";
276 UpdateFromMediaInfo(media_info);
278 representation_map_[representation_ptr->
id()] = std::move(new_representation);
279 return representation_ptr;
286 std::unique_ptr<RepresentationStateChangeListener> listener(
287 new RepresentationStateChangeListenerImpl(representation.
id(),
this));
288 std::unique_ptr<Representation> new_representation(
291 UpdateFromMediaInfo(new_representation->GetMediaInfo());
293 representation_map_[representation_ptr->
id()] = std::move(new_representation);
294 return representation_ptr;
299 content_protection_elements_.push_back(content_protection_element);
300 RemoveDuplicateAttributes(&content_protection_elements_.back());
304 const std::string& pssh) {
305 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
306 &content_protection_elements_);
310 const std::string& value) {
311 accessibilities_.push_back(Accessibility{scheme, value});
327 bool suppress_representation_width =
false;
328 bool suppress_representation_height =
false;
329 bool suppress_representation_frame_rate =
false;
331 if (id_ && !adaptation_set.
SetId(id_.value()))
335 if (!language_.empty() && language_ !=
"und" &&
341 if (video_widths_.size() == 1) {
342 suppress_representation_width =
true;
345 }
else if (video_widths_.size() > 1) {
347 *video_widths_.rbegin())) {
352 if (video_heights_.size() == 1) {
353 suppress_representation_height =
true;
356 }
else if (video_heights_.size() > 1) {
358 *video_heights_.rbegin())) {
363 if (subsegment_start_with_sap_) {
365 subsegment_start_with_sap_))
367 }
else if (start_with_sap_) {
372 if (video_frame_rates_.size() == 1) {
373 suppress_representation_frame_rate =
true;
375 "frameRate", video_frame_rates_.begin()->second)) {
378 }
else if (video_frame_rates_.size() > 1) {
380 "maxFrameRate", video_frame_rates_.rbegin()->second)) {
386 if (
IsVideo() && matrix_coefficients_ > 0 &&
388 "urn:mpeg:mpegB:cicp:MatrixCoefficients",
389 std::to_string(matrix_coefficients_))) {
394 if (
IsVideo() && color_primaries_ > 0 &&
396 "urn:mpeg:mpegB:cicp:ColourPrimaries",
397 std::to_string(color_primaries_))) {
402 if (
IsVideo() && transfer_characteristics_ > 0 &&
404 "urn:mpeg:mpegB:cicp:TransferCharacteristics",
405 std::to_string(transfer_characteristics_))) {
411 if (mpd_options_.mpd_type == MpdType::kStatic) {
412 CheckStaticSegmentAlignment();
415 if (segments_aligned_ == kSegmentAlignmentTrue) {
417 mpd_options_.dash_profile == DashProfile::kOnDemand
418 ?
"subsegmentAlignment"
419 :
"segmentAlignment",
425 if (picture_aspect_ratio_.size() == 1 &&
427 *picture_aspect_ratio_.begin())) {
431 if (!adaptation_set.AddContentProtectionElements(
432 content_protection_elements_)) {
436 std::string trick_play_reference_ids;
437 for (
const AdaptationSet* tp_adaptation_set : trick_play_references_) {
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());
444 if (!trick_play_reference_ids.empty() &&
446 "http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) {
450 std::string switching_ids;
451 for (
const AdaptationSet* s_adaptation_set : switchable_adaptation_sets_) {
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());
458 if (!switching_ids.empty() &&
460 "urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) {
464 for (
const AdaptationSet::Accessibility& accessibility : accessibilities_) {
466 accessibility.value)) {
471 for (AdaptationSet::Role role : roles_) {
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)))
494 return adaptation_set;
499 segment_alignment ? kSegmentAlignmentTrue : kSegmentAlignmentFalse;
500 force_set_segment_alignment_ =
true;
505 switchable_adaptation_sets_.push_back(adaptation_set);
509 subsegment_start_with_sap_ = sap_value;
513 start_with_sap_ = sap_value;
525 if (mpd_options_.mpd_type == MpdType::kDynamic) {
526 CheckDynamicSegmentAlignment(representation_id, start_time, duration);
528 representation_segment_start_times_[representation_id].push_back(
534 int32_t frame_duration,
536 RecordFrameRate(frame_duration, timescale);
540 trick_play_references_.push_back(adaptation_set);
543 const 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());
548 return representations;
552 return content_type_ ==
"video";
555 void AdaptationSet::UpdateFromMediaInfo(
const MediaInfo& media_info) {
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());
565 if (video_info.has_time_scale() && video_info.has_frame_duration())
566 RecordFrameRate(video_info.frame_duration(), video_info.time_scale());
568 AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
573 if (media_info.has_index()) {
574 if (index_.has_value()) {
575 index_ = std::min(index_.value(), media_info.index());
577 index_ = media_info.index();
581 if (media_info.has_dash_label())
582 label_ = media_info.dash_label();
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";
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()));
622 void AdaptationSet::CheckDynamicSegmentAlignment(uint32_t representation_id,
625 if (segments_aligned_ == kSegmentAlignmentFalse ||
626 force_set_segment_alignment_) {
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);
635 if (representation_segment_start_times_.size() != representation_map_.size())
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;
646 if (representation_start_time.empty())
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();
655 segments_aligned_ = kSegmentAlignmentFalse;
656 representation_segment_start_times_.clear();
660 segments_aligned_ = kSegmentAlignmentTrue;
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();
670 void AdaptationSet::CheckStaticSegmentAlignment() {
671 if (segments_aligned_ == kSegmentAlignmentFalse ||
672 force_set_segment_alignment_) {
675 if (representation_segment_start_times_.empty())
677 if (representation_segment_start_times_.size() == 1) {
678 segments_aligned_ = kSegmentAlignmentTrue;
685 const std::list<int64_t>& expected_time_line =
686 representation_segment_start_times_.begin()->second;
688 bool all_segment_time_line_same_length =
true;
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;
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;
705 if (!std::equal(shorter_list->begin(), shorter_list->end(),
706 longer_list->begin())) {
708 segments_aligned_ = kSegmentAlignmentFalse;
709 representation_segment_start_times_.clear();
720 if (!all_segment_time_line_same_length) {
721 segments_aligned_ = kSegmentAlignmentUnknown;
725 segments_aligned_ = kSegmentAlignmentTrue;
730 void 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.";
735 video_frame_rates_[
static_cast<double>(timescale) / frame_duration] =
736 absl::StrFormat(
"%d/%d", timescale, frame_duration);
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)
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)
const MediaInfo::ProtectedContent * protected_content() const
Return ProtectedContent.
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)
AdaptationSetType specified in MPD.
bool AddAccessibilityElement(const std::string &scheme_id_uri, const std::string &value)
bool AddLabelElement(const std::string &value)
bool AddRoleElement(const std::string &scheme_id_uri, const std::string &value)
bool AddEssentialProperty(const std::string &scheme_id_uri, const std::string &value)
bool AddSupplementalProperty(const std::string &scheme_id_uri, const std::string &value)
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.