diff options
author | fgalligan@chromium.org <fgalligan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-14 23:52:41 +0000 |
---|---|---|
committer | fgalligan@chromium.org <fgalligan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-14 23:52:41 +0000 |
commit | 4d98e445c98768bb5eb44f8947bea55c1da8334b (patch) | |
tree | b6cf720b8d7ac51b47ab9196bbcaa7c5349977f9 /media/webm | |
parent | 76e5e6d404fa7d608a9da91e3903a9a2c9165a77 (diff) | |
download | chromium_src-4d98e445c98768bb5eb44f8947bea55c1da8334b.zip chromium_src-4d98e445c98768bb5eb44f8947bea55c1da8334b.tar.gz chromium_src-4d98e445c98768bb5eb44f8947bea55c1da8334b.tar.bz2 |
Support for parsing encrypted WebM streams by src.
- Note: Only looking for comments on direction. A lot of work
still needs to be done before committing.
- Added support to FFmpegDemuxer to decrypt encrypted WebM streams.
- Added support to FFmpegDemuxer to handle the needKey and keyAdded
messages.
- Added support to WebMediaPlayerImpl to handle the needKey and
keyAdded messages.
BUG=123426
TEST=All media_unittests pass
Review URL: https://chromiumcodereview.appspot.com/10829470
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@188228 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/webm')
-rw-r--r-- | media/webm/webm_cluster_parser.cc | 49 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser_unittest.cc | 57 | ||||
-rw-r--r-- | media/webm/webm_crypto_helpers.cc | 60 | ||||
-rw-r--r-- | media/webm/webm_crypto_helpers.h | 32 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.cc | 11 |
5 files changed, 159 insertions, 50 deletions
diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc index 8a984c8..a0b95a8 100644 --- a/media/webm/webm_cluster_parser.cc +++ b/media/webm/webm_cluster_parser.cc @@ -10,19 +10,10 @@ #include "media/base/buffers.h" #include "media/base/decrypt_config.h" #include "media/webm/webm_constants.h" +#include "media/webm/webm_crypto_helpers.h" namespace media { -// Generates a 16 byte CTR counter block. The CTR counter block format is a -// CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV. -// |iv_size| is the size of |iv| in btyes. Returns a string of -// kDecryptionKeySize bytes. -static std::string GenerateCounterBlock(const uint8* iv, int iv_size) { - std::string counter_block(reinterpret_cast<const char*>(iv), iv_size); - counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0); - return counter_block; -} - WebMClusterParser::TextTrackIterator::TextTrackIterator( const TextTrackMap& text_track_map) : iterator_(text_track_map.begin()), @@ -294,39 +285,13 @@ bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num, // encrypted WebM request for comments specification is here // http://wiki.webmproject.org/encryption/webm-encryption-rfc if (!encryption_key_id.empty()) { - DCHECK_EQ(kWebMSignalByteSize, 1); - if (size < kWebMSignalByteSize) { - MEDIA_LOG(log_cb_) - << "Got a block from an encrypted stream with no data."; + scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig( + data, size, + reinterpret_cast<const uint8*>(encryption_key_id.data()), + encryption_key_id.size())); + if (!config) return false; - } - uint8 signal_byte = data[0]; - int data_offset = sizeof(signal_byte); - - // Setting the DecryptConfig object of the buffer while leaving the - // initialization vector empty will tell the decryptor that the frame is - // unencrypted. - std::string counter_block; - - if (signal_byte & kWebMFlagEncryptedFrame) { - if (size < kWebMSignalByteSize + kWebMIvSize) { - MEDIA_LOG(log_cb_) << "Got an encrypted block with not enough data " - << size; - return false; - } - counter_block = GenerateCounterBlock(data + data_offset, kWebMIvSize); - data_offset += kWebMIvSize; - } - - // TODO(fgalligan): Revisit if DecryptConfig needs to be set on unencrypted - // frames after the CDM API is finalized. - // Unencrypted frames of potentially encrypted streams currently set - // DecryptConfig. - buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig( - encryption_key_id, - counter_block, - data_offset, - std::vector<SubsampleEntry>()))); + buffer->SetDecryptConfig(config.Pass()); } buffer->SetTimestamp(timestamp); diff --git a/media/webm/webm_cluster_parser_unittest.cc b/media/webm/webm_cluster_parser_unittest.cc index 07deb05..dc73329 100644 --- a/media/webm/webm_cluster_parser_unittest.cc +++ b/media/webm/webm_cluster_parser_unittest.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/logging.h" +#include "media/base/decrypt_config.h" #include "media/webm/cluster_builder.h" #include "media/webm/webm_cluster_parser.h" #include "media/webm/webm_constants.h" @@ -32,7 +33,7 @@ struct BlockInfo { bool use_simple_block; }; -const BlockInfo kDefaultBlockInfo[] = { +static const BlockInfo kDefaultBlockInfo[] = { { kAudioTrackNum, 0, 23, true }, { kAudioTrackNum, 23, 23, true }, { kVideoTrackNum, 33, 34, true }, @@ -42,6 +43,11 @@ const BlockInfo kDefaultBlockInfo[] = { { kVideoTrackNum, 100, 33, false }, }; +static const uint8 kEncryptedFrame[] = { + 0x01, // Block is encrypted + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 // IV +}; + static scoped_ptr<Cluster> CreateCluster(int timecode, const BlockInfo* block_info, int block_count) { @@ -67,6 +73,18 @@ static scoped_ptr<Cluster> CreateCluster(int timecode, return cb.Finish(); } +// Creates a Cluster with one encrypted Block. |bytes_to_write| is number of +// bytes of the encrypted frame to write. +static scoped_ptr<Cluster> CreateEncryptedCluster(int bytes_to_write) { + CHECK_GT(bytes_to_write, 0); + CHECK_LE(bytes_to_write, static_cast<int>(sizeof(kEncryptedFrame))); + + ClusterBuilder cb; + cb.SetClusterTimecode(0); + cb.AddSimpleBlock(kVideoTrackNum, 0, 0, kEncryptedFrame, bytes_to_write); + return cb.Finish(); +} + static bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers, const WebMClusterParser::BufferQueue& video_buffers, const WebMClusterParser::BufferQueue& text_buffers, @@ -165,6 +183,15 @@ static bool VerifyTextBuffers( return true; } +static bool VerifyEncryptedBuffer( + scoped_refptr<StreamParserBuffer> buffer) { + EXPECT_TRUE(buffer->GetDecryptConfig()); + EXPECT_EQ(static_cast<unsigned long>(DecryptConfig::kDecryptionKeySize), + buffer->GetDecryptConfig()->iv().length()); + const uint8* data = buffer->GetData(); + return data[0] & kWebMFlagEncryptedFrame; +} + static void AppendToEnd(const WebMClusterParser::BufferQueue& src, WebMClusterParser::BufferQueue* dest) { for (WebMClusterParser::BufferQueue::const_iterator itr = src.begin(); @@ -440,4 +467,32 @@ TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) { } } +TEST_F(WebMClusterParserTest, ParseEncryptedBlock) { + scoped_ptr<Cluster> cluster(CreateEncryptedCluster(sizeof(kEncryptedFrame))); + + parser_.reset(new WebMClusterParser( + kTimecodeScale, kAudioTrackNum, kVideoTrackNum, + std::set<int>(), + std::set<int64>(), "", "video_key_id", + LogCB())); + int result = parser_->Parse(cluster->data(), cluster->size()); + EXPECT_EQ(cluster->size(), result); + ASSERT_EQ(1UL, parser_->video_buffers().size()); + scoped_refptr<StreamParserBuffer> buffer = parser_->video_buffers()[0]; + EXPECT_TRUE(VerifyEncryptedBuffer(buffer)); +} + +TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) { + scoped_ptr<Cluster> cluster( + CreateEncryptedCluster(sizeof(kEncryptedFrame) - 1)); + + parser_.reset(new WebMClusterParser( + kTimecodeScale, kAudioTrackNum, kVideoTrackNum, + std::set<int>(), + std::set<int64>(), "", "video_key_id", + LogCB())); + int result = parser_->Parse(cluster->data(), cluster->size()); + EXPECT_EQ(-1, result); +} + } // namespace media diff --git a/media/webm/webm_crypto_helpers.cc b/media/webm/webm_crypto_helpers.cc new file mode 100644 index 0000000..ea63f87 --- /dev/null +++ b/media/webm/webm_crypto_helpers.cc @@ -0,0 +1,60 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/webm/webm_crypto_helpers.h" + +#include "base/logging.h" +#include "base/sys_byteorder.h" +#include "media/base/decrypt_config.h" +#include "media/webm/webm_constants.h" + +namespace media { +namespace { + +// Generates a 16 byte CTR counter block. The CTR counter block format is a +// CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV. +// |iv_size| is the size of |iv| in btyes. Returns a string of +// kDecryptionKeySize bytes. +std::string GenerateWebMCounterBlock(const uint8* iv, int iv_size) { + std::string counter_block(reinterpret_cast<const char*>(iv), iv_size); + counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0); + return counter_block; +} + +} // namespace anonymous + +scoped_ptr<DecryptConfig> WebMCreateDecryptConfig( + const uint8* data, int data_size, + const uint8* key_id, int key_id_size) { + if (data_size < kWebMSignalByteSize) { + DVLOG(1) << "Got a block from an encrypted stream with no data."; + return scoped_ptr<DecryptConfig>(NULL); + } + + uint8 signal_byte = data[0]; + int frame_offset = sizeof(signal_byte); + + // Setting the DecryptConfig object of the buffer while leaving the + // initialization vector empty will tell the decryptor that the frame is + // unencrypted. + std::string counter_block; + + if (signal_byte & kWebMFlagEncryptedFrame) { + if (data_size < kWebMSignalByteSize + kWebMIvSize) { + DVLOG(1) << "Got an encrypted block with not enough data " << data_size; + return scoped_ptr<DecryptConfig>(NULL); + } + counter_block = GenerateWebMCounterBlock(data + frame_offset, kWebMIvSize); + frame_offset += kWebMIvSize; + } + + scoped_ptr<DecryptConfig> config(new DecryptConfig( + std::string(reinterpret_cast<const char*>(key_id), key_id_size), + counter_block, + frame_offset, + std::vector<SubsampleEntry>())); + return config.Pass(); +} + +} // namespace media diff --git a/media/webm/webm_crypto_helpers.h b/media/webm/webm_crypto_helpers.h new file mode 100644 index 0000000..c5f1f15 --- /dev/null +++ b/media/webm/webm_crypto_helpers.h @@ -0,0 +1,32 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_WEBM_WEBM_CRYPTO_HELPERS_H_ +#define MEDIA_WEBM_WEBM_CRYPTO_HELPERS_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/decoder_buffer.h" + +namespace media { + +// TODO(xhwang): Figure out the init data type appropriately once it's spec'ed. +// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=19096 for more +// information. +const char kWebMEncryptInitDataType[] = "video/webm"; + +// Returns an initialized DecryptConfig, which can be sent to the Decryptor if +// the stream has potentially encrypted frames. Every encrypted Block has a +// signal byte, and if the frame is encrypted, an initialization vector +// prepended to the frame. Leaving the IV empty will tell the decryptor that the +// frame is unencrypted. Returns NULL if |data| is invalid. Current encrypted +// WebM request for comments specification is here +// http://wiki.webmproject.org/encryption/webm-encryption-rfc +scoped_ptr<DecryptConfig> WebMCreateDecryptConfig( + const uint8* data, int data_size, + const uint8* key_id, int key_id_size); + +} // namespace media + +#endif // MEDIA_WEBM_WEBM_CRYPT_HELPERS_H_ diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc index b60de5d..f47fa41 100644 --- a/media/webm/webm_stream_parser.cc +++ b/media/webm/webm_stream_parser.cc @@ -14,14 +14,12 @@ #include "media/webm/webm_cluster_parser.h" #include "media/webm/webm_constants.h" #include "media/webm/webm_content_encodings.h" +#include "media/webm/webm_crypto_helpers.h" #include "media/webm/webm_info_parser.h" #include "media/webm/webm_tracks_parser.h" namespace media { -// TODO(xhwang): Figure out the init data type appropriately once it's spec'ed. -static const char kWebMInitDataType[] = "video/webm"; - // Helper class that uses FFmpeg to create AudioDecoderConfig & // VideoDecoderConfig objects. // @@ -150,13 +148,12 @@ bool FFmpegConfigHelper::SetupStreamConfigs() { bool no_supported_streams = true; for (size_t i = 0; i < format_context->nb_streams; ++i) { AVStream* stream = format_context->streams[i]; - AVCodecContext* codec_context = stream->codec; - AVMediaType codec_type = codec_context->codec_type; + AVMediaType codec_type = stream->codec->codec_type; if (codec_type == AVMEDIA_TYPE_AUDIO && stream->codec->codec_id == CODEC_ID_VORBIS && !audio_config_.IsValidConfig()) { - AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_); + AVStreamToAudioDecoderConfig(stream, &audio_config_); no_supported_streams = false; continue; } @@ -473,7 +470,7 @@ void WebMStreamParser::FireNeedKey(const std::string& key_id) { DCHECK_GT(key_id_size, 0); scoped_array<uint8> key_id_array(new uint8[key_id_size]); memcpy(key_id_array.get(), key_id.data(), key_id_size); - need_key_cb_.Run(kWebMInitDataType, key_id_array.Pass(), key_id_size); + need_key_cb_.Run(kWebMEncryptInitDataType, key_id_array.Pass(), key_id_size); } } // namespace media |