7 #include "packager/mpd/base/representation.h" 9 #include <gflags/gflags.h> 13 #include "packager/base/logging.h" 14 #include "packager/file/file.h" 15 #include "packager/media/base/muxer_util.h" 16 #include "packager/mpd/base/mpd_options.h" 17 #include "packager/mpd/base/mpd_utils.h" 18 #include "packager/mpd/base/xml/xml_node.h" 23 std::string GetMimeType(
const std::string& prefix,
24 MediaInfo::ContainerType container_type) {
25 switch (container_type) {
26 case MediaInfo::CONTAINER_MP4:
27 return prefix +
"/mp4";
28 case MediaInfo::CONTAINER_MPEG2_TS:
30 return prefix +
"/MP2T";
31 case MediaInfo::CONTAINER_WEBM:
32 return prefix +
"/webm";
38 LOG(ERROR) <<
"Unrecognized container type: " << container_type;
45 bool HasRequiredVideoFields(
const MediaInfo_VideoInfo& video_info) {
46 if (!video_info.has_height() || !video_info.has_width()) {
48 <<
"Width and height are required fields for generating a valid MPD.";
53 LOG_IF(WARNING, !video_info.has_time_scale())
54 <<
"Video info does not contain timescale required for " 55 "calculating framerate. @frameRate is required for DASH IOP.";
56 LOG_IF(WARNING, !video_info.has_pixel_width())
57 <<
"Video info does not contain pixel_width to calculate the sample " 58 "aspect ratio required for DASH IOP.";
59 LOG_IF(WARNING, !video_info.has_pixel_height())
60 <<
"Video info does not contain pixel_height to calculate the sample " 61 "aspect ratio required for DASH IOP.";
65 uint32_t GetTimeScale(
const MediaInfo& media_info) {
66 if (media_info.has_reference_time_scale()) {
67 return media_info.reference_time_scale();
70 if (media_info.has_video_info()) {
71 return media_info.video_info().time_scale();
74 if (media_info.has_audio_info()) {
75 return media_info.audio_info().time_scale();
78 LOG(WARNING) <<
"No timescale specified, using 1 as timescale.";
82 int64_t LastSegmentStartTime(
const SegmentInfo& segment_info) {
83 return segment_info.start_time + segment_info.duration * segment_info.repeat;
87 int64_t LastSegmentEndTime(
const SegmentInfo& segment_info) {
88 return segment_info.start_time +
89 segment_info.duration * (segment_info.repeat + 1);
92 int64_t LatestSegmentStartTime(
const std::list<SegmentInfo>& segments) {
93 DCHECK(!segments.empty());
94 const SegmentInfo& latest_segment = segments.back();
95 return LastSegmentStartTime(latest_segment);
100 uint64_t SearchTimedOutRepeatIndex(int64_t timeshift_limit,
101 const SegmentInfo& segment_info) {
102 DCHECK_LE(timeshift_limit, LastSegmentEndTime(segment_info));
103 if (timeshift_limit < segment_info.start_time)
106 return (timeshift_limit - segment_info.start_time) / segment_info.duration;
112 const MediaInfo& media_info,
115 std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
116 : media_info_(media_info),
118 mpd_options_(mpd_options),
119 state_change_listener_(
std::move(state_change_listener)),
120 allow_approximate_segment_timeline_(
123 media_info.segment_template().find(
"$Time") ==
std::string::npos &&
124 mpd_options_.mpd_params.allow_approximate_segment_timeline) {}
128 std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
130 representation.mpd_options_,
132 std::move(state_change_listener)) {
133 mime_type_ = representation.mime_type_;
134 codecs_ = representation.codecs_;
136 start_number_ = representation.start_number_;
137 for (
const SegmentInfo& segment_info : representation.segment_infos_)
138 start_number_ += segment_info.repeat + 1;
141 Representation::~Representation() {}
144 if (!AtLeastOneTrue(media_info_.has_video_info(),
145 media_info_.has_audio_info(),
146 media_info_.has_text_info())) {
150 LOG(ERROR) <<
"Representation needs one of video, audio, or text.";
154 if (MoreThanOneTrue(media_info_.has_video_info(),
155 media_info_.has_audio_info(),
156 media_info_.has_text_info())) {
157 LOG(ERROR) <<
"Only one of VideoInfo, AudioInfo, or TextInfo can be set.";
161 if (media_info_.container_type() == MediaInfo::CONTAINER_UNKNOWN) {
162 LOG(ERROR) <<
"'container_type' in MediaInfo cannot be CONTAINER_UNKNOWN.";
166 if (media_info_.has_video_info()) {
167 mime_type_ = GetVideoMimeType();
168 if (!HasRequiredVideoFields(media_info_.video_info())) {
169 LOG(ERROR) <<
"Missing required fields to create a video Representation.";
172 }
else if (media_info_.has_audio_info()) {
173 mime_type_ = GetAudioMimeType();
174 }
else if (media_info_.has_text_info()) {
175 mime_type_ = GetTextMimeType();
178 if (mime_type_.empty())
181 codecs_ = GetCodecs(media_info_);
187 content_protection_elements_.push_back(content_protection_element);
188 RemoveDuplicateAttributes(&content_protection_elements_.back());
192 const std::string& pssh) {
193 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
194 &content_protection_elements_);
200 if (start_time == 0 && duration == 0) {
201 LOG(WARNING) <<
"Got segment with start_time and duration == 0. Ignoring.";
205 if (state_change_listener_)
206 state_change_listener_->OnNewSegmentForRepresentation(start_time, duration);
208 AddSegmentInfo(start_time, duration);
211 size, static_cast<double>(duration) / media_info_.reference_time_scale());
214 DCHECK_GE(segment_infos_.size(), 1u);
220 if (media_info_.has_audio_info() || media_info_.has_video_info())
221 frame_duration_ = frame_duration;
223 if (media_info_.has_video_info()) {
224 media_info_.mutable_video_info()->set_frame_duration(frame_duration);
225 if (state_change_listener_) {
226 state_change_listener_->OnSetFrameRateForRepresentation(
227 frame_duration, media_info_.video_info().time_scale());
243 if (!HasRequiredMediaInfoFields()) {
244 LOG(ERROR) <<
"MediaInfo missing required fields.";
245 return xml::scoped_xml_ptr<xmlNode>();
248 const uint64_t bandwidth = media_info_.has_bandwidth()
249 ? media_info_.bandwidth()
250 : bandwidth_estimator_.
Max();
252 DCHECK(!(HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)));
256 representation.
SetId(id_);
258 if (!codecs_.empty())
262 const bool has_video_info = media_info_.has_video_info();
263 const bool has_audio_info = media_info_.has_audio_info();
265 if (has_video_info &&
267 media_info_.video_info(),
268 !(output_suppression_flags_ & kSuppressWidth),
269 !(output_suppression_flags_ & kSuppressHeight),
270 !(output_suppression_flags_ & kSuppressFrameRate))) {
271 LOG(ERROR) <<
"Failed to add video info to Representation XML.";
272 return xml::scoped_xml_ptr<xmlNode>();
275 if (has_audio_info &&
276 !representation.
AddAudioInfo(media_info_.audio_info())) {
277 LOG(ERROR) <<
"Failed to add audio info to Representation XML.";
278 return xml::scoped_xml_ptr<xmlNode>();
281 if (!representation.AddContentProtectionElements(
282 content_protection_elements_)) {
283 return xml::scoped_xml_ptr<xmlNode>();
286 if (HasVODOnlyFields(media_info_) &&
288 LOG(ERROR) <<
"Failed to add VOD info.";
289 return xml::scoped_xml_ptr<xmlNode>();
292 if (HasLiveOnlyFields(media_info_) &&
295 LOG(ERROR) <<
"Failed to add Live info.";
296 return xml::scoped_xml_ptr<xmlNode>();
301 output_suppression_flags_ = 0;
306 output_suppression_flags_ |= flag;
310 double presentation_time_offset) {
311 int64_t pto = presentation_time_offset * media_info_.reference_time_scale();
314 media_info_.set_presentation_time_offset(pto);
318 double* start_timestamp_seconds,
319 double* end_timestamp_seconds)
const {
320 if (segment_infos_.empty())
323 if (start_timestamp_seconds) {
324 *start_timestamp_seconds =
325 static_cast<double>(segment_infos_.begin()->start_time) /
326 GetTimeScale(media_info_);
328 if (end_timestamp_seconds) {
329 *end_timestamp_seconds =
330 static_cast<double>(segment_infos_.rbegin()->start_time +
331 segment_infos_.rbegin()->duration *
332 (segment_infos_.rbegin()->repeat + 1)) /
333 GetTimeScale(media_info_);
338 bool Representation::HasRequiredMediaInfoFields()
const {
339 if (HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)) {
340 LOG(ERROR) <<
"MediaInfo cannot have both VOD and Live fields.";
344 if (!media_info_.has_container_type()) {
345 LOG(ERROR) <<
"MediaInfo missing required field: container_type.";
352 void Representation::AddSegmentInfo(int64_t start_time, int64_t duration) {
353 const uint64_t kNoRepeat = 0;
354 const int64_t adjusted_duration = AdjustDuration(duration);
356 if (!segment_infos_.empty()) {
358 const SegmentInfo& previous = segment_infos_.back();
359 const int64_t previous_segment_end_time =
360 previous.start_time + previous.duration * (previous.repeat + 1);
363 if (ApproximiatelyEqual(previous_segment_end_time, start_time)) {
364 const int64_t segment_end_time_for_same_duration =
365 previous_segment_end_time + previous.duration;
366 const int64_t actual_segment_end_time = start_time + duration;
369 if (ApproximiatelyEqual(segment_end_time_for_same_duration,
370 actual_segment_end_time)) {
371 ++segment_infos_.back().repeat;
373 segment_infos_.push_back(
374 {previous_segment_end_time,
375 actual_segment_end_time - previous_segment_end_time, kNoRepeat});
381 const int64_t kRoundingErrorGrace = 5;
382 if (previous_segment_end_time + kRoundingErrorGrace < start_time) {
383 LOG(WARNING) <<
"Found a gap of size " 384 << (start_time - previous_segment_end_time)
385 <<
" > kRoundingErrorGrace (" << kRoundingErrorGrace
386 <<
"). The new segment starts at " << start_time
387 <<
" but the previous segment ends at " 388 << previous_segment_end_time <<
".";
392 if (start_time < previous_segment_end_time - kRoundingErrorGrace) {
394 <<
"Segments should not be overlapping. The new segment starts at " 395 << start_time <<
" but the previous segment ends at " 396 << previous_segment_end_time <<
".";
400 segment_infos_.push_back({start_time, adjusted_duration, kNoRepeat});
403 bool Representation::ApproximiatelyEqual(int64_t time1, int64_t time2)
const {
404 if (!allow_approximate_segment_timeline_)
405 return time1 == time2;
415 const double kErrorThresholdSeconds = 0.05;
418 const uint32_t error_threshold =
419 std::min(frame_duration_,
420 static_cast<uint32_t>(kErrorThresholdSeconds *
421 media_info_.reference_time_scale()));
422 return std::abs(time1 - time2) <= error_threshold;
425 int64_t Representation::AdjustDuration(int64_t duration)
const {
426 if (!allow_approximate_segment_timeline_)
428 const int64_t scaled_target_duration =
430 return ApproximiatelyEqual(scaled_target_duration, duration)
431 ? scaled_target_duration
435 void Representation::SlideWindow() {
436 DCHECK(!segment_infos_.empty());
438 mpd_options_.mpd_type == MpdType::kStatic)
441 const uint32_t time_scale = GetTimeScale(media_info_);
442 DCHECK_GT(time_scale, 0u);
444 int64_t time_shift_buffer_depth =
static_cast<int64_t
>(
449 const int64_t current_play_time = LatestSegmentStartTime(segment_infos_);
450 if (current_play_time <= time_shift_buffer_depth)
453 const int64_t timeshift_limit = current_play_time - time_shift_buffer_depth;
457 std::list<SegmentInfo>::iterator first = segment_infos_.begin();
458 std::list<SegmentInfo>::iterator last = first;
459 for (; last != segment_infos_.end(); ++last) {
460 const int64_t last_segment_end_time = LastSegmentEndTime(*last);
461 if (timeshift_limit < last_segment_end_time)
463 RemoveSegments(last->start_time, last->duration, last->repeat + 1);
464 start_number_ += last->repeat + 1;
466 segment_infos_.erase(first, last);
469 SegmentInfo* first_segment_info = &segment_infos_.front();
470 DCHECK_LE(timeshift_limit, LastSegmentEndTime(*first_segment_info));
473 const uint64_t repeat_index =
474 SearchTimedOutRepeatIndex(timeshift_limit, *first_segment_info);
475 if (repeat_index == 0)
478 RemoveSegments(first_segment_info->start_time, first_segment_info->duration,
481 first_segment_info->start_time = first_segment_info->start_time +
482 first_segment_info->duration * repeat_index;
483 first_segment_info->repeat = first_segment_info->repeat - repeat_index;
484 start_number_ += repeat_index;
487 void Representation::RemoveSegments(int64_t start_time,
489 uint64_t num_segments) {
493 for (
size_t i = 0; i < num_segments; ++i) {
494 segments_to_be_removed_.push_back(media::GetSegmentName(
495 media_info_.segment_template(), start_time + i * duration,
496 start_number_ - 1 + i, media_info_.bandwidth()));
498 while (segments_to_be_removed_.size() >
500 VLOG(2) <<
"Deleting " << segments_to_be_removed_.front();
502 segments_to_be_removed_.pop_front();
506 std::string Representation::GetVideoMimeType()
const {
507 return GetMimeType(
"video", media_info_.container_type());
510 std::string Representation::GetAudioMimeType()
const {
511 return GetMimeType(
"audio", media_info_.container_type());
514 std::string Representation::GetTextMimeType()
const {
515 CHECK(media_info_.has_text_info());
516 if (media_info_.text_info().codec() ==
"ttml") {
517 switch (media_info_.container_type()) {
518 case MediaInfo::CONTAINER_TEXT:
519 return "application/ttml+xml";
520 case MediaInfo::CONTAINER_MP4:
521 return "application/mp4";
523 LOG(ERROR) <<
"Failed to determine MIME type for TTML container: " 524 << media_info_.container_type();
528 if (media_info_.text_info().codec() ==
"wvtt") {
529 if (media_info_.container_type() == MediaInfo::CONTAINER_TEXT) {
531 }
else if (media_info_.container_type() == MediaInfo::CONTAINER_MP4) {
532 return "application/mp4";
534 LOG(ERROR) <<
"Failed to determine MIME type for VTT container: " 535 << media_info_.container_type();
539 LOG(ERROR) <<
"Cannot determine MIME type for format: " 540 << media_info_.text_info().codec()
541 <<
" 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.
static bool Delete(const char *file_name)
size_t preserved_segments_outside_live_window
Representation(const MediaInfo &media_info, const MpdOptions &mpd_options, uint32_t representation_id, std::unique_ptr< RepresentationStateChangeListener > state_change_listener)
virtual void AddNewSegment(int64_t start_time, int64_t duration, uint64_t size)
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)
void AddBlock(uint64_t size_in_bytes, double duration)
bool AddLiveOnlyInfo(const MediaInfo &media_info, const std::list< SegmentInfo > &segment_infos, uint32_t start_number)
xml::scoped_xml_ptr< xmlNode > GetXml()
void SetPresentationTimeOffset(double presentation_time_offset)
Set in SegmentBase / SegmentTemplate.
double target_segment_duration
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)
bool GetStartAndEndTimestamps(double *start_timestamp_seconds, double *end_timestamp_seconds) const