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:
parent
6e8aa27e74
commit
28828b8a15
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "packager/base/logging.h"
|
||||
#include "packager/base/memory/ref_counted.h"
|
||||
#include "packager/media/base/decrypt_config.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
@ -117,6 +118,10 @@ class MediaSample : public base::RefCountedThreadSafe<MediaSample> {
|
|||
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) {
|
||||
data_.assign(data, data + data_size);
|
||||
}
|
||||
|
@ -133,6 +138,10 @@ class MediaSample : public base::RefCountedThreadSafe<MediaSample> {
|
|||
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.
|
||||
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.
|
||||
std::string config_id_;
|
||||
|
||||
// Decrypt configuration.
|
||||
std::unique_ptr<DecryptConfig> decrypt_config_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MediaSample);
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ MediaStream::MediaStream(scoped_refptr<StreamInfo> info, Demuxer* demuxer)
|
|||
MediaStream::~MediaStream() {}
|
||||
|
||||
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.
|
||||
while (samples_.empty()) {
|
||||
|
|
|
@ -42,11 +42,16 @@ class VideoStreamInfo : public StreamInfo {
|
|||
uint32_t pixel_height() const { return pixel_height_; }
|
||||
uint8_t nalu_length_size() const { return nalu_length_size_; }
|
||||
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_height(uint32_t height) { height_ = height; }
|
||||
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_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:
|
||||
~VideoStreamInfo() override;
|
||||
|
@ -65,6 +70,10 @@ class VideoStreamInfo : public StreamInfo {
|
|||
// (H.264).
|
||||
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
|
||||
// generated copy constructor and assignment operator. Since the extra data is
|
||||
// typically small, the performance impact is minimal.
|
||||
|
|
|
@ -46,6 +46,27 @@ void AddAccessUnitDelimiter(BufferWriter* buffer_writer) {
|
|||
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
|
||||
|
||||
void EscapeNalByteSequence(const uint8_t* input,
|
||||
|
@ -144,17 +165,39 @@ bool NalUnitToByteStreamConverter::ConvertUnitToByteStream(
|
|||
size_t sample_size,
|
||||
bool is_key_frame,
|
||||
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) {
|
||||
LOG(WARNING) << "Sample is empty.";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (subsamples && escape_data_) {
|
||||
LOG(ERROR) << "escape_data_ should not be set when updating subsamples.";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
BufferWriter buffer_writer(sample_size);
|
||||
buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode));
|
||||
AddAccessUnitDelimiter(&buffer_writer);
|
||||
if (is_key_frame)
|
||||
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);
|
||||
Nalu nalu;
|
||||
NaluReader::Result result = nalu_reader.Advance(&nalu);
|
||||
|
@ -166,12 +209,50 @@ bool NalUnitToByteStreamConverter::ConvertUnitToByteStream(
|
|||
case Nalu::H264_SPS:
|
||||
FALLTHROUGH_INTENDED;
|
||||
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;
|
||||
default:
|
||||
buffer_writer.AppendArray(kNaluStartCode, arraysize(kNaluStartCode));
|
||||
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;
|
||||
}
|
||||
|
||||
result = nalu_reader.Advance(&nalu);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "packager/base/macros.h"
|
||||
#include "packager/base/memory/ref_counted.h"
|
||||
#include "packager/media/base/decrypt_config.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
@ -53,13 +54,30 @@ class NalUnitToByteStreamConverter {
|
|||
/// SAMPLE-AES encryption.
|
||||
/// @param sample is the sample to be converted.
|
||||
/// @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.
|
||||
virtual bool ConvertUnitToByteStream(const uint8_t* sample,
|
||||
size_t sample_size,
|
||||
bool is_key_frame,
|
||||
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:
|
||||
friend class NalUnitToByteStreamConverterTest;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "packager/media/base/media_sample.h"
|
||||
#include "packager/media/codecs/nal_unit_to_byte_stream_converter.h"
|
||||
#include "packager/media/formats/mp4/box_definitions_comparison.h"
|
||||
|
||||
namespace shaka {
|
||||
namespace media {
|
||||
|
@ -236,7 +237,7 @@ TEST(NalUnitToByteStreamConverterTest, ConvertUnitToByteStreamWithEscape) {
|
|||
// NALU ending with 0 must have 3 appended.
|
||||
TEST(NalUnitToByteStreamConverterTest, NaluEndingWithZero) {
|
||||
const uint8_t kNaluEndingWithZero[] = {
|
||||
0x00, 0x00, 0x00, 0x03, // Size 10 NALU.
|
||||
0x00, 0x00, 0x00, 0x03, // Size 3 NALU.
|
||||
0x06, // NAL unit type.
|
||||
0xAA, 0x00, // Ends with 0.
|
||||
};
|
||||
|
@ -270,7 +271,7 @@ TEST(NalUnitToByteStreamConverterTest, NaluEndingWithZero) {
|
|||
// configuration is not used.
|
||||
TEST(NalUnitToByteStreamConverterTest, NonKeyFrameSample) {
|
||||
const uint8_t kNonKeyFrameStream[] = {
|
||||
0x00, 0x00, 0x00, 0x03, // Size 10 NALU.
|
||||
0x00, 0x00, 0x00, 0x03, // Size 3 NALU.
|
||||
0x06, // NAL unit type.
|
||||
0x33, 0x88,
|
||||
};
|
||||
|
@ -304,7 +305,7 @@ TEST(NalUnitToByteStreamConverterTest, NonKeyFrameSample) {
|
|||
// The zeros aren't contiguous but the escape byte was inserted.
|
||||
TEST(NalUnitToByteStreamConverterTest, DispersedZeros) {
|
||||
const uint8_t kDispersedZeros[] = {
|
||||
0x00, 0x00, 0x00, 0x08, // Size 10 NALU.
|
||||
0x00, 0x00, 0x00, 0x08, // Size 8 NALU.
|
||||
0x06, // NAL unit type.
|
||||
// After 2 zeros (including the first byte of the NALU followed by 0, 1,
|
||||
// 2, or 3 caused it to insert the escape byte.
|
||||
|
@ -370,5 +371,241 @@ TEST(NalUnitToByteStreamConverterTest, DoNotEscape) {
|
|||
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 shaka
|
||||
|
|
|
@ -562,13 +562,26 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
const bool is_encrypted =
|
||||
entry.sinf.info.track_encryption.default_is_protected == 1;
|
||||
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,
|
||||
codec_string, entry.codec_configuration.data.data(),
|
||||
entry.codec_configuration.data.size(), coded_width, coded_height,
|
||||
pixel_width, pixel_height,
|
||||
0, // trick_play_rate
|
||||
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(
|
||||
buf, runs_->sample_size(), runs_->is_keyframe()));
|
||||
if (runs_->is_encrypted()) {
|
||||
if (!decryptor_source_) {
|
||||
std::unique_ptr<DecryptConfig> decrypt_config = runs_->GetDecryptConfig();
|
||||
if (!decrypt_config) {
|
||||
*err = true;
|
||||
LOG(ERROR) << "Encrypted media sample encountered, but decryption is not "
|
||||
"enabled";
|
||||
LOG(ERROR) << "Missing decrypt config.";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<DecryptConfig> decrypt_config = runs_->GetDecryptConfig();
|
||||
if (!decrypt_config ||
|
||||
!decryptor_source_->DecryptSampleBuffer(decrypt_config.get(),
|
||||
stream_sample->writable_data(),
|
||||
stream_sample->data_size())) {
|
||||
if (!decryptor_source_) {
|
||||
// If the demuxer does not have the decryptor_source_, store
|
||||
// decrypt_config so that the demuxed sample can be decrypted later.
|
||||
stream_sample->set_decrypt_config(std::move(decrypt_config));
|
||||
stream_sample->set_is_encrypted(true);
|
||||
} else if (!decryptor_source_->DecryptSampleBuffer(
|
||||
decrypt_config.get(), stream_sample->writable_data(),
|
||||
stream_sample->data_size())) {
|
||||
*err = true;
|
||||
LOG(ERROR) << "Cannot decrypt samples.";
|
||||
return false;
|
||||
|
|
|
@ -228,9 +228,13 @@ TEST_F(MP4MediaParserTest, NON_FRAGMENTED_MP4) {
|
|||
}
|
||||
|
||||
TEST_F(MP4MediaParserTest, CencWithoutDecryptionSource) {
|
||||
// Parsing should fail but it will get the streams successfully.
|
||||
EXPECT_FALSE(ParseMP4File("bear-640x360-v_frag-cenc-aux.mp4", 512));
|
||||
EXPECT_TRUE(ParseMP4File("bear-640x360-v_frag-cenc-aux.mp4", 512));
|
||||
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) {
|
||||
|
|
|
@ -373,13 +373,13 @@ bool WebMClusterParser::OnBlock(bool is_simple_block,
|
|||
|
||||
if (decrypt_config) {
|
||||
if (!decryptor_source_) {
|
||||
LOG(ERROR) << "Encrypted media sample encountered, but decryption is "
|
||||
"not enabled";
|
||||
return false;
|
||||
}
|
||||
if (!decryptor_source_->DecryptSampleBuffer(decrypt_config.get(),
|
||||
buffer->writable_data(),
|
||||
buffer->data_size())) {
|
||||
// If the demuxer does not have the decryptor_source_, store
|
||||
// decrypt_config so that the demuxed sample can be decrypted later.
|
||||
buffer->set_decrypt_config(std::move(decrypt_config));
|
||||
buffer->set_is_encrypted(true);
|
||||
} else if (!decryptor_source_->DecryptSampleBuffer(
|
||||
decrypt_config.get(), buffer->writable_data(),
|
||||
buffer->data_size())) {
|
||||
LOG(ERROR) << "Cannot decrypt samples";
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue