Add encryption information in demuxer.

- Add decrypt_config in media sample and stream.
  - Update subsamples when converting NAL unit to bytestream.

Change-Id: I7b8975a453f81b22cf74bee3c9a58b7e458cbaae
This commit is contained in:
Haoming Chen 2016-08-16 17:57:34 -07:00
parent 6e8aa27e74
commit 28828b8a15
9 changed files with 400 additions and 23 deletions

View File

@ -13,6 +13,7 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/memory/ref_counted.h" #include "packager/base/memory/ref_counted.h"
#include "packager/media/base/decrypt_config.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -117,6 +118,10 @@ class MediaSample : public base::RefCountedThreadSafe<MediaSample> {
return side_data_.size(); return side_data_.size();
} }
const DecryptConfig* decrypt_config() const {
return decrypt_config_.get();
}
void set_data(const uint8_t* data, const size_t data_size) { void set_data(const uint8_t* data, const size_t data_size) {
data_.assign(data, data + data_size); data_.assign(data, data + data_size);
} }
@ -133,6 +138,10 @@ class MediaSample : public base::RefCountedThreadSafe<MediaSample> {
is_encrypted_ = value; is_encrypted_ = value;
} }
void set_decrypt_config(std::unique_ptr<DecryptConfig> decrypt_config) {
decrypt_config_ = std::move(decrypt_config);
}
// If there's no data in this buffer, it represents end of stream. // If there's no data in this buffer, it represents end of stream.
bool end_of_stream() const { return data_.size() == 0; } bool end_of_stream() const { return data_.size() == 0; }
@ -178,6 +187,9 @@ class MediaSample : public base::RefCountedThreadSafe<MediaSample> {
// For now this is the cue identifier for WebVTT. // For now this is the cue identifier for WebVTT.
std::string config_id_; std::string config_id_;
// Decrypt configuration.
std::unique_ptr<DecryptConfig> decrypt_config_;
DISALLOW_COPY_AND_ASSIGN(MediaSample); DISALLOW_COPY_AND_ASSIGN(MediaSample);
}; };

View File

