Refactor ProgramMapTableWriter

To make it easier to support more codecs.

Change-Id: Ifcde0f3d7d6f74015d723a0b74401a86c8e65a72
This commit is contained in:
KongQun Yang 2017-10-18 13:25:20 -07:00
parent d66d307fc2
commit b5a8185543
7 changed files with 212 additions and 229 deletions

View File

@ -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;

View File

@ -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));
if (descriptors_size > 0) {
DCHECK(descriptors);
pmt_body.AppendArray(descriptors, descriptors_size); 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

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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_;

View File

@ -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(