Use MuxerListener in TsMuxer
- MuxerListener is used in TsMuxer so that a listener can be used to e.g. generate manifests. Change-Id: I11c745e1c2b71d5ec901387fe42713d4ad69dc03
This commit is contained in:
parent
13202f91b6
commit
f3ed07a64e
|
@ -32,6 +32,18 @@
|
|||
'../filters/filters.gyp:filters',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'mock_muxer_listener',
|
||||
'type': '<(component)',
|
||||
'sources': [
|
||||
'mock_muxer_listener.cc',
|
||||
'mock_muxer_listener.h',
|
||||
],
|
||||
'dependencies': [
|
||||
'../../testing/gmock.gyp:gmock',
|
||||
'media_event',
|
||||
],
|
||||
},
|
||||
{
|
||||
'target_name': 'media_event_unittest',
|
||||
'type': '<(gtest_target_type)',
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2016 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
|
||||
|
||||
#include "packager/media/event/mock_muxer_listener.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
MockMuxerListener::MockMuxerListener() {}
|
||||
MockMuxerListener::~MockMuxerListener() {}
|
||||
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2016 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
|
||||
|
||||
#ifndef PACKAGER_MEDIA_EVENT_MOCK_MUXER_LISTENER_H_
|
||||
#define PACKAGER_MEDIA_EVENT_MOCK_MUXER_LISTENER_H_
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "packager/media/base/muxer_options.h"
|
||||
#include "packager/media/base/protection_system_specific_info.h"
|
||||
#include "packager/media/base/stream_info.h"
|
||||
#include "packager/media/event/muxer_listener.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
class MockMuxerListener : public MuxerListener {
|
||||
public:
|
||||
MockMuxerListener();
|
||||
~MockMuxerListener() override;
|
||||
|
||||
MOCK_METHOD4(
|
||||
OnEncryptionInfoReady,
|
||||
void(bool is_initial_encryption_info,
|
||||
const std::vector<uint8_t>& key_id,
|
||||
const std::vector<uint8_t>& iv,
|
||||
const std::vector<ProtectionSystemSpecificInfo>& key_system_info));
|
||||
|
||||
MOCK_METHOD4(OnMediaStart,
|
||||
void(const MuxerOptions& muxer_options,
|
||||
const StreamInfo& stream_info,
|
||||
uint32_t time_scale,
|
||||
ContainerType container_type));
|
||||
|
||||
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_METHOD4(OnNewSegment,
|
||||
void(const std::string& segment_name,
|
||||
uint64_t start_time,
|
||||
uint64_t duration,
|
||||
uint64_t segment_file_size));
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace edash_packager
|
||||
|
||||
#endif // PACKAGER_MEDIA_EVENT_MOCK_MUXER_LISTENER_H_
|
|
@ -64,6 +64,7 @@
|
|||
'dependencies': [
|
||||
'../../../testing/gtest.gyp:gtest',
|
||||
'../../../testing/gmock.gyp:gmock',
|
||||
'../../event/media_event.gyp:mock_muxer_listener',
|
||||
'../../filters/filters.gyp:filters',
|
||||
'../../test/media_test.gyp:media_test_support',
|
||||
'../mpeg/mpeg.gyp:mpeg',
|
||||
|
|
|
@ -10,23 +10,50 @@ namespace edash_packager {
|
|||
namespace media {
|
||||
namespace mp2t {
|
||||
|
||||
TsMuxer::TsMuxer(const MuxerOptions& muxer_options)
|
||||
: Muxer(muxer_options), segmenter_(options()) {}
|
||||
namespace {
|
||||
const uint32_t kTsTimescale = 90000;
|
||||
} // namespace
|
||||
|
||||
TsMuxer::TsMuxer(const MuxerOptions& muxer_options) : Muxer(muxer_options) {}
|
||||
TsMuxer::~TsMuxer() {}
|
||||
|
||||
Status TsMuxer::Initialize() {
|
||||
if (streams().size() > 1u)
|
||||
return Status(error::MUXER_FAILURE, "Cannot handle more than one streams.");
|
||||
return segmenter_.Initialize(*streams()[0]->info());
|
||||
|
||||
segmenter_.reset(new TsSegmenter(options(), muxer_listener()));
|
||||
Status status = segmenter_->Initialize(*streams()[0]->info());
|
||||
FireOnMediaStartEvent();
|
||||
return status;
|
||||
}
|
||||
|
||||
Status TsMuxer::Finalize() {
|
||||
return segmenter_.Finalize();
|
||||
FireOnMediaEndEvent();
|
||||
return segmenter_->Finalize();
|
||||
}
|
||||
|
||||
Status TsMuxer::DoAddSample(const MediaStream* stream,
|
||||
scoped_refptr<MediaSample> sample) {
|
||||
return segmenter_.AddSample(sample);
|
||||
return segmenter_->AddSample(sample);
|
||||
}
|
||||
|
||||
void TsMuxer::FireOnMediaStartEvent() {
|
||||
if (!muxer_listener())
|
||||
return;
|
||||
muxer_listener()->OnMediaStart(options(), *streams().front()->info(),
|
||||
kTsTimescale, MuxerListener::kContainerWebM);
|
||||
}
|
||||
|
||||
void TsMuxer::FireOnMediaEndEvent() {
|
||||
if (!muxer_listener())
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace mp2t
|
||||
|
|
|
@ -29,7 +29,10 @@ class TsMuxer : public Muxer {
|
|||
Status DoAddSample(const MediaStream* stream,
|
||||
scoped_refptr<MediaSample> sample) override;
|
||||
|
||||
TsSegmenter segmenter_;
|
||||
void FireOnMediaStartEvent();
|
||||
void FireOnMediaEndEvent();
|
||||
|
||||
scoped_ptr<TsSegmenter> segmenter_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TsMuxer);
|
||||
};
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "packager/media/base/muxer_util.h"
|
||||
#include "packager/media/base/status.h"
|
||||
#include "packager/media/event/muxer_listener.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -19,8 +20,9 @@ namespace {
|
|||
const double kTsTimescale = 90000;
|
||||
} // namespace
|
||||
|
||||
TsSegmenter::TsSegmenter(const MuxerOptions& options)
|
||||
TsSegmenter::TsSegmenter(const MuxerOptions& options, MuxerListener* listener)
|
||||
: muxer_options_(options),
|
||||
listener_(listener),
|
||||
ts_writer_(new TsWriter()),
|
||||
pes_packet_generator_(new PesPacketGenerator()) {}
|
||||
TsSegmenter::~TsSegmenter() {}
|
||||
|
@ -91,6 +93,8 @@ Status TsSegmenter::OpenNewSegmentIfClosed(uint32_t next_pts) {
|
|||
segment_number_++, muxer_options_.bandwidth);
|
||||
if (!ts_writer_->NewSegment(segment_name))
|
||||
return Status(error::MUXER_FAILURE, "Failed to initilize TsPacketWriter.");
|
||||
current_segment_start_time_ = next_pts;
|
||||
current_segment_path_ = segment_name;
|
||||
ts_writer_file_opened_ = true;
|
||||
return Status::OK;
|
||||
}
|
||||
|
@ -125,9 +129,18 @@ Status TsSegmenter::Flush() {
|
|||
if (!ts_writer_->FinalizeSegment()) {
|
||||
return Status(error::MUXER_FAILURE, "Failed to finalize TsWriter.");
|
||||
}
|
||||
if (listener_) {
|
||||
const int64_t file_size =
|
||||
File::GetFileSize(current_segment_path_.c_str());
|
||||
listener_->OnNewSegment(
|
||||
current_segment_path_, current_segment_start_time_,
|
||||
current_segment_total_sample_duration_ * kTsTimescale, file_size);
|
||||
}
|
||||
ts_writer_file_opened_ = false;
|
||||
}
|
||||
current_segment_total_sample_duration_ = 0.0;
|
||||
current_segment_start_time_ = 0;
|
||||
current_segment_path_.clear();
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
||||
class MuxerListener;
|
||||
|
||||
namespace mp2t {
|
||||
|
||||
// TODO(rkuroiwa): For now, this implements multifile segmenter. Like other
|
||||
|
@ -26,7 +29,9 @@ class TsSegmenter {
|
|||
public:
|
||||
/// @param options is the options for this muxer. This must stay valid
|
||||
/// throughout the life time of the instance.
|
||||
explicit TsSegmenter(const MuxerOptions& options);
|
||||
/// @param listener is the MuxerListener that should be used to notify events.
|
||||
/// This may be null, in which case no events are sent.
|
||||
TsSegmenter(const MuxerOptions& options, MuxerListener* listener);
|
||||
~TsSegmenter();
|
||||
|
||||
/// Initialize the object.
|
||||
|
@ -65,6 +70,7 @@ class TsSegmenter {
|
|||
Status Flush();
|
||||
|
||||
const MuxerOptions& muxer_options_;
|
||||
MuxerListener* const listener_;
|
||||
|
||||
// Scale used to scale the input stream to TS's timesccale (which is 90000).
|
||||
// Used for calculating the duration in seconds fo the current segment.
|
||||
|
@ -85,6 +91,12 @@ class TsSegmenter {
|
|||
bool ts_writer_file_opened_ = false;
|
||||
scoped_ptr<PesPacketGenerator> pes_packet_generator_;
|
||||
|
||||
// For OnNewSegment().
|
||||
uint64_t current_segment_start_time_ = 0;
|
||||
// Path of the current segment so that File::GetFileSize() can be used after
|
||||
// the segment has been finalized.
|
||||
std::string current_segment_path_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TsSegmenter);
|
||||
};
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "packager/media/base/audio_stream_info.h"
|
||||
#include "packager/media/base/test/status_test_util.h"
|
||||
#include "packager/media/base/video_stream_info.h"
|
||||
#include "packager/media/event/mock_muxer_listener.h"
|
||||
#include "packager/media/formats/mp2t/ts_segmenter.h"
|
||||
|
||||
namespace edash_packager {
|
||||
|
@ -94,7 +95,7 @@ TEST_F(TsSegmenterTest, Initialize) {
|
|||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
MuxerOptions options;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options);
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
|
@ -115,7 +116,7 @@ TEST_F(TsSegmenterTest, AddSample) {
|
|||
MuxerOptions options;
|
||||
options.segment_duration = 10.0;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options);
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
|
@ -173,7 +174,11 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
|||
MuxerOptions options;
|
||||
options.segment_duration = 10.0;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options);
|
||||
|
||||
MockMuxerListener mock_listener;
|
||||
TsSegmenter segmenter(options, &mock_listener);
|
||||
|
||||
const uint32_t kFirstPts = 1000;
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
|
@ -191,6 +196,12 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
|||
// Expect the segment to be finalized.
|
||||
sample1->set_duration(kInputTimescale * 11);
|
||||
|
||||
// (Finalize is not called at the end of this test so) Expect one segment
|
||||
// event. The length should be the same as the above sample that exceeds the
|
||||
// duration.
|
||||
EXPECT_CALL(mock_listener,
|
||||
OnNewSegment("file1.ts", kFirstPts, kTimeScale * 11, _));
|
||||
|
||||
// Doesn't really matter how long this is.
|
||||
sample2->set_duration(kInputTimescale * 7);
|
||||
|
||||
|
@ -239,9 +250,11 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
|||
|
||||
// The pointers are released inside the segmenter.
|
||||
Sequence pes_packet_sequence;
|
||||
PesPacket* first_pes = new PesPacket();
|
||||
first_pes->set_pts(kFirstPts);
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, GetNextPesPacketMock())
|
||||
.InSequence(pes_packet_sequence)
|
||||
.WillOnce(Return(new PesPacket()));
|
||||
.WillOnce(Return(first_pes));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, GetNextPesPacketMock())
|
||||
.InSequence(pes_packet_sequence)
|
||||
.WillOnce(Return(new PesPacket()));
|
||||
|
@ -263,7 +276,7 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) {
|
|||
MuxerOptions options;
|
||||
options.segment_duration = 10.0;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options);
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
|
@ -292,7 +305,7 @@ TEST_F(TsSegmenterTest, Finalize) {
|
|||
MuxerOptions options;
|
||||
options.segment_duration = 10.0;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options);
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
|
@ -321,7 +334,7 @@ TEST_F(TsSegmenterTest, SegmentOnlyBeforeKeyFrame) {
|
|||
MuxerOptions options;
|
||||
options.segment_duration = 10.0;
|
||||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options);
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
|
|
Loading…
Reference in New Issue