7 #include "packager/mpd/base/representation.h" 9 #include "packager/base/logging.h" 10 #include "packager/mpd/base/mpd_options.h" 11 #include "packager/mpd/base/mpd_utils.h" 12 #include "packager/mpd/base/xml/xml_node.h" 17 std::string GetMimeType(
const std::string& prefix,
18 MediaInfo::ContainerType container_type) {
19 switch (container_type) {
20 case MediaInfo::CONTAINER_MP4:
21 return prefix +
"/mp4";
22 case MediaInfo::CONTAINER_MPEG2_TS:
24 return prefix +
"/MP2T";
25 case MediaInfo::CONTAINER_WEBM:
26 return prefix +
"/webm";
32 LOG(ERROR) <<
"Unrecognized container type: " << container_type;
39 bool HasRequiredVideoFields(
const MediaInfo_VideoInfo& video_info) {
40 if (!video_info.has_height() || !video_info.has_width()) {
42 <<
"Width and height are required fields for generating a valid MPD.";
47 LOG_IF(WARNING, !video_info.has_time_scale())
48 <<
"Video info does not contain timescale required for " 49 "calculating framerate. @frameRate is required for DASH IOP.";
50 LOG_IF(WARNING, !video_info.has_pixel_width())
51 <<
"Video info does not contain pixel_width to calculate the sample " 52 "aspect ratio required for DASH IOP.";
53 LOG_IF(WARNING, !video_info.has_pixel_height())
54 <<
"Video info does not contain pixel_height to calculate the sample " 55 "aspect ratio required for DASH IOP.";
59 uint32_t GetTimeScale(
const MediaInfo& media_info) {
60 if (media_info.has_reference_time_scale()) {
61 return media_info.reference_time_scale();
64 if (media_info.has_video_info()) {
65 return media_info.video_info().time_scale();
68 if (media_info.has_audio_info()) {
69 return media_info.audio_info().time_scale();
72 LOG(WARNING) <<
"No timescale specified, using 1 as timescale.";
76 uint64_t LastSegmentStartTime(
const SegmentInfo& segment_info) {
77 return segment_info.start_time + segment_info.duration * segment_info.repeat;
81 uint64_t LastSegmentEndTime(
const SegmentInfo& segment_info) {
82 return segment_info.start_time +
83 segment_info.duration * (segment_info.repeat + 1);
86 uint64_t LatestSegmentStartTime(
const std::list<SegmentInfo>& segments) {
87 DCHECK(!segments.empty());
88 const SegmentInfo& latest_segment = segments.back();
89 return LastSegmentStartTime(latest_segment);
94 int SearchTimedOutRepeatIndex(uint64_t timeshift_limit,
95 const SegmentInfo& segment_info) {
96 DCHECK_LE(timeshift_limit, LastSegmentEndTime(segment_info));
97 if (timeshift_limit < segment_info.start_time)
100 return (timeshift_limit - segment_info.start_time) / segment_info.duration;
106 const MediaInfo& media_info,
109 std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
110 : media_info_(media_info),
113 mpd_options_(mpd_options),
115 state_change_listener_(
std::move(state_change_listener)),
116 output_suppression_flags_(0) {}
120 uint64_t presentation_time_offset,
121 std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
123 representation.mpd_options_,
125 std::move(state_change_listener)) {
126 mime_type_ = representation.mime_type_;
127 codecs_ = representation.codecs_;
129 start_number_ = representation.start_number_;
130 for (
const SegmentInfo& segment_info : representation.segment_infos_)
131 start_number_ += segment_info.repeat + 1;
133 media_info_.set_presentation_time_offset(presentation_time_offset);
136 Representation::~Representation() {}
139 if (!AtLeastOneTrue(media_info_.has_video_info(),
140 media_info_.has_audio_info(),
141 media_info_.has_text_info())) {
145 LOG(ERROR) <<
"Representation needs one of video, audio, or text.";
149 if (MoreThanOneTrue(media_info_.has_video_info(),
150 media_info_.has_audio_info(),
151 media_info_.has_text_info())) {
152 LOG(ERROR) <<
"Only one of VideoInfo, AudioInfo, or TextInfo can be set.";
156 if (media_info_.container_type() == MediaInfo::CONTAINER_UNKNOWN) {
157 LOG(ERROR) <<
"'container_type' in MediaInfo cannot be CONTAINER_UNKNOWN.";
161 if (media_info_.has_video_info()) {
162 mime_type_ = GetVideoMimeType();
163 if (!HasRequiredVideoFields(media_info_.video_info())) {
164 LOG(ERROR) <<
"Missing required fields to create a video Representation.";
167 }
else if (media_info_.has_audio_info()) {
168 mime_type_ = GetAudioMimeType();
169 }
else if (media_info_.has_text_info()) {
170 mime_type_ = GetTextMimeType();
173 if (mime_type_.empty())
176 codecs_ = GetCodecs(media_info_);
182 content_protection_elements_.push_back(content_protection_element);
183 RemoveDuplicateAttributes(&content_protection_elements_.back());
187 const std::string& pssh) {
188 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
189 &content_protection_elements_);
195 if (start_time == 0 && duration == 0) {
196 LOG(WARNING) <<
"Got segment with start_time and duration == 0. Ignoring.";
200 if (state_change_listener_)
201 state_change_listener_->OnNewSegmentForRepresentation(start_time, duration);
202 if (IsContiguous(start_time, duration, size)) {
203 ++segment_infos_.back().repeat;
206 segment_infos_.push_back(s);
209 bandwidth_estimator_.AddBlock(
210 size, static_cast<double>(duration) / media_info_.reference_time_scale());
213 DCHECK_GE(segment_infos_.size(), 1u);
217 if (media_info_.has_video_info()) {
218 media_info_.mutable_video_info()->set_frame_duration(sample_duration);
219 if (state_change_listener_) {
220 state_change_listener_->OnSetFrameRateForRepresentation(
221 sample_duration, media_info_.video_info().time_scale());
237 if (!HasRequiredMediaInfoFields()) {
238 LOG(ERROR) <<
"MediaInfo missing required fields.";
239 return xml::scoped_xml_ptr<xmlNode>();
242 const uint64_t bandwidth = media_info_.has_bandwidth()
243 ? media_info_.bandwidth()
244 : bandwidth_estimator_.Estimate();
246 DCHECK(!(HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)));
250 representation.
SetId(id_);
252 if (!codecs_.empty())
256 const bool has_video_info = media_info_.has_video_info();
257 const bool has_audio_info = media_info_.has_audio_info();
259 if (has_video_info &&
261 media_info_.video_info(),
262 !(output_suppression_flags_ & kSuppressWidth),
263 !(output_suppression_flags_ & kSuppressHeight),
264 !(output_suppression_flags_ & kSuppressFrameRate))) {
265 LOG(ERROR) <<
"Failed to add video info to Representation XML.";
266 return xml::scoped_xml_ptr<xmlNode>();
269 if (has_audio_info &&
270 !representation.
AddAudioInfo(media_info_.audio_info())) {
271 LOG(ERROR) <<
"Failed to add audio info to Representation XML.";
272 return xml::scoped_xml_ptr<xmlNode>();
275 if (!representation.AddContentProtectionElements(
276 content_protection_elements_)) {
277 return xml::scoped_xml_ptr<xmlNode>();
280 if (HasVODOnlyFields(media_info_) &&
282 LOG(ERROR) <<
"Failed to add VOD segment info.";
283 return xml::scoped_xml_ptr<xmlNode>();
286 if (HasLiveOnlyFields(media_info_) &&
289 LOG(ERROR) <<
"Failed to add Live info.";
290 return xml::scoped_xml_ptr<xmlNode>();
295 output_suppression_flags_ = 0;
300 output_suppression_flags_ |= flag;
304 DCHECK(timestamp_seconds);
306 if (segment_infos_.empty())
309 *timestamp_seconds =
static_cast<double>(segment_infos_.begin()->start_time) /
310 GetTimeScale(media_info_);
315 return media_info_.media_duration_seconds();
318 bool Representation::HasRequiredMediaInfoFields()
const {
319 if (HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)) {
320 LOG(ERROR) <<
"MediaInfo cannot have both VOD and Live fields.";
324 if (!media_info_.has_container_type()) {
325 LOG(ERROR) <<
"MediaInfo missing required field: container_type.";
329 if (HasVODOnlyFields(media_info_) && !media_info_.has_bandwidth()) {
330 LOG(ERROR) <<
"Missing 'bandwidth' field. MediaInfo requires bandwidth for " 331 "static profile for generating a valid MPD.";
335 VLOG_IF(3, HasLiveOnlyFields(media_info_) && !media_info_.has_bandwidth())
336 <<
"MediaInfo missing field 'bandwidth'. Using estimated from " 342 bool Representation::IsContiguous(uint64_t start_time,
344 uint64_t size)
const {
345 if (segment_infos_.empty())
349 const SegmentInfo& previous = segment_infos_.back();
350 const uint64_t previous_segment_end_time =
351 previous.start_time + previous.duration * (previous.repeat + 1);
352 if (previous_segment_end_time == start_time &&
353 segment_infos_.back().duration == duration) {
358 const uint64_t previous_segment_start_time =
359 previous.start_time + previous.duration * previous.repeat;
360 if (previous_segment_start_time >= start_time) {
361 LOG(ERROR) <<
"Segments should not be out of order segment. Adding segment " 362 "with start_time == " 363 << start_time <<
" but the previous segment starts at " 364 << previous_segment_start_time <<
".";
369 const uint64_t kRoundingErrorGrace = 5;
370 if (previous_segment_end_time + kRoundingErrorGrace < start_time) {
371 LOG(WARNING) <<
"Found a gap of size " 372 << (start_time - previous_segment_end_time)
373 <<
" > kRoundingErrorGrace (" << kRoundingErrorGrace
374 <<
"). The new segment starts at " << start_time
375 <<
" but the previous segment ends at " 376 << previous_segment_end_time <<
".";
381 if (start_time < previous_segment_end_time - kRoundingErrorGrace) {
383 <<
"Segments should not be overlapping. The new segment starts at " 384 << start_time <<
" but the previous segment ends at " 385 << previous_segment_end_time <<
".";
393 void Representation::SlideWindow() {
394 DCHECK(!segment_infos_.empty());
396 mpd_options_.mpd_type == MpdType::kStatic)
399 const uint32_t time_scale = GetTimeScale(media_info_);
400 DCHECK_GT(time_scale, 0u);
402 uint64_t time_shift_buffer_depth =
static_cast<uint64_t
>(
407 const uint64_t current_play_time = LatestSegmentStartTime(segment_infos_);
408 if (current_play_time <= time_shift_buffer_depth)
411 const uint64_t timeshift_limit = current_play_time - time_shift_buffer_depth;
415 std::list<SegmentInfo>::iterator first = segment_infos_.begin();
416 std::list<SegmentInfo>::iterator last = first;
417 size_t num_segments_removed = 0;
418 for (; last != segment_infos_.end(); ++last) {
419 const uint64_t last_segment_end_time = LastSegmentEndTime(*last);
420 if (timeshift_limit < last_segment_end_time)
422 num_segments_removed += last->repeat + 1;
424 segment_infos_.erase(first, last);
425 start_number_ += num_segments_removed;
428 SegmentInfo* first_segment_info = &segment_infos_.front();
429 DCHECK_LE(timeshift_limit, LastSegmentEndTime(*first_segment_info));
432 const int repeat_index =
433 SearchTimedOutRepeatIndex(timeshift_limit, *first_segment_info);
434 CHECK_GE(repeat_index, 0);
435 if (repeat_index == 0)
438 first_segment_info->start_time = first_segment_info->start_time +
439 first_segment_info->duration * repeat_index;
441 first_segment_info->repeat = first_segment_info->repeat - repeat_index;
442 start_number_ += repeat_index;
445 std::string Representation::GetVideoMimeType()
const {
446 return GetMimeType(
"video", media_info_.container_type());
449 std::string Representation::GetAudioMimeType()
const {
450 return GetMimeType(
"audio", media_info_.container_type());
453 std::string Representation::GetTextMimeType()
const {
454 CHECK(media_info_.has_text_info());
455 if (media_info_.text_info().format() ==
"ttml") {
456 switch (media_info_.container_type()) {
457 case MediaInfo::CONTAINER_TEXT:
458 return "application/ttml+xml";
459 case MediaInfo::CONTAINER_MP4:
460 return "application/mp4";
462 LOG(ERROR) <<
"Failed to determine MIME type for TTML container: " 463 << media_info_.container_type();
467 if (media_info_.text_info().format() ==
"vtt") {
468 if (media_info_.container_type() == MediaInfo::CONTAINER_TEXT) {
470 }
else if (media_info_.container_type() == MediaInfo::CONTAINER_MP4) {
471 return "application/mp4";
473 LOG(ERROR) <<
"Failed to determine MIME type for VTT container: " 474 << media_info_.container_type();
478 LOG(ERROR) <<
"Cannot determine MIME type for format: " 479 << media_info_.text_info().format()
480 <<
" container: " << media_info_.container_type();
bool AddVideoInfo(const MediaInfo::VideoInfo &video_info, bool set_width, bool set_height, bool set_frame_rate)
virtual const MediaInfo & GetMediaInfo() const
RepresentationType in MPD.
virtual void AddNewSegment(uint64_t start_time, uint64_t duration, uint64_t size)
float GetDurationSeconds() const
Representation(const MediaInfo &media_info, const MpdOptions &mpd_options, uint32_t representation_id, std::unique_ptr< RepresentationStateChangeListener > state_change_listener)
virtual void SetSampleDuration(uint32_t sample_duration)
scoped_xml_ptr< xmlNode > PassScopedPtr()
All the methods that are virtual are virtual for mocking.
bool AddVODOnlyInfo(const MediaInfo &media_info)
void SetStringAttribute(const char *attribute_name, const std::string &attribute)
bool AddLiveOnlyInfo(const MediaInfo &media_info, const std::list< SegmentInfo > &segment_infos, uint32_t start_number)
bool GetEarliestTimestamp(double *timestamp_seconds) const
xml::scoped_xml_ptr< xmlNode > GetXml()
virtual void AddContentProtectionElement(const ContentProtectionElement &element)
void SetIntegerAttribute(const char *attribute_name, uint64_t number)
virtual void UpdateContentProtectionPssh(const std::string &drm_uuid, const std::string &pssh)
double time_shift_buffer_depth
void SuppressOnce(SuppressFlag flag)
bool AddAudioInfo(const MediaInfo::AudioInfo &audio_info)