Pass sample duration to MPD

- Get the first sample duration in Segmenter.
- Pass the value all the way to Representation thru MuxerListener.

Change-Id: I76fd970f8140d359863363dc347958f680cca5ae
This commit is contained in:
Rintaro Kuroiwa 2015-06-15 14:12:42 -07:00
parent fcaac3de33
commit d685edef62
15 changed files with 88 additions and 5 deletions

View File

@ -64,6 +64,11 @@ void MpdNotifyMuxerListener::OnMediaStart(
} }
} }
void MpdNotifyMuxerListener::OnSampleDurationReady(
uint32_t sample_duration) {
mpd_notifier_->NotifySampleDuration(notification_id_, sample_duration);
}
void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range, void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range,
uint64_t init_range_start, uint64_t init_range_start,
uint64_t init_range_end, uint64_t init_range_end,

View File

@ -41,7 +41,7 @@ class MpdNotifyMuxerListener : public MuxerListener {
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type, ContainerType container_type,
bool is_encrypted) OVERRIDE; bool is_encrypted) OVERRIDE;
virtual void OnSampleDurationReady(uint32_t sample_duration) OVERRIDE;
virtual void OnMediaEnd(bool has_init_range, virtual void OnMediaEnd(bool has_init_range,
uint64_t init_range_start, uint64_t init_range_start,
uint64_t init_range_end, uint64_t init_range_end,
@ -50,7 +50,6 @@ class MpdNotifyMuxerListener : public MuxerListener {
uint64_t index_range_end, uint64_t index_range_end,
float duration_seconds, float duration_seconds,
uint64_t file_size) OVERRIDE; uint64_t file_size) OVERRIDE;
virtual void OnNewSegment(uint64_t start_time, virtual void OnNewSegment(uint64_t start_time,
uint64_t duration, uint64_t duration,
uint64_t segment_file_size) OVERRIDE; uint64_t segment_file_size) OVERRIDE;

View File

@ -19,6 +19,10 @@ namespace media {
class StreamInfo; class StreamInfo;
struct MuxerOptions; struct MuxerOptions;
/// MuxerListener is an event handler that can be registered to a muxer.
/// A MuxerListener cannot be shared amongst muxer instances, in other words,
/// every muxer instance either owns a unique MuxerListener instance.
/// This also assumes that there is one media stream per muxer.
class MuxerListener { class MuxerListener {
public: public:
enum ContainerType { enum ContainerType {
@ -41,6 +45,10 @@ class MuxerListener {
ContainerType container_type, ContainerType container_type,
bool is_encrypted) = 0; bool is_encrypted) = 0;
/// Called when the average sample duration of the media is determined.
/// @param sample_duration in timescale of the media.
virtual void OnSampleDurationReady(uint32_t sample_duration) = 0;
// Called when all files are written out and the muxer object does not output // Called when all files are written out and the muxer object does not output
// any more files. // any more files.
// Note: This event might not be very interesting to MPEG DASH Live profile. // Note: This event might not be very interesting to MPEG DASH Live profile.

View File

@ -55,6 +55,14 @@ void VodMediaInfoDumpMuxerListener::OnMediaStart(
} }
} }
void VodMediaInfoDumpMuxerListener::OnSampleDurationReady(
uint32_t sample_duration) {
// Assume one VideoInfo.
if (media_info_->video_info_size() > 0) {
media_info_->mutable_video_info(0)->set_frame_duration(sample_duration);
}
}
void VodMediaInfoDumpMuxerListener::OnMediaEnd(bool has_init_range, void VodMediaInfoDumpMuxerListener::OnMediaEnd(bool has_init_range,
uint64_t init_range_start, uint64_t init_range_start,
uint64_t init_range_end, uint64_t init_range_end,

View File

@ -41,7 +41,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type, ContainerType container_type,
bool is_encrypted) OVERRIDE; bool is_encrypted) OVERRIDE;
virtual void OnSampleDurationReady(uint32_t sample_duration) OVERRIDE;
virtual void OnMediaEnd(bool has_init_range, virtual void OnMediaEnd(bool has_init_range,
uint64_t init_range_start, uint64_t init_range_start,
uint64_t init_range_end, uint64_t init_range_end,
@ -50,7 +50,6 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
uint64_t index_range_end, uint64_t index_range_end,
float duration_seconds, float duration_seconds,
uint64_t file_size) OVERRIDE; uint64_t file_size) OVERRIDE;
virtual void OnNewSegment(uint64_t start_time, virtual void OnNewSegment(uint64_t start_time,
uint64_t duration, uint64_t duration,
uint64_t segment_file_size) OVERRIDE; uint64_t segment_file_size) OVERRIDE;

View File

@ -185,6 +185,7 @@ Status MultiSegmentSegmenter::WriteSegment() {
UpdateProgress(segment_duration); UpdateProgress(segment_duration);
if (muxer_listener()) { if (muxer_listener()) {
muxer_listener()->OnSampleDurationReady(sample_duration());
muxer_listener()->OnNewSegment( muxer_listener()->OnNewSegment(
sidx()->earliest_presentation_time, segment_duration, segment_size); sidx()->earliest_presentation_time, segment_duration, segment_size);
} }

View File

@ -127,7 +127,8 @@ Segmenter::Segmenter(const MuxerOptions& options,
muxer_listener_(NULL), muxer_listener_(NULL),
progress_listener_(NULL), progress_listener_(NULL),
progress_target_(0), progress_target_(0),
accumulated_progress_(0) { accumulated_progress_(0),
sample_duration_(0u) {
} }
Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); } Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); }
@ -295,6 +296,8 @@ Status Segmenter::AddSample(const MediaStream* stream,
if (!status.ok()) if (!status.ok())
return status; return status;
if (sample_duration_ == 0)
sample_duration_ = sample->duration();
moov_->tracks[stream_id].media.header.duration += sample->duration(); moov_->tracks[stream_id].media.header.duration += sample->duration();
segment_durations_[stream_id] += sample->duration(); segment_durations_[stream_id] += sample->duration();
return Status::OK; return Status::OK;

View File

@ -96,6 +96,10 @@ class Segmenter {
/// @return The total length, in seconds, of segmented media files. /// @return The total length, in seconds, of segmented media files.
double GetDuration() const; double GetDuration() const;
/// @return The sample duration in the timescale of the media.
/// Returns 0 if no samples are added yet.
uint32_t sample_duration() const { return sample_duration_; }
protected: protected:
/// Update segmentation progress using ProgressListener. /// Update segmentation progress using ProgressListener.
void UpdateProgress(uint64_t progress); void UpdateProgress(uint64_t progress);
@ -140,6 +144,7 @@ class Segmenter {
ProgressListener* progress_listener_; ProgressListener* progress_listener_;
uint64_t progress_target_; uint64_t progress_target_;
uint64_t accumulated_progress_; uint64_t accumulated_progress_;
uint32_t sample_duration_;
DISALLOW_COPY_AND_ASSIGN(Segmenter); DISALLOW_COPY_AND_ASSIGN(Segmenter);
}; };

View File

@ -225,6 +225,7 @@ Status SingleSegmentSegmenter::DoFinalizeSegment() {
UpdateProgress(vod_ref.subsegment_duration); UpdateProgress(vod_ref.subsegment_duration);
if (muxer_listener()) { if (muxer_listener()) {
muxer_listener()->OnSampleDurationReady(sample_duration());
muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time, muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time,
vod_ref.subsegment_duration, segment_size); vod_ref.subsegment_duration, segment_size);
} }

View File

@ -666,6 +666,12 @@ void Representation::AddNewSegment(uint64_t start_time,
DCHECK_GE(segment_infos_.size(), 1u); DCHECK_GE(segment_infos_.size(), 1u);
} }
void Representation::SetSampleDuration(uint32_t sample_duration) {
// Assume single video info.
if (media_info_.video_info_size() > 0)
media_info_.mutable_video_info(0)->set_frame_duration(sample_duration);
}
// Uses info in |media_info_| and |content_protection_elements_| to create a // Uses info in |media_info_| and |content_protection_elements_| to create a
// "Representation" node. // "Representation" node.
// MPD schema has strict ordering. The following must be done in order. // MPD schema has strict ordering. The following must be done in order.

View File

@ -233,6 +233,13 @@ class Representation {
/// @param size of the segment in bytes. /// @param size of the segment in bytes.
void AddNewSegment(uint64_t start_time, uint64_t duration, uint64_t size); void AddNewSegment(uint64_t start_time, uint64_t duration, uint64_t size);
/// Set the sample duration of this Representation.
/// In most cases, the sample duration is not available right away. This
/// allows setting the sample duration after the Representation has been
/// initialized.
/// @param sample_duration is the duration of a sample.
void SetSampleDuration(uint32_t sample_duration);
/// @return Copy of <Representation>. /// @return Copy of <Representation>.
xml::ScopedXmlPtr<xmlNode>::type GetXml(); xml::ScopedXmlPtr<xmlNode>::type GetXml();
@ -247,6 +254,7 @@ class Representation {
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, InvalidMediaInfo); FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, InvalidMediaInfo);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml); FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId); FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId);
FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetSampleDuration);
/// @param media_info is a MediaInfo containing information on the media. /// @param media_info is a MediaInfo containing information on the media.
/// @a media_info.bandwidth is required for 'static' profile. If @a /// @a media_info.bandwidth is required for 'static' profile. If @a

