Support encrypted AC3 in TS
Implemented MPEG-2 Stream Encryption Format for HTTP Live Streaming specified in https://goo.gl/N7Tvqi. This change also moved ProgramMapTableWriter creation from TsWriter to TsSegment. Issue #165 Change-Id: Ia89dd16a5e6405706dd3d85ec6b6de580f7b13a7
This commit is contained in:
parent
d7f531fe10
commit
72df5af150
|
@ -19,6 +19,9 @@ namespace mp2t {
|
|||
namespace {
|
||||
const size_t kTsPacketSize = 188;
|
||||
const uint8_t kAacBasicProfileExtraData[] = {0x12, 0x10};
|
||||
// Bogus data, the value does not matter.
|
||||
const uint8_t kAc3SetupData[] = {0x00, 0x01, 0x02, 0x03, 0x04,
|
||||
0x05, 0x06, 0x07, 0x08, 0x09};
|
||||
} // namespace
|
||||
|
||||
class ProgramMapTableWriterTest : public ::testing::Test {
|
||||
|
@ -192,27 +195,63 @@ TEST_F(ProgramMapTableWriterTest, ClearAac) {
|
|||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
const uint8_t kPmtAac[] = {
|
||||
0x00, // pointer field
|
||||
0x02, // table id must be 0x02.
|
||||
0xB0, // assumes length is <= 256 bytes.
|
||||
0x12, // length of the rest of this array.
|
||||
0x00, 0x01, // program number.
|
||||
0xC1, // version 0, current next indicator 1.
|
||||
0x00, // section number
|
||||
0x00, // last section number.
|
||||
0xE0, // first 3 bits reserved.
|
||||
0x50, // PCR PID is the elementary streams PID.
|
||||
0xF0, // first 4 bits reserved.
|
||||
0x00, // No descriptor at this level.
|
||||
0x0F, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x00, // Es_info_length is 0.
|
||||
// CRC32.
|
||||
0xE0, 0x6F, 0x1A, 0x31,
|
||||
0x00, // pointer field
|
||||
0x02, // table id must be 0x02.
|
||||
0xB0, // assumes length is <= 256 bytes.
|
||||
0x12, // length of the rest of this array.
|
||||
0x00, 0x01, // program number.
|
||||
0xC1, // version 0, current next indicator 1.
|
||||
0x00, // section number
|
||||
0x00, // last section number.
|
||||
0xE0, // first 3 bits reserved.
|
||||
0x50, // PCR PID is the elementary streams PID.
|
||||
0xF0, // first 4 bits reserved.
|
||||
0x00, // No descriptor at this level.
|
||||
0x0F, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x00, // Es_info_length is 0.
|
||||
0xE0, 0x6F, 0x1A, 0x31, // CRC32.
|
||||
};
|
||||
EXPECT_NO_FATAL_FAILURE(
|
||||
ExpectTsPacketEqual(kExpectedPmtPrefix, arraysize(kExpectedPmtPrefix),
|
||||
160, kPmtAac, arraysize(kPmtAac), buffer.Buffer()));
|
||||
}
|
||||
|
||||
TEST_F(ProgramMapTableWriterTest, ClearAc3) {
|
||||
const std::vector<uint8_t> audio_specific_config(std::begin(kAc3SetupData),
|
||||
std::end(kAc3SetupData));
|
||||
AudioProgramMapTableWriter writer(kCodecAC3, audio_specific_config);
|
||||
BufferWriter buffer;
|
||||
writer.ClearSegmentPmt(&buffer);
|
||||
|
||||
const uint8_t kExpectedPmtPrefix[] = {
|
||||
0x47, // Sync byte.
|
||||
0x40, // payload_unit_start_indicator set.
|
||||
0x20, // pid.
|
||||
0x30, // Adaptation field and payload are both present. counter = 0.
|
||||
0xA1, // Adaptation Field length.
|
||||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
const uint8_t kPmtAc3[] = {
|
||||
0x00, // pointer field
|
||||
0x02, // table id must be 0x02.
|
||||
0xB0, // assumes length is <= 256 bytes.
|
||||
0x12, // length of the rest of this array.
|
||||
0x00, 0x01, // program number.
|
||||
0xC1, // version 0, current next indicator 1.
|
||||
0x00, // section number
|
||||
0x00, // last section number.
|
||||
0xE0, // first 3 bits reserved.
|
||||
0x50, // PCR PID is the elementary streams PID.
|
||||
0xF0, // first 4 bits reserved.
|
||||
0x00, // No descriptor at this level.
|
||||
0x81, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x00, // Es_info_length is 0.
|
||||
0x1E, 0xFC, 0x57, 0x12, // CRC32.
|
||||
};
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(
|
||||
ExpectTsPacketEqual(kExpectedPmtPrefix, arraysize(kExpectedPmtPrefix),
|
||||
160, kPmtAac, arraysize(kPmtAac), buffer.Buffer()));
|
||||
160, kPmtAc3, arraysize(kPmtAc3), buffer.Buffer()));
|
||||
}
|
||||
|
||||
// Verify that PSI for encrypted segments after clear lead is generated
|
||||
|
@ -251,23 +290,21 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
|||
0xF0, // first 4 bits reserved.
|
||||
0x00, // No descriptor at this level.
|
||||
0xCF, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x16, // Es_info_length is 5 for private_data_indicator
|
||||
0xF0, 0x16, // Es_info_length is 22 for descriptors.
|
||||
0x0F, // private_data_indicator descriptor_tag.
|
||||
0x04, // Length of the rest of this descriptor
|
||||
0x61, 0x61, 0x63, 0x64, // 'aacd'.
|
||||
0x05, // registration_descriptor tag.
|
||||
// space for 'zaac' + priming (0x0000) + version (0x01) +
|
||||
// setup_data_length size + size of kAacBasicProfileExtraData + space for
|
||||
// 'apad'. Which is 14.
|
||||
0x0E,
|
||||
0x0E, // space for 'zaac' + priming (0x0000) + version (0x01) +
|
||||
// setup_data_length size + size of kAacBasicProfileExtraData +
|
||||
// space for 'apad'. Which is 14.
|
||||
0x61, 0x70, 0x61, 0x64, // 'apad'.
|
||||
0x7A, 0x61, 0x61, 0x63, // 'zaac'.
|
||||
0x00, 0x00, // priming.
|
||||
0x01, // version.
|
||||
0x02, // setup_data_length == extra data length
|
||||
0x12, 0x10, // setup_data == extra data.
|
||||
// CRC32.
|
||||
0xC6, 0xB3, 0x31, 0x3A,
|
||||
0xC6, 0xB3, 0x31, 0x3A, // CRC32.
|
||||
};
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
|
||||
|
@ -297,36 +334,34 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) {
|
|||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
const uint8_t kPmtEncryptedAac[] = {
|
||||
0x00, // pointer field
|
||||
0x02, // table id.
|
||||
0xB0, // The first 4 bits must be '1011'.
|
||||
0x28, // length of the rest of this array.
|
||||
0x00, 0x01, // Program number.
|
||||
0xC1, // version 0, current next indicator 1.
|
||||
0x00, // section number
|
||||
0x00, // last section number.
|
||||
0xE0, // first 3 bits reserved.
|
||||
0x50, // PCR PID is the elementary streams PID.
|
||||
0xF0, // first 4 bits reserved.
|
||||
0x00, // No descriptor at this level.
|
||||
0xCF, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x16, // Es_info_length is 5 for private_data_indicator
|
||||
0x0F, // private_data_indicator descriptor_tag.
|
||||
0x04, // Length of the rest of this descriptor
|
||||
0x00, // pointer field
|
||||
0x02, // table id.
|
||||
0xB0, // The first 4 bits must be '1011'.
|
||||
0x28, // length of the rest of this array.
|
||||
0x00, 0x01, // Program number.
|
||||
0xC1, // version 0, current next indicator 1.
|
||||
0x00, // section number
|
||||
0x00, // last section number.
|
||||
0xE0, // first 3 bits reserved.
|
||||
0x50, // PCR PID is the elementary streams PID.
|
||||
0xF0, // first 4 bits reserved.
|
||||
0x00, // No descriptor at this level.
|
||||
0xCF, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x16, // Es_info_length is 22 for private_data_indicator
|
||||
0x0F, // private_data_indicator descriptor_tag.
|
||||
0x04, // Length of the rest of this descriptor
|
||||
0x61, 0x61, 0x63, 0x64, // 'aacd'.
|
||||
0x05, // registration_descriptor tag.
|
||||
// space for 'zaac' + priming (0x0000) + version (0x01) +
|
||||
// setup_data_length size + size of kAacBasicProfileExtraData + space for
|
||||
// 'apad'. Which is 14.
|
||||
0x0E,
|
||||
0x0E, // space for 'zaac' + priming (0x0000) + version (0x01) +
|
||||
// setup_data_length size + size of kAacBasicProfileExtraData +
|
||||
// space for 'apad'. Which is 14.
|
||||
0x61, 0x70, 0x61, 0x64, // 'apad'.
|
||||
0x7A, 0x61, 0x61, 0x63, // 'zaac'.
|
||||
0x00, 0x00, // priming.
|
||||
0x01, // version.
|
||||
0x02, // setup_data_length == extra data length
|
||||
0x12, 0x10, // setup_data == extra data.
|
||||
// CRC32.
|
||||
0xF7, 0xD5, 0x2A, 0x53,
|
||||
0xF7, 0xD5, 0x2A, 0x53, // CRC32.
|
||||
};
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
|
||||
|
@ -335,6 +370,60 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) {
|
|||
buffer.Buffer()));
|
||||
}
|
||||
|
||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAc3Pmt) {
|
||||
const std::vector<uint8_t> audio_specific_config(std::begin(kAc3SetupData),
|
||||
std::end(kAc3SetupData));
|
||||
AudioProgramMapTableWriter writer(kCodecAC3, audio_specific_config);
|
||||
BufferWriter buffer;
|
||||
writer.EncryptedSegmentPmt(&buffer);
|
||||
|
||||
EXPECT_EQ(kTsPacketSize, buffer.Size());
|
||||
|
||||
// Second PMT is for the encrypted segments after clear lead.
|
||||
const uint8_t kPmtEncryptedAc3Prefix[] = {
|
||||
0x47, // Sync byte.
|
||||
0x40, // payload_unit_start_indicator set.
|
||||
0x20, // pid.
|
||||
0x30, // Adaptation field and payload are both present. counter = 0.
|
||||
0x83, // Adaptation Field length.
|
||||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
const uint8_t kPmtEncryptedAc3[] = {
|
||||
0x00, // pointer field
|
||||
0x02, // table id.
|
||||
0xB0, // The first 4 bits must be '1011'.
|
||||
0x30, // length of the rest of this array.
|
||||
0x00, 0x01, // Program number.
|
||||
0xC1, // version 0, current next indicator 1.
|
||||
0x00, // section number
|
||||
0x00, // last section number.
|
||||
0xE0, // first 3 bits reserved.
|
||||
0x50, // PCR PID is the elementary streams PID.
|
||||
0xF0, // first 4 bits reserved.
|
||||
0x00, // No descriptor at this level.
|
||||
0xC1, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x1E, // Es_info_length is 30 for private_data_indicator
|
||||
0x0F, // private_data_indicator descriptor_tag.
|
||||
0x04, // Length of the rest of this descriptor
|
||||
0x61, 0x63, 0x33, 0x64, // 'ac3d'.
|
||||
0x05, // registration_descriptor tag.
|
||||
0x16, // space for 'zac3' + priming (0x0000) + version (0x01) +
|
||||
// setup_data_length size + size of kAc3SetupData + space for
|
||||
// 'apad'. Which is 22.
|
||||
0x61, 0x70, 0x61, 0x64, // 'apad'.
|
||||
0x7A, 0x61, 0x63, 0x33, // 'zac3'.
|
||||
0x00, 0x00, // priming.
|
||||
0x01, // version.
|
||||
0x0A, // setup_data_length
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, // setup_data
|
||||
0xCE, 0xB6, 0x52, 0x5C, // CRC32.
|
||||
};
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
|
||||
kPmtEncryptedAc3Prefix, arraysize(kPmtEncryptedAc3Prefix), 130,
|
||||
kPmtEncryptedAc3, arraysize(kPmtEncryptedAc3), buffer.Buffer()));
|
||||
}
|
||||
|
||||
} // namespace mp2t
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
|
|
@ -8,8 +8,12 @@
|
|||
|
||||
#include <memory>
|
||||
|
||||
#include "packager/media/base/audio_stream_info.h"
|
||||
#include "packager/media/base/muxer_util.h"
|
||||
#include "packager/media/base/video_stream_info.h"
|
||||
#include "packager/media/event/muxer_listener.h"
|
||||
#include "packager/media/formats/mp2t/pes_packet.h"
|
||||
#include "packager/media/formats/mp2t/program_map_table_writer.h"
|
||||
#include "packager/status.h"
|
||||
|
||||
namespace shaka {
|
||||
|
@ -18,25 +22,43 @@ namespace mp2t {
|
|||
|
||||
namespace {
|
||||
const double kTsTimescale = 90000;
|
||||
|
||||
bool IsAudioCodec(Codec codec) {
|
||||
return codec >= kCodecAudio && codec < kCodecAudioMaxPlusOne;
|
||||
}
|
||||
|
||||
bool IsVideoCodec(Codec codec) {
|
||||
return codec >= kCodecVideo && codec < kCodecVideoMaxPlusOne;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TsSegmenter::TsSegmenter(const MuxerOptions& options, MuxerListener* listener)
|
||||
: muxer_options_(options),
|
||||
listener_(listener),
|
||||
ts_writer_(new TsWriter()),
|
||||
pes_packet_generator_(new PesPacketGenerator()) {}
|
||||
TsSegmenter::~TsSegmenter() {}
|
||||
|
||||
Status TsSegmenter::Initialize(const StreamInfo& stream_info) {
|
||||
if (muxer_options_.segment_template.empty())
|
||||
return Status(error::MUXER_FAILURE, "Segment template not specified.");
|
||||
if (!ts_writer_->Initialize(stream_info))
|
||||
return Status(error::MUXER_FAILURE, "Failed to initialize TsWriter.");
|
||||
if (!pes_packet_generator_->Initialize(stream_info)) {
|
||||
return Status(error::MUXER_FAILURE,
|
||||
"Failed to initialize PesPacketGenerator.");
|
||||
}
|
||||
|
||||
const StreamType stream_type = stream_info.stream_type();
|
||||
if (stream_type != StreamType::kStreamVideo &&
|
||||
stream_type != StreamType::kStreamAudio) {
|
||||
LOG(ERROR) << "TsWriter cannot handle stream type " << stream_type
|
||||
<< " yet.";
|
||||
return Status(error::MUXER_FAILURE, "Unsupported stream type.");
|
||||
}
|
||||
|
||||
codec_ = stream_info.codec();
|
||||
if (stream_type == StreamType::kStreamAudio)
|
||||
audio_codec_config_ = stream_info.codec_config();
|
||||
|
||||
timescale_scale_ = kTsTimescale / stream_info.time_scale();
|
||||
return Status::OK;
|
||||
}
|
||||
|
@ -46,6 +68,33 @@ Status TsSegmenter::Finalize() {
|
|||
}
|
||||
|
||||
Status TsSegmenter::AddSample(const MediaSample& sample) {
|
||||
if (!ts_writer_) {
|
||||
std::unique_ptr<ProgramMapTableWriter> pmt_writer;
|
||||
if (codec_ == kCodecAC3) {
|
||||
// https://goo.gl/N7Tvqi MPEG-2 Stream Encryption Format for HTTP Live
|
||||
// Streaming 2.3.2.2 AC-3 Setup: For AC-3, the setup_data in the
|
||||
// audio_setup_information is the first 10 bytes of the audio data (the
|
||||
// syncframe()).
|
||||
// For unencrypted AC3, the setup_data is not used, so what is in there
|
||||
// does not matter.
|
||||
const size_t kSetupDataSize = 10u;
|
||||
if (sample.data_size() < kSetupDataSize) {
|
||||
LOG(ERROR) << "Sample is too small for AC3: " << sample.data_size();
|
||||
return Status(error::MUXER_FAILURE, "Sample is too small for AC3.");
|
||||
}
|
||||
const std::vector<uint8_t> setup_data(sample.data(),
|
||||
sample.data() + kSetupDataSize);
|
||||
pmt_writer.reset(new AudioProgramMapTableWriter(codec_, setup_data));
|
||||
} else if (IsAudioCodec(codec_)) {
|
||||
pmt_writer.reset(
|
||||
new AudioProgramMapTableWriter(codec_, audio_codec_config_));
|
||||
} else {
|
||||
DCHECK(IsVideoCodec(codec_));
|
||||
pmt_writer.reset(new VideoProgramMapTableWriter(codec_));
|
||||
}
|
||||
ts_writer_.reset(new TsWriter(std::move(pmt_writer)));
|
||||
}
|
||||
|
||||
if (sample.is_encrypted())
|
||||
ts_writer_->SignalEncrypted();
|
||||
|
||||
|
|
|
@ -80,6 +80,10 @@ class TsSegmenter {
|
|||
const MuxerOptions& muxer_options_;
|
||||
MuxerListener* const listener_;
|
||||
|
||||
// Codec for the stream.
|
||||
Codec codec_ = kUnknownCodec;
|
||||
std::vector<uint8_t> audio_codec_config_;
|
||||
|
||||
// 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.
|
||||
double timescale_scale_ = 1.0;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#include "packager/media/base/audio_stream_info.h"
|
||||
#include "packager/media/base/video_stream_info.h"
|
||||
#include "packager/media/event/mock_muxer_listener.h"
|
||||
#include "packager/media/formats/mp2t/pes_packet.h"
|
||||
#include "packager/media/formats/mp2t/program_map_table_writer.h"
|
||||
#include "packager/media/formats/mp2t/ts_segmenter.h"
|
||||
#include "packager/status_test_util.h"
|
||||
|
||||
|
@ -69,7 +71,11 @@ class MockPesPacketGenerator : public PesPacketGenerator {
|
|||
|
||||
class MockTsWriter : public TsWriter {
|
||||
public:
|
||||
MOCK_METHOD1(Initialize, bool(const StreamInfo& stream_info));
|
||||
MockTsWriter()
|
||||
: TsWriter(std::unique_ptr<ProgramMapTableWriter>(
|
||||
// Create a bogus pmt writer, which we don't really care.
|
||||
new VideoProgramMapTableWriter(kUnknownCodec))) {}
|
||||
|
||||
MOCK_METHOD1(NewSegment, bool(const std::string& file_name));
|
||||
MOCK_METHOD0(SignalEncrypted, void());
|
||||
MOCK_METHOD0(FinalizeSegment, bool());
|
||||
|
@ -105,11 +111,9 @@ TEST_F(TsSegmenterTest, Initialize) {
|
|||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
segmenter.InjectPesPacketGeneratorForTesting(
|
||||
std::move(mock_pes_packet_generator_));
|
||||
|
||||
|
@ -126,7 +130,6 @@ TEST_F(TsSegmenterTest, AddSample) {
|
|||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -156,11 +159,11 @@ TEST_F(TsSegmenterTest, AddSample) {
|
|||
EXPECT_CALL(*mock_pes_packet_generator_, GetNextPesPacketMock())
|
||||
.WillOnce(Return(new PesPacket()));
|
||||
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
segmenter.InjectPesPacketGeneratorForTesting(
|
||||
std::move(mock_pes_packet_generator_));
|
||||
|
||||
EXPECT_OK(segmenter.Initialize(*stream_info));
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
EXPECT_OK(segmenter.AddSample(*sample));
|
||||
}
|
||||
|
||||
|
@ -182,7 +185,6 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
|||
|
||||
const uint32_t kFirstPts = 1000;
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -253,10 +255,10 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
|||
.InSequence(pes_packet_sequence)
|
||||
.WillOnce(Return(new PesPacket()));
|
||||
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
segmenter.InjectPesPacketGeneratorForTesting(
|
||||
std::move(mock_pes_packet_generator_));
|
||||
EXPECT_OK(segmenter.Initialize(*stream_info));
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
EXPECT_OK(segmenter.AddSample(*sample1));
|
||||
EXPECT_OK(segmenter.FinalizeSegment(kFirstPts, sample1->duration()));
|
||||
EXPECT_OK(segmenter.AddSample(*sample2));
|
||||
|
@ -273,7 +275,6 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) {
|
|||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -281,7 +282,6 @@ TEST_F(TsSegmenterTest, InitializeThenFinalize) {
|
|||
ON_CALL(*mock_pes_packet_generator_, NumberOfReadyPesPackets())
|
||||
.WillByDefault(Return(0));
|
||||
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
segmenter.InjectPesPacketGeneratorForTesting(
|
||||
std::move(mock_pes_packet_generator_));
|
||||
EXPECT_OK(segmenter.Initialize(*stream_info));
|
||||
|
@ -302,7 +302,6 @@ TEST_F(TsSegmenterTest, FinalizeSegment) {
|
|||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -312,10 +311,10 @@ TEST_F(TsSegmenterTest, FinalizeSegment) {
|
|||
.WillOnce(Return(0u));
|
||||
EXPECT_CALL(*mock_ts_writer_, FinalizeSegment()).WillOnce(Return(true));
|
||||
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
segmenter.InjectPesPacketGeneratorForTesting(
|
||||
std::move(mock_pes_packet_generator_));
|
||||
EXPECT_OK(segmenter.Initialize(*stream_info));
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
segmenter.SetTsWriterFileOpenedForTesting(true);
|
||||
EXPECT_OK(segmenter.FinalizeSegment(0, 100 /* arbitrary duration */));
|
||||
}
|
||||
|
@ -333,7 +332,6 @@ TEST_F(TsSegmenterTest, EncryptedSample) {
|
|||
MockMuxerListener mock_listener;
|
||||
TsSegmenter segmenter(options, &mock_listener);
|
||||
|
||||
ON_CALL(*mock_ts_writer_, Initialize(_)).WillByDefault(Return(true));
|
||||
ON_CALL(*mock_ts_writer_, NewSegment(_)).WillByDefault(Return(true));
|
||||
ON_CALL(*mock_ts_writer_, FinalizeSegment()).WillByDefault(Return(true));
|
||||
ON_CALL(*mock_ts_writer_, AddPesPacketMock(_)).WillByDefault(Return(true));
|
||||
|
@ -390,11 +388,11 @@ TEST_F(TsSegmenterTest, EncryptedSample) {
|
|||
|
||||
MockTsWriter* mock_ts_writer_raw = mock_ts_writer_.get();
|
||||
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
segmenter.InjectPesPacketGeneratorForTesting(
|
||||
std::move(mock_pes_packet_generator_));
|
||||
|
||||
EXPECT_OK(segmenter.Initialize(*stream_info));
|
||||
segmenter.InjectTsWriterForTesting(std::move(mock_ts_writer_));
|
||||
EXPECT_OK(segmenter.AddSample(*sample1));
|
||||
|
||||
EXPECT_OK(segmenter.FinalizeSegment(1, sample1->duration()));
|
||||
|
|
|
@ -21,7 +21,8 @@ enum class TsStreamType {
|
|||
// ATSC Standard A/52.
|
||||
kAc3 = 0x81,
|
||||
kEac3 = 0x87,
|
||||
// Encrypted: https://goo.gl/N7Tvqi.
|
||||
// MPEG-2 Stream Encryption Format for HTTP Live Streaming:
|
||||
// https://goo.gl/N7Tvqi.
|
||||
kEncryptedAc3 = 0xC1,
|
||||
kEncryptedEac3 = 0xC2,
|
||||
kEncryptedAdtsAac = 0xCF,
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
#include <algorithm>
|
||||
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/media/base/audio_stream_info.h"
|
||||
#include "packager/media/base/buffer_writer.h"
|
||||
#include "packager/media/base/stream_info.h"
|
||||
#include "packager/media/base/video_stream_info.h"
|
||||
#include "packager/media/base/media_sample.h"
|
||||
#include "packager/media/formats/mp2t/pes_packet.h"
|
||||
#include "packager/media/formats/mp2t/program_map_table_writer.h"
|
||||
#include "packager/media/formats/mp2t/ts_packet_writer_util.h"
|
||||
|
||||
namespace shaka {
|
||||
|
@ -158,34 +158,11 @@ bool WritePesToFile(const PesPacket& pes,
|
|||
|
||||
} // namespace
|
||||
|
||||
TsWriter::TsWriter() {}
|
||||
TsWriter::TsWriter(std::unique_ptr<ProgramMapTableWriter> pmt_writer)
|
||||
: pmt_writer_(std::move(pmt_writer)) {}
|
||||
|
||||
TsWriter::~TsWriter() {}
|
||||
|
||||
bool TsWriter::Initialize(const StreamInfo& stream_info) {
|
||||
const StreamType stream_type = stream_info.stream_type();
|
||||
if (stream_type != StreamType::kStreamVideo &&
|
||||
stream_type != StreamType::kStreamAudio) {
|
||||
LOG(ERROR) << "TsWriter cannot handle stream type " << stream_type
|
||||
<< " yet.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stream_info.stream_type() == StreamType::kStreamVideo) {
|
||||
const VideoStreamInfo& video_stream_info =
|
||||
static_cast<const VideoStreamInfo&>(stream_info);
|
||||
pmt_writer_.reset(
|
||||
new VideoProgramMapTableWriter(video_stream_info.codec()));
|
||||
} else {
|
||||
DCHECK_EQ(stream_type, StreamType::kStreamAudio);
|
||||
const AudioStreamInfo& audio_stream_info =
|
||||
static_cast<const AudioStreamInfo&>(stream_info);
|
||||
pmt_writer_.reset(new AudioProgramMapTableWriter(
|
||||
audio_stream_info.codec(), audio_stream_info.codec_config()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TsWriter::NewSegment(const std::string& file_name) {
|
||||
if (current_file_) {
|
||||
LOG(ERROR) << "File " << current_file_->file_name() << " still open.";
|
||||
|
@ -237,11 +214,6 @@ bool TsWriter::AddPesPacket(std::unique_ptr<PesPacket> pes_packet) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void TsWriter::SetProgramMapTableWriterForTesting(
|
||||
std::unique_ptr<ProgramMapTableWriter> table_writer) {
|
||||
pmt_writer_ = std::move(table_writer);
|
||||
}
|
||||
|
||||
} // namespace mp2t
|
||||
} // namespace media
|
||||
} // namespace shaka
|
||||
|
|
|
@ -15,28 +15,21 @@
|
|||
#include "packager/file/file.h"
|
||||
#include "packager/file/file_closer.h"
|
||||
#include "packager/media/formats/mp2t/continuity_counter.h"
|
||||
#include "packager/media/formats/mp2t/pes_packet.h"
|
||||
#include "packager/media/formats/mp2t/program_map_table_writer.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
||||
class StreamInfo;
|
||||
|
||||
namespace mp2t {
|
||||
|
||||
class PesPacket;
|
||||
class ProgramMapTableWriter;
|
||||
|
||||
/// This class takes PesPackets, encapsulates them into TS packets, and write
|
||||
/// the data to file. This also creates PSI from StreamInfo.
|
||||
class TsWriter {
|
||||
public:
|
||||
TsWriter();
|
||||
explicit TsWriter(std::unique_ptr<ProgramMapTableWriter> pmt_writer);
|
||||
virtual ~TsWriter();
|
||||
|
||||
/// This must be called before calling other methods.
|
||||
/// @param stream_info is the information about this stream.
|
||||
/// @return true on success, false otherwise.
|
||||
virtual bool Initialize(const StreamInfo& stream_info);
|
||||
|
||||
/// This will fail if the current segment is not finalized.
|
||||
/// @param file_name is the output file name.
|
||||
/// @param encrypted must be true if the new segment is encrypted.
|
||||
|
@ -57,11 +50,10 @@ class TsWriter {
|
|||
/// @return true on success, false otherwise.
|
||||
virtual bool AddPesPacket(std::unique_ptr<PesPacket> pes_packet);
|
||||
|
||||
/// Only for testing.
|
||||
void SetProgramMapTableWriterForTesting(
|
||||
std::unique_ptr<ProgramMapTableWriter> table_writer);
|
||||
|
||||
private:
|
||||
TsWriter(const TsWriter&) = delete;
|
||||
TsWriter& operator=(const TsWriter&) = delete;
|
||||
|
||||
// True if further segments generated by this instance should be encrypted.
|
||||
bool encrypted_ = false;
|
||||
|
||||
|
@ -71,8 +63,6 @@ class TsWriter {
|
|||
std::unique_ptr<ProgramMapTableWriter> pmt_writer_;
|
||||
|
||||
std::unique_ptr<File, FileCloser> current_file_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TsWriter);
|
||||
};
|
||||
|
||||
} // namespace mp2t
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "packager/media/base/buffer_writer.h"
|
||||
#include "packager/media/base/video_stream_info.h"
|
||||
#include "packager/media/formats/mp2t/pes_packet.h"
|
||||
#include "packager/media/formats/mp2t/program_map_table_writer.h"
|
||||
#include "packager/media/formats/mp2t/ts_writer.h"
|
||||
|
||||
using ::testing::InSequence;
|
||||
|
@ -26,40 +27,12 @@ namespace mp2t {
|
|||
namespace {
|
||||
|
||||
const int kTsPacketSize = 188;
|
||||
|
||||
// Only {Audio,Video}Codec matter for this test. Other values are bogus.
|
||||
const Codec kH264Codec = Codec::kCodecH264;
|
||||
const Codec kAacCodec = Codec::kCodecAAC;
|
||||
const int kTrackId = 0;
|
||||
const uint32_t kTimeScale = 90000;
|
||||
const uint64_t kDuration = 180000;
|
||||
const char kCodecString[] = "avc1";
|
||||
const char kLanguage[] = "eng";
|
||||
const uint32_t kWidth = 1280;
|
||||
const uint32_t kHeight = 720;
|
||||
const uint32_t kPixelWidth = 1;
|
||||
const uint32_t kPixelHeight = 1;
|
||||
const uint16_t kTrickPlayFactor = 1;
|
||||
const uint8_t kNaluLengthSize = 1;
|
||||
const bool kIsEncrypted = false;
|
||||
|
||||
const uint8_t kSampleBits = 16;
|
||||
const uint8_t kNumChannels = 2;
|
||||
const uint32_t kSamplingFrequency = 44100;
|
||||
const uint64_t kSeekPreroll = 0;
|
||||
const uint64_t kCodecDelay = 0;
|
||||
const uint32_t kMaxBitrate = 320000;
|
||||
const uint32_t kAverageBitrate = 256000;
|
||||
|
||||
// Bogus extra data.
|
||||
const uint8_t kExtraData[] = {0x01, 0x02};
|
||||
|
||||
const uint8_t kAacBasicProfileExtraData[] = {0x12, 0x10};
|
||||
const Codec kCodecForTesting = kCodecH264;
|
||||
|
||||
class MockProgramMapTableWriter : public ProgramMapTableWriter {
|
||||
public:
|
||||
MockProgramMapTableWriter() : ProgramMapTableWriter(kUnknownCodec) {}
|
||||
~MockProgramMapTableWriter() override {}
|
||||
MockProgramMapTableWriter() : ProgramMapTableWriter(kCodecForTesting) {}
|
||||
~MockProgramMapTableWriter() override = default;
|
||||
|
||||
MOCK_METHOD1(EncryptedSegmentPmt, bool(BufferWriter* writer));
|
||||
MOCK_METHOD1(ClearSegmentPmt, bool(BufferWriter* writer));
|
||||
|
@ -158,29 +131,10 @@ class TsWriterTest : public ::testing::Test {
|
|||
}
|
||||
|
||||
std::string test_file_name_;
|
||||
TsWriter ts_writer_;
|
||||
|
||||
base::FilePath test_file_path_;
|
||||
};
|
||||
|
||||
TEST_F(TsWriterTest, InitializeVideoH264) {
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
}
|
||||
|
||||
TEST_F(TsWriterTest, InitializeAudioAac) {
|
||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kSampleBits, kNumChannels, kSamplingFrequency,
|
||||
kSeekPreroll, kCodecDelay, kMaxBitrate, kAverageBitrate, kLanguage,
|
||||
kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
}
|
||||
|
||||
// Verify that PAT and PMT are correct for clear segment.
|
||||
// This test covers verifies the PAT, and since it doesn't change, other tests
|
||||
// shouldn't have to check this.
|
||||
|
@ -189,16 +143,9 @@ TEST_F(TsWriterTest, ClearH264Psi) {
|
|||
new MockProgramMapTableWriter());
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
ASSERT_TRUE(ts_writer_.FinalizeSegment());
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
ASSERT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -244,16 +191,9 @@ TEST_F(TsWriterTest, ClearAacPmt) {
|
|||
new MockProgramMapTableWriter());
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||
|
||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
ASSERT_TRUE(ts_writer_.FinalizeSegment());
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
ASSERT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -272,16 +212,9 @@ TEST_F(TsWriterTest, ClearLeadH264Pmt) {
|
|||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_))
|
||||
.WillOnce(WriteTwoPmts());
|
||||
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer_.FinalizeSegment());
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -299,15 +232,8 @@ TEST_F(TsWriterTest, ClearSegmentPmtFailure) {
|
|||
new MockProgramMapTableWriter());
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(false));
|
||||
|
||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_FALSE(ts_writer_.NewSegment(test_file_name_));
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_FALSE(ts_writer.NewSegment(test_file_name_));
|
||||
}
|
||||
|
||||
// Check the encrypted segments' PMT (after clear lead).
|
||||
|
@ -318,21 +244,14 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
|||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer_.FinalizeSegment());
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
// Overwrite the file but as encrypted segment.
|
||||
ts_writer_.SignalEncrypted();
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer_.FinalizeSegment());
|
||||
ts_writer.SignalEncrypted();
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -350,19 +269,12 @@ TEST_F(TsWriterTest, EncryptedSegmentPmtFailure) {
|
|||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(Return(false));
|
||||
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer_.FinalizeSegment());
|
||||
|
||||
ts_writer_.SignalEncrypted();
|
||||
EXPECT_FALSE(ts_writer_.NewSegment(test_file_name_));
|
||||
ts_writer.SignalEncrypted();
|
||||
EXPECT_FALSE(ts_writer.NewSegment(test_file_name_));
|
||||
}
|
||||
|
||||
// Same as ClearLeadH264Pmt but for AAC.
|
||||
|
@ -372,16 +284,9 @@ TEST_F(TsWriterTest, ClearLeadAacPmt) {
|
|||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_))
|
||||
.WillOnce(WriteTwoPmts());
|
||||
|
||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
ASSERT_TRUE(ts_writer_.FinalizeSegment());
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
ASSERT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -402,21 +307,14 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) {
|
|||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||
|
||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer_.FinalizeSegment());
|
||||
TsWriter ts_writer(std::move(mock_pmt_writer));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
// Overwrite the file but as encrypted segment.
|
||||
ts_writer_.SignalEncrypted();
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer_.FinalizeSegment());
|
||||
ts_writer.SignalEncrypted();
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
EXPECT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -429,13 +327,9 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) {
|
|||
|
||||
|
||||
TEST_F(TsWriterTest, AddPesPacket) {
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
TsWriter ts_writer(std::unique_ptr<ProgramMapTableWriter>(
|
||||
new VideoProgramMapTableWriter(kCodecForTesting)));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
|
||||
std::unique_ptr<PesPacket> pes(new PesPacket());
|
||||
pes->set_stream_id(0xE0);
|
||||
|
@ -446,8 +340,8 @@ TEST_F(TsWriterTest, AddPesPacket) {
|
|||
};
|
||||
pes->mutable_data()->assign(kAnyData, kAnyData + arraysize(kAnyData));
|
||||
|
||||
EXPECT_TRUE(ts_writer_.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer_.FinalizeSegment());
|
||||
EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -495,13 +389,9 @@ TEST_F(TsWriterTest, AddPesPacket) {
|
|||
|
||||
// Verify that PES packet > 64KiB can be handled.
|
||||
TEST_F(TsWriterTest, BigPesPacket) {
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
TsWriter ts_writer(std::unique_ptr<ProgramMapTableWriter>(
|
||||
new VideoProgramMapTableWriter(kCodecForTesting)));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
|
||||
std::unique_ptr<PesPacket> pes(new PesPacket());
|
||||
pes->set_pts(0);
|
||||
|
@ -510,8 +400,8 @@ TEST_F(TsWriterTest, BigPesPacket) {
|
|||
const std::vector<uint8_t> big_data(400, 0x23);
|
||||
*pes->mutable_data() = big_data;
|
||||
|
||||
EXPECT_TRUE(ts_writer_.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer_.FinalizeSegment());
|
||||
EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -532,13 +422,9 @@ TEST_F(TsWriterTest, BigPesPacket) {
|
|||
// Bug found in code review. It should check whether PTS is present not whether
|
||||
// PTS (implicilty) cast to bool is true.
|
||||
TEST_F(TsWriterTest, PesPtsZeroNoDts) {
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
TsWriter ts_writer(std::unique_ptr<ProgramMapTableWriter>(
|
||||
new VideoProgramMapTableWriter(kCodecForTesting)));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
|
||||
std::unique_ptr<PesPacket> pes(new PesPacket());
|
||||
pes->set_stream_id(0xE0);
|
||||
|
@ -548,8 +434,8 @@ TEST_F(TsWriterTest, PesPtsZeroNoDts) {
|
|||
};
|
||||
pes->mutable_data()->assign(kAnyData, kAnyData + arraysize(kAnyData));
|
||||
|
||||
EXPECT_TRUE(ts_writer_.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer_.FinalizeSegment());
|
||||
EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
std::vector<uint8_t> content;
|
||||
ASSERT_TRUE(ReadFileToVector(test_file_path_, &content));
|
||||
|
@ -593,13 +479,9 @@ TEST_F(TsWriterTest, PesPtsZeroNoDts) {
|
|||
// Verify that TS packet with payload 183 is handled correctly, e.g.
|
||||
// adaptation_field_length should be 0.
|
||||
TEST_F(TsWriterTest, TsPacketPayload183Bytes) {
|
||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
TsWriter ts_writer(std::unique_ptr<ProgramMapTableWriter>(
|
||||
new VideoProgramMapTableWriter(kCodecForTesting)));
|
||||
EXPECT_TRUE(ts_writer.NewSegment(test_file_name_));
|
||||
|
||||
std::unique_ptr<PesPacket> pes(new PesPacket());
|
||||
pes->set_stream_id(0xE0);
|
||||
|
@ -613,8 +495,8 @@ TEST_F(TsWriterTest, TsPacketPayload183Bytes) {
|
|||
std::vector<uint8_t> pes_payload(157 + 183, 0xAF);
|
||||
*pes->mutable_data() = pes_payload;
|
||||
|
||||
EXPECT_TRUE(ts_writer_.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer_.FinalizeSegment());
|
||||
EXPECT_TRUE(ts_writer.AddPesPacket(std::move(pes)));
|
||||
ASSERT_TRUE(ts_writer.FinalizeSegment());
|
||||
|
||||
const uint8_t kExpectedOutputPrefix[] = {
|
||||
0x47, // Sync byte.
|
||||
|
|
Loading…
Reference in New Issue