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(uint64_t start_time,
149 uint64_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);
171 base::AtomicSequenceNumber* counter)
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_->GetNext();
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_.dash_profile == DashProfile::kOnDemand) {
278 CheckVodSegmentAlignment();
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);
361 if (mpd_options_.dash_profile == DashProfile::kLive) {
362 CheckLiveSegmentAlignment(representation_id, start_time, duration);
364 representation_segment_start_times_[representation_id].push_back(
370 uint32_t frame_duration,
371 uint32_t timescale) {
372 RecordFrameRate(frame_duration, timescale);
376 trick_play_references_.push_back(adaptation_set);
379 const std::list<Representation*> AdaptationSet::GetRepresentations()
const {
380 std::list<Representation*> representations;
381 for (
const auto& representation_pair : representation_map_) {
382 representations.push_back(representation_pair.second.get());
384 return representations;
388 return content_type_ ==
"video";
391 void AdaptationSet::UpdateFromMediaInfo(
const MediaInfo& media_info) {
394 if (media_info.has_video_info()) {
395 const MediaInfo::VideoInfo& video_info = media_info.video_info();
396 DCHECK(video_info.has_width());
397 DCHECK(video_info.has_height());
398 video_widths_.insert(video_info.width());
399 video_heights_.insert(video_info.height());
401 if (video_info.has_time_scale() && video_info.has_frame_duration())
402 RecordFrameRate(video_info.frame_duration(), video_info.time_scale());
404 AddPictureAspectRatio(video_info, &picture_aspect_ratio_);
407 if (media_info.has_video_info()) {
408 content_type_ =
"video";
409 }
else if (media_info.has_audio_info()) {
410 content_type_ =
"audio";
411 }
else if (media_info.has_text_info()) {
412 content_type_ =
"text";
414 if (media_info.text_info().has_type() &&
415 (media_info.text_info().type() != MediaInfo::TextInfo::UNKNOWN)) {
416 roles_.insert(MediaInfoTextTypeToRole(media_info.text_info().type()));
445 void AdaptationSet::CheckLiveSegmentAlignment(uint32_t representation_id,
448 if (segments_aligned_ == kSegmentAlignmentFalse ||
449 force_set_segment_alignment_) {
453 std::list<uint64_t>& representation_start_times =
454 representation_segment_start_times_[representation_id];
455 representation_start_times.push_back(start_time);
458 if (representation_segment_start_times_.size() != representation_map_.size())
461 DCHECK(!representation_start_times.empty());
462 const uint64_t expected_start_time = representation_start_times.front();
463 for (RepresentationTimeline::const_iterator it =
464 representation_segment_start_times_.begin();
465 it != representation_segment_start_times_.end(); ++it) {
469 if (it->second.empty())
472 if (expected_start_time != it->second.front()) {
475 segments_aligned_ = kSegmentAlignmentFalse;
476 representation_segment_start_times_.clear();
480 segments_aligned_ = kSegmentAlignmentTrue;
482 for (RepresentationTimeline::iterator it =
483 representation_segment_start_times_.begin();
484 it != representation_segment_start_times_.end(); ++it) {
485 it->second.pop_front();
491 void AdaptationSet::CheckVodSegmentAlignment() {
492 if (segments_aligned_ == kSegmentAlignmentFalse ||
493 force_set_segment_alignment_) {
496 if (representation_segment_start_times_.empty())
498 if (representation_segment_start_times_.size() == 1) {
499 segments_aligned_ = kSegmentAlignmentTrue;
506 const std::list<uint64_t>& expected_time_line =
507 representation_segment_start_times_.begin()->second;
509 bool all_segment_time_line_same_length =
true;
511 RepresentationTimeline::const_iterator it =
512 representation_segment_start_times_.begin();
513 for (++it; it != representation_segment_start_times_.end(); ++it) {
514 const std::list<uint64_t>& other_time_line = it->second;
515 if (expected_time_line.size() != other_time_line.size()) {
516 all_segment_time_line_same_length =
false;
519 const std::list<uint64_t>* longer_list = &other_time_line;
520 const std::list<uint64_t>* shorter_list = &expected_time_line;
521 if (expected_time_line.size() > other_time_line.size()) {
522 shorter_list = &other_time_line;
523 longer_list = &expected_time_line;
526 if (!std::equal(shorter_list->begin(), shorter_list->end(),
527 longer_list->begin())) {
529 segments_aligned_ = kSegmentAlignmentFalse;
530 representation_segment_start_times_.clear();
541 if (!all_segment_time_line_same_length) {
542 segments_aligned_ = kSegmentAlignmentUnknown;
546 segments_aligned_ = kSegmentAlignmentTrue;
551 void AdaptationSet::RecordFrameRate(uint32_t frame_duration,
552 uint32_t timescale) {
553 if (frame_duration == 0) {
554 LOG(ERROR) <<
"Frame duration is 0 and cannot be set.";
557 video_frame_rates_[
static_cast<double>(timescale) / frame_duration] =
558 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, base::AtomicSequenceNumber *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)