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_zach = 0x7A616368,
|
||||
FOURCC_zacp = 0x7A616370,
|
||||
FOURCC_zavc = 0x7A617663,
|
||||
};
|
||||
|
||||
const FourCC kAppleSampleAesProtectionScheme = FOURCC_cbca;
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "packager/media/base/buffer_writer.h"
|
||||
#include "packager/media/base/fourccs.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"
|
||||
|
||||
namespace shaka {
|
||||
|
@ -119,65 +118,6 @@ uint32_t Crc32Mpeg2(const uint8_t* data, size_t data_size) {
|
|||
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,
|
||||
size_t pmt_size,
|
||||
ContinuityCounter* continuity_counter,
|
||||
|
@ -261,8 +201,7 @@ void WritePmtWithParameters(uint8_t stream_type,
|
|||
int current_next_indicator,
|
||||
const uint8_t* descriptors,
|
||||
size_t descriptors_size,
|
||||
ContinuityCounter* continuity_counter,
|
||||
BufferWriter* output) {
|
||||
BufferWriter* pmt) {
|
||||
DCHECK(current_next_indicator == kCurrent || current_next_indicator == kNext);
|
||||
// Body starting from program number.
|
||||
BufferWriter pmt_body;
|
||||
|
@ -291,106 +230,135 @@ void WritePmtWithParameters(uint8_t stream_type,
|
|||
|
||||
// 4 reserved bits followed by ES_info_length.
|
||||
pmt_body.AppendInt(static_cast<uint16_t>(0xF000 | 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
|
||||
// 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));
|
||||
pmt->Clear();
|
||||
// Pointer field is not really part of the PMT but it's there so that an extra
|
||||
// buffer isn't required to prepend the 0x00 byte.
|
||||
const uint8_t kPointerField = 0;
|
||||
pmt->AppendInt(kPointerField);
|
||||
pmt->AppendInt(kProgramMapTableId);
|
||||
// First four bits must be '1011'. +4 for CRC.
|
||||
pmt.AppendInt(static_cast<uint16_t>(0xB000 | (pmt_body.Size() + 4)));
|
||||
pmt.AppendBuffer(pmt_body);
|
||||
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);
|
||||
const uint32_t crc = Crc32Mpeg2(pmt->Buffer() + 1, pmt->Size() - 1);
|
||||
pmt->AppendInt(crc);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ProgramMapTableWriter::ProgramMapTableWriter() {}
|
||||
ProgramMapTableWriter::~ProgramMapTableWriter() {}
|
||||
ProgramMapTableWriter::ProgramMapTableWriter(Codec codec) : codec_(codec) {}
|
||||
|
||||
H264ProgramMapTableWriter::H264ProgramMapTableWriter(
|
||||
ContinuityCounter* continuity_counter)
|
||||
: continuity_counter_(continuity_counter) {
|
||||
DCHECK(continuity_counter);
|
||||
bool ProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
||||
uint8_t stream_type;
|
||||
switch (codec_) {
|
||||
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) {
|
||||
WritePmtWithParameters(
|
||||
kStreamTypeEncryptedH264, has_clear_lead_ ? kVersion1 : kVersion0,
|
||||
kCurrent, kPrivateDataIndicatorDescriptorEncryptedH264,
|
||||
arraysize(kPrivateDataIndicatorDescriptorEncryptedH264),
|
||||
continuity_counter_, writer);
|
||||
const bool has_clear_lead = clear_pmt_.Size() > 0;
|
||||
WritePmtWithParameters(stream_type, has_clear_lead ? kVersion1 : kVersion0,
|
||||
kCurrent, descriptors.Buffer(), descriptors.Size(),
|
||||
&encrypted_pmt_);
|
||||
DCHECK_NE(encrypted_pmt_.Size(), 0u);
|
||||
}
|
||||
WritePmtToBuffer(encrypted_pmt_.Buffer(), encrypted_pmt_.Size(),
|
||||
&continuity_counter_, writer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool H264ProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
||||
has_clear_lead_ = true;
|
||||
WritePmtToBuffer(kPmtH264, arraysize(kPmtH264), continuity_counter_, writer);
|
||||
// Cannot insert PMT for following encrypted segments because
|
||||
// some players consider encrypted segments as "zavc" codec which is different
|
||||
// from "avc1" codec, which causes problems.
|
||||
bool ProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
||||
uint8_t stream_type;
|
||||
switch (codec_) {
|
||||
case kCodecH264:
|
||||
stream_type = kStreamTypeH264;
|
||||
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;
|
||||
}
|
||||
|
||||
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_);
|
||||
VideoProgramMapTableWriter::VideoProgramMapTableWriter(Codec codec)
|
||||
: ProgramMapTableWriter(codec) {}
|
||||
|
||||
bool VideoProgramMapTableWriter::WriteDescriptors(
|
||||
BufferWriter* descriptors) const {
|
||||
FourCC fourcc;
|
||||
switch (codec()) {
|
||||
case kCodecH264:
|
||||
fourcc = FOURCC_zavc;
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "Codec " << codec() << " is not supported in TS yet.";
|
||||
return false;
|
||||
}
|
||||
|
||||
AacProgramMapTableWriter::~AacProgramMapTableWriter() {}
|
||||
|
||||
// TODO(rkuroiwa): Cache the PMT for encrypted segments, it doesn't need to
|
||||
// be recalculated.
|
||||
bool AacProgramMapTableWriter::EncryptedSegmentPmt(BufferWriter* writer) {
|
||||
// Version 1 and current.
|
||||
return EncryptedSegmentPmtWithParameters(
|
||||
has_clear_lead_ ? kVersion1 : kVersion0, kCurrent, writer);
|
||||
}
|
||||
|
||||
bool AacProgramMapTableWriter::ClearSegmentPmt(BufferWriter* writer) {
|
||||
has_clear_lead_ = true;
|
||||
WritePmtToBuffer(kPmtAac, arraysize(kPmtAac), continuity_counter_, writer);
|
||||
WritePrivateDataIndicatorDescriptor(fourcc, descriptors);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AacProgramMapTableWriter::EncryptedSegmentPmtWithParameters(
|
||||
int version,
|
||||
int current_next_indicator,
|
||||
BufferWriter* writer) {
|
||||
AudioProgramMapTableWriter::AudioProgramMapTableWriter(
|
||||
Codec codec,
|
||||
const std::vector<uint8_t>& audio_specific_config)
|
||||
: 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
|
||||
// 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) {
|
||||
LOG(ERROR) << "AACAudioSpecificConfig of size: "
|
||||
<< aac_audio_specific_config_.size()
|
||||
<< 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;
|
||||
return WriteRegistrationDescriptorForEncryptedAudio(
|
||||
audio_specific_config_.data(), audio_specific_config_.size(),
|
||||
descriptors);
|
||||
}
|
||||
|
||||
} // namespace mp2t
|
||||
|
|
|
@ -11,7 +11,10 @@
|
|||
|
||||
#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 media {
|
||||
|
@ -20,20 +23,19 @@ 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();
|
||||
explicit ProgramMapTableWriter(Codec codec);
|
||||
virtual ~ProgramMapTableWriter() = default;
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
// 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.
|
||||
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 EncryptedSegmentPmt(BufferWriter* writer) override;
|
||||
bool ClearSegmentPmt(BufferWriter* writer) override;
|
||||
protected:
|
||||
/// @return the underlying codec.
|
||||
Codec codec() const { return codec_; }
|
||||
|
||||
private:
|
||||
ContinuityCounter* const continuity_counter_;
|
||||
// Set to true if ClearLeadSegmentPmt() has been called. This determines the
|
||||
// version number set in EncryptedSegmentPmt().
|
||||
bool has_clear_lead_ = false;
|
||||
ProgramMapTableWriter(const ProgramMapTableWriter&) = delete;
|
||||
ProgramMapTableWriter& operator=(const ProgramMapTableWriter&) = delete;
|
||||
|
||||
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
|
||||
// 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 {
|
||||
/// ProgramMapTableWriter for video codecs.
|
||||
class VideoProgramMapTableWriter : public ProgramMapTableWriter {
|
||||
public:
|
||||
AacProgramMapTableWriter(
|
||||
const std::vector<uint8_t>& aac_audio_specific_config,
|
||||
ContinuityCounter* continuity_counter);
|
||||
~AacProgramMapTableWriter() override;
|
||||
|
||||
bool EncryptedSegmentPmt(BufferWriter* writer) override;
|
||||
bool ClearSegmentPmt(BufferWriter* writer) override;
|
||||
explicit VideoProgramMapTableWriter(Codec codec);
|
||||
~VideoProgramMapTableWriter() override = default;
|
||||
|
||||
private:
|
||||
bool EncryptedSegmentPmtWithParameters(int version,
|
||||
int current_next_indicator,
|
||||
BufferWriter* writer);
|
||||
VideoProgramMapTableWriter(const VideoProgramMapTableWriter&) = delete;
|
||||
VideoProgramMapTableWriter& operator=(const VideoProgramMapTableWriter&) =
|
||||
delete;
|
||||
|
||||
const std::vector<uint8_t> aac_audio_specific_config_;
|
||||
ContinuityCounter* const continuity_counter_;
|
||||
// Set to true if ClearLeadSegmentPmt() has been called. This determines the
|
||||
// version number set in EncryptedSegmentPmt().
|
||||
bool has_clear_lead_ = false;
|
||||
bool WriteDescriptors(BufferWriter* writer) const override;
|
||||
};
|
||||
|
||||
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
|
||||
|
|
|
@ -49,8 +49,7 @@ class ProgramMapTableWriterTest : public ::testing::Test {
|
|||
};
|
||||
|
||||
TEST_F(ProgramMapTableWriterTest, ClearH264) {
|
||||
ContinuityCounter counter;
|
||||
H264ProgramMapTableWriter writer(&counter);
|
||||
VideoProgramMapTableWriter writer(kCodecH264);
|
||||
BufferWriter buffer;
|
||||
writer.ClearSegmentPmt(&buffer);
|
||||
|
||||
|
@ -91,8 +90,7 @@ TEST_F(ProgramMapTableWriterTest, ClearH264) {
|
|||
// Verify that PSI for encrypted segments after clear lead is generated
|
||||
// correctly.
|
||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadH264) {
|
||||
ContinuityCounter counter;
|
||||
H264ProgramMapTableWriter writer(&counter);
|
||||
VideoProgramMapTableWriter writer(kCodecH264);
|
||||
BufferWriter buffer;
|
||||
writer.ClearSegmentPmt(&buffer);
|
||||
buffer.Clear();
|
||||
|
@ -136,8 +134,7 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadH264) {
|
|||
|
||||
// Verify that PMT for encrypted segments can be generated (without clear lead).
|
||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) {
|
||||
ContinuityCounter counter;
|
||||
H264ProgramMapTableWriter writer(&counter);
|
||||
VideoProgramMapTableWriter writer(kCodecH264);
|
||||
BufferWriter buffer;
|
||||
writer.EncryptedSegmentPmt(&buffer);
|
||||
|
||||
|
@ -179,11 +176,10 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsH264Pmt) {
|
|||
}
|
||||
|
||||
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);
|
||||
std::begin(kAacBasicProfileExtraData),
|
||||
std::end(kAacBasicProfileExtraData));
|
||||
AudioProgramMapTableWriter writer(kCodecAAC, aac_audio_specific_config);
|
||||
BufferWriter buffer;
|
||||
writer.ClearSegmentPmt(&buffer);
|
||||
|
||||
|
@ -222,11 +218,10 @@ TEST_F(ProgramMapTableWriterTest, ClearAac) {
|
|||
// Verify that PSI for encrypted segments after clear lead is generated
|
||||
// correctly.
|
||||
TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
||||
ContinuityCounter counter;
|
||||
const std::vector<uint8_t> aac_audio_specific_config(
|
||||
kAacBasicProfileExtraData,
|
||||
kAacBasicProfileExtraData + arraysize(kAacBasicProfileExtraData));
|
||||
AacProgramMapTableWriter writer(aac_audio_specific_config, &counter);
|
||||
std::begin(kAacBasicProfileExtraData),
|
||||
std::end(kAacBasicProfileExtraData));
|
||||
AudioProgramMapTableWriter writer(kCodecAAC, aac_audio_specific_config);
|
||||
BufferWriter buffer;
|
||||
writer.ClearSegmentPmt(&buffer);
|
||||
|
||||
|
@ -283,11 +278,10 @@ TEST_F(ProgramMapTableWriterTest, EncryptedSegmentsAfterClearLeadAac) {
|
|||
|
||||
// Verify that PMT for encrypted segments can be generated (without clear lead).
|
||||
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);
|
||||
std::begin(kAacBasicProfileExtraData),
|
||||
std::end(kAacBasicProfileExtraData));
|
||||
AudioProgramMapTableWriter writer(kCodecAAC, aac_audio_specific_config);
|
||||
BufferWriter buffer;
|
||||
writer.EncryptedSegmentPmt(&buffer);
|
||||
|
||||
|
|
|
@ -173,23 +173,14 @@ bool TsWriter::Initialize(const StreamInfo& stream_info) {
|
|||
if (stream_info.stream_type() == StreamType::kStreamVideo) {
|
||||
const VideoStreamInfo& video_stream_info =
|
||||
static_cast<const VideoStreamInfo&>(stream_info);
|
||||
if (video_stream_info.codec() != Codec::kCodecH264) {
|
||||
LOG(ERROR) << "TsWriter cannot handle video codec "
|
||||
<< video_stream_info.codec() << " yet.";
|
||||
return false;
|
||||
}
|
||||
pmt_writer_.reset(new H264ProgramMapTableWriter(&pmt_continuity_counter_));
|
||||
pmt_writer_.reset(
|
||||
new VideoProgramMapTableWriter(video_stream_info.codec()));
|
||||
} else {
|
||||
DCHECK_EQ(stream_type, StreamType::kStreamAudio);
|
||||
const AudioStreamInfo& audio_stream_info =
|
||||
static_cast<const AudioStreamInfo&>(stream_info);
|
||||
if (audio_stream_info.codec() != Codec::kCodecAAC) {
|
||||
LOG(ERROR) << "TsWriter cannot handle audio codec "
|
||||
<< audio_stream_info.codec() << " yet.";
|
||||
return false;
|
||||
}
|
||||
pmt_writer_.reset(new AacProgramMapTableWriter(
|
||||
audio_stream_info.codec_config(), &pmt_continuity_counter_));
|
||||
pmt_writer_.reset(new AudioProgramMapTableWriter(
|
||||
audio_stream_info.codec(), audio_stream_info.codec_config()));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -65,7 +65,6 @@ class TsWriter {
|
|||
// True if further segments generated by this instance should be encrypted.
|
||||
bool encrypted_ = false;
|
||||
|
||||
ContinuityCounter pmt_continuity_counter_;
|
||||
ContinuityCounter pat_continuity_counter_;
|
||||
ContinuityCounter elementary_stream_continuity_counter_;
|
||||
|
||||
|
|
|
@ -58,11 +58,18 @@ const uint8_t kAacBasicProfileExtraData[] = {0x12, 0x10};
|
|||
|
||||
class MockProgramMapTableWriter : public ProgramMapTableWriter {
|
||||
public:
|
||||
MockProgramMapTableWriter() {}
|
||||
MockProgramMapTableWriter() : ProgramMapTableWriter(kUnknownCodec) {}
|
||||
~MockProgramMapTableWriter() override {}
|
||||
|
||||
MOCK_METHOD1(EncryptedSegmentPmt, 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
|
||||
|
@ -165,15 +172,6 @@ TEST_F(TsWriterTest, InitializeVideoH264) {
|
|||
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) {
|
||||
std::shared_ptr<AudioStreamInfo> stream_info(new AudioStreamInfo(
|
||||
kTrackId, kTimeScale, kDuration, kAacCodec, kCodecString, kExtraData,
|
||||
|
@ -183,15 +181,6 @@ TEST_F(TsWriterTest, InitializeAudioAac) {
|
|||
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.
|
||||
// This test covers verifies the PAT, and since it doesn't change, other tests
|
||||
// shouldn't have to check this.
|
||||
|
@ -305,6 +294,22 @@ TEST_F(TsWriterTest, ClearLeadH264Pmt) {
|
|||
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).
|
||||
TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
||||
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||
|
@ -338,6 +343,28 @@ TEST_F(TsWriterTest, EncryptedSegmentsH264Pmt) {
|
|||
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.
|
||||
TEST_F(TsWriterTest, ClearLeadAacPmt) {
|
||||
std::unique_ptr<MockProgramMapTableWriter> mock_pmt_writer(
|
||||
|
|
Loading…
Reference in New Issue