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 uint64_t LastSegmentStartTime(
const SegmentInfo& segment_info) {
83 return segment_info.start_time + segment_info.duration * segment_info.repeat;
87 uint64_t LastSegmentEndTime(
const SegmentInfo& segment_info) {
88 return segment_info.start_time +
89 segment_info.duration * (segment_info.repeat + 1);
92 uint64_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(uint64_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),
119 mpd_options_(mpd_options),
120 state_change_listener_(
std::move(state_change_listener)),
121 allow_approximate_segment_timeline_(
124 media_info.segment_template().find(
"$Time") ==
std::string::npos &&
125 mpd_options_.mpd_params.allow_approximate_segment_timeline) {}
129 std::unique_ptr<RepresentationStateChangeListener> state_change_listener)
131 representation.mpd_options_,
133 std::move(state_change_listener)) {
134 mime_type_ = representation.mime_type_;
135 codecs_ = representation.codecs_;
137 start_number_ = representation.start_number_;
138 for (
const SegmentInfo& segment_info : representation.segment_infos_)
139 start_number_ += segment_info.repeat + 1;
142 Representation::~Representation() {}
145 if (!AtLeastOneTrue(media_info_.has_video_info(),
146 media_info_.has_audio_info(),
147 media_info_.has_text_info())) {
151 LOG(ERROR) <<
"Representation needs one of video, audio, or text.";
155 if (MoreThanOneTrue(media_info_.has_video_info(),
156 media_info_.has_audio_info(),
157 media_info_.has_text_info())) {
158 LOG(ERROR) <<
"Only one of VideoInfo, AudioInfo, or TextInfo can be set.";
162 if (media_info_.container_type() == MediaInfo::CONTAINER_UNKNOWN) {
163 LOG(ERROR) <<
"'container_type' in MediaInfo cannot be CONTAINER_UNKNOWN.";
167 if (media_info_.has_video_info()) {
168 mime_type_ = GetVideoMimeType();
169 if (!HasRequiredVideoFields(media_info_.video_info())) {
170 LOG(ERROR) <<
"Missing required fields to create a video Representation.";
173 }
else if (media_info_.has_audio_info()) {
174 mime_type_ = GetAudioMimeType();
175 }
else if (media_info_.has_text_info()) {
176 mime_type_ = GetTextMimeType();
179 if (mime_type_.empty())
182 codecs_ = GetCodecs(media_info_);
188 content_protection_elements_.push_back(content_protection_element);
189 RemoveDuplicateAttributes(&content_protection_elements_.back());
193 const std::string& pssh) {
194 UpdateContentProtectionPsshHelper(drm_uuid, pssh,
195 &content_protection_elements_);
201 if (start_time == 0 && duration == 0) {
202 LOG(WARNING) <<
"Got segment with start_time and duration == 0. Ignoring.";
206 if (state_change_listener_)
207 state_change_listener_->OnNewSegmentForRepresentation(start_time, duration);
209 AddSegmentInfo(start_time, duration);
212 size, static_cast<double>(duration) / media_info_.reference_time_scale());
215 DCHECK_GE(segment_infos_.size(), 1u);
221 if (media_info_.has_audio_info() || media_info_.has_video_info())
222 frame_duration_ = frame_duration;
224 if (media_info_.has_video_info()) {
225 media_info_.mutable_video_info()->set_frame_duration(frame_duration);
226 if (state_change_listener_) {
227 state_change_listener_->OnSetFrameRateForRepresentation(
228 frame_duration, media_info_.video_info().time_scale());
244 if (!HasRequiredMediaInfoFields()) {
245 LOG(ERROR) <<
"MediaInfo missing required fields.";
246 return xml::scoped_xml_ptr<xmlNode>();
249 const uint64_t bandwidth = media_info_.has_bandwidth()
250 ? media_info_.bandwidth()
251 : bandwidth_estimator_.
Max();
253 DCHECK(!(HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)));
257 representation.
SetId(id_);
259 if (!codecs_.empty())
263 const bool has_video_info = media_info_.has_video_info();
264 const bool has_audio_info = media_info_.has_audio_info();
266 if (has_video_info &&
268 media_info_.video_info(),
269 !(output_suppression_flags_ & kSuppressWidth),
270 !(output_suppression_flags_ & kSuppressHeight),
271 !(output_suppression_flags_ & kSuppressFrameRate))) {
272 LOG(ERROR) <<
"Failed to add video info to Representation XML.";
273 return xml::scoped_xml_ptr<xmlNode>();
276 if (has_audio_info &&
277 !representation.
AddAudioInfo(media_info_.audio_info())) {
278 LOG(ERROR) <<
"Failed to add audio info to Representation XML.";
279 return xml::scoped_xml_ptr<xmlNode>();
282 if (!representation.AddContentProtectionElements(
283 content_protection_elements_)) {
284 return xml::scoped_xml_ptr<xmlNode>();
287 if (HasVODOnlyFields(media_info_) &&
289 LOG(ERROR) <<
"Failed to add VOD info.";
290 return xml::scoped_xml_ptr<xmlNode>();
293 if (HasLiveOnlyFields(media_info_) &&
296 LOG(ERROR) <<
"Failed to add Live info.";
297 return xml::scoped_xml_ptr<xmlNode>();
302 output_suppression_flags_ = 0;
307 output_suppression_flags_ |= flag;
311 double presentation_time_offset) {
312 uint64_t pto = presentation_time_offset * media_info_.reference_time_scale();
315 media_info_.set_presentation_time_offset(pto);
319 double* start_timestamp_seconds,
320 double* end_timestamp_seconds)
const {
321 if (segment_infos_.empty())
324 if (start_timestamp_seconds) {
325 *start_timestamp_seconds =
326 static_cast<double>(segment_infos_.begin()->start_time) /
327 GetTimeScale(media_info_);
329 if (end_timestamp_seconds) {
330 *end_timestamp_seconds =
331 static_cast<double>(segment_infos_.rbegin()->start_time +
332 segment_infos_.rbegin()->duration *
333 (segment_infos_.rbegin()->repeat + 1)) /
334 GetTimeScale(media_info_);
339 bool Representation::HasRequiredMediaInfoFields()
const {
340 if (HasVODOnlyFields(media_info_) && HasLiveOnlyFields(media_info_)) {
341 LOG(ERROR) <<
"MediaInfo cannot have both VOD and Live fields.";
345 if (!media_info_.has_container_type()) {
346 LOG(ERROR) <<
"MediaInfo missing required field: container_type.";
353 void Representation::AddSegmentInfo(uint64_t start_time, uint64_t duration) {
354 const uint64_t kNoRepeat = 0;
355 const uint64_t adjusted_duration = AdjustDuration(duration);
357 if (!segment_infos_.empty()) {
359 const SegmentInfo& previous = segment_infos_.back();
360 const uint64_t previous_segment_end_time =
361 previous.start_time + previous.duration * (previous.repeat + 1);
364 if (ApproximiatelyEqual(previous_segment_end_time, start_time)) {
365 const uint64_t segment_end_time_for_same_duration =
366 previous_segment_end_time + previous.duration;
367 const uint64_t actual_segment_end_time = start_time + duration;
370 if (ApproximiatelyEqual(segment_end_time_for_same_duration,
371 actual_segment_end_time)) {
372 ++segment_infos_.back().repeat;
374 segment_infos_.push_back(
375 {previous_segment_end_time,
376 actual_segment_end_time - previous_segment_end_time, kNoRepeat});
382 const uint64_t kRoundingErrorGrace = 5;
383 if (previous_segment_end_time + kRoundingErrorGrace < start_time) {
384 LOG(WARNING) <<
"Found a gap of size " 385 << (start_time - previous_segment_end_time)
386 <<
" > kRoundingErrorGrace (" << kRoundingErrorGrace
387 <<
"). The new segment starts at " << start_time
388 <<
" but the previous segment ends at " 389 << previous_segment_end_time <<
".";
393 if (start_time < previous_segment_end_time - kRoundingErrorGrace) {
395 <<
"Segments should not be overlapping. The new segment starts at " 396 << start_time <<
" but the previous segment ends at " 397 << previous_segment_end_time <<
".";
401 segment_infos_.push_back({start_time, adjusted_duration, kNoRepeat});
404 bool Representation::ApproximiatelyEqual(uint64_t time1, uint64_t time2)
const {
405 if (!allow_approximate_segment_timeline_)
406 return time1 == time2;
416 const double kErrorThresholdSeconds = 0.05;
419 const uint32_t error_threshold =
420 std::min(frame_duration_,
421 static_cast<uint32_t>(kErrorThresholdSeconds *
422 media_info_.reference_time_scale()));
423 return time1 < time2 + error_threshold && time2 < time1 + error_threshold;
426 uint64_t Representation::AdjustDuration(uint64_t duration)
const {
427 if (!allow_approximate_segment_timeline_)
429 const uint64_t scaled_target_duration =
431 return ApproximiatelyEqual(scaled_target_duration, duration)
432 ? scaled_target_duration
436 void Representation::SlideWindow() {
437 DCHECK(!segment_infos_.empty());
439 mpd_options_.mpd_type == MpdType::kStatic)
442 const uint32_t time_scale = GetTimeScale(media_info_);
443 DCHECK_GT(time_scale, 0u);
445 uint64_t time_shift_buffer_depth =
static_cast<uint64_t
>(
450 const uint64_t current_play_time = LatestSegmentStartTime(segment_infos_);
451 if (current_play_time <= time_shift_buffer_depth)
454 const uint64_t timeshift_limit = current_play_time - time_shift_buffer_depth;
458 std::list<SegmentInfo>::iterator first = segment_infos_.begin();
459 std::list<SegmentInfo>::iterator last = first;
460 for (; last != segment_infos_.end(); ++last) {
461 const uint64_t last_segment_end_time = LastSegmentEndTime(*last);
462 if (timeshift_limit < last_segment_end_time)
464 RemoveSegments(last->start_time, last->duration, last->repeat + 1);
465 start_number_ += last->repeat + 1;
467 segment_infos_.erase(first, last);
470 SegmentInfo* first_segment_info = &segment_infos_.front();
471 DCHECK_LE(timeshift_limit, LastSegmentEndTime(*first_segment_info));
474 const uint64_t repeat_index =
475 SearchTimedOutRepeatIndex(timeshift_limit, *first_segment_info);
476 if (repeat_index == 0)
479 RemoveSegments(first_segment_info->start_time, first_segment_info->duration,
482 first_segment_info->start_time = first_segment_info->start_time +
483 first_segment_info->duration * repeat_index;
484 first_segment_info->repeat = first_segment_info->repeat - repeat_index;
485 start_number_ += repeat_index;
488 void Representation::RemoveSegments(uint64_t start_time,
490 uint64_t num_segments) {
494 for (
size_t i = 0; i < num_segments; ++i) {
495 segments_to_be_removed_.push_back(media::GetSegmentName(
496 media_info_.segment_template(), start_time + i * duration,
497 start_number_ - 1 + i, media_info_.bandwidth()));
499 while (segments_to_be_removed_.size() >
501 VLOG(2) <<
"Deleting " << segments_to_be_removed_.front();
503 segments_to_be_removed_.pop_front();
507 std::string Representation::GetVideoMimeType()
const {
508 return GetMimeType(
"video", media_info_.container_type());
511 std::string Representation::GetAudioMimeType()
const {
512 return GetMimeType(
"audio", media_info_.container_type());
515 std::string Representation::GetTextMimeType()
const {
516 CHECK(media_info_.has_text_info());
517 if (media_info_.text_info().codec() ==
"ttml") {
518 switch (media_info_.container_type()) {
519 case MediaInfo::CONTAINER_TEXT:
520 return "application/ttml+xml";
521 case MediaInfo::CONTAINER_MP4:
522 return "application/mp4";
524 LOG(ERROR) <<
"Failed to determine MIME type for TTML container: " 525 << media_info_.container_type();
529 if (media_info_.text_info().codec() ==
"wvtt") {
530 if (media_info_.container_type() == MediaInfo::CONTAINER_TEXT) {
532 }
else if (media_info_.container_type() == MediaInfo::CONTAINER_MP4) {
533 return "application/mp4";
535 LOG(ERROR) <<
"Failed to determine MIME type for VTT container: " 536 << media_info_.container_type();
540 LOG(ERROR) <<
"Cannot determine MIME type for format: " 541 << media_info_.text_info().codec()
542 <<
" 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)
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 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 AddBlock(uint64_t size, double duration)
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)
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