Refactor ProgramMapTableWriter
To make it easier to support more codecs. Change-Id: Ifcde0f3d7d6f74015d723a0b74401a86c8e65a72
This commit is contained in:
parent
d66d307fc2
commit
b5a8185543
|
@ -147,6 +147,7 @@ enum FourCC : uint32_t {
|
||||||
FOURCC_zaac = 0x7A616163,
|
FOURCC_zaac = 0x7A616163,
|
||||||
FOURCC_zach = 0x7A616368,
|
FOURCC_zach = 0x7A616368,
|
||||||
FOURCC_zacp = 0x7A616370,
|
FOURCC_zacp = 0x7A616370,
|
||||||
|
FOURCC_zavc = 0x7A617663,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FourCC kAppleSampleAesProtectionScheme = FOURCC_cbca;
|
const FourCC kAppleSampleAesProtectionScheme = FOURCC_cbca;
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include "packager/media/base/buffer_writer.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
#include "packager/media/base/fourccs.h"
|
#include "packager/media/base/fourccs.h"
|
||||||
#include "packager/media/codecs/aac_audio_specific_config.h"
|
#include "packager/media/codecs/aac_audio_specific_config.h"
|
||||||
#include "packager/media/formats/mp2t/continuity_counter.h"
|
|
||||||
#include "packager/media/formats/mp2t/ts_packet_writer_util.h"
|
#include "packager/media/formats/mp2t/ts_packet_writer_util.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -119,65 +118,6 @@ uint32_t Crc32Mpeg2(const uint8_t* data, size_t data_size) {
|
||||||
return crc;
|
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,
|
void WritePmtToBuffer(const uint8_t* pmt,
|
||||||
size_t pmt_size,
|
size_t pmt_size,
|
||||||
ContinuityCounter* continuity_counter,
|
ContinuityCounter* continuity_counter,
|
||||||
|
@ -261,8 +201,7 @@ void WritePmtWithParameters(uint8_t stream_type,
|
||||||
int current_next_indicator,
|
int current_next_indicator,
|
||||||
const uint8_t* descriptors,
|
const uint8_t* descriptors,
|
||||||
size_t descriptors_size,
|
size_t descriptors_size,
|
||||||
ContinuityCounter* continuity_counter,
|
BufferWriter* pmt) {
|
||||||
BufferWriter* output) {
|
|
||||||
DCHECK(current_next_indicator == kCurrent || current_next_indicator == kNext);
|
DCHECK(current_next_indicator == kCurrent || current_next_indicator == kNext);
|
||||||
// Body starting from program number.
|
// Body starting from program number.
|
||||||
BufferWriter pmt_body;
|
BufferWriter pmt_body;
|
||||||
|
@ -291,106 +230,135 @@ void WritePmtWithParameters(uint8_t stream_type,
|
||||||
|
|
||||||
// 4 reserved bits followed by ES_info_length.
|
// 4 reserved bits followed by ES_info_length.
|
||||||
pmt_body.AppendInt(static_cast<uint16_t>(0xF000 | descriptors_size));
|
pmt_body.AppendInt(static_cast<uint16_t>(0xF000 | descriptors_size));
|
||||||
pmt_body.AppendArray(descriptors, descriptors_size);
|
if (descriptors_size > 0) {
|
||||||
|
DCHECK(descriptors);
|
||||||
|
pmt_body.AppendArray(descriptors, descriptors_size);
|
||||||
|
}
|
||||||
|
|
||||||
// The whole PMT has 3 bytes before the body and 4 more bytes for CRC. This
|
pmt->Clear();
|
||||||
// also includes pointer field (1 byte) so + 8 in total.
|
// Pointer field is not really part of the PMT but it's there so that an extra
|
||||||
BufferWriter pmt(pmt_body.Size() + 8);
|
// buffer isn't required to prepend the 0x00 byte.
|
||||||
// Pointer field.
|
const uint8_t kPointerField = 0;
|
||||||
pmt.AppendInt(static_cast<uint8_t>(0x00));
|
pmt->AppendInt(kPointerField);
|
||||||
// PMT table ID is always 2.
|
pmt->AppendInt(kProgramMapTableId);
|
||||||
pmt.AppendInt(static_cast<uint8_t>(0x02));
|
|
||||||
// First four bits must be '1011'. +4 for CRC.
|
// First four bits must be '1011'. +4 for CRC.
|
||||||
pmt.AppendInt(static_cast<uint16_t>(0xB000 | (pmt_body.Size() + 4)));
|
pmt->AppendInt(static_cast<uint16_t>(0xB000 | (pmt_body.Size() + 4)));
|
||||||
pmt.AppendBuffer(pmt_body);
|
pmt->AppendBuffer(pmt_body);
|
||||||
|
|
||||||
// Don't include the pointer field.
|
// Don't include the pointer field.
|
||||||
const uint32_t crc = Crc32Mpeg2(pmt.Buffer() + 1, pmt.Size() - 1);
|
const uint32_t crc = Crc32Mpeg2(pmt->Buffer() + 1, pmt->Size() - 1);
|
||||||
pmt.AppendInt(crc);
|
pmt->AppendInt(crc);
|
||||||
WritePmtToBuffer(pmt.Buffer(), pmt.Size(), continuity_counter, output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ProgramMapTableWriter::ProgramMapTableWriter() {}
|
ProgramMapTableWriter::ProgramMapTableWriter(Codec codec) : codec_(codec) {}
|
||||||
ProgramMapTableWriter::~ProgramMapTableWriter() {}
|
|
||||||
|
|
||||||
H264ProgramMapTableWriter::H264ProgramMapTableWriter(
|
bool ProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
||||||
ContinuityCounter* continuity_counter)
|
uint8_t stream_type;
|
||||||
: continuity_counter_(continuity_counter) {
|
switch (codec_) {
|
||||||
DCHECK(continuity_counter);
|
case kCodecH264:
|
||||||
}
|
stream_type = kStreamTypeEncryptedH264;
|
||||||
|
break;
|
||||||
|
case kCodecAAC:
|
||||||
|
stream_type = kStreamTypeEncryptedAdtsAac;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Codec " << codec_ << " is not supported in TS yet.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
H264ProgramMapTableWriter::~H264ProgramMapTableWriter() {}
|
if (encrypted_pmt_.Size() == 0) {
|
||||||
|
BufferWriter descriptors;
|
||||||
|
if (!WriteDescriptors(&descriptors))
|
||||||
|
return false;
|
||||||
|
|
||||||
bool H264ProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
const bool has_clear_lead = clear_pmt_.Size() > 0;
|
||||||
WritePmtWithParameters(
|
WritePmtWithParameters(stream_type, has_clear_lead ? kVersion1 : kVersion0,
|
||||||
kStreamTypeEncryptedH264, has_clear_lead_ ? kVersion1 : kVersion0,
|
kCurrent, descriptors.Buffer(), descriptors.Size(),
|
||||||
kCurrent, kPrivateDataIndicatorDescriptorEncryptedH264,
|
&encrypted_pmt_);
|
||||||
arraysize(kPrivateDataIndicatorDescriptorEncryptedH264),
|
DCHECK_NE(encrypted_pmt_.Size(), 0u);
|
||||||
continuity_counter_, writer);
|
}
|
||||||
|
WritePmtToBuffer(encrypted_pmt_.Buffer(), encrypted_pmt_.Size(),
|
||||||
|
&continuity_counter_, writer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool H264ProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
bool ProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
||||||
has_clear_lead_ = true;
|
uint8_t stream_type;
|
||||||
WritePmtToBuffer(kPmtH264, arraysize(kPmtH264), continuity_counter_, writer);
|
switch (codec_) {
|
||||||
// Cannot insert PMT for following encrypted segments because
|
case kCodecH264:
|
||||||
// some players consider encrypted segments as "zavc" codec which is different
|
stream_type = kStreamTypeH264;
|
||||||
// from "avc1" codec, which causes problems.
|
break;
|
||||||
|
case kCodecAAC:
|
||||||
|
stream_type = kStreamTypeAdtsAac;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Codec " << codec_ << " is not supported in TS yet.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear_pmt_.Size() == 0) {
|
||||||
|
WritePmtWithParameters(stream_type, kVersion0, kCurrent, nullptr, 0,
|
||||||
|
&clear_pmt_);
|
||||||
|
DCHECK_NE(clear_pmt_.Size(), 0u);
|
||||||
|
}
|
||||||
|
WritePmtToBuffer(clear_pmt_.Buffer(), clear_pmt_.Size(), &continuity_counter_,
|
||||||
|
writer);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AacProgramMapTableWriter::AacProgramMapTableWriter(
|
VideoProgramMapTableWriter::VideoProgramMapTableWriter(Codec codec)
|
||||||
const std::vector<uint8_t>& aac_audio_specific_config,
|
: ProgramMapTableWriter(codec) {}
|
||||||
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() {}
|
bool VideoProgramMapTableWriter::WriteDescriptors(
|
||||||
|
BufferWriter* descriptors) const {
|
||||||
// TODO(rkuroiwa): Cache the PMT for encrypted segments, it doesn't need to
|
FourCC fourcc;
|
||||||
// be recalculated.
|
switch (codec()) {
|
||||||
bool AacProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
case kCodecH264:
|
||||||
// Version 1 and current.
|
fourcc = FOURCC_zavc;
|
||||||
return EncryptedSegmentPmtWithParameters(
|
break;
|
||||||
has_clear_lead_ ? kVersion1 : kVersion0, kCurrent, writer);
|
default:
|
||||||
}
|
LOG(ERROR) << "Codec " << codec() << " is not supported in TS yet.";
|
||||||
|
return false;
|
||||||
bool AacProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
}
|
||||||
has_clear_lead_ = true;
|
WritePrivateDataIndicatorDescriptor(fourcc, descriptors);
|
||||||
WritePmtToBuffer(kPmtAac, arraysize(kPmtAac), continuity_counter_, writer);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AacProgramMapTableWriter::EncryptedSegmentPmtWithParameters(
|
AudioProgramMapTableWriter::AudioProgramMapTableWriter(
|
||||||
int version,
|
Codec codec,
|
||||||
int current_next_indicator,
|
const std::vector<uint8_t>& audio_specific_config)
|
||||||
BufferWriter* writer) {
|
: ProgramMapTableWriter(codec),
|
||||||
|
audio_specific_config_(audio_specific_config) {
|
||||||
|
DCHECK(!audio_specific_config.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioProgramMapTableWriter::WriteDescriptors(
|
||||||
|
BufferWriter* descriptors) const {
|
||||||
|
FourCC fourcc;
|
||||||
|
switch (codec()) {
|
||||||
|
case kCodecAAC:
|
||||||
|
fourcc = FOURCC_aacd;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG(ERROR) << "Codec " << codec() << " is not supported in TS yet.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
WritePrivateDataIndicatorDescriptor(fourcc, descriptors);
|
||||||
|
|
||||||
// -12 because there are 12 bytes between 'descriptor_length' in
|
// -12 because there are 12 bytes between 'descriptor_length' in
|
||||||
// registration_descriptor and 'setup_data_length' in audio_setup_information.
|
// registration_descriptor and 'setup_data_length' in audio_setup_information.
|
||||||
if (aac_audio_specific_config_.size() >
|
if (audio_specific_config_.size() >
|
||||||
std::numeric_limits<uint8_t>::max() - 12U) {
|
std::numeric_limits<uint8_t>::max() - 12U) {
|
||||||
LOG(ERROR) << "AACAudioSpecificConfig of size: "
|
LOG(ERROR) << "AACAudioSpecificConfig of size: "
|
||||||
<< aac_audio_specific_config_.size()
|
<< audio_specific_config_.size()
|
||||||
<< " will not fit in the descriptor.";
|
<< " will not fit in the descriptor.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
BufferWriter descriptors;
|
return WriteRegistrationDescriptorForEncryptedAudio(
|
||||||
WritePrivateDataIndicatorDescriptor(FOURCC_aacd, &descriptors);
|
audio_specific_config_.data(), audio_specific_config_.size(),
|
||||||
if (!WriteRegistrationDescriptorForEncryptedAudio(
|
descriptors);
|
||||||
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 mp2t
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/base/macros.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
|
// TODO(kqyang): Move codec to codec.h.
|
||||||
|
#include "packager/media/base/stream_info.h"
|
||||||
|
#include "packager/media/formats/mp2t/continuity_counter.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
@ -20,20 +23,19 @@ class BufferWriter;
|
||||||
|
|
||||||
namespace mp2t {
|
namespace mp2t {
|
||||||
|
|
||||||
class ContinuityCounter;
|
|
||||||
|
|
||||||
/// Puts PMT into TS packets and writes them to buffer.
|
/// Puts PMT into TS packets and writes them to buffer.
|
||||||
/// Note that this does not currently allow encryption without clear lead.
|
|
||||||
class ProgramMapTableWriter {
|
class ProgramMapTableWriter {
|
||||||
public:
|
public:
|
||||||
ProgramMapTableWriter();
|
explicit ProgramMapTableWriter(Codec codec);
|
||||||
virtual ~ProgramMapTableWriter();
|
virtual ~ProgramMapTableWriter() = default;
|
||||||
|
|
||||||
/// Writes TS packets with PMT for encrypted segments.
|
/// Writes TS packets with PMT for encrypted segments.
|
||||||
virtual bool EncryptedSegmentPmt(BufferWriter* writer) = 0;
|
// Virtual for testing.
|
||||||
|
virtual bool EncryptedSegmentPmt(BufferWriter* writer);
|
||||||
|
|
||||||
/// Writes TS packets with PMT for clear segments.
|
/// Writes TS packets with PMT for clear segments.
|
||||||
virtual bool ClearSegmentPmt(BufferWriter* writer) = 0;
|
// Virtual for testing.
|
||||||
|
virtual bool ClearSegmentPmt(BufferWriter* writer);
|
||||||
|
|
||||||
// The pid can be 13 bits long but 8 bits is sufficient for this library.
|
// 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.
|
// This is the minimum PID that can be used for PMT.
|
||||||
|
@ -41,53 +43,54 @@ class ProgramMapTableWriter {
|
||||||
|
|
||||||
// This is arbitrary number that is not reserved by the spec.
|
// This is arbitrary number that is not reserved by the spec.
|
||||||
static const uint8_t kElementaryPid = 0x50;
|
static const uint8_t kElementaryPid = 0x50;
|
||||||
};
|
|
||||||
|
|
||||||
/// <em>This is not a general purpose PMT writer. This is intended to be used by
|
protected:
|
||||||
/// TsWriter.</em>
|
/// @return the underlying codec.
|
||||||
class H264ProgramMapTableWriter : public ProgramMapTableWriter {
|
Codec codec() const { return codec_; }
|
||||||
public:
|
|
||||||
explicit H264ProgramMapTableWriter(ContinuityCounter* continuity_counter);
|
|
||||||
~H264ProgramMapTableWriter() override;
|
|
||||||
|
|
||||||
bool EncryptedSegmentPmt(BufferWriter* writer) override;
|
|
||||||
bool ClearSegmentPmt(BufferWriter* writer) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ContinuityCounter* const continuity_counter_;
|
ProgramMapTableWriter(const ProgramMapTableWriter&) = delete;
|
||||||
// Set to true if ClearLeadSegmentPmt() has been called. This determines the
|
ProgramMapTableWriter& operator=(const ProgramMapTableWriter&) = delete;
|
||||||
// version number set in EncryptedSegmentPmt().
|
|
||||||
bool has_clear_lead_ = false;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(H264ProgramMapTableWriter);
|
// Writes descriptors for PMT (only needed for encrypted PMT).
|
||||||
|
virtual bool WriteDescriptors(BufferWriter* writer) const = 0;
|
||||||
|
|
||||||
|
const Codec codec_;
|
||||||
|
ContinuityCounter continuity_counter_;
|
||||||
|
BufferWriter clear_pmt_;
|
||||||
|
BufferWriter encrypted_pmt_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO(rkuroiwa): For now just handle AAC, we would want AudioProgramMapTable
|
/// ProgramMapTableWriter for video codecs.
|
||||||
// later when we support other audio codecs.
|
class VideoProgramMapTableWriter : public ProgramMapTableWriter {
|
||||||
/// <em>This is not a general purpose PMT writer. This is intended to be used by
|
|
||||||
/// TsWriter.</em>
|
|
||||||
class AacProgramMapTableWriter : public ProgramMapTableWriter {
|
|
||||||
public:
|
public:
|
||||||
AacProgramMapTableWriter(
|
explicit VideoProgramMapTableWriter(Codec codec);
|
||||||
const std::vector<uint8_t>& aac_audio_specific_config,
|
~VideoProgramMapTableWriter() override = default;
|
||||||
ContinuityCounter* continuity_counter);
|
|
||||||
~AacProgramMapTableWriter() override;
|
|
||||||
|
|
||||||
bool EncryptedSegmentPmt(BufferWriter* writer) override;
|
|
||||||
bool ClearSegmentPmt(BufferWriter* writer) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool EncryptedSegmentPmtWithParameters(int version,
|
VideoProgramMapTableWriter(const VideoProgramMapTableWriter&) = delete;
|
||||||
int current_next_indicator,
|
VideoProgramMapTableWriter& operator=(const VideoProgramMapTableWriter&) =
|
||||||
BufferWriter* writer);
|
delete;
|
||||||
|
|
||||||
const std::vector<uint8_t> aac_audio_specific_config_;
|
bool WriteDescriptors(BufferWriter* writer) const override;
|
||||||
ContinuityCounter* const continuity_counter_;
|
};
|
||||||
// Set to true if ClearLeadSegmentPmt() has been called. This determines the
|
|
||||||
// version number set in EncryptedSegmentPmt().
|
|
||||||
bool has_clear_lead_ = false;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(AacProgramMapTableWriter);
|
/// ProgramMapTableWriter for video codecs.
|
||||||
|
class AudioProgramMapTableWriter : public ProgramMapTableWriter {
|
||||||
|
public:
|
||||||
|
AudioProgramMapTableWriter(Codec codec,
|
||||||
|
const std::vector<uint8_t>& audio_specific_config);
|
||||||
|
~AudioProgramMapTableWriter() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AudioProgramMapTableWriter(const AudioProgramMapTableWriter&) = delete;
|
||||||
|
AudioProgramMapTableWriter& operator=(const AudioProgramMapTableWriter&) =
|
||||||
|
delete;
|
||||||
|
|
||||||
|
// Writers descriptors for PMT (only needed for encrypted PMT).
|
||||||
|
bool WriteDescriptors(BufferWriter* descriptors) const override;
|
||||||
|
|
||||||
|
const std::vector<uint8_t> audio_specific_config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace mp2t
|
} // namespace mp2t
|
||||||
|
|
|
@ -49,8 +49,7 @@ class ProgramMapTableWriterTest : public ::testing::Test {
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ProgramMapTableWriterTest, ClearH264) {
|
TEST_F(ProgramMapTableWriterTest, ClearH264) {
|
||||||
ContinuityCounter counter;
|
VideoProgramMapTableWriter writer(kCodecH264);
|
||||||
H264ProgramMapTableWriter writer(&counter);
|
|
||||||
BufferWriter buffer;
|
BufferWriter buffer;
|
||||||
writer.ClearSegmentPmt(&buffer);
|
writer.ClearSegmentPmt(&buffer);
|
||||||
|
|
||||||
|
@ -91,8 +90,7 @@ TEST_F(ProgramMapTableWriterTest, ClearH264) {
|
||||||
// Verify that PSI for encrypted segments after clear lead is generated
|
// Verify that PSI for encrypted segments after clear lead is generated
|
||||||
// correctly.
|
// correctly.
|
||||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadH264) {
|
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadH264) {
|
||||||
ContinuityCounter counter;
|
VideoProgramMapTableWriter writer(kCodecH264);
|
||||||
H264ProgramMapTableWriter writer(&counter);
|
|
||||||
BufferWriter buffer;
|
BufferWriter buffer;
|
||||||
writer.ClearSegmentPmt(&buffer);
|
writer.ClearSegmentPmt(&buffer);
|
||||||
buffer.Clear();
|
buffer.Clear();
|
||||||
|
@ -136,8 +134,7 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadH264) {
|
||||||
|
|
||||||
// Verify that PMT for encrypted segments can be generated (without clear lead).
|
// Verify that PMT for encrypted segments can be generated (without clear lead).
|
||||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) {
|
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) {
|
||||||
ContinuityCounter counter;
|
VideoProgramMapTableWriter writer(kCodecH264);
|
||||||
H264ProgramMapTableWriter writer(&counter);
|
|
||||||
BufferWriter buffer;
|
BufferWriter buffer;
|
||||||
writer.EncryptedSegmentPmt(&buffer);
|
writer.EncryptedSegmentPmt(&buffer);
|
||||||
|
|
||||||
|
@ -179,11 +176,10 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ProgramMapTableWriterTest, ClearAac) {
|
TEST_F(ProgramMapTableWriterTest, ClearAac) {
|
||||||
ContinuityCounter counter;
|
|
||||||
const std::vector<uint8_t> aac_audio_specific_config(
|
const std::vector<uint8_t> aac_audio_specific_config(
|
||||||
kAacBasicProfileExtraData,
|
std::begin(kAacBasicProfileExtraData),
|
||||||
kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData));
|
std::end(kAacBasicProfileExtraData));
|
||||||
AacProgramMapTableWriter writer(aac_audio_specific_config, &counter);
|
AudioProgramMapTableWriter writer(kCodecAAC, aac_audio_specific_config);
|
||||||
BufferWriter buffer;
|
BufferWriter buffer;
|
||||||
writer.ClearSegmentPmt(&buffer);
|
writer.ClearSegmentPmt(&buffer);
|
||||||
|
|
||||||
|
@ -222,11 +218,10 @@ TEST_F(ProgramMapTableWriterTest, ClearAac) {
|
||||||
// Verify that PSI for encrypted segments after clear lead is generated
|
// Verify that PSI for encrypted segments after clear lead is generated
|
||||||
// correctly.
|
// correctly.
|
||||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
||||||
ContinuityCounter counter;
|
|
||||||
const std::vector<uint8_t> aac_audio_specific_config(
|
const std::vector<uint8_t> aac_audio_specific_config(
|
||||||
kAacBasicProfileExtraData,
|
std::begin(kAacBasicProfileExtraData),
|
||||||
kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData));
|
std::end(kAacBasicProfileExtraData));
|
||||||
AacProgramMapTableWriter writer(aac_audio_specific_config, &counter);
|
AudioProgramMapTableWriter writer(kCodecAAC, aac_audio_specific_config);
|
||||||
BufferWriter buffer;
|
BufferWriter buffer;
|
||||||
writer.ClearSegmentPmt(&buffer);
|
writer.ClearSegmentPmt(&buffer);
|
||||||
|
|
||||||
|
@ -283,11 +278,10 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
||||||
|
|
||||||
// Verify that PMT for encrypted segments can be generated (without clear lead).
|
// Verify that PMT for encrypted segments can be generated (without clear lead).
|
||||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) {
|
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAacPmt) {
|
||||||
ContinuityCounter counter;
|
|
||||||
const std::vector<uint8_t> aac_audio_specific_config(
|
const std::vector<uint8_t> aac_audio_specific_config(
|
||||||
kAacBasicProfileExtraData,
|
std::begin(kAacBasicProfileExtraData),
|
||||||
kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData));
|
std::end(kAacBasicProfileExtraData));
|
||||||
AacProgramMapTableWriter writer(aac_audio_specific_config, &counter);
|
AudioProgramMapTableWriter writer(kCodecAAC, aac_audio_specific_config);
|
||||||
BufferWriter buffer;
|
BufferWriter buffer;
|
||||||
writer.EncryptedSegmentPmt(&buffer);
|
writer.EncryptedSegmentPmt(&buffer);
|
||||||
|
|
||||||
|
|
|
@ -173,23 +173,14 @@ bool TsWriter::Initialize(const StreamInfo& stream_info) {
|
||||||
if (stream_info.stream_type() == StreamType::kStreamVideo) {
|
if (stream_info.stream_type() == StreamType::kStreamVideo) {
|
||||||
const VideoStreamInfo& video_stream_info =
|
const VideoStreamInfo& video_stream_info =
|
||||||
static_cast<const VideoStreamInfo&>(stream_info);
|
static_cast<const VideoStreamInfo&>(stream_info);
|
||||||
if (video_stream_info.codec() != Codec::kCodecH264) {
|
pmt_writer_.reset(
|
||||||
LOG(ERROR) << "TsWriter cannot handle video codec "
|
new VideoProgramMapTableWriter(video_stream_info.codec()));
|
||||||
<< video_stream_info.codec() << " yet.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pmt_writer_.reset(new H264ProgramMapTableWriter(&pmt_continuity_counter_));
|
|
||||||
} else {
|
} else {
|
||||||
DCHECK_EQ(stream_type, StreamType::kStreamAudio);
|
DCHECK_EQ(stream_type, StreamType::kStreamAudio);
|
||||||
const AudioStreamInfo& audio_stream_info =
|
const AudioStreamInfo& audio_stream_info =
|
||||||
static_cast<const AudioStreamInfo&>(stream_info);
|
static_cast<const AudioStreamInfo&>(stream_info);
|
||||||
if (audio_stream_info.codec() != Codec::kCodecAAC) {
|
pmt_writer_.reset(new AudioProgramMapTableWriter(
|
||||||
LOG(ERROR) << "TsWriter cannot handle audio codec "
|
audio_stream_info.codec(), audio_stream_info.codec_config()));
|
||||||
<< audio_stream_info.codec() << " yet.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pmt_writer_.reset(new AacProgramMapTableWriter(
|
|
||||||
audio_stream_info.codec_config(), &pmt_continuity_counter_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -65,7 +65,6 @@ class TsWriter {
|
||||||
// True if further segments generated by this instance should be encrypted.
|
// True if further segments generated by this instance should be encrypted.
|
||||||
bool encrypted_ = false;
|
bool encrypted_ = false;
|
||||||
|
|
||||||
ContinuityCounter pmt_continuity_counter_;
|
|
||||||
ContinuityCounter pat_continuity_counter_;
|
ContinuityCounter pat_continuity_counter_;
|
||||||
ContinuityCounter elementary_stream_continuity_counter_;
|
ContinuityCounter elementary_stream_continuity_counter_;
|
||||||
|
|
||||||
|
|
|
@ -58,11 +58,18 @@ const uint8_t kAacBasicProfileExtraData[] = {0x12, 0x10};
|
||||||
|
|
||||||
class MockProgramMapTableWriter : public ProgramMapTableWriter {
|
class MockProgramMapTableWriter : public ProgramMapTableWriter {
|
||||||
public:
|
public:
|
||||||
MockProgramMapTableWriter() {}
|
MockProgramMapTableWriter() : ProgramMapTableWriter(kUnknownCodec) {}
|
||||||
~MockProgramMapTableWriter() override {}
|
~MockProgramMapTableWriter() override {}
|
||||||
|
|
||||||
MOCK_METHOD1(EncryptedSegmentPmt, bool(BufferWriter* writer));
|
MOCK_METHOD1(EncryptedSegmentPmt, bool(BufferWriter* writer));
|
||||||
MOCK_METHOD1(ClearSegmentPmt, bool(BufferWriter* writer));
|
MOCK_METHOD1(ClearSegmentPmt, bool(BufferWriter* writer));
|
||||||
|
|
||||||
|
private:
|
||||||
|
MockProgramMapTableWriter(const MockProgramMapTableWriter&) = delete;
|
||||||
|
MockProgramMapTableWriter& operator=(const MockProgramMapTableWriter&) =
|
||||||
|
delete;
|
||||||
|
|
||||||
|
bool WriteDescriptors(BufferWriter* writer) const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is not a real TS Packet. But is used to check that the result from the
|
// This is not a real TS Packet. But is used to check that the result from the
|
||||||
|
@ -165,15 +172,6 @@ TEST_F(TsWriterTest, InitializeVideoH264) {
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TsWriterTest, InitializeVideoNonH264) {
|
|
||||||
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
|
||||||
kTrackId, kTimeScale, kDuration, Codec::kCodecVP9,
|
|
||||||
H26xStreamFormat::kUnSpecified, kCodecString, kExtraData,
|
|
||||||
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
|
||||||
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
|
||||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(TsWriterTest, InitializeAudioAac) {
|
TEST_F(TsWriterTest, InitializeAudioAac) {
|
||||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString, kExtraData,
|
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString, kExtraData,
|
||||||
|
@ -183,15 +181,6 @@ TEST_F(TsWriterTest, InitializeAudioAac) {
|
||||||
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TsWriterTest, InitializeAudioNonAac) {
|
|
||||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
|
||||||
kTrackId, kTimeScale, kDuration, Codec::kCodecOpus, kCodecString,
|
|
||||||
kExtraData, arraysize(kExtraData), kSampleBits, kNumChannels,
|
|
||||||
kSamplingFrequency, kSeekPreroll, kCodecDelay, kMaxBitrate,
|
|
||||||
kAverageBitrate, kLanguage, kIsEncrypted));
|
|
||||||
EXPECT_FALSE(ts_writer_.Initialize(*stream_info));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that PAT and PMT are correct for clear segment.
|
// Verify that PAT and PMT are correct for clear segment.
|
||||||
// This test covers verifies the PAT, and since it doesn't change, other tests
|
// This test covers verifies the PAT, and since it doesn't change, other tests
|
||||||
// shouldn't have to check this.
|
// shouldn't have to check this.
|
||||||
|
@ -305,6 +294,22 @@ TEST_F(TsWriterTest, ClearLeadH264Pmt) {
|
||||||
kTsPacketSize));
|
kTsPacketSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TsWriterTest, ClearSegmentPmtFailure) {
|
||||||
|
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||||
|
new MockProgramMapTableWriter());
|
||||||
|
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(false));
|
||||||
|
|
||||||
|
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||||
|
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString,
|
||||||
|
kAacBasicProfileExtraData, arraysize(kAacBasicProfileExtraData),
|
||||||
|
kSampleBits, kNumChannels, kSamplingFrequency, kSeekPreroll, kCodecDelay,
|
||||||
|
kMaxBitrate, kAverageBitrate, kLanguage, kIsEncrypted));
|
||||||
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
|
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||||
|
EXPECT_FALSE(ts_writer_.NewSegment(test_file_name_));
|
||||||
|
}
|
||||||
|
|
||||||
// Check the encrypted segments' PMT (after clear lead).
|
// Check the encrypted segments' PMT (after clear lead).
|
||||||
TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
||||||
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||||
|
@ -338,6 +343,28 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
||||||
kTsPacketSize));
|
kTsPacketSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(TsWriterTest, EncryptedSegmentPmtFailure) {
|
||||||
|
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||||
|
new MockProgramMapTableWriter());
|
||||||
|
InSequence s;
|
||||||
|
EXPECT_CALL(*mock_pmt_writer, ClearSegmentPmt(_)).WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*mock_pmt_writer, EncryptedSegmentPmt(_)).WillOnce(Return(false));
|
||||||
|
|
||||||
|
std::shared_ptr<VideoStreamInfo> stream_info(new VideoStreamInfo(
|
||||||
|
kTrackId, kTimeScale, kDuration, kH264Codec,
|
||||||
|
H26xStreamFormat::kAnnexbByteStream, kCodecString, kExtraData,
|
||||||
|
arraysize(kExtraData), kWidth, kHeight, kPixelWidth, kPixelHeight,
|
||||||
|
kTrickPlayFactor, kNaluLengthSize, kLanguage, kIsEncrypted));
|
||||||
|
EXPECT_TRUE(ts_writer_.Initialize(*stream_info));
|
||||||
|
|
||||||
|
ts_writer_.SetProgramMapTableWriterForTesting(std::move(mock_pmt_writer));
|
||||||
|
EXPECT_TRUE(ts_writer_.NewSegment(test_file_name_));
|
||||||
|
EXPECT_TRUE(ts_writer_.FinalizeSegment());
|
||||||
|
|
||||||
|
ts_writer_.SignalEncrypted();
|
||||||
|
EXPECT_FALSE(ts_writer_.NewSegment(test_file_name_));
|
||||||
|
}
|
||||||
|
|
||||||
// Same as ClearLeadH264Pmt but for AAC.
|
// Same as ClearLeadH264Pmt but for AAC.
|
||||||
TEST_F(TsWriterTest, ClearLeadAacPmt) {
|
TEST_F(TsWriterTest, ClearLeadAacPmt) {
|
||||||
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||||
|
|
Loading…
Reference in New Issue