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:
72 std::string GetPictureAspectRatio(uint32_t width,
75 uint32_t pixel_height) {
76 const uint32_t scaled_width = pixel_width * width;
77 const uint32_t scaled_height = pixel_height * height;
78 const double par =
static_cast<double>(scaled_width) / scaled_height;
82 const uint32_t kLargestPossibleParY = 19;
86 double min_error = 1.0;
87 for (uint32_t den = 1; den <= kLargestPossibleParY; ++den) {
88 uint32_t num = par * den + 0.5;
89 double error = fabs(par - static_cast<double>(num) / den);
90 if (error < min_error) {
98 VLOG(2) <<
"width*pix_width : height*pixel_height (" << scaled_width <<
":" 99 << scaled_height <<
") reduced to " << par_num <<
":" << par_den
100 <<
" with error " << min_error <<
".";
102 return base::IntToString(par_num) +
":" + base::IntToString(par_den);
107 void AddPictureAspectRatio(
const MediaInfo::VideoInfo& video_info,
108 std::set<std::string>* picture_aspect_ratio) {
111 if (picture_aspect_ratio->size() > 1)
114 if (video_info.width() == 0 || video_info.height() == 0 ||
115 video_info.pixel_width() == 0 || video_info.pixel_height() == 0) {
120 picture_aspect_ratio->insert(
"bogus");
121 picture_aspect_ratio->insert(
"entries");
125 const std::string par = GetPictureAspectRatio(
126 video_info.width(), video_info.height(), video_info.pixel_width(),
127 video_info.pixel_height());
128 DVLOG(1) <<
"Setting par as: " << par
129 <<
" for video with width: " << video_info.width()
130 <<
" height: " << video_info.height()
131 <<
" pixel_width: " << video_info.pixel_width() <<
" pixel_height; " 132 << video_info.pixel_height();
133 picture_aspect_ratio->insert(par);
136 class RepresentationStateChangeListenerImpl
137 :
public RepresentationStateChangeListener {
140 RepresentationStateChangeListenerImpl(uint32_t representation_id,
141 AdaptationSet* adaptation_set)
142 : representation_id_(representation_id), adaptation_set_(adaptation_set) {
143 DCHECK(adaptation_set_);
145 ~RepresentationStateChangeListenerImpl()
override {}
148 void OnNewSegmentForRepresentation(int64_t start_time,
149 int64_t duration)
override {
150 adaptation_set_->OnNewSegmentForRepresentation(representation_id_,
151 start_time, duration);
154 void OnSetFrameRateForRepresentation(uint32_t frame_duration,
155 uint32_t timescale)
override {
156 adaptation_set_->OnSetFrameRateForRepresentation(representation_id_,
157 frame_duration, timescale);
161 const uint32_t representation_id_;
162 AdaptationSet*
const adaptation_set_;
164 DISALLOW_COPY_AND_ASSIGN(RepresentationStateChangeListenerImpl);
172 : representation_counter_(counter),
174 mpd_options_(mpd_options),
175 segments_aligned_(kSegmentAlignmentUnknown),
176 force_set_segment_alignment_(false) {
180 AdaptationSet::~AdaptationSet() {}
183 const uint32_t representation_id = (*representation_counter_)++;
186 std::unique_ptr<RepresentationStateChangeListener> listener(
187 new RepresentationStateChangeListenerImpl(representation_id,
this));
188 std::unique_ptr<Representation> new_representation(
new Representation(
189 media_info, mpd_options_, representation_id, std::move(listener)));
191 if (!new_representation->Init()) {
192 LOG(ERROR) <<
"Failed to initialize Representation.";
195 UpdateFromMediaInfo(media_info);
197 representation_map_[representation_ptr->
id()] = std::move(new_representation);
198 return representation_ptr;
205 std::unique_ptr<RepresentationStateChangeListener> listener(
206 new RepresentationStateChangeListenerImpl(representation.
id(),
this));
207 std::unique_ptr<Representation> new_representation(
210 UpdateFromMediaInfo(new_representation->GetMediaInfo());
212 representation_map_[representation_ptr->id()] = std::move(new_representation);
213 return representation_ptr;
218 content_protection_elements_.push_back(content_protection_element);
219 RemoveDuplicateAttributes(&content_protection_elements_.back());
223 const std::string& pssh) {
224 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
225 &content_protection_elements_);
241 bool suppress_representation_width =
false;
242 bool suppress_representation_height =
false;
243 bool suppress_representation_frame_rate =
false;
246 adaptation_set.
SetId(id_.value());
248 if (!language_.empty() && language_ !=
"und") {
253 if (video_widths_.size() == 1) {
254 suppress_representation_width =
true;
256 }
else if (video_widths_.size() > 1) {
259 if (video_heights_.size() == 1) {
260 suppress_representation_height =
true;
262 }
else if (video_heights_.size() > 1) {
266 if (video_frame_rates_.size() == 1) {
267 suppress_representation_frame_rate =
true;
269 video_frame_rates_.begin()->second);
270 }
else if (video_frame_rates_.size() > 1) {
272 video_frame_rates_.rbegin()->second);
277 if (mpd_options_.mpd_type == MpdType::kStatic) {
278 CheckStaticSegmentAlignment();
281 if (segments_aligned_ == kSegmentAlignmentTrue) {
283 mpd_options_.dash_profile == DashProfile::kOnDemand
284 ?
"subsegmentAlignment" 285 :
"segmentAlignment",
289 if (picture_aspect_ratio_.size() == 1)
292 if (!adaptation_set.AddContentProtectionElements(
293 content_protection_elements_)) {
294 return xml::scoped_xml_ptr<xmlNode>();
297 std::string trick_play_reference_ids;
298 for (
const AdaptationSet* adaptation_set : trick_play_references_) {
299 if (!trick_play_reference_ids.empty())
300 trick_play_reference_ids +=
',';
301 CHECK(adaptation_set->has_id());
302 trick_play_reference_ids += std::to_string(adaptation_set->id());
304 if (!trick_play_reference_ids.empty()) {
306 "http://dashif.org/guidelines/trickmode", trick_play_reference_ids);
309 std::string switching_ids;
310 for (
const AdaptationSet* adaptation_set : switchable_adaptation_sets_) {
311 if (!switching_ids.empty())
312 switching_ids +=
',';
313 CHECK(adaptation_set->has_id());
314 switching_ids += std::to_string(adaptation_set->id());
316 if (!switching_ids.empty()) {
318 "urn:mpeg:dash:adaptation-set-switching:2016", switching_ids);
321 for (AdaptationSet::Role role : roles_)
322 adaptation_set.
AddRoleElement(
"urn:mpeg:dash:role:2011", RoleToText(role));
324 for (
const auto& representation_pair : representation_map_) {
325 const auto& representation = representation_pair.second;
326 if (suppress_representation_width)
327 representation->SuppressOnce(Representation::kSuppressWidth);
328 if (suppress_representation_height)
329 representation->SuppressOnce(Representation::kSuppressHeight);
330 if (suppress_representation_frame_rate)
331 representation->SuppressOnce(Representation::kSuppressFrameRate);
332 xml::scoped_xml_ptr<xmlNode> child(representation->GetXml());
333 if (!child || !adaptation_set.
AddChild(std::move(child)))
334 return xml::scoped_xml_ptr<xmlNode>();
342 segment_alignment ? kSegmentAlignmentTrue : kSegmentAlignmentFalse;
343 force_set_segment_alignment_ =
true;
348 switchable_adaptation_sets_.push_back(adaptation_set);
360 if (mpd_options_.mpd_type == MpdType::kDynamic) {
361 CheckDynamicSegmentAlignment(representation_id, start_time, duration);
363 representation_segment_start_times_[representation_id].push_back(
369 uint32_t frame_duration,
370 uint32_t timescale) {
371 RecordFrameRate(frame_duration, timescale);
375 trick_play_references_.push_back(adaptation_set);
378 const std::list<Representation*> AdaptationSet::GetRepresentations()
const {
379 std::list<Representation*> representations;
380 for (
const auto& representation_pair : representation_map_) {
381 representations.push_back(representation_pair.second.get());
383 return representations;
387 return content_type_ ==
"video";
390 void AdaptationSet::UpdateFromMediaInfo(
const MediaInfo& media_info) {
393 if (media_info.has_video_info()) {
394 const MediaInfo::VideoInfo& video_info = media_info.video_info();
395 DCHECK(video_info.has_width());
396 DCHECK(video_info.has_height());
397 video_widths_.insert(video_info.width());
398 video_heights_.insert(video_info.height());
400 if (video_info.has_time_scale() && video_info.has_frame_duration())
401 RecordFrameRate(video_info.frame_duration(), video_info.time_scale());
403 AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
406 if (media_info.has_video_info()) {
407 content_type_ =
"video";
408 }
else if (media_info.has_audio_info()) {
409 content_type_ =
"audio";
410 }
else if (media_info.has_text_info()) {
411 content_type_ =
"text";
413 if (media_info.text_info().has_type() &&
414 (media_info.text_info().type() != MediaInfo::TextInfo::UNKNOWN)) {
415 roles_.insert(MediaInfoTextTypeToRole(media_info.text_info().type()));
444 void AdaptationSet::CheckDynamicSegmentAlignment(uint32_t representation_id,
447 if (segments_aligned_ == kSegmentAlignmentFalse ||
448 force_set_segment_alignment_) {
452 std::list<uint64_t>& current_representation_start_times =
453 representation_segment_start_times_[representation_id];
454 current_representation_start_times.push_back(start_time);
457 if (representation_segment_start_times_.size() != representation_map_.size())
460 DCHECK(!current_representation_start_times.empty());
461 const uint64_t expected_start_time =
462 current_representation_start_times.front();
463 for (
const auto& key_value : representation_segment_start_times_) {
464 const std::list<uint64_t>& representation_start_time = key_value.second;
468 if (representation_start_time.empty())
471 if (expected_start_time != representation_start_time.front()) {
472 VLOG(1) <<
"Seeing Misaligned segments with different start_times: " 473 << expected_start_time <<
" vs " 474 << representation_start_time.front();
477 segments_aligned_ = kSegmentAlignmentFalse;
478 representation_segment_start_times_.clear();
482 segments_aligned_ = kSegmentAlignmentTrue;
484 for (
auto& key_value : representation_segment_start_times_) {
485 std::list<uint64_t>& representation_start_time = key_value.second;
486 representation_start_time.pop_front();
492 void AdaptationSet::CheckStaticSegmentAlignment() {
493 if (segments_aligned_ == kSegmentAlignmentFalse ||
494 force_set_segment_alignment_) {
497 if (representation_segment_start_times_.empty())
499 if (representation_segment_start_times_.size() == 1) {
500 segments_aligned_ = kSegmentAlignmentTrue;
507 const std::list<uint64_t>& expected_time_line =
508 representation_segment_start_times_.begin()->second;
510 bool all_segment_time_line_same_length =
true;
512 RepresentationTimeline::const_iterator it =
513 representation_segment_start_times_.begin();
514 for (++it; it != representation_segment_start_times_.end(); ++it) {
515 const std::list<uint64_t>& other_time_line = it->second;
516 if (expected_time_line.size() != other_time_line.size()) {
517 all_segment_time_line_same_length =
false;
520 const std::list<uint64_t>* longer_list = &other_time_line;
521 const std::list<uint64_t>* shorter_list = &expected_time_line;
522 if (expected_time_line.size() > other_time_line.size()) {
523 shorter_list = &other_time_line;
524 longer_list = &expected_time_line;
527 if (!std::equal(shorter_list->begin(), shorter_list->end(),
528 longer_list->begin())) {
530 segments_aligned_ = kSegmentAlignmentFalse;
531 representation_segment_start_times_.clear();
542 if (!all_segment_time_line_same_length) {
543 segments_aligned_ = kSegmentAlignmentUnknown;
547 segments_aligned_ = kSegmentAlignmentTrue;
552 void AdaptationSet::RecordFrameRate(uint32_t frame_duration,
553 uint32_t timescale) {
554 if (frame_duration == 0) {
555 LOG(ERROR) <<
"Frame duration is 0 and cannot be set.";
558 video_frame_rates_[
static_cast<double>(timescale) / frame_duration] =
559 base::IntToString(timescale) +
"/" + base::IntToString(frame_duration);
void OnSetFrameRateForRepresentation(uint32_t representation_id, uint32_t frame_duration, uint32_t timescale)
AdaptationSetType specified in MPD.
virtual Representation * AddRepresentation(const MediaInfo &media_info)
scoped_xml_ptr< xmlNode > PassScopedPtr()
All the methods that are virtual are virtual for mocking.
void AddEssentialProperty(const std::string &scheme_id_uri, const std::string &value)
virtual void AddContentProtectionElement(const ContentProtectionElement &element)
virtual Representation * CopyRepresentation(const Representation &representation)
void SetStringAttribute(const char *attribute_name, const std::string &attribute)
bool AddChild(scoped_xml_ptr< xmlNode > child)
virtual void AddRole(Role role)
virtual void UpdateContentProtectionPssh(const std::string &drm_uuid, const std::string &pssh)
AdaptationSet(const std::string &language, const MpdOptions &mpd_options, uint32_t *representation_counter)
virtual void ForceSetSegmentAlignment(bool segment_alignment)
void AddSupplementalProperty(const std::string &scheme_id_uri, const std::string &value)
xml::scoped_xml_ptr< xmlNode > GetXml()
virtual void AddAdaptationSetSwitching(const AdaptationSet *adaptation_set)
void SetIntegerAttribute(const char *attribute_name, uint64_t number)
void AddRoleElement(const std::string &scheme_id_uri, const std::string &value)
void OnNewSegmentForRepresentation(uint32_t representation_id, uint64_t start_time, uint64_t duration)
virtual void AddTrickPlayReference(const AdaptationSet *adaptation_set)