7 #include "packager/mpd/base/representation.h"
9 #include <gflags/gflags.h>
13 #include "packager/base/logging.h"
14 #include "packager/base/strings/stringprintf.h"
15 #include "packager/file/file.h"
16 #include "packager/media/base/muxer_util.h"
17 #include "packager/mpd/base/mpd_options.h"
18 #include "packager/mpd/base/mpd_utils.h"
19 #include "packager/mpd/base/xml/xml_node.h"
24 std::string GetMimeType(
const std::string& prefix,
25 MediaInfo::ContainerType container_type) {
26 switch (container_type) {
27 case MediaInfo::CONTAINER_MP4:
28 return prefix +
"/mp4";
29 case MediaInfo::CONTAINER_MPEG2_TS:
31 return prefix +
"/MP2T";
32 case MediaInfo::CONTAINER_WEBM:
33 return prefix +
"/webm";
39 LOG(ERROR) <<
"Unrecognized container type: " << container_type;
46 bool HasRequiredVideoFields(
const MediaInfo_VideoInfo& video_info) {
47 if (!video_info.has_height() || !video_info.has_width()) {
49 <<
"Width and height are required fields for generating a valid MPD.";
54 LOG_IF(WARNING, !video_info.has_time_scale())
55 <<
"Video info does not contain timescale required for "
56 "calculating framerate. @frameRate is required for DASH IOP.";
57 LOG_IF(WARNING, !video_info.has_pixel_width())
58 <<
"Video info does not contain pixel_width to calculate the sample "
59 "aspect ratio required for DASH IOP.";
60 LOG_IF(WARNING, !video_info.has_pixel_height())
61 <<
"Video info does not contain pixel_height to calculate the sample "
62 "aspect ratio required for DASH IOP.";
66 uint32_t GetTimeScale(
const MediaInfo& media_info) {
67 if (media_info.has_reference_time_scale()) {
68 return media_info.reference_time_scale();
71 if (media_info.has_video_info()) {
72 return media_info.video_info().time_scale();
75 if (media_info.has_audio_info()) {
76 return media_info.audio_info().time_scale();
79 LOG(WARNING) <<
"No timescale specified, using 1 as timescale.";
86 const MediaInfo& media_info,
89 std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
90 : media_info_(media_info),
92 mpd_options_(mpd_options),
93 state_change_listener_(std::move(state_change_listener)),
94 allow_approximate_segment_timeline_(
97 media_info.segment_template().find(
"$Time") == std::string::npos &&
98 mpd_options_.mpd_params.allow_approximate_segment_timeline) {}
102 std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
104 representation.mpd_options_,
106 std::move(state_change_listener)) {
107 mime_type_ = representation.mime_type_;
108 codecs_ = representation.codecs_;
110 start_number_ = representation.start_number_;
111 for (
const SegmentInfo& segment_info : representation.segment_infos_)
112 start_number_ += segment_info.repeat + 1;
115 Representation::~Representation() {}
118 if (!AtLeastOneTrue(media_info_.has_video_info(),
119 media_info_.has_audio_info(),
120 media_info_.has_text_info())) {
124 LOG(ERROR) <<
"Representation needs one of video, audio, or text.";
128 if (MoreThanOneTrue(media_info_.has_video_info(),
129 media_info_.has_audio_info(),
130 media_info_.has_text_info())) {
131 LOG(ERROR) <<
"Only one of VideoInfo, AudioInfo, or TextInfo can be set.";
135 if (media_info_.container_type() == MediaInfo::CONTAINER_UNKNOWN) {
136 LOG(ERROR) <<
"'container_type' in MediaInfo cannot be CONTAINER_UNKNOWN.";
140 if (media_info_.has_video_info()) {
141 mime_type_ = GetVideoMimeType();
142 if (!HasRequiredVideoFields(media_info_.video_info())) {
143 LOG(ERROR) <<
"Missing required fields to create a video Representation.";
146 }
else if (media_info_.has_audio_info()) {
147 mime_type_ = GetAudioMimeType();
148 }
else if (media_info_.has_text_info()) {
149 mime_type_ = GetTextMimeType();
152 if (mime_type_.empty())
155 codecs_ = GetCodecs(media_info_);
161 content_protection_elements_.push_back(content_protection_element);
162 RemoveDuplicateAttributes(&content_protection_elements_.back());
166 const std::string& pssh) {
167 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
168 &content_protection_elements_);
174 if (start_time == 0 && duration == 0) {
175 LOG(WARNING) <<
"Got segment with start_time and duration == 0. Ignoring.";
186 if (state_change_listener_)
187 state_change_listener_->OnNewSegmentForRepresentation(start_time, duration);
189 AddSegmentInfo(start_time, duration);
190 current_buffer_depth_ += segment_infos_.back().duration;
193 size,
static_cast<double>(duration) / media_info_.reference_time_scale());
199 if (media_info_.has_audio_info() || media_info_.has_video_info())
200 frame_duration_ = frame_duration;
202 if (media_info_.has_video_info()) {
203 media_info_.mutable_video_info()->set_frame_duration(frame_duration);
204 if (state_change_listener_) {
205 state_change_listener_->OnSetFrameRateForRepresentation(
206 frame_duration, media_info_.video_info().time_scale());
222 if (!HasRequiredMediaInfoFields()) {
223 LOG(ERROR) <<
"MediaInfo missing required fields.";
224 return base::nullopt;
227 const uint64_t bandwidth = media_info_.has_bandwidth()
228 ? media_info_.bandwidth()
229 : bandwidth_estimator_.
Max();
231 DCHECK(!(HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)));
235 if (!representation.
SetId(id_) ||
240 return base::nullopt;
243 const bool has_video_info = media_info_.has_video_info();
244 const bool has_audio_info = media_info_.has_audio_info();
246 if (has_video_info &&
248 media_info_.video_info(),
249 !(output_suppression_flags_ & kSuppressWidth),
250 !(output_suppression_flags_ & kSuppressHeight),
251 !(output_suppression_flags_ & kSuppressFrameRate))) {
252 LOG(ERROR) <<
"Failed to add video info to Representation XML.";
253 return base::nullopt;
256 if (has_audio_info &&
257 !representation.
AddAudioInfo(media_info_.audio_info())) {
258 LOG(ERROR) <<
"Failed to add audio info to Representation XML.";
259 return base::nullopt;
262 if (!representation.AddContentProtectionElements(
263 content_protection_elements_)) {
264 return base::nullopt;
267 if (HasVODOnlyFields(media_info_) &&
271 LOG(ERROR) <<
"Failed to add VOD info.";
272 return base::nullopt;
275 if (HasLiveOnlyFields(media_info_) &&
278 LOG(ERROR) <<
"Failed to add Live info.";
279 return base::nullopt;
284 output_suppression_flags_ = 0;
285 return std::move(representation);
289 output_suppression_flags_ |= flag;
293 double presentation_time_offset) {
294 int64_t pto = presentation_time_offset * media_info_.reference_time_scale();
297 media_info_.set_presentation_time_offset(pto);
301 double* start_timestamp_seconds,
302 double* end_timestamp_seconds)
const {
303 if (segment_infos_.empty())
306 if (start_timestamp_seconds) {
307 *start_timestamp_seconds =
308 static_cast<double>(segment_infos_.begin()->start_time) /
309 GetTimeScale(media_info_);
311 if (end_timestamp_seconds) {
312 *end_timestamp_seconds =
313 static_cast<double>(segment_infos_.rbegin()->start_time +
314 segment_infos_.rbegin()->duration *
315 (segment_infos_.rbegin()->repeat + 1)) /
316 GetTimeScale(media_info_);
321 bool Representation::HasRequiredMediaInfoFields()
const {
322 if (HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)) {
323 LOG(ERROR) <<
"MediaInfo cannot have both VOD and Live fields.";
327 if (!media_info_.has_container_type()) {
328 LOG(ERROR) <<
"MediaInfo missing required field: container_type.";
335 void Representation::AddSegmentInfo(int64_t start_time, int64_t duration) {
336 const uint64_t kNoRepeat = 0;
337 const int64_t adjusted_duration = AdjustDuration(duration);
339 if (!segment_infos_.empty()) {
341 const SegmentInfo& previous = segment_infos_.back();
342 const int64_t previous_segment_end_time =
343 previous.start_time + previous.duration * (previous.repeat + 1);
346 if (ApproximiatelyEqual(previous_segment_end_time, start_time)) {
347 const int64_t segment_end_time_for_same_duration =
348 previous_segment_end_time + previous.duration;
349 const int64_t actual_segment_end_time = start_time + duration;
352 if (ApproximiatelyEqual(segment_end_time_for_same_duration,
353 actual_segment_end_time)) {
354 ++segment_infos_.back().repeat;
356 segment_infos_.push_back(
357 {previous_segment_end_time,
358 actual_segment_end_time - previous_segment_end_time, kNoRepeat});
364 const int64_t kRoundingErrorGrace = 5;
365 if (previous_segment_end_time + kRoundingErrorGrace < start_time) {
366 LOG(WARNING) << RepresentationAsString() <<
" Found a gap of size "
367 << (start_time - previous_segment_end_time)
368 <<
" > kRoundingErrorGrace (" << kRoundingErrorGrace
369 <<
"). The new segment starts at " << start_time
370 <<
" but the previous segment ends at "
371 << previous_segment_end_time <<
".";
375 if (start_time < previous_segment_end_time - kRoundingErrorGrace) {
377 << RepresentationAsString()
378 <<
" Segments should not be overlapping. The new segment starts at "
379 << start_time <<
" but the previous segment ends at "
380 << previous_segment_end_time <<
".";
384 segment_infos_.push_back({start_time, adjusted_duration, kNoRepeat});
387 bool Representation::ApproximiatelyEqual(int64_t time1, int64_t time2)
const {
388 if (!allow_approximate_segment_timeline_)
389 return time1 == time2;
399 const double kErrorThresholdSeconds = 0.05;
402 const uint32_t error_threshold =
403 std::min(frame_duration_,
404 static_cast<uint32_t
>(kErrorThresholdSeconds *
405 media_info_.reference_time_scale()));
406 return std::abs(time1 - time2) <= error_threshold;
409 int64_t Representation::AdjustDuration(int64_t duration)
const {
410 if (!allow_approximate_segment_timeline_)
412 const int64_t scaled_target_duration =
414 media_info_.reference_time_scale();
415 return ApproximiatelyEqual(scaled_target_duration, duration)
416 ? scaled_target_duration
420 void Representation::SlideWindow() {
422 mpd_options_.mpd_type == MpdType::kStatic)
425 const uint32_t time_scale = GetTimeScale(media_info_);
426 DCHECK_GT(time_scale, 0u);
428 const int64_t time_shift_buffer_depth =
static_cast<int64_t
>(
431 if (current_buffer_depth_ <= time_shift_buffer_depth)
434 std::list<SegmentInfo>::iterator first = segment_infos_.begin();
435 std::list<SegmentInfo>::iterator last = first;
436 for (; last != segment_infos_.end(); ++last) {
439 while (last->repeat >= 0 &&
440 current_buffer_depth_ - last->duration >= time_shift_buffer_depth) {
441 current_buffer_depth_ -= last->duration;
442 RemoveOldSegment(&*last);
445 if (last->repeat >= 0)
448 segment_infos_.erase(first, last);
451 void Representation::RemoveOldSegment(SegmentInfo* segment_info) {
452 int64_t segment_start_time = segment_info->start_time;
453 segment_info->start_time += segment_info->duration;
454 segment_info->repeat--;
459 segments_to_be_removed_.push_back(
460 media::GetSegmentName(media_info_.segment_template(), segment_start_time,
461 start_number_ - 1, media_info_.bandwidth()));
462 while (segments_to_be_removed_.size() >
464 VLOG(2) <<
"Deleting " << segments_to_be_removed_.front();
465 if (!
File::Delete(segments_to_be_removed_.front().c_str())) {
466 LOG(WARNING) <<
"Failed to delete " << segments_to_be_removed_.front()
467 <<
"; Will retry later.";
470 segments_to_be_removed_.pop_front();
474 std::string Representation::GetVideoMimeType()
const {
475 return GetMimeType(
"video", media_info_.container_type());
478 std::string Representation::GetAudioMimeType()
const {
479 return GetMimeType(
"audio", media_info_.container_type());
482 std::string Representation::GetTextMimeType()
const {
483 CHECK(media_info_.has_text_info());
484 if (media_info_.text_info().codec() ==
"ttml") {
485 switch (media_info_.container_type()) {
486 case MediaInfo::CONTAINER_TEXT:
487 return "application/ttml+xml";
488 case MediaInfo::CONTAINER_MP4:
489 return "application/mp4";
491 LOG(ERROR) <<
"Failed to determine MIME type for TTML container: "
492 << media_info_.container_type();
496 if (media_info_.text_info().codec() ==
"wvtt") {
497 if (media_info_.container_type() == MediaInfo::CONTAINER_TEXT) {
499 }
else if (media_info_.container_type() == MediaInfo::CONTAINER_MP4) {
500 return "application/mp4";
502 LOG(ERROR) <<
"Failed to determine MIME type for VTT container: "
503 << media_info_.container_type();
507 LOG(ERROR) <<
"Cannot determine MIME type for format: "
508 << media_info_.text_info().codec()
509 <<
" container: " << media_info_.container_type();
513 std::string Representation::RepresentationAsString()
const {
514 std::string s = base::StringPrintf(
"Representation (id=%d,", id_);
515 if (media_info_.has_video_info()) {
516 const MediaInfo_VideoInfo& video_info = media_info_.video_info();
517 base::StringAppendF(&s,
"codec='%s',width=%d,height=%d",
518 video_info.codec().c_str(), video_info.width(),
519 video_info.height());
520 }
else if (media_info_.has_audio_info()) {
521 const MediaInfo_AudioInfo& audio_info = media_info_.audio_info();
523 &s,
"codec='%s',frequency=%d,language='%s'", audio_info.codec().c_str(),
524 audio_info.sampling_frequency(), audio_info.language().c_str());
525 }
else if (media_info_.has_text_info()) {
526 const MediaInfo_TextInfo& text_info = media_info_.text_info();
527 base::StringAppendF(&s,
"codec='%s',language='%s'",
528 text_info.codec().c_str(),
529 text_info.language().c_str());
531 base::StringAppendF(&s,
")");
void AddBlock(uint64_t size_in_bytes, double duration)
static bool Delete(const char *file_name)
virtual void SetSampleDuration(uint32_t sample_duration)
virtual void AddContentProtectionElement(const ContentProtectionElement &element)
virtual void UpdateContentProtectionPssh(const std::string &drm_uuid, const std::string &pssh)
void SuppressOnce(SuppressFlag flag)
virtual const MediaInfo & GetMediaInfo() const
bool GetStartAndEndTimestamps(double *start_timestamp_seconds, double *end_timestamp_seconds) const
Representation(const MediaInfo &media_info, const MpdOptions &mpd_options, uint32_t representation_id, std::unique_ptr< RepresentationStateChangeListener > state_change_listener)
void SetPresentationTimeOffset(double presentation_time_offset)
Set @presentationTimeOffset in SegmentBase / SegmentTemplate.
base::Optional< xml::XmlNode > GetXml()
virtual void AddNewSegment(int64_t start_time, int64_t duration, uint64_t size)
RepresentationType in MPD.
bool AddVideoInfo(const MediaInfo::VideoInfo &video_info, bool set_width, bool set_height, bool set_frame_rate) WARN_UNUSED_RESULT
bool AddLiveOnlyInfo(const MediaInfo &media_info, const std::list< SegmentInfo > &segment_infos, uint32_t start_number) WARN_UNUSED_RESULT
bool AddVODOnlyInfo(const MediaInfo &media_info, bool use_segment_list, double target_segment_duration) WARN_UNUSED_RESULT
bool AddAudioInfo(const MediaInfo::AudioInfo &audio_info) WARN_UNUSED_RESULT
bool SetIntegerAttribute(const std::string &attribute_name, uint64_t number) WARN_UNUSED_RESULT
bool SetStringAttribute(const std::string &attribute_name, const std::string &attribute) WARN_UNUSED_RESULT
bool SetId(uint32_t id) WARN_UNUSED_RESULT
All the methods that are virtual are virtual for mocking.
size_t preserved_segments_outside_live_window
double target_segment_duration
double time_shift_buffer_depth