Fix TS and Media Playlist with clear lead
- Cannot include PMT for following encrypted segments in the clear lead. Some players consider them as conflicting codecs. - Add EXT-X-DISCONTINUITY in front of first EXT-X-KEY to notify that the codec is switching from e.g. "avc1" to "zavc". b/29621230 Change-Id: I45c74813630c229d66245e992eb3a5117326bb14
This commit is contained in:
parent
50fe17fa9f
commit
1095b73a44
|
@ -280,9 +280,20 @@ bool MediaPlaylist::WriteToFile(media::File* file) {
|
|||
if (type_ == MediaPlaylistType::kVod) {
|
||||
header += "#EXT-X-PLAYLIST-TYPE:VOD\n";
|
||||
}
|
||||
|
||||
std::string body;
|
||||
for (const auto& entry : entries_) {
|
||||
body.append(entry->ToString());
|
||||
if (!entries_.empty()) {
|
||||
const bool first_is_ext_key =
|
||||
entries_.front()->type() == HlsEntry::EntryType::kExtKey;
|
||||
bool inserted_discontinuity_tag = false;
|
||||
for (const auto& entry : entries_) {
|
||||
if (!first_is_ext_key && !inserted_discontinuity_tag &&
|
||||
entry->type() == HlsEntry::EntryType::kExtKey) {
|
||||
body.append("#EXT-X-DISCONTINUITY\n");
|
||||
inserted_discontinuity_tag = true;
|
||||
}
|
||||
body.append(entry->ToString());
|
||||
}
|
||||
}
|
||||
|
||||
std::string content = header + body;
|
||||
|
|
|
@ -276,6 +276,40 @@ TEST_F(MediaPlaylistTest, WriteToFileWithEncryptionInfoEmptyIv) {
|
|||
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
|
||||
}
|
||||
|
||||
// Verify that EXT-X-DISCONTINUITY is inserted before EXT-X-KEY.
|
||||
TEST_F(MediaPlaylistTest, WriteToFileWithClearLead) {
|
||||
valid_video_media_info_.set_reference_time_scale(90000);
|
||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||
|
||||
media_playlist_.AddSegment("file1.ts", 900000, 1000000);
|
||||
|
||||
media_playlist_.AddEncryptionInfo(MediaPlaylist::EncryptionMethod::kSampleAes,
|
||||
"http://example.com", "0x12345678",
|
||||
"com.widevine", "1/2/4");
|
||||
media_playlist_.AddSegment("file2.ts", 2700000, 5000000);
|
||||
const std::string kExpectedOutput =
|
||||
"#EXTM3U\n"
|
||||
"#EXT-X-VERSION:5\n"
|
||||
"#EXT-X-TARGETDURATION:30\n"
|
||||
"#EXT-X-PLAYLIST-TYPE:VOD\n"
|
||||
"#EXTINF:10.000,\n"
|
||||
"file1.ts\n"
|
||||
"#EXT-X-DISCONTINUITY\n"
|
||||
"#EXT-X-KEY:METHOD=SAMPLE-AES,"
|
||||
"URI=\"http://example.com\",IV=0x12345678,KEYFORMATVERSIONS=\"1/2/4\","
|
||||
"KEYFORMAT=\"com.widevine\"\n"
|
||||
"#EXTINF:30.000,\n"
|
||||
"file2.ts\n"
|
||||
"#EXT-X-ENDLIST\n";
|
||||
|
||||
MockFile file;
|
||||
EXPECT_CALL(file,
|
||||
Write(MatchesString(kExpectedOutput), kExpectedOutput.size()))
|
||||
.WillOnce(ReturnArg<1>());
|
||||
EXPECT_TRUE(media_playlist_.WriteToFile(&file));
|
||||
}
|
||||
|
||||
|
||||
TEST_F(MediaPlaylistTest, RemoveOldestSegment) {
|
||||
valid_video_media_info_.set_reference_time_scale(90000);
|
||||
ASSERT_TRUE(media_playlist_.SetMediaInfo(valid_video_media_info_));
|
||||
|
|
|
@ -328,17 +328,6 @@ H264ProgramMapTableWriter::H264ProgramMapTableWriter(
|
|||
|
||||
H264ProgramMapTableWriter::~H264ProgramMapTableWriter() {}
|
||||
|
||||
bool H264ProgramMapTableWriter::ClearLeadSegmentPmt(BufferWriter* writer) {
|
||||
has_clear_lead_ = true;
|
||||
WritePmtToBuffer(kPmtH264, arraysize(kPmtH264), continuity_counter_, writer);
|
||||
WritePmtWithParameters(
|
||||
kStreamTypeEncryptedH264, kVersion1, kNext,
|
||||
kPrivateDataIndicatorDescriptorEncryptedH264,
|
||||
arraysize(kPrivateDataIndicatorDescriptorEncryptedH264),
|
||||
continuity_counter_, writer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool H264ProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
||||
WritePmtWithParameters(
|
||||
kStreamTypeEncryptedH264, has_clear_lead_ ? kVersion1 : kVersion0,
|
||||
|
@ -349,7 +338,11 @@ bool H264ProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
|||
}
|
||||
|
||||
bool H264ProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
||||
has_clear_lead_ = true;
|
||||
WritePmtToBuffer(kPmtH264, arraysize(kPmtH264), continuity_counter_, writer);
|
||||
// Cannot insert PMT for following encrypted segments because
|
||||
// some players consider encrypted segments as "zavc" codec which is different
|
||||
// from "avc1" codec, which causes problems.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -366,13 +359,6 @@ AacProgramMapTableWriter::~AacProgramMapTableWriter() {}
|
|||
|
||||
// TODO(rkuroiwa): Cache the PMT for encrypted segments, it doesn't need to
|
||||
// be recalculated.
|
||||
bool AacProgramMapTableWriter::ClearLeadSegmentPmt(BufferWriter* writer) {
|
||||
has_clear_lead_ = true;
|
||||
WritePmtToBuffer(kPmtAac, arraysize(kPmtAac), continuity_counter_, writer);
|
||||
// Version 1 and next.
|
||||
return EncryptedSegmentPmtWithParameters(kVersion1, kNext, writer);
|
||||
}
|
||||
|
||||
bool AacProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
||||
// Version 1 and current.
|
||||
return EncryptedSegmentPmtWithParameters(
|
||||
|
@ -380,6 +366,7 @@ bool AacProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
|||
}
|
||||
|
||||
bool AacProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
||||
has_clear_lead_ = true;
|
||||
WritePmtToBuffer(kPmtAac, arraysize(kPmtAac), continuity_counter_, writer);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -29,17 +29,10 @@ class ProgramMapTableWriter {
|
|||
ProgramMapTableWriter();
|
||||
virtual ~ProgramMapTableWriter();
|
||||
|
||||
/// Writes TS packets with PMT for clear lead followed by another PMT for
|
||||
/// encrypted segments.
|
||||
virtual bool ClearLeadSegmentPmt(BufferWriter* writer) = 0;
|
||||
|
||||
/// Writes TS packets with PMT for encrypted segments, the version number will
|
||||
/// be 1.
|
||||
/// Writes TS packets with PMT for encrypted segments.
|
||||
virtual bool EncryptedSegmentPmt(BufferWriter* writer) = 0;
|
||||
|
||||
/// This is the same as ClearLeadSegmentPmt() but does not append the extra PMT
|
||||
/// for encrypted segments that the clear segments. IOW use this if the entire
|
||||
/// stream is in the clear.
|
||||
/// Writes TS packets with PMT for clear segments.
|
||||
virtual bool ClearSegmentPmt(BufferWriter* writer) = 0;
|
||||
|
||||
// The pid can be 13 bits long but 8 bits is sufficient for this library.
|
||||
|
@ -57,7 +50,6 @@ class H264ProgramMapTableWriter : public ProgramMapTableWriter {
|
|||
explicit H264ProgramMapTableWriter(ContinuityCounter* continuity_counter);
|
||||
~H264ProgramMapTableWriter() override;
|
||||
|
||||
bool ClearLeadSegmentPmt(BufferWriter* writer) override;
|
||||
bool EncryptedSegmentPmt(BufferWriter* writer) override;
|
||||
bool ClearSegmentPmt(BufferWriter* writer) override;
|
||||
|
||||
|
@ -81,7 +73,6 @@ class AacProgramMapTableWriter : public ProgramMapTableWriter {
|
|||
ContinuityCounter* continuity_counter);
|
||||
~AacProgramMapTableWriter() override;
|
||||
|
||||
bool ClearLeadSegmentPmt(BufferWriter* writer) override;
|
||||
bool EncryptedSegmentPmt(BufferWriter* writer) override;
|
||||
bool ClearSegmentPmt(BufferWriter* writer) override;
|
||||
|
||||
|
|
|
@ -88,89 +88,13 @@ TEST_F(ProgramMapTableWriterTest, ClearH264) {
|
|||
kPmtH264, arraysize(kPmtH264), buffer.Buffer()));
|
||||
}
|
||||
|
||||
TEST_F(ProgramMapTableWriterTest, ClearLeadH264) {
|
||||
ContinuityCounter counter;
|
||||
H264ProgramMapTableWriter writer(&counter);
|
||||
BufferWriter buffer;
|
||||
writer.ClearLeadSegmentPmt(&buffer);
|
||||
|
||||
EXPECT_EQ(kTsPacketSize * 2, buffer.Size());
|
||||
|
||||
// First PMT is for the clear lead segments.
|
||||
const uint8_t kFirstTsPrefix[] = {
|
||||
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 kClearPmtH264[] = {
|
||||
0x00, // pointer field
|
||||
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.
|
||||
0x1B, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x00, // Es_info_length is 0.
|
||||
// CRC32.
|
||||
0x43, 0x49, 0x97, 0xbe,
|
||||
};
|
||||
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
|
||||
kFirstTsPrefix, arraysize(kFirstTsPrefix), 160, kClearPmtH264,
|
||||
arraysize(kClearPmtH264), buffer.Buffer()));
|
||||
|
||||
// Second PMT is for the encrypted segments after clear lead.
|
||||
const uint8_t kSecondTsPrefix[] = {
|
||||
0x47, // Sync byte.
|
||||
0x40, // payload_unit_start_indicator set.
|
||||
0x20, // pid.
|
||||
0x31, // Adaptation field and payload are both present. counter = 1.
|
||||
0x9B, // Adaptation Field length.
|
||||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
const uint8_t kPmtForClearLeadEncryptedH264[] = {
|
||||
0x00, // pointer field
|
||||
0x02, // table id.
|
||||
0xB0, // The first 4 bits must be '1011'.
|
||||
0x18, // length of the rest of this array.
|
||||
0x00, 0x01, // Program number.
|
||||
0xC2, // version 1, current next indicator 0.
|
||||
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.
|
||||
0xDB, 0xE0, 0x50, // stream_type -> PID.
|
||||
0xF0, 0x06, // Es_info_length is 6 for private_data_indicator
|
||||
0x0F, // private_data_indicator descriptor_tag.
|
||||
0x04, // Length of the rest of this descriptor
|
||||
0x7A, 0x61, 0x76, 0x63, // 'zavc'.
|
||||
// CRC32.
|
||||
0x2E, 0xAB, 0xF2, 0x54,
|
||||
};
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
|
||||
kSecondTsPrefix, arraysize(kSecondTsPrefix), 154,
|
||||
kPmtForClearLeadEncryptedH264, arraysize(kPmtForClearLeadEncryptedH264),
|
||||
buffer.Buffer() + kTsPacketSize));
|
||||
}
|
||||
|
||||
// Verify that PSI for encrypted segments after clear lead is generated
|
||||
// correctly.
|
||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadH264) {
|
||||
ContinuityCounter counter;
|
||||
H264ProgramMapTableWriter writer(&counter);
|
||||
BufferWriter buffer;
|
||||
writer.ClearLeadSegmentPmt(&buffer);
|
||||
writer.ClearSegmentPmt(&buffer);
|
||||
buffer.Clear();
|
||||
writer.EncryptedSegmentPmt(&buffer);
|
||||
EXPECT_EQ(kTsPacketSize, buffer.Size());
|
||||
|
@ -179,7 +103,7 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadH264) {
|
|||
0x47, // Sync byte.
|
||||
0x40, // payload_unit_start_indicator set.
|
||||
0x20, // pid.
|
||||
0x32, // Adaptation field and payload are both present. counter = 2.
|
||||
0x31, // Adaptation field and payload are both present. counter = 1.
|
||||
0x9B, // Adaptation Field length.
|
||||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
|
@ -295,98 +219,6 @@ TEST_F(ProgramMapTableWriterTest, ClearAac) {
|
|||
160, kPmtAac, arraysize(kPmtAac), buffer.Buffer()));
|
||||
}
|
||||
|
||||
TEST_F(ProgramMapTableWriterTest, ClearLeadAac) {
|
||||
ContinuityCounter counter;
|
||||
const std::vector<uint8_t> aac_audio_specific_config(
|
||||
kAacBasicProfileExtraData,
|
||||
kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData));
|
||||
AacProgramMapTableWriter writer(aac_audio_specific_config, &counter);
|
||||
BufferWriter buffer;
|
||||
writer.ClearLeadSegmentPmt(&buffer);
|
||||
|
||||
EXPECT_EQ(kTsPacketSize * 2, buffer.Size());
|
||||
|
||||
// First PMT is for the clear lead segments.
|
||||
const uint8_t kFirstTsPrefix[] = {
|
||||
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 kClearPmtAac[] = {
|
||||
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,
|
||||
};
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
|
||||
kFirstTsPrefix, arraysize(kFirstTsPrefix),
|
||||
160, kClearPmtAac, arraysize(kClearPmtAac),
|
||||
buffer.Buffer()));
|
||||
|
||||
// Second PMT is for the encrypted segments after clear lead.
|
||||
const uint8_t kSecondTsPrefix[] = {
|
||||
0x47, // Sync byte.
|
||||
0x40, // payload_unit_start_indicator set.
|
||||
0x20, // pid.
|
||||
0x31, // Adaptation field and payload are both present. counter = 1.
|
||||
0x8B, // Adaptation Field length.
|
||||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
const uint8_t kPmtForClearLeadEncryptedAac[] = {
|
||||
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.
|
||||
0xC2, // version 1, current next indicator 0.
|
||||
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
|
||||
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,
|
||||
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.
|
||||
0x5C, 0x60, 0xB2, 0x55,
|
||||
};
|
||||
|
||||
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
|
||||
kSecondTsPrefix, arraysize(kSecondTsPrefix), 138,
|
||||
kPmtForClearLeadEncryptedAac, arraysize(kPmtForClearLeadEncryptedAac),
|
||||
buffer.Buffer() + kTsPacketSize));
|
||||
}
|
||||
|
||||
// Verify that PSI for encrypted segments after clear lead is generated
|
||||
// correctly.
|
||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
||||
|
@ -396,7 +228,7 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
|||
kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData));
|
||||
AacProgramMapTableWriter writer(aac_audio_specific_config, &counter);
|
||||
BufferWriter buffer;
|
||||
writer.ClearLeadSegmentPmt(&buffer);
|
||||
writer.ClearSegmentPmt(&buffer);
|
||||
|
||||
buffer.Clear();
|
||||
writer.EncryptedSegmentPmt(&buffer);
|
||||
|
@ -406,7 +238,7 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
|||
0x47, // Sync byte.
|
||||
0x40, // payload_unit_start_indicator set.
|
||||
0x20, // pid.
|
||||
0x32, // Adaptation field and payload are both present. counter = 2.
|
||||
0x31, // Adaptation field and payload are both present. counter = 1.
|
||||
0x8B, // Adaptation Field length.
|
||||
0x00, // All adaptation field flags 0.
|
||||
};
|
||||
|
|
|
@ -37,7 +37,7 @@ Status TsSegmenter::Initialize(const StreamInfo& stream_info,
|
|||
double clear_lead_in_seconds) {
|
||||
if (muxer_options_.segment_template.empty())
|
||||
return Status(error::MUXER_FAILURE, "Segment template not specified.");
|
||||
if (!ts_writer_->Initialize(stream_info, false))
|
||||
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,
|
||||
|
|
|
@ -73,8 +73,7 @@ class MockPesPacketGenerator : public PesPacketGenerator {
|
|||
|
||||
class MockTsWriter : public TsWriter {
|
||||
public:
|
||||
MOCK_METHOD2(Initialize,
|
||||
bool(const StreamInfo& stream_info, bool will_be_encrypted));
|
||||
MOCK_METHOD1(Initialize, bool(const StreamInfo& stream_info));
|
||||
MOCK_METHOD1(NewSegment, bool(const std::string& file_name));
|
||||
MOCK_METHOD0(SignalEncypted, void());
|
||||
MOCK_METHOD0(FinalizeSegment, bool());
|
||||
|
@ -115,7 +114,7 @@ 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_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -136,7 +135,7 @@ 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_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -195,7 +194,7 @@ TEST_F(TsSegmenterTest, PassedSegmentDuration) {
|
|||
|
||||
const uint32_t kFirstPts = 1000;
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -290,7 +289,7 @@ 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_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -319,7 +318,7 @@ TEST_F(TsSegmenterTest, Finalize) {
|
|||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -348,7 +347,7 @@ TEST_F(TsSegmenterTest, SegmentOnlyBeforeKeyFrame) {
|
|||
options.segment_template = "file$Number$.ts";
|
||||
TsSegmenter segmenter(options, nullptr);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
||||
|
@ -457,7 +456,7 @@ TEST_F(TsSegmenterTest, WithEncryptionNoClearLead) {
|
|||
EXPECT_CALL(mock_listener, OnEncryptionInfoReady(_, _, _, _, _));
|
||||
TsSegmenter segmenter(options, &mock_listener);
|
||||
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_ts_writer_, Initialize(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_ts_writer_, SignalEncypted());
|
||||
EXPECT_CALL(*mock_pes_packet_generator_, Initialize(_))
|
||||
.WillOnce(Return(true));
|
||||
|
@ -497,7 +496,7 @@ TEST_F(TsSegmenterTest, WithEncryptionWithClearLead) {
|
|||
MockMuxerListener mock_listener;
|
||||
TsSegmenter segmenter(options, &mock_listener);
|
||||
|
||||
ON_CALL(*mock_ts_writer_, Initialize(_, _)).WillByDefault(Return(true));
|
||||
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));
|
||||
|
|
|
@ -162,8 +162,7 @@ bool WritePesToFile(const PesPacket& pes,
|
|||
TsWriter::TsWriter() {}
|
||||
TsWriter::~TsWriter() {}
|
||||
|
||||
bool TsWriter::Initialize(const StreamInfo& stream_info,
|
||||
bool will_be_encrypted) {
|
||||
bool TsWriter::Initialize(const StreamInfo& stream_info) {
|
||||
const StreamType stream_type = stream_info.stream_type();
|
||||
if (stream_type != StreamType::kStreamVideo &&
|
||||
stream_type != StreamType::kStreamAudio) {
|
||||
|
@ -194,7 +193,6 @@ bool TsWriter::Initialize(const StreamInfo& stream_info,
|
|||
audio_stream_info.codec_config(), &pmt_continuity_counter_));
|
||||
}
|
||||
|
||||
will_be_encrypted_ = will_be_encrypted;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -211,11 +209,7 @@ bool TsWriter::NewSegment(const std::string& file_name) {
|
|||
|
||||
BufferWriter psi;
|
||||
WritePatToBuffer(kPat, arraysize(kPat), &pat_continuity_counter_, &psi);
|
||||
if (will_be_encrypted_ && !encrypted_) {
|
||||
if (!pmt_writer_->ClearLeadSegmentPmt(&psi)) {
|
||||
return false;
|
||||
}
|
||||
} else if (encrypted_) {
|
||||
if (encrypted_) {
|
||||
if (!pmt_writer_->EncryptedSegmentPmt(&psi)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -32,12 +32,8 @@ class TsWriter {
|
|||
|
||||
/// This must be called before calling other methods.
|
||||
/// @param stream_info is the information about this stream.
|
||||
/// @param will_be_encrypted must be true if some segment would be encrypted.
|
||||
/// It is ok if the entire stream is not encrypted but have this true
|
||||
/// e.g. if the clear lead is very long.
|
||||
/// @return true on success, false otherwise.
|
||||
virtual bool Initialize(const StreamInfo& stream_info,
|
||||
bool will_be_encrypted);
|
||||
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.
|
||||
|
@ -46,7 +42,6 @@ class TsWriter {
|
|||
virtual bool NewSegment(const std::string& file_name);
|
||||
|
||||
/// Signals the writer that the rest of the segments are encrypted.
|
||||
/// |will_be_encrypted| passed to Initialize() should be true.
|
||||
virtual void SignalEncypted();
|
||||
|
||||
/// Flush all the pending PesPackets that have not been written to file and
|
||||
|
@ -67,8 +62,6 @@ class TsWriter {
|
|||
private:
|
||||
// True if further segments generated by this instance should be encrypted.
|
||||
bool encrypted_ = false;
|
||||
// The stream will be encrypted some time later.
|
||||
bool will_be_encrypted_ = false;
|
||||
|
||||
ContinuityCounter pmt_continuity_counter_;
|
||||
ContinuityCounter pat_continuity_counter_;
|
||||
|
|
|
@ -56,14 +56,11 @@ const uint8_t kExtraData[] = {0x01, 0x02};
|
|||
|
||||
const uint8_t kAacBasicProfileExtraData[] = {0x12, 0x10};
|
||||
|
||||
const bool kWillBeEncrypted = true;
|
||||
|
||||
class MockProgramMapTableWriter : public ProgramMapTableWriter {
|
||||
public:
|
||||
MockProgramMapTableWriter() {}
|
||||
~MockProgramMapTableWriter() override {}
|
||||
|
||||
MOCK_METHOD1(ClearLeadSegmentPmt, bool(BufferWriter* writer));
|
||||
MOCK_METHOD1(EncryptedSegmentPmt, bool(BufferWriter* writer));
|
||||
MOCK_METHOD1(ClearSegmentPmt, bool(BufferWriter* writer));
|
||||
};
|
||||
|
@ -163,7 +160,7 @@ TEST_F(TsWriterTest, InitializeVideoH264) {
|
|||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
}
|
||||
|
||||
TEST_F(TsWriterTest, InitializeVideoNonH264) {
|
||||
|
@ -171,7 +168,7 @@ TEST_F(TsWriterTest, InitializeVideoNonH264) {
|
|||
kTrackId, kTimeScale, kDuration, VideoCodec::kCodecVP9, kCodecString,
|
||||
kLanguage, kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
||||
}
|
||||
|
||||
TEST_F(TsWriterTest, InitializeAudioAac) {
|
||||
|
@ -180,7 +177,7 @@ TEST_F(TsWriterTest, InitializeAudioAac) {
|
|||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kExtraData, arraysize(kExtraData),
|
||||
kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
}
|
||||
|
||||
TEST_F(TsWriterTest, InitializeAudioNonAac) {
|
||||
|
@ -189,7 +186,7 @@ TEST_F(TsWriterTest, InitializeAudioNonAac) {
|
|||
kLanguage, kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll,
|
||||
kCodecDelay, kMaxBitrate, kAverageBitrate, kExtraData,
|
||||
arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
||||
}
|
||||
|
||||
// Verify that PAT and PMT are correct for clear segment.
|
||||
|
@ -204,7 +201,7 @@ TEST_F(TsWriterTest, ClearH264Psi) {
|
|||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
@ -259,7 +256,7 @@ TEST_F(TsWriterTest, ClearAacPmt) {
|
|||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kAacBasicProfileExtraData,
|
||||
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
@ -279,14 +276,14 @@ TEST_F(TsWriterTest, ClearAacPmt) {
|
|||
TEST_F(TsWriterTest, ClearLeadH264Pmt) {
|
||||
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||
new MockProgramMapTableWriter());
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearLeadSegmentPmt(_))
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_))
|
||||
.WillOnce(WriteTwoPmts());
|
||||
|
||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
@ -308,14 +305,14 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
|||
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||
new MockProgramMapTableWriter());
|
||||
InSequence s;
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearLeadSegmentPmt(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||
|
||||
scoped_refptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
@ -339,7 +336,7 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
|||
TEST_F(TsWriterTest, ClearLeadAacPmt) {
|
||||
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||
new MockProgramMapTableWriter());
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearLeadSegmentPmt(_))
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_))
|
||||
.WillOnce(WriteTwoPmts());
|
||||
|
||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
|
@ -347,7 +344,7 @@ TEST_F(TsWriterTest, ClearLeadAacPmt) {
|
|||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kAacBasicProfileExtraData,
|
||||
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
@ -369,7 +366,7 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) {
|
|||
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||
new MockProgramMapTableWriter());
|
||||
InSequence s;
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearLeadSegmentPmt(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(true));
|
||||
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
|
||||
|
||||
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
|
@ -377,7 +374,7 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) {
|
|||
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||
kMaxBitrate, kAverageBitrate, kAacBasicProfileExtraData,
|
||||
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
|
||||
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
@ -403,7 +400,7 @@ TEST_F(TsWriterTest, AddPesPacket) {
|
|||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
||||
scoped_ptr<PesPacket> pes(new PesPacket());
|
||||
|
@ -468,7 +465,7 @@ TEST_F(TsWriterTest, BigPesPacket) {
|
|||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
||||
scoped_ptr<PesPacket> pes(new PesPacket());
|
||||
|
@ -504,7 +501,7 @@ TEST_F(TsWriterTest, PesPtsZeroNoDts) {
|
|||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
||||
scoped_ptr<PesPacket> pes(new PesPacket());
|
||||
|
@ -564,7 +561,7 @@ TEST_F(TsWriterTest, TsPacketPayload183Bytes) {
|
|||
kTrackId, kTimeScale, kDuration, kH264VideoCodec, kCodecString, kLanguage,
|
||||
kWidth, kHeight, kPixelWidth, kPixelHeight, kTrickPlayRate,
|
||||
kNaluLengthSize, kExtraData, arraysize(kExtraData), kIsEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
|
||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||
|
||||
scoped_ptr<PesPacket> pes(new PesPacket());
|
||||
|
|
Loading…
Reference in New Issue