PMT writers for H264 and AAC

- For encrypted segments, PMT specified by SAMPLE-AES specification is
  generated.
- Remove time_scale_ field from TsWriter. It is not used.
- Move ContinuityCounter to a separate file.
- Add ProgramMapTableWriter and implementations.
- continuity_counter is incremented for PAT and PMT.

Change-Id: If0dd0990203cb6990f39a6832048bdc3ff08decd
This commit is contained in:
Rintaro Kuroiwa 2016-04-13 22:40:24 -07:00
parent 9bb6c5d8d2
commit 7d8322377e
18 changed files with 1507 additions and 341 deletions

View File

@ -10,13 +10,15 @@
namespace edash_packager {
namespace media {
enum FourCC {
enum FourCC : uint32_t {
FOURCC_NULL = 0,
FOURCC_ID32 = 0x49443332,
FOURCC_PRIV = 0x50524956,
FOURCC_aacd = 0x61616364,
FOURCC_ac_3 = 0x61632d33, // "ac-3"
FOURCC_apad = 0x61706164,
FOURCC_avc1 = 0x61766331,
FOURCC_avcC = 0x61766343,
FOURCC_bloc = 0x626C6F63,
@ -131,6 +133,9 @@ enum FourCC {
FOURCC_vtte = 0x76747465,
FOURCC_wide = 0x77696465,
FOURCC_wvtt = 0x77767474,
FOURCC_zaac = 0x7A616163,
FOURCC_zach = 0x7A616368,
FOURCC_zacp = 0x7A616370,
};
const inline std::string FourCCToString(FourCC fourcc) {

View File

@ -0,0 +1,25 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/formats/mp2t/continuity_counter.h"
namespace edash_packager {
namespace media {
namespace mp2t {
ContinuityCounter::ContinuityCounter() {}
ContinuityCounter::~ContinuityCounter() {}
int ContinuityCounter::GetNext() {
int ret = counter_;
++counter_;
counter_ %= 16;
return ret;
}
} // namespace mp2t
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,35 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef PACKAGER_MEDIA_FORMATS_MP2T_CONTINUITY_COUNTER_H_
#define PACKAGER_MEDIA_FORMATS_MP2T_CONTINUITY_COUNTER_H_
#include "packager/base/macros.h"
namespace edash_packager {
namespace media {
namespace mp2t {
class ContinuityCounter {
public:
ContinuityCounter();
~ContinuityCounter();
/// As specified by the spec, this starts from 0 and is incremented by 1 until
/// it wraps back to 0 when it reaches 16.
/// @return counter value.
int GetNext();
private:
int counter_ = 0;
DISALLOW_COPY_AND_ASSIGN(ContinuityCounter);
};
} // namespace mp2t
} // namespace media
} // namespace edash_packager
#endif // PACKAGER_MEDIA_FORMATS_MP2T_CONTINUITY_COUNTER_H_

View File

@ -15,6 +15,8 @@
'sources': [
'adts_header.cc',
'adts_header.h',
'continuity_counter.cc',
'continuity_counter.h',
'es_parser_adts.cc',
'es_parser_adts.h',
'es_parser_h264.cc',
@ -30,10 +32,14 @@
'pes_packet.h',
'pes_packet_generator.cc',
'pes_packet_generator.h',
'program_map_table_writer.cc',
'program_map_table_writer.h',
'ts_muxer.cc',
'ts_muxer.h',
'ts_packet.cc',
'ts_packet.h',
'ts_packet_writer_util.cc',
'ts_packet_writer_util.h',
'ts_section_pat.cc',
'ts_section_pat.h',
'ts_section_pes.cc',
@ -64,6 +70,7 @@
'es_parser_h26x_unittest.cc',
'mp2t_media_parser_unittest.cc',
'pes_packet_generator_unittest.cc',
'program_map_table_writer_unittest.cc',
'ts_segmenter_unittest.cc',
'ts_writer_unittest.cc',
],

View File

@ -0,0 +1,412 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/formats/mp2t/program_map_table_writer.h"
#include <algorithm>
#include "packager/base/logging.h"
#include "packager/media/base/buffer_writer.h"
#include "packager/media/base/fourccs.h"
#include "packager/media/formats/mp2t/continuity_counter.h"
#include "packager/media/formats/mp2t/ts_packet_writer_util.h"
#include "packager/media/formats/mp4/aac_audio_specific_config.h"
namespace edash_packager {
namespace media {
namespace mp2t {
namespace {
// Values for version. Only 0 and 1 are necessary for the implementation.
const int kVersion1 = 1;
// Values for current_next_indicator.
const int kCurrent = 1;
const int kNext= 0;
// Program number is 16 bits but 8 bits is sufficient.
const uint8_t kProgramNumber = 0x01;
const uint8_t kProgramMapTableId = 0x02;
// Stream types.
// Clear.
const uint8_t kStreamTypeH264 = 0x1B;
const uint8_t kStreamTypeAdtsAac = 0x0F;
// Encrypted.
const uint8_t kStreamTypeEncryptedH264 = 0xDB;
const uint8_t kStreamTypeEncryptedAdtsAac = 0xCF;
// Table for CRC32/MPEG2.
const uint32_t kCrcTable[] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
};
// Note there are dozens of CRCs. This is one of them.
// http://reveng.sourceforge.net/crc-catalogue/all.htm
uint32_t Crc32Mpeg2(const uint8_t* data, size_t data_size) {
uint32_t crc = 0xFFFFFFFF;
for (size_t i = 0; i < data_size; ++i) {
crc = kCrcTable[((crc >> 24) ^ data[i]) & 0xFF] ^ (crc << 8);
}
return crc;
}
// For all the pointer fields in the PMTs, they are not really part of the PMT
// but it's there so that an extra buffer isn't required to prepend the 0x00
// byte.
// PMT for H264 clear segments.
// Note that this is version 0, so it only works for clear lead or clear stream.
const uint8_t kPmtH264[] = {
0x00, // pointer field
kProgramMapTableId,
0xB0, // assumes length is <= 256 bytes.
0x12, // length of the rest of this array.
0x00, kProgramNumber,
0xC1, // version 0, current next indicator 1.
0x00, // section number
0x00, // last section number.
0xE0, // first 3 bits reserved.
// PCR PID is the elementary streams PID.
ProgramMapTableWriter::kElementaryPid,
0xF0, // first 4 bits reserved.
0x00, // No descriptor at this level.
// stream_type -> PID.
kStreamTypeH264, 0xE0, ProgramMapTableWriter::kElementaryPid,
0xF0, 0x00, // Es_info_length is 0.
// CRC32.
0x43, 0x49, 0x97, 0xBE,
};
// PMT for AAC clear segments.
// Note that this is version 0, so it only works for clear lead or clear stream.
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, kProgramNumber,
0xC1, // version 0, current next indicator 1.
0x00, // section number
0x00, // last section number.
0xE0, // first 3 bits reserved.
// PCR PID is the elementary streams PID.
ProgramMapTableWriter::kElementaryPid,
0xF0, // first 4 bits reserved.
0x00, // No descriptor at this level.
// stream_type -> PID.
kStreamTypeAdtsAac, 0xE0, ProgramMapTableWriter::kElementaryPid,
0xF0, 0x00, // Es_info_length is 0.
// CRC32.
0xE0, 0x6F, 0x1A, 0x31,
};
// private_data_indicator for SAMPLE-AES H264. This is the same for all H264
// streams.
const uint8_t kPrivateDataIndicatorDescriptorEncryptedH264[] = {
0x0F, // descriptor_tag.
0x04, // Length of the rest of this descriptor.
// 'zavc'.
0x7A, 0x61, 0x76, 0x63,
};
void WritePmtToBuffer(const uint8_t* pmt,
int pmt_size,
ContinuityCounter* continuity_counter,
BufferWriter* writer) {
const bool kPayloadUnitStartIndicator = true;
const bool kHasPcr = true;
const uint64_t kAnyPcrBase = 0;
WritePayloadToBufferWriter(pmt, pmt_size, kPayloadUnitStartIndicator,
ProgramMapTableWriter::kPmtPid, !kHasPcr,
kAnyPcrBase, continuity_counter, writer);
}
void WritePrivateDataIndicatorDescriptor(FourCC fourcc, BufferWriter* output) {
const uint8_t kPrivateDataIndicatorDescriptor = 15;
output->AppendInt(kPrivateDataIndicatorDescriptor);
output->AppendInt(static_cast<uint8_t>(sizeof(FOURCC_aacd)));
output->AppendInt(FOURCC_aacd);
}
bool WriteAacAudioSetupInformation(const uint8_t* aac_audio_specific_config,
size_t aac_audio_specific_config_size,
BufferWriter* audio_setup_information) {
mp4::AACAudioSpecificConfig config;
const bool result = config.Parse(std::vector<uint8_t>(
aac_audio_specific_config,
aac_audio_specific_config + aac_audio_specific_config_size));
if (!result) {
LOG(WARNING) << "Failed to parse config. Assuming AAC-LC.";
return false;
}
const uint8_t kAacLc = 2;
const uint8_t kAacHeV1 = 5;
const uint8_t kAacHeV2 = 29;
uint8_t audio_object_type = 2;
audio_object_type = config.audio_object_type();
switch (audio_object_type) {
case kAacLc:
audio_setup_information->AppendInt(FOURCC_zaac);
break;
case kAacHeV1:
audio_setup_information->AppendInt(FOURCC_zach);
break;
case kAacHeV2:
audio_setup_information->AppendInt(FOURCC_zacp);
break;
default:
LOG(ERROR) << "Unknown object type for aac " << audio_object_type;
return false;
}
// Priming. Since no info from encoder, set it to 0x0000.
audio_setup_information->AppendInt(static_cast<uint16_t>(0x0000));
// Version is always 0x01.
audio_setup_information->AppendInt(static_cast<uint8_t>(0x01));
audio_setup_information->AppendInt(
static_cast<uint8_t>(aac_audio_specific_config_size));
audio_setup_information->AppendArray(aac_audio_specific_config,
aac_audio_specific_config_size);
return true;
}
bool WriteRegistrationDescriptorForEncryptedAudio(const uint8_t* setup_data,
size_t setup_data_size,
BufferWriter* output) {
const uint8_t kRegistrationDescriptor = 5;
BufferWriter audio_setup_information;
if (!WriteAacAudioSetupInformation(setup_data, setup_data_size,
&audio_setup_information)) {
return false;
}
output->AppendInt(kRegistrationDescriptor);
// Length of the rest of this descriptor is size of audio_setup_information +
// 4 bytes (for 'apad').
output->AppendInt(static_cast<uint8_t>(audio_setup_information.Size() +
sizeof(FOURCC_apad)));
output->AppendInt(FOURCC_apad);
output->AppendBuffer(audio_setup_information);
return true;
}
void WritePmtWithParameters(uint8_t stream_type,
int version,
int current_next_indicator,
const uint8_t* descriptors,
size_t descriptors_size,
ContinuityCounter* continuity_counter,
BufferWriter* output) {
DCHECK(current_next_indicator == kCurrent || current_next_indicator == kNext);
// Body starting from program number.
BufferWriter pmt_body;
pmt_body.AppendInt(static_cast<uint8_t>(0x00));
pmt_body.AppendInt(kProgramNumber);
// resevered bits then version and current_next_indicator.
pmt_body.AppendInt(
static_cast<uint8_t>(0xC0 |
static_cast<uint8_t>(version) << 1 |
static_cast<uint8_t>(current_next_indicator)));
// section number.
pmt_body.AppendInt(static_cast<uint8_t>(0x00));
// last section number.
pmt_body.AppendInt(static_cast<uint8_t>(0x00));
// first 3 bits reserved. Rest is unused bits for PCR PID.
pmt_body.AppendInt(static_cast<uint8_t>(0xE0));
pmt_body.AppendInt(ProgramMapTableWriter::kElementaryPid);
// First 4 bits are reserved. Next 12 bits is program_info_length which is 0.
pmt_body.AppendInt(static_cast<uint8_t>(0xF0));
pmt_body.AppendInt(static_cast<uint8_t>(0x00));
pmt_body.AppendInt(stream_type);
// 3 reserved bits followed by 13 bit elementary_PID.
pmt_body.AppendInt(static_cast<uint8_t>(0xE0));
pmt_body.AppendInt(ProgramMapTableWriter::kElementaryPid);
// 4 reserved bits followed by ES_info_length.
pmt_body.AppendInt(static_cast<uint16_t>(0xF000 | descriptors_size));
pmt_body.AppendArray(descriptors, descriptors_size);
// The whole PMT has 3 bytes before the body and 4 more bytes for CRC. This
// also includes pointer field (1 byte) so + 8 in total.
BufferWriter pmt(pmt_body.Size() + 8);
// Pointer field.
pmt.AppendInt(static_cast<uint8_t>(0x00));
// PMT table ID is always 2.
pmt.AppendInt(static_cast<uint8_t>(0x02));
// First four bits must be '1011'. +4 for CRC.
pmt.AppendInt(static_cast<uint16_t>(0xB000 | (pmt_body.Size() + 4)));
pmt.AppendBuffer(pmt_body);
// Don't include the pointer field.
const uint32_t crc = Crc32Mpeg2(pmt.Buffer() + 1, pmt.Size() - 1);
pmt.AppendInt(crc);
WritePmtToBuffer(pmt.Buffer(), pmt.Size(), continuity_counter, output);
}
} // namespace
ProgramMapTableWriter::ProgramMapTableWriter() {}
ProgramMapTableWriter::~ProgramMapTableWriter() {}
H264ProgramMapTableWriter::H264ProgramMapTableWriter(
ContinuityCounter* continuity_counter)
: continuity_counter_(continuity_counter) {
DCHECK(continuity_counter);
}
H264ProgramMapTableWriter::~H264ProgramMapTableWriter() {}
bool H264ProgramMapTableWriter::ClearLeadSegmentPmt(BufferWriter* writer) {
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, kVersion1, kCurrent,
kPrivateDataIndicatorDescriptorEncryptedH264,
arraysize(kPrivateDataIndicatorDescriptorEncryptedH264),
continuity_counter_, writer);
return true;
}
bool H264ProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
WritePmtToBuffer(kPmtH264, arraysize(kPmtH264), continuity_counter_, writer);
return true;
}
AacProgramMapTableWriter::AacProgramMapTableWriter(
const std::vector<uint8_t>& aac_audio_specific_config,
ContinuityCounter* continuity_counter)
: aac_audio_specific_config_(aac_audio_specific_config),
continuity_counter_(continuity_counter) {
DCHECK(!aac_audio_specific_config.empty());
DCHECK(continuity_counter_);
}
AacProgramMapTableWriter::~AacProgramMapTableWriter() {}
// TODO(rkuroiwa): Cache the PMT for encrypted segments, it doesn't need to
// be recalculated.
bool AacProgramMapTableWriter::ClearLeadSegmentPmt(BufferWriter* writer) {
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(kVersion1, kCurrent, writer);
}
bool AacProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
WritePmtToBuffer(kPmtAac, arraysize(kPmtAac), continuity_counter_, writer);
return true;
}
bool AacProgramMapTableWriter::EncryptedSegmentPmtWithParameters(
int version,
int current_next_indicator,
BufferWriter* writer) {
// -12 because there are 12 bytes between 'descriptor_length' in
// registartion_descriptor and 'setup_data_length' in audio_setup_information.
if (aac_audio_specific_config_.size() >
std::numeric_limits<uint8_t>::max() - 12) {
LOG(ERROR) << "AACAudioSpecificConfig of size: "
<< aac_audio_specific_config_.size()
<< " will not fit in the descriptor.";
return false;
}
BufferWriter descriptors;
WritePrivateDataIndicatorDescriptor(FOURCC_aacd, &descriptors);
if (!WriteRegistrationDescriptorForEncryptedAudio(
aac_audio_specific_config_.data(), aac_audio_specific_config_.size(),
&descriptors)) {
return false;
}
WritePmtWithParameters(
kStreamTypeEncryptedAdtsAac, version, current_next_indicator,
descriptors.Buffer(), descriptors.Size(), continuity_counter_, writer);
return true;
}
} // namespace mp2t
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,100 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef PACKAGER_MEDIA_FORMATS_MP2T_PROGRAM_MAP_TABLE_WRITER_H_
#define PACKAGER_MEDIA_FORMATS_MP2T_PROGRAM_MAP_TABLE_WRITER_H_
#include <stdint.h>
#include <vector>
#include "packager/base/macros.h"
namespace edash_packager {
namespace media {
class BufferWriter;
namespace mp2t {
class ContinuityCounter;
/// Puts PMT into TS packets and writes them to buffer.
/// Note that this does not currently allow encryption without clear lead.
class ProgramMapTableWriter {
public:
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.
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.
virtual bool ClearSegmentPmt(BufferWriter* writer) = 0;
// The pid can be 13 bits long but 8 bits is sufficient for this library.
// This is the minimum PID that can be used for PMT.
static const uint8_t kPmtPid = 0x20;
// This is arbitrary number that is not reserved by the spec.
static const uint8_t kElementaryPid = 0x50;
};
/// <em>This is not a general purpose PMT writer. This is intended to be used by
/// TsWriter.</em>
class H264ProgramMapTableWriter : public ProgramMapTableWriter {
public:
explicit H264ProgramMapTableWriter(ContinuityCounter* continuity_counter);
~H264ProgramMapTableWriter() override;
bool ClearLeadSegmentPmt(BufferWriter* writer) override;
bool EncryptedSegmentPmt(BufferWriter* writer) override;
bool ClearSegmentPmt(BufferWriter* writer) override;
private:
ContinuityCounter* const continuity_counter_;
DISALLOW_COPY_AND_ASSIGN(H264ProgramMapTableWriter);
};
// TODO(rkuroiwa): For now just handle AAC, we would want AudioProgramMapTable
// later when we support other audio codecs.
/// <em>This is not a general purpose PMT writer. This is intended to be used by
/// TsWriter.</em>
class AacProgramMapTableWriter : public ProgramMapTableWriter {
public:
AacProgramMapTableWriter(
const std::vector<uint8_t>& aac_audio_specific_config,
ContinuityCounter* continuity_counter);
~AacProgramMapTableWriter() override;
bool ClearLeadSegmentPmt(BufferWriter* writer) override;
bool EncryptedSegmentPmt(BufferWriter* writer) override;
bool ClearSegmentPmt(BufferWriter* writer) override;
private:
bool EncryptedSegmentPmtWithParameters(int version,
int current_next_indicator,
BufferWriter* writer);
const std::vector<uint8_t> aac_audio_specific_config_;
ContinuityCounter* const continuity_counter_;
DISALLOW_COPY_AND_ASSIGN(AacProgramMapTableWriter);
};
} // namespace mp2t
} // namespace media
} // namespace edash_packager
#endif // PACKAGER_MEDIA_FORMATS_MP2T_PROGRAM_MAP_TABLE_WRITER_H_

View File

@ -0,0 +1,402 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gtest/gtest.h>
#include <vector>
#include "packager/media/base/buffer_writer.h"
#include "packager/media/formats/mp2t/continuity_counter.h"
#include "packager/media/formats/mp2t/program_map_table_writer.h"
namespace edash_packager {
namespace media {
namespace mp2t {
namespace {
const size_t kTsPacketSize = 188;
const uint8_t kAacBasicProfileExtraData[] = {0x12, 0x10};
} // namespace
class ProgramMapTableWriterTest : public ::testing::Test {
protected:
void ExpectTsPacketEqual(const uint8_t* prefix,
size_t prefix_size,
int padding_length,
const uint8_t* suffix,
size_t suffix_size,
const uint8_t* actual) {
std::vector<uint8_t> actual_prefix(actual, actual + prefix_size);
EXPECT_EQ(std::vector<uint8_t>(prefix, prefix + prefix_size),
actual_prefix);
// Padding until the payload.
for (size_t i = prefix_size; i < kTsPacketSize - suffix_size; ++i) {
EXPECT_EQ(0xFF, actual[i]) << "at index " << i;
}
std::vector<uint8_t> actual_suffix(actual + prefix_size + padding_length,
actual + kTsPacketSize);
EXPECT_EQ(std::vector<uint8_t>(suffix, suffix + suffix_size),
actual_suffix);
}
};
TEST_F(ProgramMapTableWriterTest, ClearH264) {
ContinuityCounter counter;
H264ProgramMapTableWriter writer(&counter);
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 int kExpectedPmtPrefixSize = arraysize(kExpectedPmtPrefix);
const uint8_t kPmtH264[] = {
0x00, // pointer field
0x02,
0xB0, // assumes length is <= 256 bytes.
0x12, // length of the rest of this array.
0x00, 0x01,
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,
};
ASSERT_EQ(kTsPacketSize, buffer.Size());
EXPECT_NO_FATAL_FAILURE(
ExpectTsPacketEqual(kExpectedPmtPrefix, kExpectedPmtPrefixSize, 160,
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));
}
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.
0xC3, // version 1, 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.
0xAF, 0xCC, 0x24, 0x21,
};
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
kPmtEncryptedH264Prefix, arraysize(kPmtEncryptedH264Prefix), 154,
kPmtEncryptedH264, arraysize(kPmtEncryptedH264), buffer.Buffer()));
}
TEST_F(ProgramMapTableWriterTest, ClearAac) {
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.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 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,
};
EXPECT_NO_FATAL_FAILURE(
ExpectTsPacketEqual(kExpectedPmtPrefix, arraysize(kExpectedPmtPrefix),
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));
}
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) {
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.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.
0xC3, // version 1, 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.
0xC6, 0xB3, 0x31, 0x3A,
};
EXPECT_NO_FATAL_FAILURE(ExpectTsPacketEqual(
kPmtEncryptedAacPrefix, arraysize(kPmtEncryptedAacPrefix), 138,
kPmtEncryptedAac, arraysize(kPmtEncryptedAac),
buffer.Buffer()));
}
} // namespace mp2t
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,165 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/formats/mp2t/ts_packet_writer_util.h"
#include "packager/base/logging.h"
#include "packager/media/base/buffer_writer.h"
#include "packager/media/formats/mp2t/continuity_counter.h"
namespace edash_packager {
namespace media {
namespace mp2t {
namespace {
const int kPcrFieldsSize = 6;
const uint8_t kSyncByte = 0x47;
// This is the size of the first few fields in a TS packet, i.e. TS packet size
// without adaptation field or the payload.
const int kTsPacketHeaderSize = 4;
const int kTsPacketSize = 188;
const int kTsPacketMaximumPayloadSize =
kTsPacketSize - kTsPacketHeaderSize;
// Used for adaptation field padding bytes.
const uint8_t kPaddingBytes[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
static_assert(arraysize(kPaddingBytes) >= kTsPacketMaximumPayloadSize,
"Padding array is not big enough.");
// |remaining_data_size| is the amount of data that has to be written. This may
// be bigger than a TS packet size.
// |remaining_data_size| matters if it is short and requires padding.
void WriteAdaptationField(bool has_pcr,
uint64_t pcr_base,
size_t remaining_data_size,
BufferWriter* writer) {
// Special case where a TS packet requires 1 byte padding.
if (!has_pcr && remaining_data_size == kTsPacketMaximumPayloadSize - 1) {
writer->AppendInt(static_cast<uint8_t>(0));
return;
}
// The size of the field itself.
const int kAdaptationFieldLengthSize = 1;
// The size of all leading flags (not including the adaptation_field_length).
const int kAdaptationFieldHeaderSize = 1;
int adaptation_field_length =
kAdaptationFieldHeaderSize + (has_pcr ? kPcrFieldsSize : 0);
if (remaining_data_size < kTsPacketMaximumPayloadSize) {
const int current_ts_size = kTsPacketHeaderSize + remaining_data_size +
adaptation_field_length +
kAdaptationFieldLengthSize;
if (current_ts_size < kTsPacketSize) {
adaptation_field_length += kTsPacketSize - current_ts_size;
}
}
writer->AppendInt(static_cast<uint8_t>(adaptation_field_length));
int remaining_bytes = adaptation_field_length;
writer->AppendInt(static_cast<uint8_t>(
// All flags except PCR_flag are 0.
static_cast<uint8_t>(has_pcr) << 4));
remaining_bytes -= 1;
if (has_pcr) {
// program_clock_reference_extension = 0.
const uint32_t most_significant_32bits_pcr =
static_cast<uint32_t>(pcr_base >> 1);
const uint16_t pcr_last_bit_reserved_and_pcr_extension =
((pcr_base & 1) << 15);
writer->AppendInt(most_significant_32bits_pcr);
writer->AppendInt(pcr_last_bit_reserved_and_pcr_extension);
remaining_bytes -= kPcrFieldsSize;
}
DCHECK_GE(remaining_bytes, 0);
if (remaining_bytes == 0)
return;
DCHECK_GE(static_cast<int>(arraysize(kPaddingBytes)), remaining_bytes);
writer->AppendArray(kPaddingBytes, remaining_bytes);
}
} // namespace
void WritePayloadToBufferWriter(const uint8_t* payload,
size_t payload_size,
bool payload_unit_start_indicator,
int pid,
bool has_pcr,
uint64_t pcr_base,
ContinuityCounter* continuity_counter,
BufferWriter* writer) {
size_t payload_bytes_written = 0;
do {
const bool must_write_adaptation_header = has_pcr;
const size_t bytes_left = payload_size - payload_bytes_written;
const bool has_adaptation_field = must_write_adaptation_header ||
bytes_left < kTsPacketMaximumPayloadSize;
writer->AppendInt(kSyncByte);
writer->AppendInt(static_cast<uint16_t>(
// transport_error_indicator and transport_priority are both '0'.
static_cast<int>(payload_unit_start_indicator) << 14 | pid));
const uint8_t adaptation_field_control =
((has_adaptation_field ? 1 : 0) << 1) | ((bytes_left != 0) ? 1 : 0);
// transport_scrambling_control is '00'.
writer->AppendInt(static_cast<uint8_t>(adaptation_field_control << 4 |
continuity_counter->GetNext()));
if (has_adaptation_field) {
const size_t before = writer->Size();
WriteAdaptationField(has_pcr, pcr_base, bytes_left, writer);
const size_t bytes_for_adaptation_field = writer->Size() - before;
const int write_bytes =
kTsPacketMaximumPayloadSize - bytes_for_adaptation_field;
writer->AppendArray(payload + payload_bytes_written, write_bytes);
payload_bytes_written += write_bytes;
} else {
writer->AppendArray(payload + payload_bytes_written,
kTsPacketMaximumPayloadSize);
payload_bytes_written += kTsPacketMaximumPayloadSize;
}
// Once written, not needed for this payload.
has_pcr = false;
payload_unit_start_indicator = false;
} while (payload_bytes_written < payload_size);
}
} // namespace mp2t
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,47 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// This file contains utility functions that help write TS packets to buffer.
#ifndef PACKAGER_MEDIA_FORMATS_MP2T_TS_WRITER_UTIL_H_
#define PACKAGER_MEDIA_FORMATS_MP2T_TS_WRITER_UTIL_H_
#include <stddef.h>
#include <stdint.h>
namespace edash_packager {
namespace media {
class BufferWriter;
namespace mp2t {
class ContinuityCounter;
/// General purpose TS packet writing function. The output goes to @a output.
/// @param payload can be any payload. Most likely raw PSI tables or PES packet
/// payload.
/// @param payload_size is the size of payload.
/// @param payload_unit_start_indicator is the same as the definition in spec.
/// @param pid is the same the same defition in the spec.
/// @param has_pcr is true if @a pcr_base should be used.
/// @param pcr_base is the PCR_base value in the spec.
/// @param continuity_counter is the continuity_counter for this TS packet.
/// @param output is where the TS packets get written.
void WritePayloadToBufferWriter(const uint8_t* payload,
size_t payload_size,
bool payload_unit_start_indicator,
int pid,
bool has_pcr,
uint64_t pcr_base,
ContinuityCounter* continuity_counter,
BufferWriter* output);
} // namespace mp2t
} // namespace media
} // namespace edash_packager
#endif // PACKAGER_MEDIA_FORMATS_MP2T_TS_WRITER_UTIL_H_

View File

@ -30,7 +30,7 @@ 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))
if (!ts_writer_->Initialize(stream_info, false))
return Status(error::MUXER_FAILURE, "Failed to initialize TsWriter.");
if (!pes_packet_generator_->Initialize(stream_info)) {
return Status(error::MUXER_FAILURE,

View File

@ -63,7 +63,8 @@ class MockPesPacketGenerator : public PesPacketGenerator {
class MockTsWriter : public TsWriter {
public:
MOCK_METHOD1(Initialize, bool(const StreamInfo& stream_info));
MOCK_METHOD2(Initialize,
bool(const StreamInfo& stream_info, bool will_be_encrypted));
MOCK_METHOD1(NewSegment, bool(const std::string& file_name));
MOCK_METHOD0(FinalizeSegment, bool());
@ -97,7 +98,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));
@ -118,7 +119,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));
@ -180,7 +181,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));
@ -278,7 +279,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));
@ -307,7 +308,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));
@ -336,7 +337,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));

