From 28828b8a15b2f29274043613eb0b929bb2d0333e Mon Sep 17 00:00:00 2001 From: Haoming Chen Date: Tue, 16 Aug 2016 17:57:34 -0700 Subject: [PATCH] Add encryption information in demuxer. - Add decrypt_config in media sample and stream. - Update subsamples when converting NAL unit to bytestream. Change-Id: I7b8975a453f81b22cf74bee3c9a58b7e458cbaae --- packager/media/base/media_sample.h | 12 + packager/media/base/media_stream.cc | 2 +- packager/media/base/video_stream_info.h | 9 + .../nal_unit_to_byte_stream_converter.cc | 81 ++++++ .../nal_unit_to_byte_stream_converter.h | 20 +- ..._unit_to_byte_stream_converter_unittest.cc | 243 +++++++++++++++++- .../media/formats/mp4/mp4_media_parser.cc | 34 ++- .../formats/mp4/mp4_media_parser_unittest.cc | 8 +- .../media/formats/webm/webm_cluster_parser.cc | 14 +- 9 files changed, 400 insertions(+), 23 deletions(-) diff --git a/packager/media/base/media_sample.h b/packager/media/base/media_sample.h index 720b0e9dfa..166a2ec26d 100644 --- a/packager/media/base/media_sample.h +++ b/packager/media/base/media_sample.h @@ -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 { 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 { is_encrypted_ = value; } + void set_decrypt_config(std::unique_ptr 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 { // For now this is the cue identifier for WebVTT. std::string config_id_; + // Decrypt configuration. + std::unique_ptr decrypt_config_; + DISALLOW_COPY_AND_ASSIGN(MediaSample); }; diff --git a/packager/media/base/media_stream.cc b/packager/media/base/media_stream.cc index 3d2ff720f8..a6f2db486c 100644 --- a/packager/media/base/media_stream.cc +++ b/packager/media/base/media_stream.cc @@ -22,7 +22,7 @@ MediaStream::MediaStream(scoped_refptr info, Demuxer* demuxer) MediaStream::~MediaStream() {} Status MediaStream::PullSample(scoped_refptr* sample) { - DCHECK_EQ(state_, kPulling); + DCHECK(state_ == kPulling || state_ == kIdle); // Trigger a new parse in demuxer if no more samples. while (samples_.empty()) { diff --git a/packager/media/base/video_stream_info.h b/packager/media/base/video_stream_info.h index 163b982e12..b375689b94 100644 --- a/packager/media/base/video_stream_info.h +++ b/packager/media/base/video_stream_info.h @@ -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& 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 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. diff --git a/packager/media/codecs/nal_unit_to_byte_stream_converter.cc b/packager/media/codecs/nal_unit_to_byte_stream_converter.cc index 9bcef858e1..32c8fb7ddb 100644 --- a/packager/media/codecs/nal_unit_to_byte_stream_converter.cc +++ b/packager/media/codecs/nal_unit_to_byte_stream_converter.cc @@ -46,6 +46,27 @@ void AddAccessUnitDelimiter(BufferWriter* buffer_writer) { buffer_writer->AppendInt(kAccessUnitDelimiterRbspAnyPrimaryPicType); } +bool CheckSubsampleValid(const std::vector* 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* 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* output, + std::vector* 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); } diff --git a/packager/media/codecs/nal_unit_to_byte_stream_converter.h b/packager/media/codecs/nal_unit_to_byte_stream_converter.h index 77d6621e5f..b312d60400 100644 --- a/packager/media/codecs/nal_unit_to_byte_stream_converter.h +++ b/packager/media/codecs/nal_unit_to_byte_stream_converter.h @@ -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* 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* output, + std::vector* subsamples); + private: friend class NalUnitToByteStreamConverterTest; diff --git a/packager/media/codecs/nal_unit_to_byte_stream_converter_unittest.cc b/packager/media/codecs/nal_unit_to_byte_stream_converter_unittest.cc index b4d0dcf17d..16a5bdf062 100644 --- a/packager/media/codecs/nal_unit_to_byte_stream_converter_unittest.cc +++ b/packager/media/codecs/nal_unit_to_byte_stream_converter_unittest.cc @@ -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 subsamples{SubsampleEntry(5, 9), + SubsampleEntry(5, 7)}; + + NalUnitToByteStreamConverter converter; + EXPECT_TRUE(converter.Initialize( + kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData)); + + std::vector 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 kExpectedOutputSubsamples{ + SubsampleEntry(58, 9), SubsampleEntry(5, 7)}; + + EXPECT_EQ(std::vector(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 subsamples{SubsampleEntry(19, 7)}; + + NalUnitToByteStreamConverter converter; + EXPECT_TRUE(converter.Initialize( + kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData)); + + std::vector 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 kExpectedOutputSubsamples{ + SubsampleEntry(72, 7)}; + + EXPECT_EQ(std::vector(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 subsamples{SubsampleEntry(19, 10), + SubsampleEntry(5, 7)}; + + NalUnitToByteStreamConverter converter; + EXPECT_TRUE(converter.Initialize( + kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData)); + + std::vector 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 kExpectedOutputSubsamples{ + SubsampleEntry(72, 7)}; + + EXPECT_EQ(std::vector(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 subsamples{SubsampleEntry(34, 7)}; + + NalUnitToByteStreamConverter converter; + EXPECT_TRUE(converter.Initialize( + kTestAVCDecoderConfigurationRecord, + arraysize(kTestAVCDecoderConfigurationRecord), !kEscapeData)); + + std::vector 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 kExpectedOutputSubsamples{ + SubsampleEntry(72, 7)}; + + EXPECT_EQ(std::vector(kExpectedOutput, + kExpectedOutput + arraysize(kExpectedOutput)), + output); + EXPECT_EQ(kExpectedOutputSubsamples, subsamples); +} + } // namespace media } // namespace shaka diff --git a/packager/media/formats/mp4/mp4_media_parser.cc b/packager/media/formats/mp4/mp4_media_parser.cc index bdc3e9f46c..038c909588 100644 --- a/packager/media/formats/mp4/mp4_media_parser.cc +++ b/packager/media/formats/mp4/mp4_media_parser.cc @@ -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 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 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 stream_sample(MediaSample::CopyFrom( buf, runs_->sample_size(), runs_->is_keyframe())); if (runs_->is_encrypted()) { - if (!decryptor_source_) { + std::unique_ptr 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 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; diff --git a/packager/media/formats/mp4/mp4_media_parser_unittest.cc b/packager/media/formats/mp4/mp4_media_parser_unittest.cc index dcce7e9ff2..42896d2bb5 100644 --- a/packager/media/formats/mp4/mp4_media_parser_unittest.cc +++ b/packager/media/formats/mp4/mp4_media_parser_unittest.cc @@ -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(stream_map_[kVideoTrackId].get()) + ->eme_init_data().size()); } TEST_F(MP4MediaParserTest, CencInitWithoutDecryptionSource) { diff --git a/packager/media/formats/webm/webm_cluster_parser.cc b/packager/media/formats/webm/webm_cluster_parser.cc index 4f55202465..7df945abd2 100644 --- a/packager/media/formats/webm/webm_cluster_parser.cc +++ b/packager/media/formats/webm/webm_cluster_parser.cc @@ -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; }