View File

@ -399,6 +399,22 @@ TEST_F(CommonMpdBuilderTest, CheckRepresentationId) {
ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kRepresentationId, &representation)); ASSERT_NO_FATAL_FAILURE(CheckIdEqual(kRepresentationId, &representation));
} }
// TODO(rkuroiwa): Better way to test this is to use GetXml(). Check that
// frameRate is set once the patch that adds frameRate attribute lands.
// For now, check that media_info_ owned by Representation has
// frame_duration = sample_duration.
TEST_F(CommonMpdBuilderTest, SetSampleDuration) {
const MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1);
const uint32_t kRepresentationId = 1;
Representation representation(
video_media_info, MpdOptions(), kRepresentationId);
EXPECT_TRUE(representation.Init());
representation.SetSampleDuration(2u);
EXPECT_EQ(2u,
representation.media_info_.video_info(0).frame_duration());
}
// Add one video check the output. // Add one video check the output.
TEST_F(StaticMpdBuilderTest, Video) { TEST_F(StaticMpdBuilderTest, Video) {
MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1); MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1);

View File

@ -46,6 +46,16 @@ class MpdNotifier {
virtual bool NotifyNewContainer(const MediaInfo& media_info, virtual bool NotifyNewContainer(const MediaInfo& media_info,
uint32_t* container_id) = 0; uint32_t* container_id) = 0;
/// Change the sample duration of container with @a container_id.
/// @param container_id Container ID obtained from calling
/// NotifyNewContainer().
/// @param sample_duration is the duration of a sample in timescale of the
/// media.
/// @return true on success, false otherwise. This may fail if the container
/// specified by @a container_id does not exist.
virtual bool NotifySampleDuration(uint32_t container_id,
uint32_t sample_duration) = 0;
/// Notifies MpdBuilder that there is a new segment ready. Used only for live /// Notifies MpdBuilder that there is a new segment ready. Used only for live
/// profile. /// profile.
/// @param container_id Container ID obtained from calling /// @param container_id Container ID obtained from calling

View File

@ -74,6 +74,18 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info,
return true; return true;
} }
bool SimpleMpdNotifier::NotifySampleDuration(uint32_t container_id,
uint32_t sample_duration) {
base::AutoLock auto_lock(lock_);
RepresentationMap::iterator it = representation_map_.find(container_id);
if (it == representation_map_.end()) {
LOG(ERROR) << "Unexpected container_id: " << container_id;
return false;
}
it->second->SetSampleDuration(sample_duration);
return true;
}
bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id, bool SimpleMpdNotifier::NotifyNewSegment(uint32_t container_id,
uint64_t start_time, uint64_t start_time,
uint64_t duration, uint64_t duration,

View File

@ -38,6 +38,8 @@ class SimpleMpdNotifier : public MpdNotifier {
virtual bool Init() OVERRIDE; virtual bool Init() OVERRIDE;
virtual bool NotifyNewContainer(const MediaInfo& media_info, virtual bool NotifyNewContainer(const MediaInfo& media_info,
uint32_t* id) OVERRIDE; uint32_t* id) OVERRIDE;
virtual bool NotifySampleDuration(uint32_t container_id,
uint32_t sample_duration) OVERRIDE;
virtual bool NotifyNewSegment(uint32_t id, virtual bool NotifyNewSegment(uint32_t id,
uint64_t start_time, uint64_t start_time,
uint64_t duration, uint64_t duration,