From 0dfdace2807c5e9ec719a50b379fb47cb0f0f2c1 Mon Sep 17 00:00:00 2001 From: Rintaro Kuroiwa Date: Mon, 1 May 2017 16:17:09 -0700 Subject: [PATCH] Change MuxerListener::OnMediaEnd to take ranges - Changed OnMediaEnd() to take (sub)segment ranges. - Define a structure to pass the ranges. - This requires muxers to "cache" the segment ranges but in most cases, they already do it, e.g. 'sidx' for mp4. Change-Id: I16b974c1f0f54ca658cc6dbe605efff84a3f52eb --- packager/media/base/media_base.gyp | 1 + packager/media/base/range.h | 27 ++++++++++ .../media/event/hls_notify_muxer_listener.cc | 7 +-- .../media/event/hls_notify_muxer_listener.h | 7 +-- .../hls_notify_muxer_listener_unittest.cc | 2 +- packager/media/event/mock_muxer_listener.cc | 20 ++++++++ packager/media/event/mock_muxer_listener.h | 26 ++++++---- .../media/event/mpd_notify_muxer_listener.cc | 18 ++----- .../media/event/mpd_notify_muxer_listener.h | 7 +-- .../mpd_notify_muxer_listener_unittest.cc | 8 +-- packager/media/event/muxer_listener.h | 34 +++++++------ .../media/event/muxer_listener_internal.cc | 46 +++++------------ .../media/event/muxer_listener_internal.h | 7 +-- .../media/event/muxer_listener_test_helper.cc | 17 +++++-- .../media/event/muxer_listener_test_helper.h | 8 +-- .../vod_media_info_dump_muxer_listener.cc | 16 +----- .../vod_media_info_dump_muxer_listener.h | 7 +-- ...media_info_dump_muxer_listener_unittest.cc | 7 +-- packager/media/formats/mp2t/ts_muxer.cc | 8 ++- packager/media/formats/mp4/mp4_muxer.cc | 51 +++++++------------ packager/media/formats/mp4/mp4_muxer.h | 5 +- .../formats/mp4/multi_segment_segmenter.cc | 11 ++-- .../formats/mp4/multi_segment_segmenter.h | 1 + packager/media/formats/mp4/segmenter.h | 9 ++++ .../formats/mp4/single_segment_segmenter.cc | 17 ++++++- .../formats/mp4/single_segment_segmenter.h | 2 + .../formats/webm/multi_segment_segmenter.cc | 4 ++ .../formats/webm/multi_segment_segmenter.h | 1 + packager/media/formats/webm/segmenter.h | 8 +++ .../formats/webm/single_segment_segmenter.cc | 26 ++++++++-- .../formats/webm/single_segment_segmenter.h | 1 + packager/media/formats/webm/webm_muxer.cc | 20 ++++++-- 32 files changed, 239 insertions(+), 190 deletions(-) create mode 100644 packager/media/base/range.h diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index d214eedd14..113f35ac8c 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -75,6 +75,7 @@ 'producer_consumer_queue.h', 'protection_system_specific_info.cc', 'protection_system_specific_info.h', + 'range.h', 'rcheck.h', 'request_signer.cc', 'request_signer.h', diff --git a/packager/media/base/range.h b/packager/media/base/range.h new file mode 100644 index 0000000000..e1cf317b93 --- /dev/null +++ b/packager/media/base/range.h @@ -0,0 +1,27 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file or at +// https://developers.google.com/open-source/licenses/bsd +// +// Event handler for events fired by Muxer. + +#ifndef PACKAGER_MEDIA_BASE_RANGE_H_ +#define PACKAGER_MEDIA_BASE_RANGE_H_ + +#include + +namespace shaka { +namespace media { + +/// Structure for specifying a range. +/// The start and end are inclusive which is equivalent to [start, end]. +struct Range { + uint64_t start; + uint64_t end; +}; + +} // namespace media +} // namespace shaka + +#endif // PACKAGER_MEDIA_BASE_RANGE_H_ diff --git a/packager/media/event/hls_notify_muxer_listener.cc b/packager/media/event/hls_notify_muxer_listener.cc index cfadc6f6e1..649cbb26ab 100644 --- a/packager/media/event/hls_notify_muxer_listener.cc +++ b/packager/media/event/hls_notify_muxer_listener.cc @@ -110,12 +110,7 @@ void HlsNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options, void HlsNotifyMuxerListener::OnSampleDurationReady(uint32_t sample_duration) {} -void HlsNotifyMuxerListener::OnMediaEnd(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, +void HlsNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, float duration_seconds, uint64_t file_size) { // TODO(kqyang): Should we just Flush here to avoid calling Flush explicitly? diff --git a/packager/media/event/hls_notify_muxer_listener.h b/packager/media/event/hls_notify_muxer_listener.h index f2fac026bc..ac3a960dcb 100644 --- a/packager/media/event/hls_notify_muxer_listener.h +++ b/packager/media/event/hls_notify_muxer_listener.h @@ -51,12 +51,7 @@ class HlsNotifyMuxerListener : public MuxerListener { uint32_t time_scale, ContainerType container_type) override; void OnSampleDurationReady(uint32_t sample_duration) override; - void OnMediaEnd(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, + void OnMediaEnd(const MediaRanges& media_ranges, float duration_seconds, uint64_t file_size) override; void OnNewSegment(const std::string& file_name, diff --git a/packager/media/event/hls_notify_muxer_listener_unittest.cc b/packager/media/event/hls_notify_muxer_listener_unittest.cc index 9a0c25be53..1d2a7eebfa 100644 --- a/packager/media/event/hls_notify_muxer_listener_unittest.cc +++ b/packager/media/event/hls_notify_muxer_listener_unittest.cc @@ -313,7 +313,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnSampleDurationReady) { // Make sure it doesn't crash. TEST_F(HlsNotifyMuxerListenerTest, OnMediaEnd) { // None of these values matter, they are not used. - listener_.OnMediaEnd(false, 0, 0, false, 0, 0, 0, 0); + listener_.OnMediaEnd(MuxerListener::MediaRanges(), 0, 0); } TEST_F(HlsNotifyMuxerListenerTest, OnNewSegment) { diff --git a/packager/media/event/mock_muxer_listener.cc b/packager/media/event/mock_muxer_listener.cc index a262bab5a8..2e358e1e5e 100644 --- a/packager/media/event/mock_muxer_listener.cc +++ b/packager/media/event/mock_muxer_listener.cc @@ -12,5 +12,25 @@ namespace media { MockMuxerListener::MockMuxerListener() {} MockMuxerListener::~MockMuxerListener() {} +void MockMuxerListener::OnMediaEnd(const MediaRanges& range, + float duration_seconds, + uint64_t file_size) { + const bool has_init_range = static_cast(range.init_range); + Range init_range = {}; + if (has_init_range) { + init_range = range.init_range.value(); + } + const bool has_index_range = static_cast(range.index_range); + Range index_range = {}; + if (has_index_range) { + index_range = range.index_range.value(); + } + + OnMediaEndMock(has_init_range, init_range.start, init_range.end, + has_index_range, index_range.start, index_range.end, + !range.subsegment_ranges.empty(), range.subsegment_ranges, + duration_seconds, file_size); +} + } // namespace media } // namespace shaka diff --git a/packager/media/event/mock_muxer_listener.h b/packager/media/event/mock_muxer_listener.h index d30dab8b9d..648726e603 100644 --- a/packager/media/event/mock_muxer_listener.h +++ b/packager/media/event/mock_muxer_listener.h @@ -40,15 +40,23 @@ class MockMuxerListener : public MuxerListener { MOCK_METHOD1(OnSampleDurationReady, void(uint32_t sample_duration)); - MOCK_METHOD8(OnMediaEnd, - void(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, - float duration_seconds, - uint64_t file_size)); + MOCK_METHOD10(OnMediaEndMock, + void(bool has_init_range, + uint64_t init_range_start, + uint64_t init_range_end, + bool has_index_range, + uint64_t index_range_start, + uint64_t index_range_end, + bool has_subsegment_ranges, + const std::vector subsegment_ranges, + float duration_seconds, + uint64_t file_size)); + + // Windows 32 bit cannot mock MediaRanges because it has Optionals that use + // memory alignment of 8 bytes. The compiler fails if it is mocked. + void OnMediaEnd(const MediaRanges& range, + float duration_seconds, + uint64_t file_size) override; MOCK_METHOD4(OnNewSegment, void(const std::string& segment_name, diff --git a/packager/media/event/mpd_notify_muxer_listener.cc b/packager/media/event/mpd_notify_muxer_listener.cc index 23806e832a..4fa5c61f87 100644 --- a/packager/media/event/mpd_notify_muxer_listener.cc +++ b/packager/media/event/mpd_notify_muxer_listener.cc @@ -109,12 +109,7 @@ void MpdNotifyMuxerListener::OnSampleDurationReady( media_info_->mutable_video_info()->set_frame_duration(sample_duration); } -void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, +void MpdNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, float duration_seconds, uint64_t file_size) { if (mpd_notifier_->dash_profile() == DashProfile::kLive) { @@ -127,14 +122,7 @@ void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range, } DCHECK(media_info_); - if (!internal::SetVodInformation(has_init_range, - init_range_start, - init_range_end, - has_index_range, - index_range_start, - index_range_end, - duration_seconds, - file_size, + if (!internal::SetVodInformation(media_ranges, duration_seconds, file_size, media_info_.get())) { LOG(ERROR) << "Failed to generate VOD information from input."; return; @@ -143,6 +131,8 @@ void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range, uint32_t id; // TODO(kqyang): Check return result. mpd_notifier_->NotifyNewContainer(*media_info_, &id); + // TODO(rkuroiwa): Use media_ranges.subsegment_ranges instead of caching the + // subsegments. for (std::list::const_iterator it = subsegments_.begin(); it != subsegments_.end(); ++it) { mpd_notifier_->NotifyNewSegment(id, it->start_time, it->duration, diff --git a/packager/media/event/mpd_notify_muxer_listener.h b/packager/media/event/mpd_notify_muxer_listener.h index ed09ebb4a8..630f1b2a80 100644 --- a/packager/media/event/mpd_notify_muxer_listener.h +++ b/packager/media/event/mpd_notify_muxer_listener.h @@ -45,12 +45,7 @@ class MpdNotifyMuxerListener : public MuxerListener { uint32_t time_scale, ContainerType container_type) override; void OnSampleDurationReady(uint32_t sample_duration) override; - void OnMediaEnd(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, + void OnMediaEnd(const MediaRanges& media_ranges, float duration_seconds, uint64_t file_size) override; void OnNewSegment(const std::string& file_name, diff --git a/packager/media/event/mpd_notify_muxer_listener_unittest.cc b/packager/media/event/mpd_notify_muxer_listener_unittest.cc index ec7819aca4..b425ef9478 100644 --- a/packager/media/event/mpd_notify_muxer_listener_unittest.cc +++ b/packager/media/event/mpd_notify_muxer_listener_unittest.cc @@ -83,13 +83,7 @@ class MpdNotifyMuxerListenerTest : public ::testing::TestWithParam { void FireOnMediaEndWithParams(const OnMediaEndParameters& params) { // On success, this writes the result to |temp_file_path_|. - listener_->OnMediaEnd(params.has_init_range, - params.init_range_start, - params.init_range_end, - params.has_index_range, - params.index_range_start, - params.index_range_end, - params.duration_seconds, + listener_->OnMediaEnd(params.media_ranges, params.duration_seconds, params.file_size); } diff --git a/packager/media/event/muxer_listener.h b/packager/media/event/muxer_listener.h index bb117ebf99..4991034994 100644 --- a/packager/media/event/muxer_listener.h +++ b/packager/media/event/muxer_listener.h @@ -14,7 +14,9 @@ #include #include +#include "packager/base/optional.h" #include "packager/media/base/fourccs.h" +#include "packager/media/base/range.h" namespace shaka { namespace media { @@ -36,6 +38,20 @@ class MuxerListener { kContainerWebM }; + /// Structure for specifying ranges within a media file. This is mainly for + /// VOD content where OnMediaEnd() is actually used for finalization e.g. + /// writing out manifests. + struct MediaRanges { + /// Range of the initialization section of a segment. + base::Optional init_range; + /// Range of the index section of a segment. + base::Optional index_range; + /// Ranges of the subsegments (e.g. fragments). + /// The vector is empty if ranges are not specified. For example it + /// may not be a single file. + std::vector subsegment_ranges; + }; + virtual ~MuxerListener() {}; /// Called when the media's encryption information is ready. @@ -89,24 +105,10 @@ class MuxerListener { /// 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. - /// @param has_init_range is true if @a init_range_start and @a init_range_end - /// actually define an initialization range of a segment. The range is - /// inclusive for both start and end. - /// @param init_range_start is the start of the initialization range. - /// @param init_range_end is the end of the initialization range. - /// @param has_index_range is true if @a index_range_start and @a - /// index_range_end actually define an index range of a segment. The - /// range is inclusive for both start and end. - /// @param index_range_start is the start of the index range. - /// @param index_range_end is the end of the index range. + /// @param media_ranges is the ranges of the media file. /// @param duration_seconds is the length of the media in seconds. /// @param file_size is the size of the file in bytes. - virtual void OnMediaEnd(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, + virtual void OnMediaEnd(const MediaRanges& media_ranges, float duration_seconds, uint64_t file_size) = 0; diff --git a/packager/media/event/muxer_listener_internal.cc b/packager/media/event/muxer_listener_internal.cc index b36baa1c78..af8b569780 100644 --- a/packager/media/event/muxer_listener_internal.cc +++ b/packager/media/event/muxer_listener_internal.cc @@ -36,30 +36,14 @@ uint32_t EstimateRequiredBandwidth(uint64_t file_size, float duration_seconds) { return static_cast(ceil(bits_per_second)); } -void SetRange(uint64_t begin, uint64_t end, Range* range) { +// TODO(rkuroiwa): There is shaka::Range in MediaInfo proto and +// shaka::media::Range in media/base. Find better names. +void SetRange(uint64_t begin, uint64_t end, shaka::Range* range) { DCHECK(range); range->set_begin(begin); range->set_end(end); } -void SetMediaInfoRanges(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, - MediaInfo* media_info) { - if (has_init_range) { - SetRange( - init_range_start, init_range_end, media_info->mutable_init_range()); - } - - if (has_index_range) { - SetRange( - index_range_start, index_range_end, media_info->mutable_index_range()); - } -} - void SetMediaInfoContainerType(MuxerListener::ContainerType container_type, MediaInfo* media_info) { DCHECK(media_info); @@ -205,12 +189,7 @@ bool GenerateMediaInfo(const MuxerOptions& muxer_options, return true; } -bool SetVodInformation(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, +bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges, float duration_seconds, uint64_t file_size, MediaInfo* media_info) { @@ -226,13 +205,16 @@ bool SetVodInformation(bool has_init_range, return false; } - SetMediaInfoRanges(has_init_range, - init_range_start, - init_range_end, - has_index_range, - index_range_start, - index_range_end, - media_info); + + if (media_ranges.init_range) { + SetRange(media_ranges.init_range->start, media_ranges.init_range->end, + media_info->mutable_init_range()); + } + + if (media_ranges.index_range) { + SetRange(media_ranges.index_range->start, media_ranges.index_range->end, + media_info->mutable_index_range()); + } media_info->set_media_duration_seconds(duration_seconds); diff --git a/packager/media/event/muxer_listener_internal.h b/packager/media/event/muxer_listener_internal.h index 0a049c07e6..b58341dbc0 100644 --- a/packager/media/event/muxer_listener_internal.h +++ b/packager/media/event/muxer_listener_internal.h @@ -35,12 +35,7 @@ bool GenerateMediaInfo(const MuxerOptions& muxer_options, /// @param[in,out] media_info points to the MediaInfo object to be filled. /// @return true on success, false otherwise. -bool SetVodInformation(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, +bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges, float duration_seconds, uint64_t file_size, MediaInfo* media_info); diff --git a/packager/media/event/muxer_listener_test_helper.cc b/packager/media/event/muxer_listener_test_helper.cc index 4c79c33223..1c26a393c2 100644 --- a/packager/media/event/muxer_listener_test_helper.cc +++ b/packager/media/event/muxer_listener_test_helper.cc @@ -5,6 +5,7 @@ // https://developers.google.com/open-source/licenses/bsd #include "packager/media/event/muxer_listener_test_helper.h" +#include "packager/media/event/muxer_listener.h" #include @@ -59,17 +60,23 @@ VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() { OnMediaEndParameters GetDefaultOnMediaEndParams() { // Values for {init, index} range {start, end} are arbitrary, but makes sure // that it is monotonically increasing and contiguous. - const bool kHasInitRange = true; const uint64_t kInitRangeStart = 0; const uint64_t kInitRangeEnd = kInitRangeStart + 120; - const uint64_t kHasIndexRange = true; const uint64_t kIndexRangeStart = kInitRangeEnd + 1; const uint64_t kIndexRangeEnd = kIndexRangeStart + 100; const float kMediaDuration = 10.5f; const uint64_t kFileSize = 10000; - OnMediaEndParameters param = { - kHasInitRange, kInitRangeStart, kInitRangeEnd, kHasIndexRange, - kIndexRangeStart, kIndexRangeEnd, kMediaDuration, kFileSize}; + MuxerListener::MediaRanges media_ranges; + Range init_range; + init_range.start = kInitRangeStart; + init_range.end = kInitRangeEnd; + media_ranges.init_range = init_range; + Range index_range; + index_range.start = kIndexRangeStart; + index_range.end = kIndexRangeEnd; + media_ranges.index_range =index_range; + + OnMediaEndParameters param = {media_ranges, kMediaDuration, kFileSize}; return param; } diff --git a/packager/media/event/muxer_listener_test_helper.h b/packager/media/event/muxer_listener_test_helper.h index 93ba6deb1f..48b16d65c7 100644 --- a/packager/media/event/muxer_listener_test_helper.h +++ b/packager/media/event/muxer_listener_test_helper.h @@ -14,6 +14,7 @@ #include "packager/media/base/muxer_options.h" #include "packager/media/base/stream_info.h" #include "packager/media/base/video_stream_info.h" +#include "packager/media/event/muxer_listener.h" #include "packager/mpd/base/media_info.pb.h" namespace shaka { @@ -73,12 +74,7 @@ struct VideoStreamInfoParameters { // Note that this does not have vector of StreamInfo pointer. struct OnMediaEndParameters { - bool has_init_range; - uint64_t init_range_start; - uint64_t init_range_end; - bool has_index_range; - uint64_t index_range_start; - uint64_t index_range_end; + MuxerListener::MediaRanges media_ranges; float duration_seconds; uint64_t file_size; }; 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 5488152756..a05c704aa2 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener.cc @@ -72,23 +72,11 @@ void VodMediaInfoDumpMuxerListener::OnSampleDurationReady( } } -void VodMediaInfoDumpMuxerListener::OnMediaEnd(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, +void VodMediaInfoDumpMuxerListener::OnMediaEnd(const MediaRanges& media_ranges, float duration_seconds, uint64_t file_size) { DCHECK(media_info_); - if (!internal::SetVodInformation(has_init_range, - init_range_start, - init_range_end, - has_index_range, - index_range_start, - index_range_end, - duration_seconds, - file_size, + if (!internal::SetVodInformation(media_ranges, duration_seconds, file_size, media_info_.get())) { LOG(ERROR) << "Failed to generate VOD information from input."; return; 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 f35d58b640..431ceb5cb0 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener.h +++ b/packager/media/event/vod_media_info_dump_muxer_listener.h @@ -44,12 +44,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener { uint32_t time_scale, ContainerType container_type) override; void OnSampleDurationReady(uint32_t sample_duration) override; - void OnMediaEnd(bool has_init_range, - uint64_t init_range_start, - uint64_t init_range_end, - bool has_index_range, - uint64_t index_range_start, - uint64_t index_range_end, + void OnMediaEnd(const MediaRanges& media_ranges, float duration_seconds, uint64_t file_size) override; void OnNewSegment(const std::string& file_name, diff --git a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc index 2967142648..055ac8bc46 100644 --- a/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc +++ b/packager/media/event/vod_media_info_dump_muxer_listener_unittest.cc @@ -95,12 +95,7 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test { void FireOnMediaEndWithParams(const OnMediaEndParameters& params) { // On success, this writes the result to |temp_file_path_|. - listener_->OnMediaEnd(params.has_init_range, - params.init_range_start, - params.init_range_end, - params.has_index_range, - params.index_range_start, - params.index_range_end, + listener_->OnMediaEnd(params.media_ranges, params.duration_seconds, params.file_size); } diff --git a/packager/media/formats/mp2t/ts_muxer.cc b/packager/media/formats/mp2t/ts_muxer.cc index 56c1c48262..e53968510f 100644 --- a/packager/media/formats/mp2t/ts_muxer.cc +++ b/packager/media/formats/mp2t/ts_muxer.cc @@ -59,11 +59,9 @@ void TsMuxer::FireOnMediaEndEvent() { return; // For now, there is no single file TS segmenter. So all the values passed - // here are false and 0. Called just to notify the MuxerListener. - const bool kHasInitRange = true; - const bool kHasIndexRange = true; - muxer_listener()->OnMediaEnd(!kHasInitRange, 0, 0, !kHasIndexRange, 0, 0, 0, - 0); + // here are left empty. + MuxerListener::MediaRanges range; + muxer_listener()->OnMediaEnd(range, 0, 0); } } // namespace mp2t diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 4070d1b367..e6c7c0f728 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -32,12 +32,11 @@ namespace { // |start| and |end| are for byte-range-spec specified in RFC2616. void SetStartAndEndFromOffsetAndSize(size_t offset, size_t size, - uint32_t* start, - uint32_t* end) { - DCHECK(start && end); - *start = static_cast(offset); + Range* range) { + DCHECK(range); + range->start = static_cast(offset); // Note that ranges are inclusive. So we need - 1. - *end = *start + static_cast(size) - 1; + range->end = range->start + static_cast(size) - 1; } FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) { @@ -419,30 +418,30 @@ void MP4Muxer::GenerateTextTrak(const TextStreamInfo* text_info, << " handling not implemented yet."; } -bool MP4Muxer::GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end) { - DCHECK(start && end); +base::Optional MP4Muxer::GetInitRangeStartAndEnd() { size_t range_offset = 0; size_t range_size = 0; const bool has_range = segmenter_->GetInitRange(&range_offset, &range_size); if (!has_range) - return false; + return base::nullopt; - SetStartAndEndFromOffsetAndSize(range_offset, range_size, start, end); - return true; + Range range; + SetStartAndEndFromOffsetAndSize(range_offset, range_size, &range); + return range; } -bool MP4Muxer::GetIndexRangeStartAndEnd(uint32_t* start, uint32_t* end) { - DCHECK(start && end); +base::Optional MP4Muxer::GetIndexRangeStartAndEnd() { size_t range_offset = 0; size_t range_size = 0; const bool has_range = segmenter_->GetIndexRange(&range_offset, &range_size); if (!has_range) - return false; + return base::nullopt; - SetStartAndEndFromOffsetAndSize(range_offset, range_size, start, end); - return true; + Range range; + SetStartAndEndFromOffsetAndSize(range_offset, range_size, &range); + return range; } void MP4Muxer::FireOnMediaStartEvent() { @@ -464,15 +463,10 @@ void MP4Muxer::FireOnMediaEndEvent() { if (!muxer_listener()) return; - uint32_t init_range_start = 0; - uint32_t init_range_end = 0; - const bool has_init_range = - GetInitRangeStartAndEnd(&init_range_start, &init_range_end); - - uint32_t index_range_start = 0; - uint32_t index_range_end = 0; - const bool has_index_range = - GetIndexRangeStartAndEnd(&index_range_start, &index_range_end); + MuxerListener::MediaRanges media_range; + media_range.init_range = GetInitRangeStartAndEnd(); + media_range.index_range = GetIndexRangeStartAndEnd(); + media_range.subsegment_ranges = segmenter_->GetSegmentRanges(); const float duration_seconds = static_cast(segmenter_->GetDuration()); @@ -483,14 +477,7 @@ void MP4Muxer::FireOnMediaEndEvent() { return; } - muxer_listener()->OnMediaEnd(has_init_range, - init_range_start, - init_range_end, - has_index_range, - index_range_start, - index_range_end, - duration_seconds, - file_size); + muxer_listener()->OnMediaEnd(media_range, duration_seconds, file_size); } uint64_t MP4Muxer::IsoTimeNow() { diff --git a/packager/media/formats/mp4/mp4_muxer.h b/packager/media/formats/mp4/mp4_muxer.h index 8d9865ef82..4f56922ec3 100644 --- a/packager/media/formats/mp4/mp4_muxer.h +++ b/packager/media/formats/mp4/mp4_muxer.h @@ -9,6 +9,7 @@ #include +#include "packager/base/optional.h" #include "packager/media/base/muxer.h" namespace shaka { @@ -57,11 +58,11 @@ class MP4Muxer : public Muxer { // Gets |start| and |end| initialization range. Returns true if there is an // init range and sets start-end byte-range-spec specified in RFC2616. - bool GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end); + base::Optional GetInitRangeStartAndEnd(); // Gets |start| and |end| index range. Returns true if there is an index range // and sets start-end byte-range-spec specified in RFC2616. - bool GetIndexRangeStartAndEnd(uint32_t* start, uint32_t* end); + base::Optional GetIndexRangeStartAndEnd(); // Fire events if there are no errors and Muxer::muxer_listener() is not NULL. void FireOnMediaStartEvent(); diff --git a/packager/media/formats/mp4/multi_segment_segmenter.cc b/packager/media/formats/mp4/multi_segment_segmenter.cc index 8bb00532ef..9cc1f3a14c 100644 --- a/packager/media/formats/mp4/multi_segment_segmenter.cc +++ b/packager/media/formats/mp4/multi_segment_segmenter.cc @@ -38,16 +38,21 @@ MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options, MultiSegmentSegmenter::~MultiSegmentSegmenter() {} bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) { - DLOG(INFO) << "MultiSegmentSegmenter outputs init segment: " - << options().output_file_name; + VLOG(1) << "MultiSegmentSegmenter outputs init segment: " + << options().output_file_name; return false; } bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) { - DLOG(INFO) << "MultiSegmentSegmenter does not have index range."; + VLOG(1) << "MultiSegmentSegmenter does not have index range."; return false; } +std::vector MultiSegmentSegmenter::GetSegmentRanges() { + VLOG(1) << "MultiSegmentSegmenter does not have media segment ranges."; + return std::vector(); +} + Status MultiSegmentSegmenter::DoInitialize() { DCHECK(ftyp()); DCHECK(moov()); diff --git a/packager/media/formats/mp4/multi_segment_segmenter.h b/packager/media/formats/mp4/multi_segment_segmenter.h index f65cf7b1cd..f3c35e9f20 100644 --- a/packager/media/formats/mp4/multi_segment_segmenter.h +++ b/packager/media/formats/mp4/multi_segment_segmenter.h @@ -39,6 +39,7 @@ class MultiSegmentSegmenter : public Segmenter { /// @{ bool GetInitRange(size_t* offset, size_t* size) override; bool GetIndexRange(size_t* offset, size_t* size) override; + std::vector GetSegmentRanges() override; /// @} private: diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index 8e91ecbd68..1423e8e0e3 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -11,7 +11,9 @@ #include #include +#include "packager/base/optional.h" #include "packager/media/base/fourccs.h" +#include "packager/media/base/range.h" #include "packager/media/formats/mp4/box_definitions.h" #include "packager/status.h" @@ -73,6 +75,8 @@ class Segmenter { Status FinalizeSegment(size_t stream_id, std::shared_ptr segment_info); + // TODO(rkuroiwa): Change these Get*Range() methods to return + // base::Optional as well. /// @return true if there is an initialization range, while setting @a offset /// and @a size; or false if initialization range does not apply. virtual bool GetInitRange(size_t* offset, size_t* size) = 0; @@ -81,6 +85,11 @@ class Segmenter { /// and @a size; or false if index byte range does not apply. virtual bool GetIndexRange(size_t* offset, size_t* size) = 0; + // Returns an empty vector if there are no specific ranges for the segments, + // e.g. the media is in multiple files. + // Otherwise, a vector of ranges for the media segments are returned. + virtual std::vector GetSegmentRanges() = 0; + uint32_t GetReferenceTimeScale() const; /// @return The total length, in seconds, of segmented media files. diff --git a/packager/media/formats/mp4/single_segment_segmenter.cc b/packager/media/formats/mp4/single_segment_segmenter.cc index 048b9a7fcf..77df75e462 100644 --- a/packager/media/formats/mp4/single_segment_segmenter.cc +++ b/packager/media/formats/mp4/single_segment_segmenter.cc @@ -10,7 +10,6 @@ #include "packager/media/base/buffer_writer.h" #include "packager/media/base/muxer_options.h" -#include "packager/media/event/muxer_listener.h" #include "packager/media/event/progress_listener.h" #include "packager/media/file/file.h" #include "packager/media/file/file_util.h" @@ -49,6 +48,22 @@ bool SingleSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) { return true; } +std::vector SingleSegmentSegmenter::GetSegmentRanges() { + std::vector ranges; + uint64_t next_offset = + ftyp()->ComputeSize() + moov()->ComputeSize() + vod_sidx_->ComputeSize() + + vod_sidx_->first_offset; + for (const SegmentReference& segment_reference : vod_sidx_->references) { + Range r; + r.start = next_offset; + // Ranges are inclusive, so -1 to the size. + r.end = r.start + segment_reference.referenced_size - 1; + next_offset = r.end + 1; + ranges.push_back(r); + } + return ranges; +} + Status SingleSegmentSegmenter::DoInitialize() { // Single segment segmentation involves two stages: // Stage 1: Create media subsegments from media samples diff --git a/packager/media/formats/mp4/single_segment_segmenter.h b/packager/media/formats/mp4/single_segment_segmenter.h index a56892d1f9..2e72a1b5bb 100644 --- a/packager/media/formats/mp4/single_segment_segmenter.h +++ b/packager/media/formats/mp4/single_segment_segmenter.h @@ -7,6 +7,7 @@ #ifndef MEDIA_FORMATS_MP4_SINGLE_SEGMENT_SEGMENTER_H_ #define MEDIA_FORMATS_MP4_SINGLE_SEGMENT_SEGMENTER_H_ +#include "packager/media/event/muxer_listener.h" #include "packager/media/file/file_closer.h" #include "packager/media/formats/mp4/segmenter.h" @@ -36,6 +37,7 @@ class SingleSegmentSegmenter : public Segmenter { /// @{ bool GetInitRange(size_t* offset, size_t* size) override; bool GetIndexRange(size_t* offset, size_t* size) override; + std::vector GetSegmentRanges() override; /// @} private: diff --git a/packager/media/formats/webm/multi_segment_segmenter.cc b/packager/media/formats/webm/multi_segment_segmenter.cc index 094e497fba..8d9cd398fc 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.cc +++ b/packager/media/formats/webm/multi_segment_segmenter.cc @@ -52,6 +52,10 @@ bool MultiSegmentSegmenter::GetIndexRangeStartAndEnd(uint64_t* start, return false; } +std::vector MultiSegmentSegmenter::GetSegmentRanges() { + return std::vector(); +} + Status MultiSegmentSegmenter::DoInitialize() { std::unique_ptr writer(new MkvWriter); Status status = writer->Open(options().output_file_name); diff --git a/packager/media/formats/webm/multi_segment_segmenter.h b/packager/media/formats/webm/multi_segment_segmenter.h index af8b816f23..66b509b1fb 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.h +++ b/packager/media/formats/webm/multi_segment_segmenter.h @@ -33,6 +33,7 @@ class MultiSegmentSegmenter : public Segmenter { bool is_subsegment) override; bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override; + std::vector GetSegmentRanges() override; /// @} protected: diff --git a/packager/media/formats/webm/segmenter.h b/packager/media/formats/webm/segmenter.h index 69a7e22ed7..1b59dba100 100644 --- a/packager/media/formats/webm/segmenter.h +++ b/packager/media/formats/webm/segmenter.h @@ -8,6 +8,9 @@ #define MEDIA_FORMATS_WEBM_SEGMENTER_H_ #include + +#include "packager/base/optional.h" +#include "packager/media/base/range.h" #include "packager/media/formats/webm/mkv_writer.h" #include "packager/media/formats/webm/seek_head.h" #include "packager/status.h" @@ -64,6 +67,11 @@ class Segmenter { /// and @a end; or false if index byte range does not apply. virtual bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) = 0; + // Returns an empty vector if there are no specific ranges for the segments, + // e.g. the media is in multiple files. + // Otherwise, a vector of ranges for the media segments are returned. + virtual std::vector GetSegmentRanges() = 0; + /// @return The total length, in seconds, of segmented media files. float GetDurationInSeconds() const; diff --git a/packager/media/formats/webm/single_segment_segmenter.cc b/packager/media/formats/webm/single_segment_segmenter.cc index 6204ab0f88..7335007a61 100644 --- a/packager/media/formats/webm/single_segment_segmenter.cc +++ b/packager/media/formats/webm/single_segment_segmenter.cc @@ -36,8 +36,6 @@ Status SingleSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp, bool SingleSegmentSegmenter::GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) { - // The init range is the header, from the start of the file to the size of - // the header. *start = 0; *end = init_end_; return true; @@ -45,13 +43,33 @@ bool SingleSegmentSegmenter::GetInitRangeStartAndEnd(uint64_t* start, bool SingleSegmentSegmenter::GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) { - // The index is the Cues element, which is always placed at the end of the - // file. *start = index_start_; *end = index_end_; return true; } +std::vector SingleSegmentSegmenter::GetSegmentRanges() { + std::vector ranges; + if (cues()->cue_entries_size() == 0) { + return ranges; + } + for (int32_t i = 0; i < cues()->cue_entries_size() - 1; ++i) { + const mkvmuxer::CuePoint* cue_point = cues()->GetCueByIndex(i); + Range r; + r.start = cue_point->cluster_pos(); + r.end = cues()->GetCueByIndex(i + 1)->cluster_pos() - 1; + ranges.push_back(r); + } + + Range last_range; + const mkvmuxer::CuePoint* last_cue_point = + cues()->GetCueByIndex(cues()->cue_entries_size() - 1); + last_range.start = last_cue_point->cluster_pos(); + last_range.end = last_range.start + cluster()->Size() - 1; + ranges.push_back(last_range); + return ranges; +} + Status SingleSegmentSegmenter::DoInitialize() { if (!writer_) { std::unique_ptr writer(new MkvWriter); diff --git a/packager/media/formats/webm/single_segment_segmenter.h b/packager/media/formats/webm/single_segment_segmenter.h index 811fd59599..8bed12a723 100644 --- a/packager/media/formats/webm/single_segment_segmenter.h +++ b/packager/media/formats/webm/single_segment_segmenter.h @@ -35,6 +35,7 @@ class SingleSegmentSegmenter : public Segmenter { bool is_subsegment) override; bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override; + std::vector GetSegmentRanges() override; /// @} protected: diff --git a/packager/media/formats/webm/webm_muxer.cc b/packager/media/formats/webm/webm_muxer.cc index 80eba5834a..6d46b85704 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -95,15 +95,31 @@ void WebMMuxer::FireOnMediaEndEvent() { if (!muxer_listener()) return; + MuxerListener::MediaRanges media_range; + uint64_t init_range_start = 0; uint64_t init_range_end = 0; const bool has_init_range = segmenter_->GetInitRangeStartAndEnd(&init_range_start, &init_range_end); + if (has_init_range) { + Range r; + r.start = init_range_start; + r.end = init_range_end; + media_range.init_range = r; + } uint64_t index_range_start = 0; uint64_t index_range_end = 0; const bool has_index_range = segmenter_->GetIndexRangeStartAndEnd( &index_range_start, &index_range_end); + if (has_index_range) { + Range r; + r.start = index_range_start; + r.end = index_range_end; + media_range.index_range = r; + } + + media_range.subsegment_ranges = segmenter_->GetSegmentRanges(); const float duration_seconds = segmenter_->GetDurationInSeconds(); @@ -114,9 +130,7 @@ void WebMMuxer::FireOnMediaEndEvent() { return; } - muxer_listener()->OnMediaEnd(has_init_range, init_range_start, init_range_end, - has_index_range, index_range_start, - index_range_end, duration_seconds, file_size); + muxer_listener()->OnMediaEnd(media_range, duration_seconds, file_size); } } // namespace webm