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:
parent
9bb6c5d8d2
commit
7d8322377e
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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) {
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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',
|
||||
],
|
||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue