From a251208a7574119e05948b7732b69a2279797d26 Mon Sep 17 00:00:00 2001 From: KongQun Yang Date: Wed, 31 May 2017 11:14:11 -0700 Subject: [PATCH] Report an error on encrypted stream without decryption Change-Id: I784af57ca79aeb8a12245bd130f413ea6e3d04f1 --- packager/media/demuxer/demuxer.cc | 14 +++++- packager/media/demuxer/demuxer.gyp | 2 + packager/media/demuxer/demuxer.h | 1 + packager/media/demuxer/demuxer_unittest.cc | 58 +++++++++++++++++++++- packager/media/test/test_data_util.cc | 12 +++++ packager/media/test/test_data_util.h | 3 ++ 6 files changed, 88 insertions(+), 2 deletions(-) diff --git a/packager/media/demuxer/demuxer.cc b/packager/media/demuxer/demuxer.cc index 39769835d1..b8aec2d359 100644 --- a/packager/media/demuxer/demuxer.cc +++ b/packager/media/demuxer/demuxer.cc @@ -95,6 +95,8 @@ Status Demuxer::Run() { // info. if (all_streams_ready_ && output_handlers().empty()) return Status::OK; + if (!init_event_status_.ok()) + return init_event_status_; if (!status.ok()) return status; // Check if all specified outputs exists. @@ -258,7 +260,14 @@ void Demuxer::ParserInitEvent( stream_info->stream_type() != kStreamVideo) { stream_info->set_language(iter->second); } - DispatchStreamInfo(stream_index, stream_info); + if (stream_info->is_encrypted()) { + init_event_status_.SetError( + error::INVALID_ARGUMENT, + "A decryption key source is not provided for an encrypted stream."); + } else { + init_event_status_.Update( + DispatchStreamInfo(stream_index, stream_info)); + } } else { track_id_to_stream_index_map_[stream_info->track_id()] = kInvalidStreamIndex; @@ -278,6 +287,9 @@ bool Demuxer::NewSampleEvent(uint32_t track_id, queued_samples_.push_back(QueuedSample(track_id, sample)); return true; } + if (!init_event_status_.ok()) { + return false; + } while (!queued_samples_.empty()) { if (!PushSample(queued_samples_.front().track_id, queued_samples_.front().sample)) { diff --git a/packager/media/demuxer/demuxer.gyp b/packager/media/demuxer/demuxer.gyp index 36177edfd4..4059682bac 100644 --- a/packager/media/demuxer/demuxer.gyp +++ b/packager/media/demuxer/demuxer.gyp @@ -33,7 +33,9 @@ 'demuxer_unittest.cc', ], 'dependencies': [ + '../../testing/gmock.gyp:gmock', '../../testing/gtest.gyp:gtest', + '../base/media_base.gyp:media_handler_test_base', '../test/media_test.gyp:media_test_support', 'demuxer', ] diff --git a/packager/media/demuxer/demuxer.h b/packager/media/demuxer/demuxer.h index 1216d2971f..31b57609a3 100644 --- a/packager/media/demuxer/demuxer.h +++ b/packager/media/demuxer/demuxer.h @@ -140,6 +140,7 @@ class Demuxer : public MediaHandler { bool cancelled_ = false; // Whether to dump stream info when it is received. bool dump_stream_info_ = false; + Status init_event_status_; }; } // namespace media diff --git a/packager/media/demuxer/demuxer_unittest.cc b/packager/media/demuxer/demuxer_unittest.cc index 2421f2900e..2bc8b56fbc 100644 --- a/packager/media/demuxer/demuxer_unittest.cc +++ b/packager/media/demuxer/demuxer_unittest.cc @@ -6,16 +6,72 @@ #include "packager/media/demuxer/demuxer.h" +#include #include +#include "packager/media/base/fixed_key_source.h" +#include "packager/media/base/media_handler_test_base.h" +#include "packager/media/base/test/status_test_util.h" +#include "packager/media/test/test_data_util.h" + namespace shaka { namespace media { +namespace { -TEST(DemuxerTest, FileNotFound) { +using ::testing::_; +using ::testing::Return; +using ::testing::SetArgPointee; + +class MockKeySource : public FixedKeySource { + public: + MOCK_METHOD2(GetKey, + Status(const std::vector& key_id, EncryptionKey* key)); +}; +} // namespace + +class DemuxerTest : public MediaHandlerTestBase { + protected: + EncryptionKey GetMockEncryptionKey() { + const uint8_t kKeyId[]{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + }; + const uint8_t kKey[]{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + }; + EncryptionKey encryption_key; + encryption_key.key_id.assign(kKeyId, kKeyId + sizeof(kKeyId)); + encryption_key.key.assign(kKey, kKey + sizeof(kKey)); + return encryption_key; + } +}; + +TEST_F(DemuxerTest, FileNotFound) { Demuxer demuxer("file_not_exist.mp4"); EXPECT_EQ(error::FILE_FAILURE, demuxer.Run().error_code()); } +TEST_F(DemuxerTest, EncryptedContentWithoutKeySource) { + Demuxer demuxer( + GetAppTestDataFilePath("bear-640x360-v-cenc-golden.mp4").AsUTF8Unsafe()); + ASSERT_OK(demuxer.SetHandler("video", some_handler())); + EXPECT_EQ(error::INVALID_ARGUMENT, demuxer.Run().error_code()); +} + +TEST_F(DemuxerTest, EncryptedContentWithKeySource) { + std::unique_ptr mock_key_source(new MockKeySource); + EXPECT_CALL(*mock_key_source, GetKey(_, _)) + .WillOnce( + DoAll(SetArgPointee<1>(GetMockEncryptionKey()), Return(Status::OK))); + + Demuxer demuxer( + GetAppTestDataFilePath("bear-640x360-v-cenc-golden.mp4").AsUTF8Unsafe()); + demuxer.SetKeySource(std::move(mock_key_source)); + ASSERT_OK(demuxer.SetHandler("video", some_handler())); + EXPECT_OK(demuxer.Run()); +} + // TODO(kqyang): Add more tests. } // namespace media diff --git a/packager/media/test/test_data_util.cc b/packager/media/test/test_data_util.cc index 863f2cc608..ae029dd3bb 100644 --- a/packager/media/test/test_data_util.cc +++ b/packager/media/test/test_data_util.cc @@ -23,6 +23,18 @@ base::FilePath GetTestDataFilePath(const std::string& name) { return file_path; } +base::FilePath GetAppTestDataFilePath(const std::string& name) { + base::FilePath file_path; + CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); + + file_path = file_path.Append(FILE_PATH_LITERAL("packager")) + .Append(FILE_PATH_LITERAL("app")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("testdata")) + .AppendASCII(name); + return file_path; +} + std::vector ReadTestDataFile(const std::string& name) { std::string buffer; CHECK(base::ReadFileToString(GetTestDataFilePath(name), &buffer)); diff --git a/packager/media/test/test_data_util.h b/packager/media/test/test_data_util.h index 4f62f20cec..459f6bfa32 100644 --- a/packager/media/test/test_data_util.h +++ b/packager/media/test/test_data_util.h @@ -17,6 +17,9 @@ namespace media { // Returns a file path for a file in the media/test/data directory. base::FilePath GetTestDataFilePath(const std::string& name); +// Returns a file path for a file in the media/app/test/testdata directory. +base::FilePath GetAppTestDataFilePath(const std::string& name); + // Reads a test file from media/test/data directory and returns its content. std::vector ReadTestDataFile(const std::string& name);