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 17324acc90..aff3a5001e 100644 --- a/packager/media/formats/mp2t/program_map_table_writer_unittest.cc +++ b/packager/media/formats/mp2t/program_map_table_writer_unittest.cc @@ -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 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 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 diff --git a/packager/media/formats/mp2t/ts_segmenter.cc b/packager/media/formats/mp2t/ts_segmenter.cc index 4793084fc8..f099e9a69a 100644 --- a/packager/media/formats/mp2t/ts_segmenter.cc +++ b/packager/media/formats/mp2t/ts_segmenter.cc @@ -8,8 +8,12 @@ #include +#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 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 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(); diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index 06f7dc2ee4..dc751a0131 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -80,6 +80,10 @@ class TsSegmenter { const MuxerOptions& muxer_options_; MuxerListener* const listener_; + // Codec for the stream. + Codec codec_ = kUnknownCodec; + std::vector 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; diff --git a/packager/media/formats/mp2t/ts_segmenter_unittest.cc b/packager/media/formats/mp2t/ts_segmenter_unittest.cc index 4914a1be65..062f9597d0 100644 --- a/packager/media/formats/mp2t/ts_segmenter_unittest.cc +++ b/packager/media/formats/mp2t/ts_segmenter_unittest.cc @@ -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( + // 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())); diff --git a/packager/media/formats/mp2t/ts_stream_type.h b/packager/media/formats/mp2t/ts_stream_type.h index 10ebffc1bb..990b428ed4 100644 --- a/packager/media/formats/mp2t/ts_stream_type.h +++ b/packager/media/formats/mp2t/ts_stream_type.h @@ -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, diff --git a/packager/media/formats/mp2t/ts_writer.cc b/packager/media/formats/mp2t/ts_writer.cc index ad67f2f54a..2b2e8c78ab 100644 --- a/packager/media/formats/mp2t/ts_writer.cc +++ b/packager/media/formats/mp2t/ts_writer.cc @@ -9,10 +9,10 @@ #include #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 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(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(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 pes_packet) { return true; } -void TsWriter::SetProgramMapTableWriterForTesting( - std::unique_ptr table_writer) { - pmt_writer_ = std::move(table_writer); -} - } // namespace mp2t } // namespace media } // namespace shaka diff --git a/packager/media/formats/mp2t/ts_writer.h b/packager/media/formats/mp2t/ts_writer.h index 4efbbe9b83..80e7bfb03e 100644 --- a/packager/media/formats/mp2t/ts_writer.h +++ b/packager/media/formats/mp2t/ts_writer.h @@ -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 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 pes_packet); - /// Only for testing. - void SetProgramMapTableWriterForTesting( - std::unique_ptr 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 pmt_writer_; std::unique_ptr current_file_; - - DISALLOW_COPY_AND_ASSIGN(TsWriter); }; } // namespace mp2t diff --git a/packager/media/formats/mp2t/ts_writer_unittest.cc b/packager/media/formats/mp2t/ts_writer_unittest.cc index bdac3d22b8..83ceedd0d4 100644 --- a/packager/media/formats/mp2t/ts_writer_unittest.cc +++ b/packager/media/formats/mp2t/ts_writer_unittest.cc @@ -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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 content; ASSERT_TRUE(ReadFileToVector(test_file_path_, &content)); @@ -429,13 +327,9 @@ TEST_F(TsWriterTest, EncryptedSegmentsAacPmt) { TEST_F(TsWriterTest, AddPesPacket) { - std::shared_ptr 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( + new VideoProgramMapTableWriter(kCodecForTesting))); + EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); std::unique_ptr 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 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 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( + new VideoProgramMapTableWriter(kCodecForTesting))); + EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); std::unique_ptr pes(new PesPacket()); pes->set_pts(0); @@ -510,8 +400,8 @@ TEST_F(TsWriterTest, BigPesPacket) { const std::vector 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 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 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( + new VideoProgramMapTableWriter(kCodecForTesting))); + EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); std::unique_ptr 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 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 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( + new VideoProgramMapTableWriter(kCodecForTesting))); + EXPECT_TRUE(ts_writer.NewSegment(test_file_name_)); std::unique_ptr pes(new PesPacket()); pes->set_stream_id(0xE0); @@ -613,8 +495,8 @@ TEST_F(TsWriterTest, TsPacketPayload183Bytes) { std::vector 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.