diff --git a/packager/media/formats/mp2t/program_map_table_writer.cc b/packager/media/formats/mp2t/program_map_table_writer.cc index b82c26d07b..b09006b4e0 100644 --- a/packager/media/formats/mp2t/program_map_table_writer.cc +++ b/packager/media/formats/mp2t/program_map_table_writer.cc @@ -22,6 +22,7 @@ namespace mp2t { namespace { // Values for version. Only 0 and 1 are necessary for the implementation. +const int kVersion0 = 0; const int kVersion1 = 1; // Values for current_next_indicator. @@ -328,6 +329,7 @@ H264ProgramMapTableWriter::H264ProgramMapTableWriter( H264ProgramMapTableWriter::~H264ProgramMapTableWriter() {} bool H264ProgramMapTableWriter::ClearLeadSegmentPmt(BufferWriter* writer) { + has_clear_lead_ = true; WritePmtToBuffer(kPmtH264, arraysize(kPmtH264), continuity_counter_, writer); WritePmtWithParameters( kStreamTypeEncryptedH264, kVersion1, kNext, @@ -339,8 +341,8 @@ bool H264ProgramMapTableWriter::ClearLeadSegmentPmt(BufferWriter* writer) { bool H264ProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) { WritePmtWithParameters( - kStreamTypeEncryptedH264, kVersion1, kCurrent, - kPrivateDataIndicatorDescriptorEncryptedH264, + kStreamTypeEncryptedH264, has_clear_lead_ ? kVersion1 : kVersion0, + kCurrent, kPrivateDataIndicatorDescriptorEncryptedH264, arraysize(kPrivateDataIndicatorDescriptorEncryptedH264), continuity_counter_, writer); return true; @@ -365,6 +367,7 @@ 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); @@ -372,7 +375,8 @@ bool AacProgramMapTableWriter::ClearLeadSegmentPmt(BufferWriter* writer) { bool AacProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) { // Version 1 and current. - return EncryptedSegmentPmtWithParameters(kVersion1, kCurrent, writer); + return EncryptedSegmentPmtWithParameters( + has_clear_lead_ ? kVersion1 : kVersion0, kCurrent, writer); } bool AacProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) { diff --git a/packager/media/formats/mp2t/program_map_table_writer.h b/packager/media/formats/mp2t/program_map_table_writer.h index 1be6b3f77a..a9677d8a1c 100644 --- a/packager/media/formats/mp2t/program_map_table_writer.h +++ b/packager/media/formats/mp2t/program_map_table_writer.h @@ -63,6 +63,9 @@ class H264ProgramMapTableWriter : public ProgramMapTableWriter { private: ContinuityCounter* const continuity_counter_; + // Set to true if ClearLeadSegmentPmt() has been called. This determines the + // version number set in EncryptedSegmentPmt(). + bool has_clear_lead_ = false; DISALLOW_COPY_AND_ASSIGN(H264ProgramMapTableWriter); }; @@ -89,6 +92,9 @@ class AacProgramMapTableWriter : public ProgramMapTableWriter { const std::vector aac_audio_specific_config_; ContinuityCounter* const continuity_counter_; + // Set to true if ClearLeadSegmentPmt() has been called. This determines the + // version number set in EncryptedSegmentPmt(). + bool has_clear_lead_ = false; DISALLOW_COPY_AND_ASSIGN(AacProgramMapTableWriter); }; diff --git a/packager/media/formats/mp2t/program_map_table_writer_unittest.cc b/packager/media/formats/mp2t/program_map_table_writer_unittest.cc index 8197124d48..efbf261b36 100644 --- a/packager/media/formats/mp2t/program_map_table_writer_unittest.cc +++ b/packager/media/formats/mp2t/program_map_table_writer_unittest.cc @@ -40,8 +40,11 @@ class ProgramMapTableWriterTest : public ::testing::Test { std::vector actual_suffix(actual + prefix_size + padding_length, actual + kTsPacketSize); - EXPECT_EQ(std::vector(suffix, suffix + suffix_size), - actual_suffix); + ASSERT_EQ(suffix_size, actual_suffix.size()); + + for (size_t i = 0; i < suffix_size; ++i) { + EXPECT_EQ(suffix[i], actual_suffix[i]) << "at index " << i; + } } }; @@ -161,20 +164,22 @@ TEST_F(ProgramMapTableWriterTest, ClearLeadH264) { buffer.Buffer() + kTsPacketSize)); } - -TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) { +// 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); + buffer.Clear(); writer.EncryptedSegmentPmt(&buffer); - EXPECT_EQ(kTsPacketSize, buffer.Size()); const uint8_t kPmtEncryptedH264Prefix[] = { 0x47, // Sync byte. 0x40, // payload_unit_start_indicator set. 0x20, // pid. - 0x30, // Adaptation field and payload are both present. counter = 0. + 0x32, // Adaptation field and payload are both present. counter = 2. 0x9B, // Adaptation Field length. 0x00, // All adaptation field flags 0. }; @@ -205,6 +210,50 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) { kPmtEncryptedH264, arraysize(kPmtEncryptedH264), buffer.Buffer())); } +// Verify that PMT for encrypted segments can be generated (without clear lead). +TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) { + ContinuityCounter counter; + H264ProgramMapTableWriter writer(&counter); + BufferWriter buffer; + writer.EncryptedSegmentPmt(&buffer); + + EXPECT_EQ(kTsPacketSize, buffer.Size()); + + const uint8_t kPmtEncryptedH264Prefix[] = { + 0x47, // Sync byte. + 0x40, // payload_unit_start_indicator set. + 0x20, // pid. + 0x30, // Adaptation field and payload are both present. counter = 0. + 0x9B, // Adaptation Field length. + 0x00, // All adaptation field flags 0. + }; + + const uint8_t kPmtEncryptedH264[] = { + 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. + 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. + 0xDB, 0xE0, 0x50, // stream_type -> PID. + 0xF0, 0x06, // Es_info_length is 6 for private_data_indicator + 0x0F, // descriptor_tag. + 0x04, // Length of the rest of this descriptor + 0x7A, 0x61, 0x76, 0x63, // 'zavc'. + // CRC32. + 0xA9, 0xC2, 0x95, 0x7C, + }; + EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual( + kPmtEncryptedH264Prefix, arraysize(kPmtEncryptedH264Prefix), 154, + kPmtEncryptedH264, arraysize(kPmtEncryptedH264), buffer.Buffer())); +} + TEST_F(ProgramMapTableWriterTest, ClearAac) { ContinuityCounter counter; const std::vector aac_audio_specific_config( @@ -338,23 +387,26 @@ TEST_F(ProgramMapTableWriterTest, ClearLeadAac) { buffer.Buffer() + kTsPacketSize)); } -TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) { +// Verify that PSI for encrypted segments after clear lead is generated +// correctly. +TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) { ContinuityCounter counter; const std::vector aac_audio_specific_config( kAacBasicProfileExtraData, kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData)); AacProgramMapTableWriter writer(aac_audio_specific_config, &counter); BufferWriter buffer; - writer.EncryptedSegmentPmt(&buffer); + writer.ClearLeadSegmentPmt(&buffer); + buffer.Clear(); + writer.EncryptedSegmentPmt(&buffer); EXPECT_EQ(kTsPacketSize, buffer.Size()); - // Second PMT is for the encrypted segments after clear lead. const uint8_t kPmtEncryptedAacPrefix[] = { 0x47, // Sync byte. 0x40, // payload_unit_start_indicator set. 0x20, // pid. - 0x30, // Adaptation field and payload are both present. counter = 0. + 0x32, // Adaptation field and payload are both present. counter = 2. 0x8B, // Adaptation Field length. 0x00, // All adaptation field flags 0. }; @@ -397,6 +449,66 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) { buffer.Buffer())); } +// Verify that PMT for encrypted segments can be generated (without clear lead). +TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) { + ContinuityCounter counter; + const std::vector aac_audio_specific_config( + kAacBasicProfileExtraData, + kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData)); + AacProgramMapTableWriter writer(aac_audio_specific_config, &counter); + BufferWriter buffer; + writer.EncryptedSegmentPmt(&buffer); + + EXPECT_EQ(kTsPacketSize, buffer.Size()); + + // Second PMT is for the encrypted segments after clear lead. + const uint8_t kPmtEncryptedAacPrefix[] = { + 0x47, // Sync byte. + 0x40, // payload_unit_start_indicator set. + 0x20, // pid. + 0x30, // Adaptation field and payload are both present. counter = 0. + 0x8B, // Adaptation Field length. + 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 + 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. + 0xF7, 0xD5, 0x2A, 0x53, + }; + + EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual( + kPmtEncryptedAacPrefix, arraysize(kPmtEncryptedAacPrefix), 138, + kPmtEncryptedAac, arraysize(kPmtEncryptedAac), + buffer.Buffer())); +} + } // namespace mp2t } // namespace media } // namespace shaka