@ -22,7 +22,7 @@ MediaStream::MediaStream(scoped_refptr<StreamInfo> info, Demuxer* demuxer)
MediaStream::~MediaStream() {} MediaStream::~MediaStream() {}
Status MediaStream::PullSample(scoped_refptr<MediaSample>* sample) { Status MediaStream::PullSample(scoped_refptr<MediaSample>* sample) {
DCHECK_EQ(state_, kPulling); DCHECK(state_ == kPulling || state_ == kIdle);
// Trigger a new parse in demuxer if no more samples. // Trigger a new parse in demuxer if no more samples.
while (samples_.empty()) { while (samples_.empty()) {

View File

@ -42,11 +42,16 @@ class VideoStreamInfo : public StreamInfo {
uint32_t pixel_height() const { return pixel_height_; } uint32_t pixel_height() const { return pixel_height_; }
uint8_t nalu_length_size() const { return nalu_length_size_; } uint8_t nalu_length_size() const { return nalu_length_size_; }
int16_t trick_play_rate() const { return trick_play_rate_; } int16_t trick_play_rate() const { return trick_play_rate_; }
const std::vector<uint8_t>& eme_init_data() const { return eme_init_data_; }
void set_width(uint32_t width) { width_ = width; } void set_width(uint32_t width) { width_ = width; }
void set_height(uint32_t height) { height_ = height; } void set_height(uint32_t height) { height_ = height; }
void set_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; } void set_pixel_width(uint32_t pixel_width) { pixel_width_ = pixel_width; }
void set_pixel_height(uint32_t pixel_height) { pixel_height_ = pixel_height; } void set_pixel_height(uint32_t pixel_height) { pixel_height_ = pixel_height; }
void set_eme_init_data(const uint8_t* eme_init_data,
size_t eme_init_data_size) {
eme_init_data_.assign(eme_init_data, eme_init_data + eme_init_data_size);
}
private: private:
~VideoStreamInfo() override; ~VideoStreamInfo() override;
@ -65,6 +70,10 @@ class VideoStreamInfo : public StreamInfo {
// (H.264). // (H.264).
uint8_t nalu_length_size_; uint8_t nalu_length_size_;
// Container-specific data used by CDM to generate a license request:
// https://w3c.github.io/encrypted-media/#initialization-data.
std::vector<uint8_t> eme_init_data_;
// Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler // Not using DISALLOW_COPY_AND_ASSIGN here intentionally to allow the compiler
// generated copy constructor and assignment operator. Since the extra data is // generated copy constructor and assignment operator. Since the extra data is
// typically small, the performance impact is minimal. // typically small, the performance impact is minimal.

View File

@ -46,6 +46,27 @@ void AddAccessUnitDelimiter(BufferWriter* buffer_writer) {
buffer_writer->AppendInt(kAccessUnitDelimiterRbspAnyPrimaryPicType); buffer_writer->AppendInt(kAccessUnitDelimiterRbspAnyPrimaryPicType);
} }
bool CheckSubsampleValid(const std::vector<SubsampleEntry>* subsamples,
size_t subsample_id,
size_t nalu_size,
bool* is_nalu_all_clear) {
if (subsample_id >= subsamples->size()) {
LOG(ERROR) << "Subsample index exceeds subsamples' size.";
return false;
}
const SubsampleEntry& subsample = subsamples->at(subsample_id);
if (nalu_size == subsample.clear_bytes + subsample.cipher_bytes) {
*is_nalu_all_clear = false;
} else if (nalu_size < subsample.clear_bytes) {
*is_nalu_all_clear = true;
} else {
LOG(ERROR) << "Unexpected subsample entry " << subsample.clear_bytes << ":"
<< subsample.cipher_bytes << " nalu size: " << nalu_size;
return false;
}
return true;
}
} // namespace } // namespace
void EscapeNalByteSequence(const uint8_t* input, void EscapeNalByteSequence(const uint8_t* input,
@ -144,17 +165,39 @@ bool NalUnitToByteStreamConverter::ConvertUnitToByteStream(
size_t sample_size, size_t sample_size,
bool is_key_frame, bool is_key_frame,
std::vector<uint8_t>* output) { std::vector<uint8_t>* output) {
return ConvertUnitToByteStreamWithSubsamples(
sample, sample_size, is_key_frame, output,
nullptr); // Skip subsample update.
}
// This ignores all AUD, SPS, and PPS in the sample. Instead uses the data
// parsed in Initialize().
bool NalUnitToByteStreamConverter::ConvertUnitToByteStreamWithSubsamples(
const uint8_t* sample,
size_t sample_size,
bool is_key_frame,
std::vector<uint8_t>* output,
std::vector<SubsampleEntry>* subsamples) {
if (!sample || sample_size == 0) { if (!sample || sample_size == 0) {
LOG(WARNING) << "Sample is empty."; LOG(WARNING) << "Sample is empty.";
return true; return true;
} }
if (subsamples && escape_data_) {
LOG(ERROR) << "escape_data_ should not be set when updating subsamples.";
return false;
}
BufferWriter buffer_writer(sample_size); BufferWriter buffer_writer(sample_size);
buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode)); buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode));
AddAccessUnitDelimiter(&buffer_writer); AddAccessUnitDelimiter(&buffer_writer);
if (is_key_frame) if (is_key_frame)
buffer_writer.AppendVector(decoder_configuration_in_byte_stream_); buffer_writer.AppendVector(decoder_configuration_in_byte_stream_);
int adjustment = buffer_writer.Size();
size_t subsample_id = 0;
NaluReader nalu_reader(Nalu::kH264, nalu_length_size_, sample, sample_size); NaluReader nalu_reader(Nalu::kH264, nalu_length_size_, sample, sample_size);
Nalu nalu; Nalu nalu;
NaluReader::Result result = nalu_reader.Advance(&nalu); NaluReader::Result result = nalu_reader.Advance(&nalu);
@ -166,12 +209,50 @@ bool NalUnitToByteStreamConverter::ConvertUnitToByteStream(
case Nalu::H264_SPS: case Nalu::H264_SPS:
FALLTHROUGH_INTENDED; FALLTHROUGH_INTENDED;
case Nalu::H264_PPS: case Nalu::H264_PPS:
if (subsamples) {
const size_t old_nalu_size =
nalu_length_size_ + nalu.header_size() + nalu.payload_size();
bool is_nalu_all_clear;
if (!CheckSubsampleValid(subsamples, subsample_id, old_nalu_size,
&is_nalu_all_clear)) {
return false;
}
if (is_nalu_all_clear) {
// If AUD/SPS/PPS is all clear, reduce the clear bytes.
subsamples->at(subsample_id).clear_bytes -= old_nalu_size;
} else {
// If AUD/SPS/PPS has cipher, drop the corresponding subsample.
subsamples->erase(subsamples->begin() + subsample_id);
}
}
break; break;
default: default:
buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode)); buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode));
AppendNalu(nalu, nalu_length_size_, escape_data_, &buffer_writer); AppendNalu(nalu, nalu_length_size_, escape_data_, &buffer_writer);
if (subsamples) {
const size_t old_nalu_size =
nalu_length_size_ + nalu.header_size() + nalu.payload_size();
bool is_nalu_all_clear;
if (!CheckSubsampleValid(subsamples, subsample_id, old_nalu_size,
&is_nalu_all_clear)) {
return false;
}
if (is_nalu_all_clear) {
// Add this nalu to the adjustment and remove it from clear_bytes.
subsamples->at(subsample_id).clear_bytes -= old_nalu_size;
adjustment += old_nalu_size;
} else {
// Apply the adjustment on the current subsample, reset the
// adjustment and move to the next subsample.
subsamples->at(subsample_id).clear_bytes += adjustment;
subsample_id++;
adjustment = 0;
}
}
break; break;
} }
result = nalu_reader.Advance(&nalu); result = nalu_reader.Advance(&nalu);
} }

