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
This commit is contained in:
Rintaro Kuroiwa 2017-05-01 16:17:09 -07:00
parent 2db1867951
commit 0dfdace280
32 changed files with 239 additions and 190 deletions

View File

@ -75,6 +75,7 @@
'producer_consumer_queue.h', 'producer_consumer_queue.h',
'protection_system_specific_info.cc', 'protection_system_specific_info.cc',
'protection_system_specific_info.h', 'protection_system_specific_info.h',
'range.h',
'rcheck.h', 'rcheck.h',
'request_signer.cc', 'request_signer.cc',
'request_signer.h', 'request_signer.h',

View File

@ -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 <stdint.h>
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_

View File

@ -110,12 +110,7 @@ void HlsNotifyMuxerListener::OnMediaStart(const MuxerOptions& muxer_options,
void HlsNotifyMuxerListener::OnSampleDurationReady(uint32_t sample_duration) {} void HlsNotifyMuxerListener::OnSampleDurationReady(uint32_t sample_duration) {}
void HlsNotifyMuxerListener::OnMediaEnd(bool has_init_range, void HlsNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size) { uint64_t file_size) {
// TODO(kqyang): Should we just Flush here to avoid calling Flush explicitly? // TODO(kqyang): Should we just Flush here to avoid calling Flush explicitly?

View File

@ -51,12 +51,7 @@ class HlsNotifyMuxerListener : public MuxerListener {
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type) override; ContainerType container_type) override;
void OnSampleDurationReady(uint32_t sample_duration) override; void OnSampleDurationReady(uint32_t sample_duration) override;
void OnMediaEnd(bool has_init_range, void OnMediaEnd(const MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size) override; uint64_t file_size) override;
void OnNewSegment(const std::string& file_name, void OnNewSegment(const std::string& file_name,

View File

@ -313,7 +313,7 @@ TEST_F(HlsNotifyMuxerListenerTest, OnSampleDurationReady) {
// Make sure it doesn't crash. // Make sure it doesn't crash.
TEST_F(HlsNotifyMuxerListenerTest, OnMediaEnd) { TEST_F(HlsNotifyMuxerListenerTest, OnMediaEnd) {
// None of these values matter, they are not used. // 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) { TEST_F(HlsNotifyMuxerListenerTest, OnNewSegment) {

View File

@ -12,5 +12,25 @@ namespace media {
MockMuxerListener::MockMuxerListener() {} MockMuxerListener::MockMuxerListener() {}
MockMuxerListener::~MockMuxerListener() {} MockMuxerListener::~MockMuxerListener() {}
void MockMuxerListener::OnMediaEnd(const MediaRanges& range,
float duration_seconds,
uint64_t file_size) {
const bool has_init_range = static_cast<bool>(range.init_range);
Range init_range = {};
if (has_init_range) {
init_range = range.init_range.value();
}
const bool has_index_range = static_cast<bool>(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 media
} // namespace shaka } // namespace shaka

View File

@ -40,15 +40,23 @@ class MockMuxerListener : public MuxerListener {
MOCK_METHOD1(OnSampleDurationReady, void(uint32_t sample_duration)); MOCK_METHOD1(OnSampleDurationReady, void(uint32_t sample_duration));
MOCK_METHOD8(OnMediaEnd, MOCK_METHOD10(OnMediaEndMock,
void(bool has_init_range, void(bool has_init_range,
uint64_t init_range_start, uint64_t init_range_start,
uint64_t init_range_end, uint64_t init_range_end,
bool has_index_range, bool has_index_range,
uint64_t index_range_start, uint64_t index_range_start,
uint64_t index_range_end, uint64_t index_range_end,
float duration_seconds, bool has_subsegment_ranges,
uint64_t file_size)); const std::vector<Range> 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, MOCK_METHOD4(OnNewSegment,
void(const std::string& segment_name, void(const std::string& segment_name,

View File

@ -109,12 +109,7 @@ void MpdNotifyMuxerListener::OnSampleDurationReady(
media_info_->mutable_video_info()->set_frame_duration(sample_duration); media_info_->mutable_video_info()->set_frame_duration(sample_duration);
} }
void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range, void MpdNotifyMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size) { uint64_t file_size) {
if (mpd_notifier_->dash_profile() == DashProfile::kLive) { if (mpd_notifier_->dash_profile() == DashProfile::kLive) {
@ -127,14 +122,7 @@ void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range,
} }
DCHECK(media_info_); DCHECK(media_info_);
if (!internal::SetVodInformation(has_init_range, if (!internal::SetVodInformation(media_ranges, duration_seconds, file_size,
init_range_start,
init_range_end,
has_index_range,
index_range_start,
index_range_end,
duration_seconds,
file_size,
media_info_.get())) { media_info_.get())) {
LOG(ERROR) << "Failed to generate VOD information from input."; LOG(ERROR) << "Failed to generate VOD information from input.";
return; return;
@ -143,6 +131,8 @@ void MpdNotifyMuxerListener::OnMediaEnd(bool has_init_range,
uint32_t id; uint32_t id;
// TODO(kqyang): Check return result. // TODO(kqyang): Check return result.
mpd_notifier_->NotifyNewContainer(*media_info_, &id); mpd_notifier_->NotifyNewContainer(*media_info_, &id);
// TODO(rkuroiwa): Use media_ranges.subsegment_ranges instead of caching the
// subsegments.
for (std::list<SubsegmentInfo>::const_iterator it = subsegments_.begin(); for (std::list<SubsegmentInfo>::const_iterator it = subsegments_.begin();
it != subsegments_.end(); ++it) { it != subsegments_.end(); ++it) {
mpd_notifier_->NotifyNewSegment(id, it->start_time, it->duration, mpd_notifier_->NotifyNewSegment(id, it->start_time, it->duration,

View File

@ -45,12 +45,7 @@ class MpdNotifyMuxerListener : public MuxerListener {
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type) override; ContainerType container_type) override;
void OnSampleDurationReady(uint32_t sample_duration) override; void OnSampleDurationReady(uint32_t sample_duration) override;
void OnMediaEnd(bool has_init_range, void OnMediaEnd(const MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size) override; uint64_t file_size) override;
void OnNewSegment(const std::string& file_name, void OnNewSegment(const std::string& file_name,

View File

@ -83,13 +83,7 @@ class MpdNotifyMuxerListenerTest : public ::testing::TestWithParam<MpdType> {
void FireOnMediaEndWithParams(const OnMediaEndParameters& params) { void FireOnMediaEndWithParams(const OnMediaEndParameters& params) {
// On success, this writes the result to |temp_file_path_|. // On success, this writes the result to |temp_file_path_|.
listener_->OnMediaEnd(params.has_init_range, listener_->OnMediaEnd(params.media_ranges, params.duration_seconds,
params.init_range_start,
params.init_range_end,
params.has_index_range,
params.index_range_start,
params.index_range_end,
params.duration_seconds,
params.file_size); params.file_size);
} }

View File

@ -14,7 +14,9 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "packager/base/optional.h"
#include "packager/media/base/fourccs.h" #include "packager/media/base/fourccs.h"
#include "packager/media/base/range.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -36,6 +38,20 @@ class MuxerListener {
kContainerWebM 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<Range> init_range;
/// Range of the index section of a segment.
base::Optional<Range> 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<Range> subsegment_ranges;
};
virtual ~MuxerListener() {}; virtual ~MuxerListener() {};
/// Called when the media's encryption information is ready. /// 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 /// 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.
/// @param has_init_range is true if @a init_range_start and @a init_range_end /// @param media_ranges is the ranges of the media file.
/// 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 duration_seconds is the length of the media in seconds. /// @param duration_seconds is the length of the media in seconds.
/// @param file_size is the size of the file in bytes. /// @param file_size is the size of the file in bytes.
virtual void OnMediaEnd(bool has_init_range, virtual void OnMediaEnd(const MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size) = 0; uint64_t file_size) = 0;

View File

@ -36,30 +36,14 @@ uint32_t EstimateRequiredBandwidth(uint64_t file_size, float duration_seconds) {
return static_cast<uint32_t>(ceil(bits_per_second)); return static_cast<uint32_t>(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); DCHECK(range);
range->set_begin(begin); range->set_begin(begin);
range->set_end(end); 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, void SetMediaInfoContainerType(MuxerListener::ContainerType container_type,
MediaInfo* media_info) { MediaInfo* media_info) {
DCHECK(media_info); DCHECK(media_info);
@ -205,12 +189,7 @@ bool GenerateMediaInfo(const MuxerOptions& muxer_options,
return true; return true;
} }
bool SetVodInformation(bool has_init_range, bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size, uint64_t file_size,
MediaInfo* media_info) { MediaInfo* media_info) {
@ -226,13 +205,16 @@ bool SetVodInformation(bool has_init_range,
return false; return false;
} }
SetMediaInfoRanges(has_init_range,
init_range_start, if (media_ranges.init_range) {
init_range_end, SetRange(media_ranges.init_range->start, media_ranges.init_range->end,
has_index_range, media_info->mutable_init_range());
index_range_start, }
index_range_end,
media_info); 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); media_info->set_media_duration_seconds(duration_seconds);

View File

@ -35,12 +35,7 @@ bool GenerateMediaInfo(const MuxerOptions& muxer_options,
/// @param[in,out] media_info points to the MediaInfo object to be filled. /// @param[in,out] media_info points to the MediaInfo object to be filled.
/// @return true on success, false otherwise. /// @return true on success, false otherwise.
bool SetVodInformation(bool has_init_range, bool SetVodInformation(const MuxerListener::MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size, uint64_t file_size,
MediaInfo* media_info); MediaInfo* media_info);

View File

@ -5,6 +5,7 @@
// https://developers.google.com/open-source/licenses/bsd // https://developers.google.com/open-source/licenses/bsd
#include "packager/media/event/muxer_listener_test_helper.h" #include "packager/media/event/muxer_listener_test_helper.h"
#include "packager/media/event/muxer_listener.h"
#include <gtest/gtest.h> #include <gtest/gtest.h>
@ -59,17 +60,23 @@ VideoStreamInfoParameters GetDefaultVideoStreamInfoParams() {
OnMediaEndParameters GetDefaultOnMediaEndParams() { OnMediaEndParameters GetDefaultOnMediaEndParams() {
// Values for {init, index} range {start, end} are arbitrary, but makes sure // Values for {init, index} range {start, end} are arbitrary, but makes sure
// that it is monotonically increasing and contiguous. // that it is monotonically increasing and contiguous.
const bool kHasInitRange = true;
const uint64_t kInitRangeStart = 0; const uint64_t kInitRangeStart = 0;
const uint64_t kInitRangeEnd = kInitRangeStart + 120; const uint64_t kInitRangeEnd = kInitRangeStart + 120;
const uint64_t kHasIndexRange = true;
const uint64_t kIndexRangeStart = kInitRangeEnd + 1; const uint64_t kIndexRangeStart = kInitRangeEnd + 1;
const uint64_t kIndexRangeEnd = kIndexRangeStart + 100; const uint64_t kIndexRangeEnd = kIndexRangeStart + 100;
const float kMediaDuration = 10.5f; const float kMediaDuration = 10.5f;
const uint64_t kFileSize = 10000; const uint64_t kFileSize = 10000;
OnMediaEndParameters param = { MuxerListener::MediaRanges media_ranges;
kHasInitRange, kInitRangeStart, kInitRangeEnd, kHasIndexRange, Range init_range;
kIndexRangeStart, kIndexRangeEnd, kMediaDuration, kFileSize}; 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; return param;
} }

View File

@ -14,6 +14,7 @@
#include "packager/media/base/muxer_options.h" #include "packager/media/base/muxer_options.h"
#include "packager/media/base/stream_info.h" #include "packager/media/base/stream_info.h"
#include "packager/media/base/video_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" #include "packager/mpd/base/media_info.pb.h"
namespace shaka { namespace shaka {
@ -73,12 +74,7 @@ struct VideoStreamInfoParameters {
// Note that this does not have vector of StreamInfo pointer. // Note that this does not have vector of StreamInfo pointer.
struct OnMediaEndParameters { struct OnMediaEndParameters {
bool has_init_range; MuxerListener::MediaRanges media_ranges;
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; float duration_seconds;
uint64_t file_size; uint64_t file_size;
}; };

View File

@ -72,23 +72,11 @@ void VodMediaInfoDumpMuxerListener::OnSampleDurationReady(
} }
} }
void VodMediaInfoDumpMuxerListener::OnMediaEnd(bool has_init_range, void VodMediaInfoDumpMuxerListener::OnMediaEnd(const MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size) { uint64_t file_size) {
DCHECK(media_info_); DCHECK(media_info_);
if (!internal::SetVodInformation(has_init_range, if (!internal::SetVodInformation(media_ranges, duration_seconds, file_size,
init_range_start,
init_range_end,
has_index_range,
index_range_start,
index_range_end,
duration_seconds,
file_size,
media_info_.get())) { media_info_.get())) {
LOG(ERROR) << "Failed to generate VOD information from input."; LOG(ERROR) << "Failed to generate VOD information from input.";
return; return;

View File

@ -44,12 +44,7 @@ class VodMediaInfoDumpMuxerListener : public MuxerListener {
uint32_t time_scale, uint32_t time_scale,
ContainerType container_type) override; ContainerType container_type) override;
void OnSampleDurationReady(uint32_t sample_duration) override; void OnSampleDurationReady(uint32_t sample_duration) override;
void OnMediaEnd(bool has_init_range, void OnMediaEnd(const MediaRanges& media_ranges,
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, float duration_seconds,
uint64_t file_size) override; uint64_t file_size) override;
void OnNewSegment(const std::string& file_name, void OnNewSegment(const std::string& file_name,

View File

@ -95,12 +95,7 @@ class VodMediaInfoDumpMuxerListenerTest : public ::testing::Test {
void FireOnMediaEndWithParams(const OnMediaEndParameters& params) { void FireOnMediaEndWithParams(const OnMediaEndParameters& params) {
// On success, this writes the result to |temp_file_path_|. // On success, this writes the result to |temp_file_path_|.
listener_->OnMediaEnd(params.has_init_range, listener_->OnMediaEnd(params.media_ranges,
params.init_range_start,
params.init_range_end,
params.has_index_range,
params.index_range_start,
params.index_range_end,
params.duration_seconds, params.duration_seconds,
params.file_size); params.file_size);
} }

View File

@ -59,11 +59,9 @@ void TsMuxer::FireOnMediaEndEvent() {
return; return;
// For now, there is no single file TS segmenter. So all the values passed // 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. // here are left empty.
const bool kHasInitRange = true; MuxerListener::MediaRanges range;
const bool kHasIndexRange = true; muxer_listener()->OnMediaEnd(range, 0, 0);
muxer_listener()->OnMediaEnd(!kHasInitRange, 0, 0, !kHasIndexRange, 0, 0, 0,
0);
} }
} // namespace mp2t } // namespace mp2t

View File

@ -32,12 +32,11 @@ namespace {
// |start| and |end| are for byte-range-spec specified in RFC2616. // |start| and |end| are for byte-range-spec specified in RFC2616.
void SetStartAndEndFromOffsetAndSize(size_t offset, void SetStartAndEndFromOffsetAndSize(size_t offset,
size_t size, size_t size,
uint32_t* start, Range* range) {
uint32_t* end) { DCHECK(range);
DCHECK(start && end); range->start = static_cast<uint32_t>(offset);
*start = static_cast<uint32_t>(offset);
// Note that ranges are inclusive. So we need - 1. // Note that ranges are inclusive. So we need - 1.
*end = *start + static_cast<uint32_t>(size) - 1; range->end = range->start + static_cast<uint32_t>(size) - 1;
} }
FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) { FourCC CodecToFourCC(Codec codec, H26xStreamFormat h26x_stream_format) {
@ -419,30 +418,30 @@ void MP4Muxer::GenerateTextTrak(const TextStreamInfo* text_info,
<< " handling not implemented yet."; << " handling not implemented yet.";
} }
bool MP4Muxer::GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end) { base::Optional<Range> MP4Muxer::GetInitRangeStartAndEnd() {
DCHECK(start && end);
size_t range_offset = 0; size_t range_offset = 0;
size_t range_size = 0; size_t range_size = 0;
const bool has_range = segmenter_->GetInitRange(&range_offset, &range_size); const bool has_range = segmenter_->GetInitRange(&range_offset, &range_size);
if (!has_range) if (!has_range)
return false; return base::nullopt;
SetStartAndEndFromOffsetAndSize(range_offset, range_size, start, end); Range range;
return true; SetStartAndEndFromOffsetAndSize(range_offset, range_size, &range);
return range;
} }
bool MP4Muxer::GetIndexRangeStartAndEnd(uint32_t* start, uint32_t* end) { base::Optional<Range> MP4Muxer::GetIndexRangeStartAndEnd() {
DCHECK(start && end);
size_t range_offset = 0; size_t range_offset = 0;
size_t range_size = 0; size_t range_size = 0;
const bool has_range = segmenter_->GetIndexRange(&range_offset, &range_size); const bool has_range = segmenter_->GetIndexRange(&range_offset, &range_size);
if (!has_range) if (!has_range)
return false; return base::nullopt;
SetStartAndEndFromOffsetAndSize(range_offset, range_size, start, end); Range range;
return true; SetStartAndEndFromOffsetAndSize(range_offset, range_size, &range);
return range;
} }
void MP4Muxer::FireOnMediaStartEvent() { void MP4Muxer::FireOnMediaStartEvent() {
@ -464,15 +463,10 @@ void MP4Muxer::FireOnMediaEndEvent() {
if (!muxer_listener()) if (!muxer_listener())
return; return;
uint32_t init_range_start = 0; MuxerListener::MediaRanges media_range;
uint32_t init_range_end = 0; media_range.init_range = GetInitRangeStartAndEnd();
const bool has_init_range = media_range.index_range = GetIndexRangeStartAndEnd();
GetInitRangeStartAndEnd(&init_range_start, &init_range_end); media_range.subsegment_ranges = segmenter_->GetSegmentRanges();
uint32_t index_range_start = 0;
uint32_t index_range_end = 0;
const bool has_index_range =
GetIndexRangeStartAndEnd(&index_range_start, &index_range_end);
const float duration_seconds = static_cast<float>(segmenter_->GetDuration()); const float duration_seconds = static_cast<float>(segmenter_->GetDuration());
@ -483,14 +477,7 @@ void MP4Muxer::FireOnMediaEndEvent() {
return; return;
} }
muxer_listener()->OnMediaEnd(has_init_range, muxer_listener()->OnMediaEnd(media_range, duration_seconds, file_size);
init_range_start,
init_range_end,
has_index_range,
index_range_start,
index_range_end,
duration_seconds,
file_size);
} }
uint64_t MP4Muxer::IsoTimeNow() { uint64_t MP4Muxer::IsoTimeNow() {

View File

@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include "packager/base/optional.h"
#include "packager/media/base/muxer.h" #include "packager/media/base/muxer.h"
namespace shaka { namespace shaka {
@ -57,11 +58,11 @@ class MP4Muxer : public Muxer {
// Gets |start| and |end| initialization range. Returns true if there is an // Gets |start| and |end| initialization range. Returns true if there is an
// init range and sets start-end byte-range-spec specified in RFC2616. // init range and sets start-end byte-range-spec specified in RFC2616.
bool GetInitRangeStartAndEnd(uint32_t* start, uint32_t* end); base::Optional<Range> GetInitRangeStartAndEnd();
// Gets |start| and |end| index range. Returns true if there is an index range // Gets |start| and |end| index range. Returns true if there is an index range
// and sets start-end byte-range-spec specified in RFC2616. // and sets start-end byte-range-spec specified in RFC2616.
bool GetIndexRangeStartAndEnd(uint32_t* start, uint32_t* end); base::Optional<Range> GetIndexRangeStartAndEnd();
// Fire events if there are no errors and Muxer::muxer_listener() is not NULL. // Fire events if there are no errors and Muxer::muxer_listener() is not NULL.
void FireOnMediaStartEvent(); void FireOnMediaStartEvent();

View File

@ -38,16 +38,21 @@ MultiSegmentSegmenter::MultiSegmentSegmenter(const MuxerOptions& options,
MultiSegmentSegmenter::~MultiSegmentSegmenter() {} MultiSegmentSegmenter::~MultiSegmentSegmenter() {}
bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) { bool MultiSegmentSegmenter::GetInitRange(size_t* offset, size_t* size) {
DLOG(INFO) << "MultiSegmentSegmenter outputs init segment: " VLOG(1) << "MultiSegmentSegmenter outputs init segment: "
<< options().output_file_name; << options().output_file_name;
return false; return false;
} }
bool MultiSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) { 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; return false;
} }
std::vector<Range> MultiSegmentSegmenter::GetSegmentRanges() {
VLOG(1) << "MultiSegmentSegmenter does not have media segment ranges.";
return std::vector<Range>();
}
Status MultiSegmentSegmenter::DoInitialize() { Status MultiSegmentSegmenter::DoInitialize() {
DCHECK(ftyp()); DCHECK(ftyp());
DCHECK(moov()); DCHECK(moov());

View File

@ -39,6 +39,7 @@ class MultiSegmentSegmenter : public Segmenter {
/// @{ /// @{
bool GetInitRange(size_t* offset, size_t* size) override; bool GetInitRange(size_t* offset, size_t* size) override;
bool GetIndexRange(size_t* offset, size_t* size) override; bool GetIndexRange(size_t* offset, size_t* size) override;
std::vector<Range> GetSegmentRanges() override;
/// @} /// @}
private: private:

View File

@ -11,7 +11,9 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include "packager/base/optional.h"
#include "packager/media/base/fourccs.h" #include "packager/media/base/fourccs.h"
#include "packager/media/base/range.h"
#include "packager/media/formats/mp4/box_definitions.h" #include "packager/media/formats/mp4/box_definitions.h"
#include "packager/status.h" #include "packager/status.h"
@ -73,6 +75,8 @@ class Segmenter {
Status FinalizeSegment(size_t stream_id, Status FinalizeSegment(size_t stream_id,
std::shared_ptr<SegmentInfo> segment_info); std::shared_ptr<SegmentInfo> segment_info);
// TODO(rkuroiwa): Change these Get*Range() methods to return
// base::Optional<Range> as well.
/// @return true if there is an initialization range, while setting @a offset /// @return true if there is an initialization range, while setting @a offset
/// and @a size; or false if initialization range does not apply. /// and @a size; or false if initialization range does not apply.
virtual bool GetInitRange(size_t* offset, size_t* size) = 0; 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. /// and @a size; or false if index byte range does not apply.
virtual bool GetIndexRange(size_t* offset, size_t* size) = 0; 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<Range> GetSegmentRanges() = 0;
uint32_t GetReferenceTimeScale() const; uint32_t GetReferenceTimeScale() const;
/// @return The total length, in seconds, of segmented media files. /// @return The total length, in seconds, of segmented media files.

View File

@ -10,7 +10,6 @@
#include "packager/media/base/buffer_writer.h" #include "packager/media/base/buffer_writer.h"
#include "packager/media/base/muxer_options.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/event/progress_listener.h"
#include "packager/media/file/file.h" #include "packager/media/file/file.h"
#include "packager/media/file/file_util.h" #include "packager/media/file/file_util.h"
@ -49,6 +48,22 @@ bool SingleSegmentSegmenter::GetIndexRange(size_t* offset, size_t* size) {
return true; return true;
} }
std::vector<Range> SingleSegmentSegmenter::GetSegmentRanges() {
std::vector<Range> 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() { Status SingleSegmentSegmenter::DoInitialize() {
// Single segment segmentation involves two stages: // Single segment segmentation involves two stages:
// Stage 1: Create media subsegments from media samples // Stage 1: Create media subsegments from media samples

View File

@ -7,6 +7,7 @@
#ifndef MEDIA_FORMATS_MP4_SINGLE_SEGMENT_SEGMENTER_H_ #ifndef MEDIA_FORMATS_MP4_SINGLE_SEGMENT_SEGMENTER_H_
#define 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/file/file_closer.h"
#include "packager/media/formats/mp4/segmenter.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 GetInitRange(size_t* offset, size_t* size) override;
bool GetIndexRange(size_t* offset, size_t* size) override; bool GetIndexRange(size_t* offset, size_t* size) override;
std::vector<Range> GetSegmentRanges() override;
/// @} /// @}
private: private:

View File

@ -52,6 +52,10 @@ bool MultiSegmentSegmenter::GetIndexRangeStartAndEnd(uint64_t* start,
return false; return false;
} }
std::vector<Range> MultiSegmentSegmenter::GetSegmentRanges() {
return std::vector<Range>();
}
Status MultiSegmentSegmenter::DoInitialize() { Status MultiSegmentSegmenter::DoInitialize() {
std::unique_ptr<MkvWriter> writer(new MkvWriter); std::unique_ptr<MkvWriter> writer(new MkvWriter);
Status status = writer->Open(options().output_file_name); Status status = writer->Open(options().output_file_name);

View File

@ -33,6 +33,7 @@ class MultiSegmentSegmenter : public Segmenter {
bool is_subsegment) override; bool is_subsegment) override;
bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override;
bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override;
std::vector<Range> GetSegmentRanges() override;
/// @} /// @}
protected: protected:

View File

@ -8,6 +8,9 @@
#define MEDIA_FORMATS_WEBM_SEGMENTER_H_ #define MEDIA_FORMATS_WEBM_SEGMENTER_H_
#include <memory> #include <memory>
#include "packager/base/optional.h"
#include "packager/media/base/range.h"
#include "packager/media/formats/webm/mkv_writer.h" #include "packager/media/formats/webm/mkv_writer.h"
#include "packager/media/formats/webm/seek_head.h" #include "packager/media/formats/webm/seek_head.h"
#include "packager/status.h" #include "packager/status.h"
@ -64,6 +67,11 @@ class Segmenter {
/// and @a end; or false if index byte range does not apply. /// and @a end; or false if index byte range does not apply.
virtual bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) = 0; 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<Range> GetSegmentRanges() = 0;
/// @return The total length, in seconds, of segmented media files. /// @return The total length, in seconds, of segmented media files.
float GetDurationInSeconds() const; float GetDurationInSeconds() const;

View File

@ -36,8 +36,6 @@ Status SingleSegmentSegmenter::FinalizeSegment(uint64_t start_timestamp,
bool SingleSegmentSegmenter::GetInitRangeStartAndEnd(uint64_t* start, bool SingleSegmentSegmenter::GetInitRangeStartAndEnd(uint64_t* start,
uint64_t* end) { uint64_t* end) {
// The init range is the header, from the start of the file to the size of
// the header.
*start = 0; *start = 0;
*end = init_end_; *end = init_end_;
return true; return true;
@ -45,13 +43,33 @@ bool SingleSegmentSegmenter::GetInitRangeStartAndEnd(uint64_t* start,
bool SingleSegmentSegmenter::GetIndexRangeStartAndEnd(uint64_t* start, bool SingleSegmentSegmenter::GetIndexRangeStartAndEnd(uint64_t* start,
uint64_t* end) { uint64_t* end) {
// The index is the Cues element, which is always placed at the end of the
// file.
*start = index_start_; *start = index_start_;
*end = index_end_; *end = index_end_;
return true; return true;
} }
std::vector<Range> SingleSegmentSegmenter::GetSegmentRanges() {
std::vector<Range> 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() { Status SingleSegmentSegmenter::DoInitialize() {
if (!writer_) { if (!writer_) {
std::unique_ptr<MkvWriter> writer(new MkvWriter); std::unique_ptr<MkvWriter> writer(new MkvWriter);

View File

@ -35,6 +35,7 @@ class SingleSegmentSegmenter : public Segmenter {
bool is_subsegment) override; bool is_subsegment) override;
bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetInitRangeStartAndEnd(uint64_t* start, uint64_t* end) override;
bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override; bool GetIndexRangeStartAndEnd(uint64_t* start, uint64_t* end) override;
std::vector<Range> GetSegmentRanges() override;
/// @} /// @}
protected: protected:

View File

@ -95,15 +95,31 @@ void WebMMuxer::FireOnMediaEndEvent() {
if (!muxer_listener()) if (!muxer_listener())
return; return;
MuxerListener::MediaRanges media_range;
uint64_t init_range_start = 0; uint64_t init_range_start = 0;
uint64_t init_range_end = 0; uint64_t init_range_end = 0;
const bool has_init_range = const bool has_init_range =
segmenter_->GetInitRangeStartAndEnd(&init_range_start, &init_range_end); 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_start = 0;
uint64_t index_range_end = 0; uint64_t index_range_end = 0;
const bool has_index_range = segmenter_->GetIndexRangeStartAndEnd( const bool has_index_range = segmenter_->GetIndexRangeStartAndEnd(
&index_range_start, &index_range_end); &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(); const float duration_seconds = segmenter_->GetDurationInSeconds();
@ -114,9 +130,7 @@ void WebMMuxer::FireOnMediaEndEvent() {
return; return;
} }
muxer_listener()->OnMediaEnd(has_init_range, init_range_start, init_range_end, muxer_listener()->OnMediaEnd(media_range, duration_seconds, file_size);
has_index_range, index_range_start,
index_range_end, duration_seconds, file_size);
} }
} // namespace webm } // namespace webm