View File

@ -13,6 +13,7 @@
#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/formats/mp2t/ts_packet_writer_util.h"
namespace edash_packager {
namespace media {
@ -20,32 +21,12 @@ namespace mp2t {
namespace {
enum Pid : uint8_t {
// The pid can be 13 bits long but 8 bits is sufficient for this library.
// This is the minimum PID that can be used for PMT.
kPmtPid = 0x20,
// This is arbitrary number that is not reserved by the spec.
kElementaryPid = 0x50,
};
// Program number is 16 bits but 8 bits is sufficient.
const uint8_t kProgramNumber = 0x01;
const uint8_t kStreamTypeH264 = 0x1B;
const uint8_t kStreamTypeAdtsAac = 0x0F;
// For all the pointer fields in the following PAT and PMTs, they are not really
// part of PAT or PMT but it's there so that TsPacket can point to a memory
// location that starts from pointer field.
const uint8_t kProgramAssociationTableId = 0x00;
const uint8_t kProgramMapTableId = 0x02;
// TODO(rkuroiwa):
// Once encryption is added, another PAT must be used for the encrypted portion
// e.g. version number set to 1.
// But this works for clear lead and for clear segments.
// Write PSI generator.
// This PAT can be used for both encrypted and clear.
const uint8_t kPat[] = {
0x00, // pointer field
kProgramAssociationTableId,
@ -58,57 +39,14 @@ const uint8_t kPat[] = {
// program number -> PMT PID mapping.
0x00, 0x01, // program number is 1.
0xE0, // first 3 bits is reserved.
kPmtPid,
ProgramMapTableWriter::kPmtPid,
// CRC32.
0xF9, 0x62, 0xF5, 0x8B,
};
// Like PAT, with encryption different PMTs are required.
// It might make sense to add a PmtGenerator class.
const uint8_t kPmtH264[] = {
0x00, // pointer field
kProgramMapTableId,
0xB0, // assumes length is <= 256 bytes.
0x12, // length of the rest of this array.
0x00, kProgramNumber,
0xC1, // version 0, current next indicator 1.
0x00, // section number
0x00, // last section number.
0xE0, // first 3 bits reserved.
kElementaryPid, // PCR PID is the elementary streams PID.
0xF0, // first 4 bits reserved.
0x00, // No descriptor at this level.
kStreamTypeH264, 0xE0, kElementaryPid, // stream_type -> PID.
0xF0, 0x00, // Es_info_length is 0.
// CRC32.
0x43, 0x49, 0x97, 0xBE,
};
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, kProgramNumber,
0xC1, // version 0, current next indicator 1.
0x00, // section number
0x00, // last section number.
0xE0, // first 3 bits reserved.
kElementaryPid, // PCR PID is the elementary streams PID.
0xF0, // first 4 bits reserved.
0x00, // No descriptor at this level.
kStreamTypeAdtsAac, 0xE0, kElementaryPid, // stream_type -> PID.
0xF0, 0x00, // Es_info_length is 0.
// CRC32.
0xE0, 0x6F, 0x1A, 0x31,
};
const bool kHasPcr = true;
const bool kPayloadUnitStartIndicator = true;
const uint8_t kSyncByte = 0x47;
const int kPcrFieldsSize = 6;
// This is the size of the first few fields in a TS packet, i.e. TS packet size
// without adaptation field or the payload.
const int kTsPacketHeaderSize = 4;
@ -118,162 +56,13 @@ const int kTsPacketMaximumPayloadSize =
const size_t kMaxPesPacketLengthValue = 0xFFFF;
// Used for adaptation field padding bytes.
const uint8_t kPaddingBytes[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
};
static_assert(arraysize(kPaddingBytes) >= kTsPacketMaximumPayloadSize,
"Padding array is not big enough.");
// |remaining_data_size| is the amount of data that has to be written. This may
// be bigger than a TS packet size.
// |remaining_data_size| matters if it is short and requires padding.
void WriteAdaptationField(bool has_pcr,
uint64_t pcr_base,
size_t remaining_data_size,
BufferWriter* writer) {
// Special case where a TS packet requires 1 byte padding.
if (!has_pcr && remaining_data_size == kTsPacketMaximumPayloadSize - 1) {
writer->AppendInt(static_cast<uint8_t>(0));
return;
}
// The size of the field itself.
const int kAdaptationFieldLengthSize = 1;
// The size of all leading flags (not including the adaptation_field_length).
const int kAdaptationFieldHeaderSize = 1;
int adaptation_field_length =
kAdaptationFieldHeaderSize + (has_pcr ? kPcrFieldsSize : 0);
if (remaining_data_size < kTsPacketMaximumPayloadSize) {
const int current_ts_size = kTsPacketHeaderSize + remaining_data_size +
adaptation_field_length +
kAdaptationFieldLengthSize;
if (current_ts_size < kTsPacketSize) {
adaptation_field_length += kTsPacketSize - current_ts_size;
}
}
writer->AppendInt(static_cast<uint8_t>(adaptation_field_length));
int remaining_bytes = adaptation_field_length;
writer->AppendInt(static_cast<uint8_t>(
// All flags except PCR_flag are 0.
static_cast<uint8_t>(has_pcr) << 4));
remaining_bytes -= 1;
if (has_pcr) {
// program_clock_reference_extension = 0.
const uint32_t most_significant_32bits_pcr =
static_cast<uint32_t>(pcr_base >> 1);
const uint16_t pcr_last_bit_reserved_and_pcr_extension =
((pcr_base & 1) << 15);
writer->AppendInt(most_significant_32bits_pcr);
writer->AppendInt(pcr_last_bit_reserved_and_pcr_extension);
remaining_bytes -= kPcrFieldsSize;
}
DCHECK_GE(remaining_bytes, 0);
if (remaining_bytes == 0)
return;
DCHECK_GE(static_cast<int>(arraysize(kPaddingBytes)), remaining_bytes);
writer->AppendArray(kPaddingBytes, remaining_bytes);
}
// |payload| can be any payload. Most likely raw PSI tables or PES packet
// payload.
void WritePayloadToBufferWriter(const uint8_t* payload,
size_t payload_size,
bool payload_unit_start_indicator,
int pid,
bool has_pcr,
uint64_t pcr_base,
ContinuityCounter* continuity_counter,
BufferWriter* writer) {
size_t payload_bytes_written = 0;
do {
const bool must_write_adaptation_header = has_pcr;
const size_t bytes_left = payload_size - payload_bytes_written;
const bool has_adaptation_field = must_write_adaptation_header ||
bytes_left < kTsPacketMaximumPayloadSize;
writer->AppendInt(kSyncByte);
writer->AppendInt(static_cast<uint16_t>(
// transport_error_indicator and transport_priority are both '0'.
static_cast<int>(payload_unit_start_indicator) << 14 | pid));
const uint8_t adaptation_field_control =
((has_adaptation_field ? 1 : 0) << 1) | ((bytes_left != 0) ? 1 : 0);
// transport_scrambling_control is '00'.
writer->AppendInt(static_cast<uint8_t>(adaptation_field_control << 4 |
continuity_counter->GetNext()));
if (has_adaptation_field) {
const size_t before = writer->Size();
WriteAdaptationField(has_pcr, pcr_base, bytes_left, writer);
const size_t bytes_for_adaptation_field = writer->Size() - before;
const int write_bytes =
kTsPacketMaximumPayloadSize - bytes_for_adaptation_field;
writer->AppendArray(payload + payload_bytes_written, write_bytes);
payload_bytes_written += write_bytes;
} else {
writer->AppendArray(payload + payload_bytes_written,
kTsPacketMaximumPayloadSize);
payload_bytes_written += kTsPacketMaximumPayloadSize;
}
// Once written, not needed for this payload.
has_pcr = false;
payload_unit_start_indicator = false;
} while (payload_bytes_written < payload_size);
}
void WritePatPmtToBuffer(const uint8_t* data,
int data_size,
int pid,
ContinuityCounter* continuity_counter,
BufferWriter* writer) {
WritePayloadToBufferWriter(data, data_size, kPayloadUnitStartIndicator, pid,
!kHasPcr, 0, continuity_counter, writer);
}
void WritePatToBuffer(const uint8_t* pat,
int pat_size,
ContinuityCounter* continuity_counter,
BufferWriter* writer) {
const int kPatPid = 0;
WritePatPmtToBuffer(pat, pat_size, kPatPid, continuity_counter, writer);
}
void WritePmtToBuffer(const uint8_t* pmt,
int pmt_size,
ContinuityCounter* continuity_counter,
BufferWriter* writer) {
WritePatPmtToBuffer(pmt, pmt_size, kPmtPid, continuity_counter, writer);
WritePayloadToBufferWriter(pat, pat_size, kPayloadUnitStartIndicator, kPatPid,
!kHasPcr, 0, continuity_counter, writer);
}
// The only difference between writing PTS or DTS is the leading bits.
@ -310,7 +99,7 @@ bool WritePesToFile(const PesPacket& pes,
kTsPacketMaximumPayloadSize - kAdaptationFieldLengthSize -
kAdaptationFieldHeaderSize - kPcrFieldSize;
const uint64_t pcr_base = pes.has_dts() ? pes.dts() : pes.pts();
const int pid = kElementaryPid;
const int pid = ProgramMapTableWriter::kElementaryPid;
// This writer will hold part of PES packet after PES_packet_length field.
BufferWriter pes_header_writer;
@ -370,27 +159,11 @@ bool WritePesToFile(const PesPacket& pes,
} // namespace
ContinuityCounter::ContinuityCounter() {}
ContinuityCounter::~ContinuityCounter() {}
int ContinuityCounter::GetNext() {
int ret = counter_;
++counter_;
counter_ %= 16;
return ret;
}
TsWriter::TsWriter() {}
TsWriter::~TsWriter() {}
bool TsWriter::Initialize(const StreamInfo& stream_info) {
// This buffer will hold PMT data after section_length field so that this
// can be used to get the section_length.
time_scale_ = stream_info.time_scale();
if (time_scale_ == 0) {
LOG(ERROR) << "Timescale is 0.";
return false;
}
bool TsWriter::Initialize(const StreamInfo& stream_info,
bool will_be_encrypted) {
const StreamType stream_type = stream_info.stream_type();
if (stream_type != StreamType::kStreamVideo &&
stream_type != StreamType::kStreamAudio) {
@ -399,8 +172,6 @@ bool TsWriter::Initialize(const StreamInfo& stream_info) {
return false;
}
const uint8_t* pmt = nullptr;
size_t pmt_size = 0u;
if (stream_info.stream_type() == StreamType::kStreamVideo) {
const VideoStreamInfo& video_stream_info =
static_cast<const VideoStreamInfo&>(stream_info);
@ -409,8 +180,7 @@ bool TsWriter::Initialize(const StreamInfo& stream_info) {
<< video_stream_info.codec() << " yet.";
return false;
}
pmt = kPmtH264;
pmt_size = arraysize(kPmtH264);
pmt_writer_.reset(new H264ProgramMapTableWriter(&pmt_continuity_counter_));
} else {
DCHECK_EQ(stream_type, StreamType::kStreamAudio);
const AudioStreamInfo& audio_stream_info =
@ -420,24 +190,15 @@ bool TsWriter::Initialize(const StreamInfo& stream_info) {
<< audio_stream_info.codec() << " yet.";
return false;
}
pmt = kPmtAac;
pmt_size = arraysize(kPmtAac);
pmt_writer_.reset(new AacProgramMapTableWriter(
audio_stream_info.extra_data(), &pmt_continuity_counter_));
}
DCHECK(pmt);
DCHECK_GT(pmt_size, 0u);
// Most likely going to fit in 2 TS packets.
BufferWriter psi_writer(kTsPacketSize * 2);
WritePatToBuffer(kPat, arraysize(kPat), &pat_continuity_counter_,
&psi_writer);
WritePmtToBuffer(pmt, pmt_size, &pmt_continuity_counter_, &psi_writer);
psi_writer.SwapBuffer(&psi_ts_packets_);
will_be_encrypted_ = will_be_encrypted;
return true;
}
bool TsWriter::NewSegment(const std::string& file_name) {
DCHECK(!psi_ts_packets_.empty());
if (current_file_) {
LOG(ERROR) << "File " << current_file_->file_name() << " still open.";
return false;
@ -448,10 +209,23 @@ bool TsWriter::NewSegment(const std::string& file_name) {
return false;
}
// TODO(kqyang): Add WriteArrayToFile().
BufferWriter psi_writer(psi_ts_packets_.size());
psi_writer.AppendVector(psi_ts_packets_);
if (!psi_writer.WriteToFile(current_file_.get()).ok()) {
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 (!pmt_writer_->EncryptedSegmentPmt(&psi)) {
return false;
}
} else {
if (!pmt_writer_->ClearSegmentPmt(&psi)) {
return false;
}
}
if (!psi.WriteToFile(current_file_.get()).ok()) {
LOG(ERROR) << "Failed to write PSI to file.";
return false;
}
@ -459,15 +233,15 @@ bool TsWriter::NewSegment(const std::string& file_name) {
return true;
}
void TsWriter::SignalEncypted() {
encrypted_ = true;
}
bool TsWriter::FinalizeSegment() {
return current_file_.release()->Close();
}
bool TsWriter::AddPesPacket(scoped_ptr<PesPacket> pes_packet) {
if (time_scale_ == 0) {
LOG(ERROR) << "Timescale is 0.";
return false;
}
DCHECK(current_file_);
if (!WritePesToFile(*pes_packet, &elementary_stream_continuity_counter_,
current_file_.get())) {
@ -479,6 +253,11 @@ bool TsWriter::AddPesPacket(scoped_ptr<PesPacket> pes_packet) {
return true;
}
void TsWriter::SetProgramMapTableWriterForTesting(
scoped_ptr<ProgramMapTableWriter> table_writer) {
pmt_writer_ = table_writer.Pass();
}
} // namespace mp2t
} // namespace media
} // namespace edash_packager

View File

@ -15,27 +15,14 @@
#include "packager/media/base/media_stream.h"
#include "packager/media/file/file.h"
#include "packager/media/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 edash_packager {
namespace media {
namespace mp2t {
class ContinuityCounter {
public:
ContinuityCounter();
~ContinuityCounter();
/// As specified by the spec, this starts from 0 and is incremented by 1 until
/// it wraps back to 0 when it reaches 16.
/// @return counter value.
int GetNext();
private:
int counter_ = 0;
DISALLOW_COPY_AND_ASSIGN(ContinuityCounter);
};
/// This class takes PesPackets, encapsulates them into TS packets, and write
/// the data to file. This also creates PSI from StreamInfo.
class TsWriter {
@ -44,14 +31,24 @@ class TsWriter {
virtual ~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);
virtual bool Initialize(const StreamInfo& stream_info,
bool will_be_encrypted);
/// 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.
/// @return true on success, false otherwise.
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.
void SignalEncypted();
/// Flush all the pending PesPackets that have not been written to file and
/// close the file.
/// @return true on success, false otherwise.
@ -63,15 +60,22 @@ class TsWriter {
/// @return true on success, false otherwise.
virtual bool AddPesPacket(scoped_ptr<PesPacket> pes_packet);
private:
std::vector<uint8_t> psi_ts_packets_;
/// Only for testing.
void SetProgramMapTableWriterForTesting(
scoped_ptr<ProgramMapTableWriter> table_writer);
uint32_t time_scale_ = 0u;
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_;
ContinuityCounter elementary_stream_continuity_counter_;
scoped_ptr<ProgramMapTableWriter> pmt_writer_;
scoped_ptr<File, FileCloser> current_file_;
DISALLOW_COPY_AND_ASSIGN(TsWriter);

View File

@ -4,15 +4,21 @@
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "packager/base/files/file_path.h"
#include "packager/base/files/file_util.h"
#include "packager/media/base/audio_stream_info.h"
#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/ts_writer.h"
using ::testing::InSequence;
using ::testing::Return;
using ::testing::_;
namespace edash_packager {
namespace media {
namespace mp2t {
@ -43,10 +49,57 @@ const uint32_t kSamplingFrequency = 44100;
const uint32_t kMaxBitrate = 320000;
const uint32_t kAverageBitrate = 256000;
const uint8_t kExtraData[] = {
0x01, 0x02,
// Bogus extra data.
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));
};
// This is not a real TS Packet. But is used to check that the result from the
// MockProgramMapTableWriter is used at the right place.
const uint8_t kMockPmtWriterData[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23,
0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B,
0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53,
0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83,
0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B,
0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3,
0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB,
};
ACTION(WriteOnePmt) {
BufferWriter* writer = arg0;
writer->AppendArray(kMockPmtWriterData, arraysize(kMockPmtWriterData));
return true;
}
ACTION(WriteTwoPmts) {
BufferWriter* writer = arg0;
writer->AppendArray(kMockPmtWriterData, arraysize(kMockPmtWriterData));
writer->AppendArray(kMockPmtWriterData, arraysize(kMockPmtWriterData));
return true;
}
} // namespace
class TsWriterTest : public ::testing::Test {
@ -76,9 +129,11 @@ class TsWriterTest : public ::testing::Test {
// matches with |suffix|. If there is padding, then padding_length specifies
// how long the padding is between prefix and suffix.
// |actual| must be at least 188 bytes long.
void ExpectTsPacketEqual(const uint8_t* prefix, size_t prefix_size,
void ExpectTsPacketEqual(const uint8_t* prefix,
size_t prefix_size,
int padding_length,
const uint8_t* suffix, size_t suffix_size,
const uint8_t* suffix,
size_t suffix_size,
const uint8_t* actual) {
std::vector<uint8_t> actual_prefix(actual, actual + prefix_size);
EXPECT_EQ(std::vector<uint8_t>(prefix, prefix + prefix_size),
@ -106,7 +161,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));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
}
TEST_F(TsWriterTest, InitializeVideoNonH264) {
@ -114,7 +169,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));
EXPECT_FALSE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
}
TEST_F(TsWriterTest, InitializeAudioAac) {
@ -122,7 +177,7 @@ TEST_F(TsWriterTest, InitializeAudioAac) {
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
kSampleBits, kNumChannels, kSamplingFrequency, kMaxBitrate,
kAverageBitrate, kExtraData, arraysize(kExtraData), kIsEncrypted));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
}
TEST_F(TsWriterTest, InitializeAudioNonAac) {
@ -130,30 +185,39 @@ TEST_F(TsWriterTest, InitializeAudioNonAac) {
kTrackId, kTimeScale, kDuration, AudioCodec::kCodecOpus, kCodecString,
kLanguage, kSampleBits, kNumChannels, kSamplingFrequency, kMaxBitrate,
kAverageBitrate, kExtraData, arraysize(kExtraData), kIsEncrypted));
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
EXPECT_FALSE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
}
TEST_F(TsWriterTest, NewSegment) {
// 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.
TEST_F(TsWriterTest, ClearH264Psi) {
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
new MockProgramMapTableWriter());
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).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));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
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));
// 2 TS Packets. PAT, PMT.
// 2 TS Packets one for PAT and the fake PMT data.
ASSERT_EQ(376u, content.size());
const uint8_t kExpectedPatPrefix[] = {
0x47, // Sync byte.
0x40, // payload_unit_start_indicator set.
0x00, // pid.
0x30, // Adaptation field and payload are both present. counter = 0.
0xA6, // Adaptation Field length.
0x00, // All adaptation field flags 0.
0x47, // Sync byte.
0x40, // payload_unit_start_indicator set.
0x00, // pid.
0x30, // Adaptation field and payload are both present. counter = 0.
0xA6, // Adaptation Field length.
0x00, // All adaptation field flags 0.
};
const int kExpectedPatPrefixSize = arraysize(kExpectedPatPrefix);
const uint8_t kExpectedPatPayload[] = {
@ -177,45 +241,165 @@ TEST_F(TsWriterTest, NewSegment) {
kExpectedPatPrefix, kExpectedPatPrefixSize, 165, kExpectedPatPayload,
arraysize(kExpectedPatPayload), content.data()));
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 int kExpectedPmtPrefixSize = arraysize(kExpectedPmtPrefix);
const uint8_t kPmtH264[] = {
0x00, // pointer field
0x02,
0xB0, // assumes length is <= 256 bytes.
0x12, // length of the rest of this array.
0x00, 0x01,
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(
kExpectedPmtPrefix, kExpectedPmtPrefixSize, 160, kPmtH264,
arraysize(kPmtH264), content.data() + kTsPacketSize));
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + kTsPacketSize,
kTsPacketSize));
}
TEST_F(TsWriterTest, ClearAacPmt) {
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
new MockProgramMapTableWriter());
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(WriteOnePmt());
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
kSampleBits, kNumChannels, kSamplingFrequency, kMaxBitrate,
kAverageBitrate, kAacBasicProfileExtraData,
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
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));
// 2 TS Packets. PAT, PMT.
ASSERT_EQ(376u, content.size());
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + kTsPacketSize,
kTsPacketSize));
}
// The stream is flaged with will_be_encrypted. Verify that 2 PMTs are created.
// One for clear lead and another for encrypted segments that follow.
TEST_F(TsWriterTest, ClearLeadH264Pmt) {
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
new MockProgramMapTableWriter());
EXPECT_CALL(*mock_pmt_writer, ClearLeadSegmentPmt(_))
.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));
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
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));
ASSERT_EQ(564u, content.size());
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + kTsPacketSize,
kTsPacketSize));
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + 2 * kTsPacketSize,
kTsPacketSize));
}
// Check the encrypted segments' PMT (after clear lead).
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, 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));
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
EXPECT_TRUE(ts_writer_.FinalizeSegment());
// Overwrite the file but as encrypted segment.
ts_writer_.SignalEncypted();
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));
ASSERT_EQ(376u, content.size());
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + kTsPacketSize,
kTsPacketSize));
}
// Same as ClearLeadH264Pmt but for AAC.
TEST_F(TsWriterTest, ClearLeadAacPmt) {
scoped_ptr<MockProgramMapTableWriter> mock_pmt_writer(
new MockProgramMapTableWriter());
EXPECT_CALL(*mock_pmt_writer, ClearLeadSegmentPmt(_))
.WillOnce(WriteTwoPmts());
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
kSampleBits, kNumChannels, kSamplingFrequency, kMaxBitrate,
kAverageBitrate, kAacBasicProfileExtraData,
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, kWillBeEncrypted));
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
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));
ASSERT_EQ(564u, content.size());
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + kTsPacketSize,
kTsPacketSize));
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + 2 * kTsPacketSize,
kTsPacketSize));
}
// Same as EncryptedSegmentsH264Pmt but for AAC.
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, EncryptedSegmentPmt(_)).WillOnce(WriteOnePmt());
scoped_refptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
kTrackId, kTimeScale, kDuration, kAacAudioCodec, kCodecString, kLanguage,
kSampleBits, kNumChannels, kSamplingFrequency, kMaxBitrate,
kAverageBitrate, kAacBasicProfileExtraData,
arraysize(kAacBasicProfileExtraData), kIsEncrypted));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, kWillBeEncrypted));
ts_writer_.SetProgramMapTableWriterForTesting(mock_pmt_writer.Pass());
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
EXPECT_TRUE(ts_writer_.FinalizeSegment());
// Overwrite the file but as encrypted segment.
ts_writer_.SignalEncypted();
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));
ASSERT_EQ(376u, content.size());
EXPECT_EQ(0, memcmp(kMockPmtWriterData, content.data() + kTsPacketSize,
kTsPacketSize));
}
TEST_F(TsWriterTest, AddPesPacket) {
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));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
scoped_ptr<PesPacket> pes(new PesPacket());
@ -280,7 +464,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));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
scoped_ptr<PesPacket> pes(new PesPacket());
@ -305,8 +489,8 @@ TEST_F(TsWriterTest, BigPesPacket) {
// Check continuity counter.
EXPECT_EQ(0, (content[2 * 188 + 3] & 0xF));
EXPECT_EQ(1, (content[3 * 188 + 3] & 0xF));
EXPECT_EQ(2, (content[4 * 188 + 3] & 0xF));
EXPECT_EQ(1, (content[3 * 188 + 3] & 0xF));
EXPECT_EQ(2, (content[4 * 188 + 3] & 0xF));
}
// Bug found in code review. It should check whether PTS is present not whether
@ -316,7 +500,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));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
scoped_ptr<PesPacket> pes(new PesPacket());
@ -376,7 +560,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));
EXPECT_TRUE(ts_writer_.Initialize(*stream_info, !kWillBeEncrypted));
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
scoped_ptr<PesPacket> pes(new PesPacket());