diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index e786b076fb..883b68ab79 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -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, uint64_t init_range_start, uint64_t init_range_end, diff --git a/packager/media/event/mpd_notify_muxer_listener.h b/packager/media/event/mpd_notify_muxer_listener.h index 0ca94ac166..18a29ff9d2 100644 --- a/packager/media/event/mpd_notify_muxer_listener.h +++ b/packager/media/event/mpd_notify_muxer_listener.h @@ -41,7 +41,7 @@ class MpdNotifyMuxerListener : public MuxerListener { uint32_t time_scale, ContainerType container_type, bool is_encrypted) OVERRIDE; - + virtual void OnSampleDurationReady(uint32_t sample_duration) OVERRIDE; virtual void OnMediaEnd(bool has_init_range, uint64_t init_range_start, uint64_t init_range_end, @@ -50,7 +50,6 @@ class MpdNotifyMuxerListener : public MuxerListener { uint64_t index_range_end, float duration_seconds, uint64_t file_size) OVERRIDE; - virtual void OnNewSegment(uint64_t start_time, uint64_t duration, uint64_t segment_file_size) OVERRIDE; diff --git a/packager/media/event/muxer_listener.h b/packager/media/event/muxer_listener.h index 846faff178..363e013c50 100644 --- a/packager/media/event/muxer_listener.h +++ b/packager/media/event/muxer_listener.h @@ -19,6 +19,10 @@ namespace media { class StreamInfo; 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 { public: enum ContainerType { @@ -41,6 +45,10 @@ class MuxerListener { ContainerType container_type, 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 // any more files. // Note: This event might not be very interesting to MPEG DASH Live profile. diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.cc b/packager/media/event/vod_media_info_dump_muxer_listener.cc index 4e786da5a4..4e15c49172 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener.cc @@ -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, uint64_t init_range_start, uint64_t init_range_end, diff --git a/packager/media/event/vod_media_info_dump_muxer_listener.h b/packager/media/event/vod_media_info_dump_muxer_listener.h index d17c390d5b..7ed271cb9a 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.h +++ b/packager/media/event/vod_media_info_dump_muxer_listener.h @@ -41,7 +41,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener { uint32_t time_scale, ContainerType container_type, bool is_encrypted) OVERRIDE; - + virtual void OnSampleDurationReady(uint32_t sample_duration) OVERRIDE; virtual void OnMediaEnd(bool has_init_range, uint64_t init_range_start, uint64_t init_range_end, @@ -50,7 +50,6 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener { uint64_t index_range_end, float duration_seconds, uint64_t file_size) OVERRIDE; - virtual void OnNewSegment(uint64_t start_time, uint64_t duration, uint64_t segment_file_size) OVERRIDE; diff --git a/packager/media/formats/mp4/multi_segment_segmenter.cc b/packager/media/formats/mp4/multi_segment_segmenter.cc index bc6dbe6abc..93b4d4a351 100644 --- a/packager/media/formats/mp4/multi_segment_segmenter.cc +++ b/packager/media/formats/mp4/multi_segment_segmenter.cc @@ -185,6 +185,7 @@ Status MultiSegmentSegmenter::WriteSegment() { UpdateProgress(segment_duration); if (muxer_listener()) { + muxer_listener()->OnSampleDurationReady(sample_duration()); muxer_listener()->OnNewSegment( sidx()->earliest_presentation_time, segment_duration, segment_size); } diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 12e8e2daf3..419e3eae81 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -127,7 +127,8 @@ Segmenter::Segmenter(const MuxerOptions& options, muxer_listener_(NULL), progress_listener_(NULL), progress_target_(0), - accumulated_progress_(0) { + accumulated_progress_(0), + sample_duration_(0u) { } Segmenter::~Segmenter() { STLDeleteElements(&fragmenters_); } @@ -295,6 +296,8 @@ Status Segmenter::AddSample(const MediaStream* stream, if (!status.ok()) return status; + if (sample_duration_ == 0) + sample_duration_ = sample->duration(); moov_->tracks[stream_id].media.header.duration += sample->duration(); segment_durations_[stream_id] += sample->duration(); return Status::OK; diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index 3cfca3c229..fbc2f52cf1 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -96,6 +96,10 @@ class Segmenter { /// @return The total length, in seconds, of segmented media files. 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: /// Update segmentation progress using ProgressListener. void UpdateProgress(uint64_t progress); @@ -140,6 +144,7 @@ class Segmenter { ProgressListener* progress_listener_; uint64_t progress_target_; uint64_t accumulated_progress_; + uint32_t sample_duration_; DISALLOW_COPY_AND_ASSIGN(Segmenter); }; diff --git a/packager/media/formats/mp4/single_segment_segmenter.cc b/packager/media/formats/mp4/single_segment_segmenter.cc index 70e4070b31..b09755ad98 100644 --- a/packager/media/formats/mp4/single_segment_segmenter.cc +++ b/packager/media/formats/mp4/single_segment_segmenter.cc @@ -225,6 +225,7 @@ Status SingleSegmentSegmenter::DoFinalizeSegment() { UpdateProgress(vod_ref.subsegment_duration); if (muxer_listener()) { + muxer_listener()->OnSampleDurationReady(sample_duration()); muxer_listener()->OnNewSegment(vod_ref.earliest_presentation_time, vod_ref.subsegment_duration, segment_size); } diff --git a/packager/mpd/base/mpd_builder.cc b/packager/mpd/base/mpd_builder.cc index 9fb254b042..737721eadc 100644 --- a/packager/mpd/base/mpd_builder.cc +++ b/packager/mpd/base/mpd_builder.cc @@ -666,6 +666,12 @@ void Representation::AddNewSegment(uint64_t start_time, 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 // "Representation" node. // MPD schema has strict ordering. The following must be done in order. diff --git a/packager/mpd/base/mpd_builder.h b/packager/mpd/base/mpd_builder.h index c7df58b30a..3e8299c2dc 100644 --- a/packager/mpd/base/mpd_builder.h +++ b/packager/mpd/base/mpd_builder.h @@ -233,6 +233,13 @@ class Representation { /// @param size of the segment in bytes. 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 . xml::ScopedXmlPtr::type GetXml(); @@ -247,6 +254,7 @@ class Representation { FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, InvalidMediaInfo); FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckVideoInfoReflectedInXml); FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, CheckRepresentationId); + FRIEND_TEST_ALL_PREFIXES(CommonMpdBuilderTest, SetSampleDuration); /// @param media_info is a MediaInfo containing information on the media. /// @a media_info.bandwidth is required for 'static' profile. If @a diff --git a/packager/mpd/base/mpd_builder_unittest.cc b/packager/mpd/base/mpd_builder_unittest.cc index da353ec3a0..14440c473f 100644 --- a/packager/mpd/base/mpd_builder_unittest.cc +++ b/packager/mpd/base/mpd_builder_unittest.cc @@ -399,6 +399,22 @@ TEST_F(CommonMpdBuilderTest, CheckRepresentationId) { 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. TEST_F(StaticMpdBuilderTest, Video) { MediaInfo video_media_info = GetTestMediaInfo(kFileNameVideoMediaInfo1); diff --git a/packager/mpd/base/mpd_notifier.h b/packager/mpd/base/mpd_notifier.h index 38357e5e28..fbf0891a29 100644 --- a/packager/mpd/base/mpd_notifier.h +++ b/packager/mpd/base/mpd_notifier.h @@ -46,6 +46,16 @@ class MpdNotifier { virtual bool NotifyNewContainer(const MediaInfo& media_info, 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 /// profile. /// @param container_id Container ID obtained from calling diff --git a/packager/mpd/base/simple_mpd_notifier.cc b/packager/mpd/base/simple_mpd_notifier.cc index 7ffaed40d6..6b56c09eda 100644 --- a/packager/mpd/base/simple_mpd_notifier.cc +++ b/packager/mpd/base/simple_mpd_notifier.cc @@ -74,6 +74,18 @@ bool SimpleMpdNotifier::NotifyNewContainer(const MediaInfo& media_info, 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, uint64_t start_time, uint64_t duration, diff --git a/packager/mpd/base/simple_mpd_notifier.h b/packager/mpd/base/simple_mpd_notifier.h index 7050174f90..02faed6482 100644 --- a/packager/mpd/base/simple_mpd_notifier.h +++ b/packager/mpd/base/simple_mpd_notifier.h @@ -38,6 +38,8 @@ class SimpleMpdNotifier : public MpdNotifier { virtual bool Init() OVERRIDE; virtual bool NotifyNewContainer(const MediaInfo& media_info, uint32_t* id) OVERRIDE; + virtual bool NotifySampleDuration(uint32_t container_id, + uint32_t sample_duration) OVERRIDE; virtual bool NotifyNewSegment(uint32_t id, uint64_t start_time, uint64_t duration,