View File

@ -12,6 +12,7 @@
#include "packager/base/macros.h" #include "packager/base/macros.h"
#include "packager/base/memory/ref_counted.h" #include "packager/base/memory/ref_counted.h"
#include "packager/media/base/decrypt_config.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -53,13 +54,30 @@ class NalUnitToByteStreamConverter {
/// SAMPLE-AES encryption. /// SAMPLE-AES encryption.
/// @param sample is the sample to be converted. /// @param sample is the sample to be converted.
/// @param sample_size is the size of @a sample. /// @param sample_size is the size of @a sample.
/// @param output is set to the the converted sample, on success. /// @param[out] output is set to the the converted sample, on success.
/// @return true on success, false otherwise. /// @return true on success, false otherwise.
virtual bool ConvertUnitToByteStream(const uint8_t* sample, virtual bool ConvertUnitToByteStream(const uint8_t* sample,
size_t sample_size, size_t sample_size,
bool is_key_frame, bool is_key_frame,
std::vector<uint8_t>* output); std::vector<uint8_t>* output);
/// Converts unit stream to byte stream using the data passed to Initialize()
/// and update the corresponding subsamples of the media sample.
/// The method will function correctly even if @a sample is encrypted using
/// SAMPLE-AES encryption.
/// @param sample is the sample to be converted.
/// @param sample_size is the size of @a sample.
/// @param[out] output is set to the the converted sample, on success.
/// @param[in,out] subsamples has the input subsamples and output updated
/// subsamples, on sucess.
/// @return true on success, false otherwise.
virtual bool ConvertUnitToByteStreamWithSubsamples(
const uint8_t* sample,
size_t sample_size,
bool is_key_frame,
std::vector<uint8_t>* output,
std::vector<SubsampleEntry>* subsamples);
private: private:
friend class NalUnitToByteStreamConverterTest; friend class NalUnitToByteStreamConverterTest;

View File

