7 #include "packager/mpd/base/adaptation_set.h"
11 #include "packager/base/logging.h"
12 #include "packager/base/strings/string_number_conversions.h"
13 #include "packager/mpd/base/media_info.pb.h"
14 #include "packager/mpd/base/mpd_options.h"
15 #include "packager/mpd/base/mpd_utils.h"
16 #include "packager/mpd/base/representation.h"
17 #include "packager/mpd/base/xml/xml_node.h"
22 AdaptationSet::Role MediaInfoTextTypeToRole(
23 MediaInfo::TextInfo::TextType type) {
25 case MediaInfo::TextInfo::UNKNOWN:
26 LOG(WARNING) <<
"Unknown text type, assuming subtitle.";
27 return AdaptationSet::kRoleSubtitle;
28 case MediaInfo::TextInfo::CAPTION:
29 return AdaptationSet::kRoleCaption;
30 case MediaInfo::TextInfo::SUBTITLE:
31 return AdaptationSet::kRoleSubtitle;
33 NOTREACHED() <<
"Unknown MediaInfo TextType: " << type
34 <<
" assuming subtitle.";
35 return AdaptationSet::kRoleSubtitle;
39 std::string RoleToText(AdaptationSet::Role role) {
43 case AdaptationSet::kRoleCaption:
45 case AdaptationSet::kRoleSubtitle:
47 case AdaptationSet::kRoleMain:
49 case AdaptationSet::kRoleAlternate:
51 case AdaptationSet::kRoleSupplementary:
52 return "supplementary";
53 case AdaptationSet::kRoleCommentary:
55 case AdaptationSet::kRoleDub:
69 std::string GetPictureAspectRatio(uint32_t width,
72 uint32_t pixel_height) {
73 const uint32_t scaled_width = pixel_width * width;
74 const uint32_t scaled_height = pixel_height * height;
75 const double par =
static_cast<double>(scaled_width) / scaled_height;
79 const uint32_t kLargestPossibleParY = 19;
83 double min_error = 1.0;
84 for (uint32_t den = 1; den <= kLargestPossibleParY; ++den) {
85 uint32_t num = par * den + 0.5;
86 double error = fabs(par -
static_cast<double>(num) / den);
87 if (error < min_error) {
95 VLOG(2) <<
"width*pix_width : height*pixel_height (" << scaled_width <<
":"
96 << scaled_height <<
") reduced to " << par_num <<
":" << par_den
97 <<
" with error " << min_error <<
".";
99 return base::IntToString(par_num) +
":" + base::IntToString(par_den);
104 void AddPictureAspectRatio(
const MediaInfo::VideoInfo& video_info,
105 std::set<std::string>* picture_aspect_ratio) {
108 if (picture_aspect_ratio->size() > 1)
111 if (video_info.width() == 0 || video_info.height() == 0 ||
112 video_info.pixel_width() == 0 || video_info.pixel_height() == 0) {
117 picture_aspect_ratio->insert(
"bogus");
118 picture_aspect_ratio->insert(
"entries");
122 const std::string par = GetPictureAspectRatio(
123 video_info.width(), video_info.height(), video_info.pixel_width(),
124 video_info.pixel_height());
125 DVLOG(1) <<
"Setting par as: " << par
126 <<
" for video with width: " << video_info.width()
127 <<
" height: " << video_info.height()
128 <<
" pixel_width: " << video_info.pixel_width() <<
" pixel_height; "
129 << video_info.pixel_height();
130 picture_aspect_ratio->insert(par);
133 class RepresentationStateChangeListenerImpl
134 :
public RepresentationStateChangeListener {
137 RepresentationStateChangeListenerImpl(uint32_t representation_id,
138 AdaptationSet* adaptation_set)
139 : representation_id_(representation_id), adaptation_set_(adaptation_set) {
140 DCHECK(adaptation_set_);
142 ~RepresentationStateChangeListenerImpl()
override {}
145 void OnNewSegmentForRepresentation(int64_t start_time,
146 int64_t duration)
override {
147 adaptation_set_->OnNewSegmentForRepresentation(representation_id_,
148 start_time, duration);
151 void OnSetFrameRateForRepresentation(uint32_t frame_duration,
152 uint32_t timescale)
override {
153 adaptation_set_->OnSetFrameRateForRepresentation(representation_id_,
154 frame_duration, timescale);
158 const uint32_t representation_id_;
159 AdaptationSet*
const adaptation_set_;
161 DISALLOW_COPY_AND_ASSIGN(RepresentationStateChangeListenerImpl);
169 : representation_counter_(counter),
171 mpd_options_(mpd_options),
172 segments_aligned_(kSegmentAlignmentUnknown),
173 force_set_segment_alignment_(false) {
177 AdaptationSet::~AdaptationSet() {}
180 const uint32_t representation_id = (*representation_counter_)++;
183 std::unique_ptr<RepresentationStateChangeListener> listener(
184 new RepresentationStateChangeListenerImpl(representation_id,
this));
185 std::unique_ptr<Representation> new_representation(
new Representation(
186 media_info, mpd_options_, representation_id, std::move(listener)));
188 if (!new_representation->Init()) {
189 LOG(ERROR) <<
"Failed to initialize Representation.";
192 UpdateFromMediaInfo(media_info);
194 representation_map_[representation_ptr->
id()] = std::move(new_representation);
195 return representation_ptr;
202 std::unique_ptr<RepresentationStateChangeListener> listener(
203 new RepresentationStateChangeListenerImpl(representation.
id(),
this));
204 std::unique_ptr<Representation> new_representation(
207 UpdateFromMediaInfo(new_representation->GetMediaInfo());
209 representation_map_[representation_ptr->
id()] = std::move(new_representation);
210 return representation_ptr;
215 content_protection_elements_.push_back(content_protection_element);
216 RemoveDuplicateAttributes(&content_protection_elements_.back());
220 const std::string& pssh) {
221 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
222 &content_protection_elements_);
226 const std::string& value) {
227 accessibilities_.push_back(Accessibility{scheme, value});
243 bool suppress_representation_width =
false;
244 bool suppress_representation_height =
false;
245 bool suppress_representation_frame_rate =
false;
247 if (id_ && !adaptation_set.
SetId(id_.value()))
248 return base::nullopt;
250 return base::nullopt;
251 if (!language_.empty() && language_ !=
"und" &&
253 return base::nullopt;
257 if (video_widths_.size() == 1) {
258 suppress_representation_width =
true;
260 return base::nullopt;
261 }
else if (video_widths_.size() > 1) {
263 *video_widths_.rbegin())) {
264 return base::nullopt;
267 if (video_heights_.size() == 1) {
268 suppress_representation_height =
true;
270 return base::nullopt;
271 }
else if (video_heights_.size() > 1) {
273 *video_heights_.rbegin())) {
274 return base::nullopt;
278 if (video_frame_rates_.size() == 1) {
279 suppress_representation_frame_rate =
true;
281 "frameRate", video_frame_rates_.begin()->second)) {
282 return base::nullopt;
284 }
else if (video_frame_rates_.size() > 1) {
286 "maxFrameRate", video_frame_rates_.rbegin()->second)) {
287 return base::nullopt;
293 if (mpd_options_.mpd_type == MpdType::kStatic) {
294 CheckStaticSegmentAlignment();
297 if (segments_aligned_ == kSegmentAlignmentTrue) {
299 mpd_options_.dash_profile == DashProfile::kOnDemand
300 ?
"subsegmentAlignment"
301 :
"segmentAlignment",
303 return base::nullopt;
307 if (picture_aspect_ratio_.size() == 1 &&
309 *picture_aspect_ratio_.begin())) {
310 return base::nullopt;
313 if (!adaptation_set.AddContentProtectionElements(
314 content_protection_elements_)) {
315 return base::nullopt;
318 std::string trick_play_reference_ids;
319 for (
const AdaptationSet* adaptation_set : trick_play_references_) {
321 if (!trick_play_reference_ids.empty())
322 trick_play_reference_ids +=
' ';
323 CHECK(adaptation_set->has_id());
324 trick_play_reference_ids += std::to_string(adaptation_set->id());
326 if (!trick_play_reference_ids.empty() &&
328 "http://dashif.org/guidelines/trickmode", trick_play_reference_ids)) {
329 return base::nullopt;
332 std::string switching_ids;
333 for (
const AdaptationSet* adaptation_set : switchable_adaptation_sets_) {
335 if (!switching_ids.empty())
336 switching_ids +=
',';
337 CHECK(adaptation_set->has_id());
338 switching_ids += std::to_string(adaptation_set->id());
340 if (!switching_ids.empty() &&
342 "urn:mpeg:dash:adaptation-set-switching:2016", switching_ids)) {
343 return base::nullopt;
346 for (
const AdaptationSet::Accessibility& accessibility : accessibilities_) {
348 accessibility.value)) {
349 return base::nullopt;
353 for (AdaptationSet::Role role : roles_) {
356 return base::nullopt;
360 for (
const auto& representation_pair : representation_map_) {
361 const auto& representation = representation_pair.second;
362 if (suppress_representation_width)
363 representation->SuppressOnce(Representation::kSuppressWidth);
364 if (suppress_representation_height)
365 representation->SuppressOnce(Representation::kSuppressHeight);
366 if (suppress_representation_frame_rate)
367 representation->SuppressOnce(Representation::kSuppressFrameRate);
368 auto child = representation->GetXml();
369 if (!child || !adaptation_set.
AddChild(std::move(*child)))
370 return base::nullopt;
373 return std::move(adaptation_set);
378 segment_alignment ? kSegmentAlignmentTrue : kSegmentAlignmentFalse;
379 force_set_segment_alignment_ =
true;
384 switchable_adaptation_sets_.push_back(adaptation_set);
396 if (mpd_options_.mpd_type == MpdType::kDynamic) {
397 CheckDynamicSegmentAlignment(representation_id, start_time, duration);
399 representation_segment_start_times_[representation_id].push_back(
405 uint32_t frame_duration,
406 uint32_t timescale) {
407 RecordFrameRate(frame_duration, timescale);
411 trick_play_references_.push_back(adaptation_set);
414 const std::list<Representation*> AdaptationSet::GetRepresentations()
const {
415 std::list<Representation*> representations;
416 for (
const auto& representation_pair : representation_map_) {
417 representations.push_back(representation_pair.second.get());
419 return representations;
423 return content_type_ ==
"video";
426 void AdaptationSet::UpdateFromMediaInfo(
const MediaInfo& media_info) {
429 if (media_info.has_video_info()) {
430 const MediaInfo::VideoInfo& video_info = media_info.video_info();
431 DCHECK(video_info.has_width());
432 DCHECK(video_info.has_height());
433 video_widths_.insert(video_info.width());
434 video_heights_.insert(video_info.height());
436 if (video_info.has_time_scale() && video_info.has_frame_duration())
437 RecordFrameRate(video_info.frame_duration(), video_info.time_scale());
439 AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
442 if (media_info.has_video_info()) {
443 content_type_ =
"video";
444 }
else if (media_info.has_audio_info()) {
445 content_type_ =
"audio";
446 }
else if (media_info.has_text_info()) {
447 content_type_ =
"text";
449 if (media_info.text_info().has_type() &&
450 (media_info.text_info().type() != MediaInfo::TextInfo::UNKNOWN)) {
451 roles_.insert(MediaInfoTextTypeToRole(media_info.text_info().type()));
480 void AdaptationSet::CheckDynamicSegmentAlignment(uint32_t representation_id,
483 if (segments_aligned_ == kSegmentAlignmentFalse ||
484 force_set_segment_alignment_) {
488 std::list<uint64_t>& current_representation_start_times =
489 representation_segment_start_times_[representation_id];
490 current_representation_start_times.push_back(start_time);
493 if (representation_segment_start_times_.size() != representation_map_.size())
496 DCHECK(!current_representation_start_times.empty());
497 const uint64_t expected_start_time =
498 current_representation_start_times.front();
499 for (
const auto& key_value : representation_segment_start_times_) {
500 const std::list<uint64_t>& representation_start_time = key_value.second;
504 if (representation_start_time.empty())
507 if (expected_start_time != representation_start_time.front()) {
508 VLOG(1) <<
"Seeing Misaligned segments with different start_times: "
509 << expected_start_time <<
" vs "
510 << representation_start_time.front();
513 segments_aligned_ = kSegmentAlignmentFalse;
514 representation_segment_start_times_.clear();
518 segments_aligned_ = kSegmentAlignmentTrue;
520 for (
auto& key_value : representation_segment_start_times_) {
521 std::list<uint64_t>& representation_start_time = key_value.second;
522 representation_start_time.pop_front();
528 void AdaptationSet::CheckStaticSegmentAlignment() {
529 if (segments_aligned_ == kSegmentAlignmentFalse ||
530 force_set_segment_alignment_) {
533 if (representation_segment_start_times_.empty())
535 if (representation_segment_start_times_.size() == 1) {
536 segments_aligned_ = kSegmentAlignmentTrue;
543 const std::list<uint64_t>& expected_time_line =
544 representation_segment_start_times_.begin()->second;
546 bool all_segment_time_line_same_length =
true;
548 RepresentationTimeline::const_iterator it =
549 representation_segment_start_times_.begin();
550 for (++it; it != representation_segment_start_times_.end(); ++it) {
551 const std::list<uint64_t>& other_time_line = it->second;
552 if (expected_time_line.size() != other_time_line.size()) {
553 all_segment_time_line_same_length =
false;
556 const std::list<uint64_t>* longer_list = &other_time_line;
557 const std::list<uint64_t>* shorter_list = &expected_time_line;
558 if (expected_time_line.size() > other_time_line.size()) {
559 shorter_list = &other_time_line;
560 longer_list = &expected_time_line;
563 if (!std::equal(shorter_list->begin(), shorter_list->end(),
564 longer_list->begin())) {
566 segments_aligned_ = kSegmentAlignmentFalse;
567 representation_segment_start_times_.clear();
578 if (!all_segment_time_line_same_length) {
579 segments_aligned_ = kSegmentAlignmentUnknown;
583 segments_aligned_ = kSegmentAlignmentTrue;
588 void AdaptationSet::RecordFrameRate(uint32_t frame_duration,
589 uint32_t timescale) {
590 if (frame_duration == 0) {
591 LOG(ERROR) <<
"Frame duration is 0 and cannot be set.";
594 video_frame_rates_[
static_cast<double>(timescale) / frame_duration] =
595 base::IntToString(timescale) +
"/" + base::IntToString(frame_duration);