@ -8,6 +8,7 @@
#include "packager/media/base/media_sample.h" #include "packager/media/base/media_sample.h"
#include "packager/media/codecs/nal_unit_to_byte_stream_converter.h" #include "packager/media/codecs/nal_unit_to_byte_stream_converter.h"
#include "packager/media/formats/mp4/box_definitions_comparison.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -236,7 +237,7 @@ TEST(NalUnitToByteStreamConverterTest, ConvertUnitToByteStreamWithEscape) {
// NALU ending with 0 must have 3 appended. // NALU ending with 0 must have 3 appended.
TEST(NalUnitToByteStreamConverterTest, NaluEndingWithZero) { TEST(NalUnitToByteStreamConverterTest, NaluEndingWithZero) {
const uint8_t kNaluEndingWithZero[] = { const uint8_t kNaluEndingWithZero[] = {
0x00, 0x00, 0x00, 0x03, // Size 10 NALU. 0x00, 0x00, 0x00, 0x03, // Size 3 NALU.
0x06, // NAL unit type. 0x06, // NAL unit type.
0xAA, 0x00, // Ends with 0. 0xAA, 0x00, // Ends with 0.
}; };
@ -270,7 +271,7 @@ TEST(NalUnitToByteStreamConverterTest, NaluEndingWithZero) {
// configuration is not used. // configuration is not used.
TEST(NalUnitToByteStreamConverterTest, NonKeyFrameSample) { TEST(NalUnitToByteStreamConverterTest, NonKeyFrameSample) {
const uint8_t kNonKeyFrameStream[] = { const uint8_t kNonKeyFrameStream[] = {
0x00, 0x00, 0x00, 0x03, // Size 10 NALU. 0x00, 0x00, 0x00, 0x03, // Size 3 NALU.
0x06, // NAL unit type. 0x06, // NAL unit type.
0x33, 0x88, 0x33, 0x88,
}; };
@ -304,7 +305,7 @@ TEST(NalUnitToByteStreamConverterTest, NonKeyFrameSample) {
// The zeros aren't contiguous but the escape byte was inserted. // The zeros aren't contiguous but the escape byte was inserted.
TEST(NalUnitToByteStreamConverterTest, DispersedZeros) { TEST(NalUnitToByteStreamConverterTest, DispersedZeros) {
const uint8_t kDispersedZeros[] = { const uint8_t kDispersedZeros[] = {
0x00, 0x00, 0x00, 0x08, // Size 10 NALU. 0x00, 0x00, 0x00, 0x08, // Size 8 NALU.
0x06, // NAL unit type. 0x06, // NAL unit type.
// After 2 zeros (including the first byte of the NALU followed by 0, 1, // After 2 zeros (including the first byte of the NALU followed by 0, 1,
// 2, or 3 caused it to insert the escape byte. // 2, or 3 caused it to insert the escape byte.
@ -370,5 +371,241 @@ TEST(NalUnitToByteStreamConverterTest, DoNotEscape) {
output); output);
} }
// All NAL units have both clear and ciper text
TEST(NalUnitToByteStreamConverterTest, NoClearNAL) {
// Only the type of the NAL units are checked.
// This does not contain AUD, SPS, nor PPS.
const uint8_t kUnitStreamLikeMediaSample[] = {
0x00, 0x00, 0x00, 0x0A, // Size 10 NALU.
0x02, // NAL unit type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77, // Slice data
0x00, 0x00, 0x00, 0x08, // Size 8 NALU.
0x02, // NAL unit type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77, // Slice data
};
std::vector<SubsampleEntry> subsamples{SubsampleEntry(5, 9),
SubsampleEntry(5, 7)};
NalUnitToByteStreamConverter converter;
EXPECT_TRUE(converter.Initialize(
kTestAVCDecoderConfigurationRecord,
arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData));
std::vector<uint8_t> output;
EXPECT_TRUE(converter.ConvertUnitToByteStreamWithSubsamples(
kUnitStreamLikeMediaSample, arraysize(kUnitStreamLikeMediaSample),
kIsKeyFrame, &output, &subsamples));
const uint8_t kExpectedOutput[] = {
0x00, 0x00, 0x00, 0x01, // Start code.
0x09, // AUD type.
0xF0, // primary pic type is anything.
0x00, 0x00, 0x00, 0x01, // Start code.
// Some valid SPS data.
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
0x60, 0x0F, 0x16, 0x2D, 0x96,
0x00, 0x00, 0x00, 0x01, // Start code.
0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, // PPS.
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 1.
0x02, // NALU type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77,
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 2.
0x02, // NALU type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77,
};
const std::vector<SubsampleEntry> kExpectedOutputSubsamples{
SubsampleEntry(58, 9), SubsampleEntry(5, 7)};
EXPECT_EQ(std::vector<uint8_t>(kExpectedOutput,
kExpectedOutput + arraysize(kExpectedOutput)),
output);
EXPECT_EQ(kExpectedOutputSubsamples, subsamples);
}
// Some NAL units have all clear text
TEST(NalUnitToByteStreamConverterTest, WithSomeClearNAL) {
// Only the type of the NAL units are checked.
// This does not contain AUD, SPS, nor PPS.
const uint8_t kUnitStreamLikeMediaSample[] = {
0x00, 0x00, 0x00, 0x0A, // Size 10 NALU.
0x06, // NAL unit type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77,
0x00, 0x00, 0x00, 0x08, // Size 8 NALU.
0x02, // NAL unit type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77, // Slice data
};
std::vector<SubsampleEntry> subsamples{SubsampleEntry(19, 7)};
NalUnitToByteStreamConverter converter;
EXPECT_TRUE(converter.Initialize(
kTestAVCDecoderConfigurationRecord,
arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData));
std::vector<uint8_t> output;
EXPECT_TRUE(converter.ConvertUnitToByteStreamWithSubsamples(
kUnitStreamLikeMediaSample, arraysize(kUnitStreamLikeMediaSample),
kIsKeyFrame, &output, &subsamples));
const uint8_t kExpectedOutput[] = {
0x00, 0x00, 0x00, 0x01, // Start code.
0x09, // AUD type.
0xF0, // primary pic type is anything.
0x00, 0x00, 0x00, 0x01, // Start code.
// Some valid SPS data.
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
0x60, 0x0F, 0x16, 0x2D, 0x96,
0x00, 0x00, 0x00, 0x01, // Start code.
0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, // PPS.
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 1.
0x06, // NALU type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77,
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 2.
0x02, // NALU type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77,
};
const std::vector<SubsampleEntry> kExpectedOutputSubsamples{
SubsampleEntry(72, 7)};
EXPECT_EQ(std::vector<uint8_t>(kExpectedOutput,
kExpectedOutput + arraysize(kExpectedOutput)),
output);
EXPECT_EQ(kExpectedOutputSubsamples, subsamples);
}
// A encrypted PPS NALU follows a clear NALU, the PPS will be removed. So the
// corresponding subsample needs to be removed.
TEST(NalUnitToByteStreamConverterTest, EncryptedPps) {
// Only the type of the NAL units are checked.
// This does not contain AUD, SPS, nor PPS.
const uint8_t kUnitStreamLikeMediaSample[] = {
0x00, 0x00, 0x00, 0x0A, // Size 10 NALU.
0x06, // NAL unit type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77, // clear
0x00, 0x00, 0x00, 0x0B, // Size 11 NALU.
0x68, // PPS, will be removed after convertion
// The content of PPS is not checked.
0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x12, 0x12, 0x13, 0x14, 0x15, // cipher
0x00, 0x00, 0x00, 0x08, // Size 8 NALU.
0x02, // NAL unit type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77, // Slice data, cipher
};
std::vector<SubsampleEntry> subsamples{SubsampleEntry(19, 10),
SubsampleEntry(5, 7)};
NalUnitToByteStreamConverter converter;
EXPECT_TRUE(converter.Initialize(
kTestAVCDecoderConfigurationRecord,
arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData));
std::vector<uint8_t> output;
EXPECT_TRUE(converter.ConvertUnitToByteStreamWithSubsamples(
kUnitStreamLikeMediaSample, arraysize(kUnitStreamLikeMediaSample),
kIsKeyFrame, &output, &subsamples));
const uint8_t kExpectedOutput[] = {
0x00, 0x00, 0x00, 0x01, // Start code.
0x09, // AUD type.
0xF0, // primary pic type is anything.
0x00, 0x00, 0x00, 0x01, // Start code.
// Some valid SPS data.
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
0x60, 0x0F, 0x16, 0x2D, 0x96,
0x00, 0x00, 0x00, 0x01, // Start code.
0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, // PPS.
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 1.
0x06, // NALU type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77,
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 2.
0x02, // NALU type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77,
};
const std::vector<SubsampleEntry> kExpectedOutputSubsamples{
SubsampleEntry(72, 7)};
EXPECT_EQ(std::vector<uint8_t>(kExpectedOutput,
kExpectedOutput + arraysize(kExpectedOutput)),
output);
EXPECT_EQ(kExpectedOutputSubsamples, subsamples);
}
// A clear PPS NALU follows a clear NALU, the PPS will be removed. So the
// corresponding subsample's clear bytes may be reduced.
TEST(NalUnitToByteStreamConverterTest, ClearPps) {
// Only the type of the NAL units are checked.
// This does not contain AUD, SPS, nor PPS.
const uint8_t kUnitStreamLikeMediaSample[] = {
0x00, 0x00, 0x00, 0x0A, // Size 10 NALU.
0x06, // NAL unit type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77, // clear
0x00, 0x00, 0x00, 0x0B, // Size 11 NALU.
0x68, // PPS, will be removed after convertion
// The content of PPS is not checked.
0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x12, 0x12, 0x13, 0x14, 0x15, // clear
0x00, 0x00, 0x00, 0x08, // Size 8 NALU.
0x02, // NAL unit type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77, // Slice data, cipher
};
std::vector<SubsampleEntry> subsamples{SubsampleEntry(34, 7)};
NalUnitToByteStreamConverter converter;
EXPECT_TRUE(converter.Initialize(
kTestAVCDecoderConfigurationRecord,
arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData));
std::vector<uint8_t> output;
EXPECT_TRUE(converter.ConvertUnitToByteStreamWithSubsamples(
kUnitStreamLikeMediaSample, arraysize(kUnitStreamLikeMediaSample),
kIsKeyFrame, &output, &subsamples));
const uint8_t kExpectedOutput[] = {
0x00, 0x00, 0x00, 0x01, // Start code.
0x09, // AUD type.
0xF0, // primary pic type is anything.
0x00, 0x00, 0x00, 0x01, // Start code.
// Some valid SPS data.
0x67, 0x64, 0x00, 0x1E, 0xAC, 0xD9, 0x40, 0xB4,
0x2F, 0xF9, 0x7F, 0xF0, 0x00, 0x80, 0x00, 0x91,
0x00, 0x00, 0x03, 0x03, 0xE9, 0x00, 0x00, 0xEA,
0x60, 0x0F, 0x16, 0x2D, 0x96,
0x00, 0x00, 0x00, 0x01, // Start code.
0x68, 0xFE, 0xFD, 0xFC, 0xFB, 0x11, 0x12, 0x13, 0x14, 0x15, // PPS.
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 1.
0x06, // NALU type.
0xFD, 0x78, 0xA4, 0xC3, 0x82, 0x62, 0x11, 0x29, 0x77,
0x00, 0x00, 0x00, 0x01, // Start code.
// The input NALU 2.
0x02, // NALU type.
0xFD, 0x78, 0xA4, 0x82, 0x62, 0x29, 0x77,
};
const std::vector<SubsampleEntry> kExpectedOutputSubsamples{
SubsampleEntry(72, 7)};
EXPECT_EQ(std::vector<uint8_t>(kExpectedOutput,
kExpectedOutput + arraysize(kExpectedOutput)),
output);
EXPECT_EQ(kExpectedOutputSubsamples, subsamples);
}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -562,13 +562,26 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
const bool is_encrypted = const bool is_encrypted =
entry.sinf.info.track_encryption.default_is_protected == 1; entry.sinf.info.track_encryption.default_is_protected == 1;
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted; DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
streams.push_back(new VideoStreamInfo( scoped_refptr<VideoStreamInfo> video_stream_info(new VideoStreamInfo(
track->header.track_id, timescale, duration, video_codec, track->header.track_id, timescale, duration, video_codec,
codec_string, entry.codec_configuration.data.data(), codec_string, entry.codec_configuration.data.data(),
entry.codec_configuration.data.size(), coded_width, coded_height, entry.codec_configuration.data.size(), coded_width, coded_height,
pixel_width, pixel_height, pixel_width, pixel_height,
0, // trick_play_rate 0, // trick_play_rate
nalu_length_size, track->media.header.language.code, is_encrypted)); nalu_length_size, track->media.header.language.code, is_encrypted));
// Set pssh raw data if it has.
if (moov_->pssh.size() > 0) {
std::vector<uint8_t> pssh_raw_data;
for (const auto& pssh : moov_->pssh) {
pssh_raw_data.insert(pssh_raw_data.end(), pssh.raw_box.begin(),
pssh.raw_box.end());
}
video_stream_info->set_eme_init_data(pssh_raw_data.data(),
pssh_raw_data.size());
}
streams.push_back(video_stream_info);
} }
} }
@ -684,18 +697,21 @@ bool MP4MediaParser::EnqueueSample(bool* err) {
scoped_refptr<MediaSample> stream_sample(MediaSample::CopyFrom( scoped_refptr<MediaSample> stream_sample(MediaSample::CopyFrom(
buf, runs_->sample_size(), runs_->is_keyframe())); buf, runs_->sample_size(), runs_->is_keyframe()));
if (runs_->is_encrypted()) { if (runs_->is_encrypted()) {
if (!decryptor_source_) { std::unique_ptr<DecryptConfig> decrypt_config = runs_->GetDecryptConfig();
if (!decrypt_config) {
*err = true; *err = true;
LOG(ERROR) << "Encrypted media sample encountered, but decryption is not " LOG(ERROR) << "Missing decrypt config.";
"enabled";
return false; return false;
} }
std::unique_ptr<DecryptConfig> decrypt_config = runs_->GetDecryptConfig(); if (!decryptor_source_) {
if (!decrypt_config || // If the demuxer does not have the decryptor_source_, store
!decryptor_source_->DecryptSampleBuffer(decrypt_config.get(), // decrypt_config so that the demuxed sample can be decrypted later.
stream_sample->writable_data(), stream_sample->set_decrypt_config(std::move(decrypt_config));
stream_sample->data_size())) { stream_sample->set_is_encrypted(true);
} else if (!decryptor_source_->DecryptSampleBuffer(
decrypt_config.get(), stream_sample->writable_data(),
stream_sample->data_size())) {
*err = true; *err = true;
LOG(ERROR) << "Cannot decrypt samples."; LOG(ERROR) << "Cannot decrypt samples.";
return false; return false;

View File

@ -228,9 +228,13 @@ TEST_F(MP4MediaParserTest, NON_FRAGMENTED_MP4) {
} }
TEST_F(MP4MediaParserTest, CencWithoutDecryptionSource) { TEST_F(MP4MediaParserTest, CencWithoutDecryptionSource) {
// Parsing should fail but it will get the streams successfully. EXPECT_TRUE(ParseMP4File("bear-640x360-v_frag-cenc-aux.mp4", 512));
EXPECT_FALSE(ParseMP4File("bear-640x360-v_frag-cenc-aux.mp4", 512));
EXPECT_EQ(1u, num_streams_); EXPECT_EQ(1u, num_streams_);
// Check if pssh is present.
const int kVideoTrackId = 1;
EXPECT_NE(0u,
reinterpret_cast<VideoStreamInfo*>(stream_map_[kVideoTrackId].get())
->eme_init_data().size());
} }
TEST_F(MP4MediaParserTest, CencInitWithoutDecryptionSource) { TEST_F(MP4MediaParserTest, CencInitWithoutDecryptionSource) {

View File

@ -373,13 +373,13 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
if (decrypt_config) { if (decrypt_config) {
if (!decryptor_source_) { if (!decryptor_source_) {
LOG(ERROR) << "Encrypted media sample encountered, but decryption is " // If the demuxer does not have the decryptor_source_, store
"not enabled"; // decrypt_config so that the demuxed sample can be decrypted later.
return false; buffer->set_decrypt_config(std::move(decrypt_config));
} buffer->set_is_encrypted(true);
if (!decryptor_source_->DecryptSampleBuffer(decrypt_config.get(), } else if (!decryptor_source_->DecryptSampleBuffer(
buffer->writable_data(), decrypt_config.get(), buffer->writable_data(),
buffer->data_size())) { buffer->data_size())) {
LOG(ERROR) << "Cannot decrypt samples"; LOG(ERROR) << "Cannot decrypt samples";
return false; return false;
} }