diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-12 01:04:33 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-12 01:04:33 +0000 |
commit | 37141711d11b09cef8405c4c2384549dad316fe9 (patch) | |
tree | 82c6e743cb9b011d5e600069ebb42e4098b1ec81 | |
parent | 8f92ac14a5a83d336f97317a7292394f187d5c16 (diff) | |
download | chromium_src-37141711d11b09cef8405c4c2384549dad316fe9.zip chromium_src-37141711d11b09cef8405c4c2384549dad316fe9.tar.gz chromium_src-37141711d11b09cef8405c4c2384549dad316fe9.tar.bz2 |
Support encrypted audio stream in demuxer.
BUG=123421
TEST=updated media_unittest
Review URL: https://chromiumcodereview.appspot.com/11088047
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161465 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/base/audio_decoder_config.cc | 19 | ||||
-rw-r--r-- | media/base/audio_decoder_config.h | 11 | ||||
-rw-r--r-- | media/ffmpeg/ffmpeg_common.cc | 3 | ||||
-rw-r--r-- | media/filters/chunk_demuxer_unittest.cc | 173 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder_unittest.cc | 1 | ||||
-rw-r--r-- | media/mp4/mp4_stream_parser.cc | 3 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.cc | 42 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser.h | 2 | ||||
-rw-r--r-- | media/webm/webm_cluster_parser_unittest.cc | 7 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.cc | 38 | ||||
-rw-r--r-- | media/webm/webm_stream_parser.h | 3 | ||||
-rw-r--r-- | media/webm/webm_tracks_parser.cc | 42 | ||||
-rw-r--r-- | media/webm/webm_tracks_parser.h | 13 |
13 files changed, 229 insertions, 128 deletions
diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc index 1f259f3..bd0e6a7 100644 --- a/media/base/audio_decoder_config.cc +++ b/media/base/audio_decoder_config.cc @@ -16,7 +16,8 @@ AudioDecoderConfig::AudioDecoderConfig() bits_per_channel_(0), channel_layout_(CHANNEL_LAYOUT_UNSUPPORTED), samples_per_second_(0), - extra_data_size_(0) { + extra_data_size_(0), + is_encrypted_(false) { } AudioDecoderConfig::AudioDecoderConfig(AudioCodec codec, @@ -24,9 +25,10 @@ AudioDecoderConfig::AudioDecoderConfig(AudioCodec codec, ChannelLayout channel_layout, int samples_per_second, const uint8* extra_data, - size_t extra_data_size) { + size_t extra_data_size, + bool is_encrypted) { Initialize(codec, bits_per_channel, channel_layout, samples_per_second, - extra_data, extra_data_size, true); + extra_data, extra_data_size, is_encrypted, true); } void AudioDecoderConfig::Initialize(AudioCodec codec, @@ -35,6 +37,7 @@ void AudioDecoderConfig::Initialize(AudioCodec codec, int samples_per_second, const uint8* extra_data, size_t extra_data_size, + bool is_encrypted, bool record_stats) { CHECK((extra_data_size != 0) == (extra_data != NULL)); @@ -68,6 +71,8 @@ void AudioDecoderConfig::Initialize(AudioCodec codec, } else { extra_data_.reset(); } + + is_encrypted_ = is_encrypted; } AudioDecoderConfig::~AudioDecoderConfig() {} @@ -88,7 +93,8 @@ bool AudioDecoderConfig::Matches(const AudioDecoderConfig& config) const { (samples_per_second() == config.samples_per_second()) && (extra_data_size() == config.extra_data_size()) && (!extra_data() || !memcmp(extra_data(), config.extra_data(), - extra_data_size()))); + extra_data_size())) && + (is_encrypted() == config.is_encrypted())); } void AudioDecoderConfig::CopyFrom(const AudioDecoderConfig& audio_config) { @@ -98,6 +104,7 @@ void AudioDecoderConfig::CopyFrom(const AudioDecoderConfig& audio_config) { audio_config.samples_per_second(), audio_config.extra_data(), audio_config.extra_data_size(), + audio_config.is_encrypted(), false); } @@ -125,4 +132,8 @@ size_t AudioDecoderConfig::extra_data_size() const { return extra_data_size_; } +bool AudioDecoderConfig::is_encrypted() const { + return is_encrypted_; +} + } // namespace media diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h index 5ab06c5..2bc2487 100644 --- a/media/base/audio_decoder_config.h +++ b/media/base/audio_decoder_config.h @@ -51,7 +51,8 @@ class MEDIA_EXPORT AudioDecoderConfig { // |extra_data|, otherwise the memory is copied. AudioDecoderConfig(AudioCodec codec, int bits_per_channel, ChannelLayout channel_layout, int samples_per_second, - const uint8* extra_data, size_t extra_data_size); + const uint8* extra_data, size_t extra_data_size, + bool is_encrypted); ~AudioDecoderConfig(); @@ -59,6 +60,7 @@ class MEDIA_EXPORT AudioDecoderConfig { void Initialize(AudioCodec codec, int bits_per_channel, ChannelLayout channel_layout, int samples_per_second, const uint8* extra_data, size_t extra_data_size, + bool is_encrypted, bool record_stats); // Deep copies |audio_config|. @@ -82,6 +84,11 @@ class MEDIA_EXPORT AudioDecoderConfig { uint8* extra_data() const; size_t extra_data_size() const; + // Whether the audio stream is potentially encrypted. + // Note that in a potentially encrypted audio stream, individual buffers + // can be encrypted or not encrypted. + bool is_encrypted() const; + private: AudioCodec codec_; int bits_per_channel_; @@ -91,6 +98,8 @@ class MEDIA_EXPORT AudioDecoderConfig { scoped_array<uint8> extra_data_; size_t extra_data_size_; + bool is_encrypted_; + DISALLOW_COPY_AND_ASSIGN(AudioDecoderConfig); }; diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index 00d91f5..973dc81 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc @@ -222,6 +222,7 @@ void AVCodecContextToAudioDecoderConfig( samples_per_second, codec_context->extradata, codec_context->extradata_size, + false, // Not encrypted. true); } @@ -291,7 +292,7 @@ void AVStreamToVideoDecoderConfig( PixelFormatToVideoFormat(stream->codec->pix_fmt), coded_size, visible_rect, natural_size, stream->codec->extradata, stream->codec->extradata_size, - false, + false, // Not encrypted. true); } diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc index 915bcf7..eae8f69 100644 --- a/media/filters/chunk_demuxer_unittest.cc +++ b/media/filters/chunk_demuxer_unittest.cc @@ -15,6 +15,7 @@ #include "testing/gtest/include/gtest/gtest.h" using ::testing::AnyNumber; +using ::testing::Exactly; using ::testing::InSequence; using ::testing::NotNull; using ::testing::Return; @@ -32,6 +33,13 @@ static const uint8 kTracksHeader[] = { static const int kTracksHeaderSize = sizeof(kTracksHeader); static const int kTracksSizeOffset = 4; +// The size of TrackEntry element in test file "webm_vorbis_track_entry" starts +// at index 1 and spans 8 bytes. +static const int kAudioTrackSizeOffset = 1; +static const int kAudioTrackSizeWidth = 8; +static const int kAudioTrackEntryHeaderSize = kAudioTrackSizeOffset + + kAudioTrackSizeWidth; + // The size of TrackEntry element in test file "webm_vp8_track_entry" starts at // index 1 and spans 8 bytes. static const int kVideoTrackSizeOffset = 1; @@ -162,13 +170,14 @@ class ChunkDemuxerTest : public testing::Test { } void CreateInitSegment(bool has_audio, bool has_video, - bool video_content_encoded, + bool is_audio_encrypted, bool is_video_encrypted, scoped_array<uint8>* buffer, int* size) { scoped_refptr<DecoderBuffer> ebml_header; scoped_refptr<DecoderBuffer> info; scoped_refptr<DecoderBuffer> audio_track_entry; scoped_refptr<DecoderBuffer> video_track_entry; + scoped_refptr<DecoderBuffer> audio_content_encodings; scoped_refptr<DecoderBuffer> video_content_encodings; ebml_header = ReadTestDataFile("webm_ebml_element"); @@ -180,12 +189,16 @@ class ChunkDemuxerTest : public testing::Test { if (has_audio) { audio_track_entry = ReadTestDataFile("webm_vorbis_track_entry"); tracks_element_size += audio_track_entry->GetDataSize(); + if (is_audio_encrypted) { + audio_content_encodings = ReadTestDataFile("webm_content_encodings"); + tracks_element_size += audio_content_encodings->GetDataSize(); + } } if (has_video) { video_track_entry = ReadTestDataFile("webm_vp8_track_entry"); tracks_element_size += video_track_entry->GetDataSize(); - if (video_content_encoded) { + if (is_video_encrypted) { video_content_encodings = ReadTestDataFile("webm_content_encodings"); tracks_element_size += video_content_encodings->GetDataSize(); } @@ -207,16 +220,28 @@ class ChunkDemuxerTest : public testing::Test { WriteInt64(buf + kTracksSizeOffset, tracks_element_size); buf += kTracksHeaderSize; + // TODO(xhwang): Simplify this! Probably have test data files that contain + // ContentEncodings directly instead of trying to create one at run-time. if (has_audio) { memcpy(buf, audio_track_entry->GetData(), audio_track_entry->GetDataSize()); + if (is_audio_encrypted) { + memcpy(buf + audio_track_entry->GetDataSize(), + audio_content_encodings->GetData(), + audio_content_encodings->GetDataSize()); + WriteInt64(buf + kAudioTrackSizeOffset, + audio_track_entry->GetDataSize() + + audio_content_encodings->GetDataSize() - + kAudioTrackEntryHeaderSize); + buf += audio_content_encodings->GetDataSize(); + } buf += audio_track_entry->GetDataSize(); } if (has_video) { memcpy(buf, video_track_entry->GetData(), video_track_entry->GetDataSize()); - if (video_content_encoded) { + if (is_video_encrypted) { memcpy(buf + video_track_entry->GetDataSize(), video_content_encodings->GetData(), video_content_encodings->GetDataSize()); @@ -289,18 +314,24 @@ class ChunkDemuxerTest : public testing::Test { return true; } - bool AppendInitSegment(bool has_audio, bool has_video, - bool video_content_encoded) { - return AppendInitSegment(kSourceId, has_audio, has_video, - video_content_encoded); + bool AppendInitSegment(bool has_audio, bool has_video) { + return AppendInitSegmentWithSourceId(kSourceId, has_audio, has_video); + } + + bool AppendInitSegmentWithSourceId(const std::string& source_id, + bool has_audio, bool has_video) { + return AppendInitSegmentWithEncryptedInfo( + source_id, has_audio, has_video, false, false); } - bool AppendInitSegment(const std::string& source_id, - bool has_audio, bool has_video, - bool video_content_encoded) { + bool AppendInitSegmentWithEncryptedInfo(const std::string& source_id, + bool has_audio, bool has_video, + bool is_audio_encrypted, + bool is_video_encrypted) { scoped_array<uint8> info_tracks; int info_tracks_size = 0; - CreateInitSegment(has_audio, has_video, video_content_encoded, + CreateInitSegment(has_audio, has_video, + is_audio_encrypted, is_video_encrypted, &info_tracks, &info_tracks_size); return AppendData(source_id, info_tracks.get(), info_tracks_size); } @@ -337,8 +368,13 @@ class ChunkDemuxerTest : public testing::Test { expected_status); } - bool InitDemuxer(bool has_audio, bool has_video, - bool video_content_encoded) { + bool InitDemuxer(bool has_audio, bool has_video) { + return InitDemuxerWithEncryptionInfo(has_audio, has_video, false, false); + } + + bool InitDemuxerWithEncryptionInfo( + bool has_audio, bool has_video, + bool is_audio_encrypted, bool is_video_encrypted) { PipelineStatus expected_status = (has_audio || has_video) ? PIPELINE_OK : DEMUXER_ERROR_COULD_NOT_OPEN; @@ -353,7 +389,9 @@ class ChunkDemuxerTest : public testing::Test { if (AddId(kSourceId, has_audio, has_video) != ChunkDemuxer::kOk) return false; - return AppendInitSegment(has_audio, has_video, video_content_encoded); + return AppendInitSegmentWithEncryptedInfo( + kSourceId, has_audio, has_video, + is_audio_encrypted, is_video_encrypted); } bool InitDemuxerAudioAndVideoSources(const std::string& audio_id, @@ -367,8 +405,8 @@ class ChunkDemuxerTest : public testing::Test { if (AddId(video_id, false, true) != ChunkDemuxer::kOk) return false; - bool success = AppendInitSegment(audio_id, true, false, false); - success &= AppendInitSegment(video_id, false, true, false); + bool success = AppendInitSegmentWithSourceId(audio_id, true, false); + success &= AppendInitSegmentWithSourceId(video_id, false, true); return success; } @@ -709,22 +747,31 @@ class ChunkDemuxerTest : public testing::Test { }; TEST_F(ChunkDemuxerTest, TestInit) { - // Test no streams, audio-only, video-only, and audio & video scenarios, - // with video encrypted or not. - for (int i = 0; i < 8; i++) { + // Test no streams, audio-only, video-only, and audio & video scenarios. + // Audio and video streams can be encrypted or not encrypted. + for (int i = 0; i < 16; i++) { bool has_audio = (i & 0x1) != 0; bool has_video = (i & 0x2) != 0; - bool is_video_encrypted = (i & 0x4) != 0; + bool is_audio_encrypted = (i & 0x4) != 0; + bool is_video_encrypted = (i & 0x8) != 0; // No test on invalid combination. - if (!has_video && is_video_encrypted) + if ((!has_audio && is_audio_encrypted) || + (!has_video && is_video_encrypted)) { continue; + } CreateNewDemuxer(); - if (has_video && is_video_encrypted) - EXPECT_CALL(*this, NeedKeyMock(NotNull(), 16)); - ASSERT_TRUE(InitDemuxer(has_audio, has_video, is_video_encrypted)); + if (is_audio_encrypted || is_video_encrypted) { + int need_key_count = (is_audio_encrypted ? 1 : 0) + + (is_video_encrypted ? 1 : 0); + EXPECT_CALL(*this, NeedKeyMock(NotNull(), 16)) + .Times(Exactly(need_key_count)); + } + + ASSERT_TRUE(InitDemuxerWithEncryptionInfo( + has_audio, has_video, is_audio_encrypted, is_video_encrypted)); scoped_refptr<DemuxerStream> audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO); @@ -738,6 +785,8 @@ TEST_F(ChunkDemuxerTest, TestInit) { EXPECT_EQ(44100, config.samples_per_second()); EXPECT_TRUE(config.extra_data()); EXPECT_GT(config.extra_data_size(), 0u); + EXPECT_EQ(is_audio_encrypted, + audio_stream->audio_decoder_config().is_encrypted()); } else { EXPECT_FALSE(audio_stream); } @@ -768,13 +817,13 @@ TEST_F(ChunkDemuxerTest, TestShutdownBeforeAllInitSegmentsAppended) { EXPECT_EQ(AddId("audio", true, false), ChunkDemuxer::kOk); EXPECT_EQ(AddId("video", false, true), ChunkDemuxer::kOk); - EXPECT_TRUE(AppendInitSegment("audio", true, false, false)); + EXPECT_TRUE(AppendInitSegmentWithSourceId("audio", true, false)); } // Test that Seek() completes successfully when the first cluster // arrives. TEST_F(ChunkDemuxerTest, TestAppendDataAfterSeek) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> first_cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size())); @@ -798,7 +847,7 @@ TEST_F(ChunkDemuxerTest, TestAppendDataAfterSeek) { // Test that parsing errors are handled for clusters appended after init. TEST_F(ChunkDemuxerTest, TestErrorWhileParsingClusterAfterInit) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> first_cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size())); @@ -810,7 +859,7 @@ TEST_F(ChunkDemuxerTest, TestErrorWhileParsingClusterAfterInit) { // is in the middle of cluster. This is to verify that the parser // does not reset itself on a seek. TEST_F(ChunkDemuxerTest, TestSeekWhileParsingCluster) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_refptr<DemuxerStream> audio = demuxer_->GetStream(DemuxerStream::AUDIO); @@ -855,7 +904,7 @@ TEST_F(ChunkDemuxerTest, TestSeekWhileParsingCluster) { TEST_F(ChunkDemuxerTest, TestAppendDataBeforeInit) { scoped_array<uint8> info_tracks; int info_tracks_size = 0; - CreateInitSegment(true, true, false, &info_tracks, &info_tracks_size); + CreateInitSegment(true, true, false, false, &info_tracks, &info_tracks_size); EXPECT_FALSE(demuxer_->AppendData(kSourceId, info_tracks.get(), info_tracks_size)); @@ -863,7 +912,7 @@ TEST_F(ChunkDemuxerTest, TestAppendDataBeforeInit) { // Make sure Read() callbacks are dispatched with the proper data. TEST_F(ChunkDemuxerTest, TestRead) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -887,7 +936,7 @@ TEST_F(ChunkDemuxerTest, TestRead) { } TEST_F(ChunkDemuxerTest, TestOutOfOrderClusters) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -908,7 +957,7 @@ TEST_F(ChunkDemuxerTest, TestOutOfOrderClusters) { } TEST_F(ChunkDemuxerTest, TestNonMonotonicButAboveClusterTimecode) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> first_cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size())); @@ -933,7 +982,7 @@ TEST_F(ChunkDemuxerTest, TestNonMonotonicButAboveClusterTimecode) { } TEST_F(ChunkDemuxerTest, TestBackwardsAndBeforeClusterTimecode) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> first_cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size())); @@ -959,7 +1008,7 @@ TEST_F(ChunkDemuxerTest, TestBackwardsAndBeforeClusterTimecode) { TEST_F(ChunkDemuxerTest, TestPerStreamMonotonicallyIncreasingTimestamps) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> first_cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(first_cluster->data(), first_cluster->size())); @@ -1016,7 +1065,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithNoAppend) { } TEST_F(ChunkDemuxerTest, TestEndOfStreamWithNoMediaAppend) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); CheckExpectedRanges("{ }"); demuxer_->EndOfStream(PIPELINE_OK); @@ -1024,7 +1073,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithNoMediaAppend) { } TEST_F(ChunkDemuxerTest, TestDecodeErrorEndOfStream) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -1036,7 +1085,7 @@ TEST_F(ChunkDemuxerTest, TestDecodeErrorEndOfStream) { } TEST_F(ChunkDemuxerTest, TestNetworkErrorEndOfStream) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -1100,7 +1149,7 @@ class EndOfStreamHelper { // Make sure that all pending reads that we don't have media data for get an // "end of stream" buffer when EndOfStream() is called. TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster(GenerateCluster(0, 2)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -1142,7 +1191,7 @@ TEST_F(ChunkDemuxerTest, TestEndOfStreamWithPendingReads) { // Make sure that all Read() calls after we get an EndOfStream() // call return an "end of stream" buffer. TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster(GenerateCluster(0, 2)); ASSERT_TRUE(AppendData(cluster->data(), cluster->size())); @@ -1188,7 +1237,7 @@ TEST_F(ChunkDemuxerTest, TestReadsAfterEndOfStream) { } TEST_F(ChunkDemuxerTest, TestEndOfStreamDuringCanceledSeek) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_refptr<DemuxerStream> audio = demuxer_->GetStream(DemuxerStream::AUDIO); scoped_refptr<DemuxerStream> video = @@ -1245,7 +1294,7 @@ TEST_F(ChunkDemuxerTest, TestAppendingInPieces) { scoped_array<uint8> info_tracks; int info_tracks_size = 0; - CreateInitSegment(true, true, false, &info_tracks, &info_tracks_size); + CreateInitSegment(true, true, false, false, &info_tracks, &info_tracks_size); scoped_ptr<Cluster> cluster_a(kDefaultFirstCluster()); scoped_ptr<Cluster> cluster_b(kDefaultSecondCluster()); @@ -1349,7 +1398,7 @@ TEST_F(ChunkDemuxerTest, TestWebMFile_AltRefFrames) { // Verify that we output buffers before the entire cluster has been parsed. TEST_F(ChunkDemuxerTest, TestIncrementalClusterParsing) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); ASSERT_TRUE(AppendEmptyCluster(0)); scoped_ptr<Cluster> cluster(GenerateCluster(0, 6)); @@ -1436,7 +1485,7 @@ TEST_F(ChunkDemuxerTest, TestAVHeadersWithAudioOnlyType) { ASSERT_EQ(demuxer_->AddId(kSourceId, "audio/webm", codecs), ChunkDemuxer::kOk); - ASSERT_TRUE(AppendInitSegment(true, true, false)); + ASSERT_TRUE(AppendInitSegment(true, true)); } TEST_F(ChunkDemuxerTest, TestAVHeadersWithVideoOnlyType) { @@ -1450,11 +1499,11 @@ TEST_F(ChunkDemuxerTest, TestAVHeadersWithVideoOnlyType) { ASSERT_EQ(demuxer_->AddId(kSourceId, "video/webm", codecs), ChunkDemuxer::kOk); - ASSERT_TRUE(AppendInitSegment(true, true, false)); + ASSERT_TRUE(AppendInitSegment(true, true)); } TEST_F(ChunkDemuxerTest, TestMultipleHeaders) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_refptr<DemuxerStream> audio = demuxer_->GetStream(DemuxerStream::AUDIO); @@ -1465,7 +1514,7 @@ TEST_F(ChunkDemuxerTest, TestMultipleHeaders) { ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size())); // Append another identical initialization segment. - ASSERT_TRUE(AppendInitSegment(true, true, false)); + ASSERT_TRUE(AppendInitSegment(true, true)); scoped_ptr<Cluster> cluster_b(kDefaultSecondCluster()); ASSERT_TRUE(AppendData(cluster_b->data(), cluster_b->size())); @@ -1509,7 +1558,7 @@ TEST_F(ChunkDemuxerTest, TestAddIdFailures) { // Adding an id with audio/video should fail because we already added audio. ASSERT_EQ(AddId(), ChunkDemuxer::kReachedIdLimit); - ASSERT_TRUE(AppendInitSegment(audio_id, true, false, false)); + ASSERT_TRUE(AppendInitSegmentWithSourceId(audio_id, true, false)); // Adding an id after append should fail. ASSERT_EQ(AddId(video_id, false, true), ChunkDemuxer::kReachedIdLimit); @@ -1563,7 +1612,7 @@ TEST_F(ChunkDemuxerTest, TestRemoveAndAddId) { } TEST_F(ChunkDemuxerTest, TestSeekCanceled) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_refptr<DemuxerStream> audio = demuxer_->GetStream(DemuxerStream::AUDIO); @@ -1601,7 +1650,7 @@ TEST_F(ChunkDemuxerTest, TestSeekCanceled) { } TEST_F(ChunkDemuxerTest, TestSeekCanceledWhileWaitingForSeek) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_refptr<DemuxerStream> audio = demuxer_->GetStream(DemuxerStream::AUDIO); @@ -1708,7 +1757,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioIdOnly) { &host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK)); ASSERT_EQ(AddId(kSourceId, true, false), ChunkDemuxer::kOk); - ASSERT_TRUE(AppendInitSegment(true, false, false)); + ASSERT_TRUE(AppendInitSegment(true, false)); // Test a simple cluster. scoped_ptr<Cluster> cluster_1(GenerateSingleStreamCluster(0, 92, @@ -1733,7 +1782,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) { &host_, CreateInitDoneCB(kDefaultDuration(), PIPELINE_OK)); ASSERT_EQ(AddId(kSourceId, false, true), ChunkDemuxer::kOk); - ASSERT_TRUE(AppendInitSegment(false, true, false)); + ASSERT_TRUE(AppendInitSegment(false, true)); // Test a simple cluster. scoped_ptr<Cluster> cluster_1(GenerateSingleStreamCluster(0, 132, @@ -1753,7 +1802,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_VideoIdOnly) { } TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); // Audio: 0 -> 23 // Video: 0 -> 33 @@ -1847,7 +1896,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_AudioVideo) { // over-hanging tails at the end of the ranges as this is likely due to block // duration differences. TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster_a( GenerateSingleStreamCluster(0, 90, kAudioTrackNum, 90)); @@ -1866,7 +1915,7 @@ TEST_F(ChunkDemuxerTest, GetBufferedRanges_EndOfStream) { } TEST_F(ChunkDemuxerTest, TestDifferentStreamTimecodes) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_refptr<DemuxerStream> audio = demuxer_->GetStream(DemuxerStream::AUDIO); @@ -1950,7 +1999,7 @@ TEST_F(ChunkDemuxerTest, TestDifferentStreamTimecodesOutOfRange) { } TEST_F(ChunkDemuxerTest, TestClusterWithNoBuffers) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); // Generate and append an empty cluster beginning at 0. ASSERT_TRUE(AppendEmptyCluster(0)); @@ -2046,7 +2095,7 @@ TEST_F(ChunkDemuxerTest, TestGetBufferedRangesBeforeInitSegment) { TEST_F(ChunkDemuxerTest, TestEndOfStreamDuringSeek) { InSequence s; - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster_a(kDefaultFirstCluster()); scoped_ptr<Cluster> cluster_b(kDefaultSecondCluster()); @@ -2222,7 +2271,7 @@ TEST_F(ChunkDemuxerTest, TestConfigChange_Seek) { } TEST_F(ChunkDemuxerTest, TestTimestampPositiveOffset) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); ASSERT_TRUE(demuxer_->SetTimestampOffset( kSourceId, base::TimeDelta::FromSeconds(30))); @@ -2241,7 +2290,7 @@ TEST_F(ChunkDemuxerTest, TestTimestampPositiveOffset) { } TEST_F(ChunkDemuxerTest, TestTimestampNegativeOffset) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); ASSERT_TRUE(demuxer_->SetTimestampOffset( kSourceId, base::TimeDelta::FromSeconds(-1))); @@ -2307,7 +2356,7 @@ TEST_F(ChunkDemuxerTest, TestTimestampOffsetSeparateStreams) { } TEST_F(ChunkDemuxerTest, TestTimestampOffsetMidParse) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster = GenerateCluster(0, 2); // Append only part of the cluster data. @@ -2325,7 +2374,7 @@ TEST_F(ChunkDemuxerTest, TestTimestampOffsetMidParse) { } TEST_F(ChunkDemuxerTest, TestDurationChange) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); static const int kStreamDuration = kDefaultDuration().InMilliseconds(); // Add data leading up to the currently set duration. @@ -2359,7 +2408,7 @@ TEST_F(ChunkDemuxerTest, TestDurationChange) { } TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); ASSERT_TRUE(demuxer_->SetTimestampOffset(kSourceId, kDefaultDuration())); scoped_ptr<Cluster> cluster = GenerateCluster(0, 4); @@ -2371,7 +2420,7 @@ TEST_F(ChunkDemuxerTest, TestDurationChangeTimestampOffset) { } TEST_F(ChunkDemuxerTest, TestEndOfStreamTruncateDuration) { - ASSERT_TRUE(InitDemuxer(true, true, false)); + ASSERT_TRUE(InitDemuxer(true, true)); scoped_ptr<Cluster> cluster_a(kDefaultFirstCluster()); ASSERT_TRUE(AppendData(cluster_a->data(), cluster_a->size())); diff --git a/media/filters/ffmpeg_audio_decoder_unittest.cc b/media/filters/ffmpeg_audio_decoder_unittest.cc index 0b7d70b..38369cb 100644 --- a/media/filters/ffmpeg_audio_decoder_unittest.cc +++ b/media/filters/ffmpeg_audio_decoder_unittest.cc @@ -62,6 +62,7 @@ class FFmpegAudioDecoderTest : public testing::Test { 44100, vorbis_extradata_->GetData(), vorbis_extradata_->GetDataSize(), + false, // Not encrypted. true); } diff --git a/media/mp4/mp4_stream_parser.cc b/media/mp4/mp4_stream_parser.cc index fc141b5..c600537 100644 --- a/media/mp4/mp4_stream_parser.cc +++ b/media/mp4/mp4_stream_parser.cc @@ -199,10 +199,11 @@ bool MP4StreamParser::ParseMoov(BoxReader* reader) { return false; } + bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted; audio_config.Initialize(kCodecAAC, entry.samplesize, aac.channel_layout(), aac.GetOutputSamplesPerSecond(has_sbr_), - NULL, 0, false); + NULL, 0, is_encrypted, false); has_audio_ = true; audio_track_id_ = track->header.track_id; } diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc index a6867bc0..fb1c69b 100644 --- a/media/webm/webm_cluster_parser.cc +++ b/media/webm/webm_cluster_parser.cc @@ -26,8 +26,10 @@ static std::string GenerateCounterBlock(uint64 iv) { WebMClusterParser::WebMClusterParser(int64 timecode_scale, int audio_track_num, int video_track_num, + const std::string& audio_encryption_key_id, const std::string& video_encryption_key_id) : timecode_multiplier_(timecode_scale / 1000.0), + audio_encryption_key_id_(audio_encryption_key_id), video_encryption_key_id_(video_encryption_key_id), parser_(kWebMIdCluster, this), last_block_timecode_(-1), @@ -198,24 +200,34 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode, return false; } + Track* track = NULL; + std::string encryption_key_id; + if (track_num == audio_.track_num()) { + track = &audio_; + encryption_key_id = audio_encryption_key_id_; + } else if (track_num == video_.track_num()) { + track = &video_; + encryption_key_id = video_encryption_key_id_; + } else { + DVLOG(1) << "Unexpected track number " << track_num; + return false; + } + last_block_timecode_ = timecode; base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds( (cluster_timecode_ + timecode) * timecode_multiplier_); - // Every encrypted Block has a signal byte and IV prepended to it. Current - // encrypted WebM request for comments specification is here - // http://wiki.webmproject.org/encryption/webm-encryption-rfc - bool is_track_encrypted = - track_num == video_.track_num() && !video_encryption_key_id_.empty(); - // The first bit of the flags is set when the block contains only keyframes. // http://www.matroska.org/technical/specs/index.html bool is_keyframe = (flags & 0x80) != 0; scoped_refptr<StreamParserBuffer> buffer = StreamParserBuffer::CopyFrom(data, size, is_keyframe); - if (is_track_encrypted) { + // Every encrypted Block has a signal byte and IV prepended to it. Current + // encrypted WebM request for comments specification is here + // http://wiki.webmproject.org/encryption/webm-encryption-rfc + if (!encryption_key_id.empty()) { uint8 signal_byte = data[0]; int data_offset = sizeof(signal_byte); @@ -227,9 +239,8 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode, if (signal_byte & kWebMFlagEncryptedFrame) { uint64 network_iv; memcpy(&network_iv, data + data_offset, sizeof(network_iv)); - const uint64 iv = base::NetToHost64(network_iv); - counter_block = GenerateCounterBlock(iv); - data_offset += sizeof(iv); + data_offset += sizeof(network_iv); + counter_block = GenerateCounterBlock(base::NetToHost64(network_iv)); } // TODO(fgalligan): Revisit if DecryptConfig needs to be set on unencrypted @@ -237,7 +248,7 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode, // Unencrypted frames of potentially encrypted streams currently set // DecryptConfig. buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig( - video_encryption_key_id_, + encryption_key_id, counter_block, data_offset, std::vector<SubsampleEntry>()))); @@ -252,14 +263,7 @@ bool WebMClusterParser::OnBlock(int track_num, int timecode, block_duration * timecode_multiplier_)); } - if (track_num == audio_.track_num()) { - return audio_.AddBuffer(buffer); - } else if (track_num == video_.track_num()) { - return video_.AddBuffer(buffer); - } - - DVLOG(1) << "Unexpected track number " << track_num; - return false; + return track->AddBuffer(buffer); } WebMClusterParser::Track::Track(int track_num) diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h index 988f224..6748a28 100644 --- a/media/webm/webm_cluster_parser.h +++ b/media/webm/webm_cluster_parser.h @@ -22,6 +22,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { WebMClusterParser(int64 timecode_scale, int audio_track_num, int video_track_num, + const std::string& audio_encryption_key_id, const std::string& video_encryption_key_id); virtual ~WebMClusterParser(); @@ -74,6 +75,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { double timecode_multiplier_; // Multiplier used to convert timecodes into // microseconds. + std::string audio_encryption_key_id_; std::string video_encryption_key_id_; WebMListParser parser_; diff --git a/media/webm/webm_cluster_parser_unittest.cc b/media/webm/webm_cluster_parser_unittest.cc index 3737e38..d12979f 100644 --- a/media/webm/webm_cluster_parser_unittest.cc +++ b/media/webm/webm_cluster_parser_unittest.cc @@ -121,13 +121,12 @@ static void AppendToEnd(const WebMClusterParser::BufferQueue& src, } } -class WebMClusterParserTest : public testing::Test { +class WebMClusterParserTest : public testing::Test { public: WebMClusterParserTest() : parser_(new WebMClusterParser(kTimecodeScale, - kAudioTrackNum, - kVideoTrackNum, - std::string())) { + kAudioTrackNum, kVideoTrackNum, + "", "")) { } protected: diff --git a/media/webm/webm_stream_parser.cc b/media/webm/webm_stream_parser.cc index b0a18e3..2e34ed9 100644 --- a/media/webm/webm_stream_parser.cc +++ b/media/webm/webm_stream_parser.cc @@ -351,6 +351,25 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { return -1; } + bool is_audio_encrypted = !tracks_parser.audio_encryption_key_id().empty(); + AudioDecoderConfig audio_config; + if (is_audio_encrypted) { + const AudioDecoderConfig& original_audio_config = + config_helper.audio_config(); + + audio_config.Initialize(original_audio_config.codec(), + original_audio_config.bits_per_channel(), + original_audio_config.channel_layout(), + original_audio_config.samples_per_second(), + original_audio_config.extra_data(), + original_audio_config.extra_data_size(), + is_audio_encrypted, false); + + FireNeedKey(tracks_parser.audio_encryption_key_id()); + } else { + audio_config.CopyFrom(config_helper.audio_config()); + } + // TODO(xhwang): Support decryption of audio (see http://crbug.com/123421). bool is_video_encrypted = !tracks_parser.video_encryption_key_id().empty(); @@ -368,18 +387,12 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { original_video_config.extra_data_size(), is_video_encrypted, false); - // Fire needkey event. - std::string key_id = tracks_parser.video_encryption_key_id(); - int key_id_size = key_id.size(); - 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(key_id_array.Pass(), key_id_size); + FireNeedKey(tracks_parser.video_encryption_key_id()); } else { video_config.CopyFrom(config_helper.video_config()); } - if (!config_cb_.Run(config_helper.audio_config(), video_config)) { + if (!config_cb_.Run(audio_config, video_config)) { DVLOG(1) << "New config data isn't allowed."; return -1; } @@ -388,6 +401,7 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) { info_parser.timecode_scale(), tracks_parser.audio_track_num(), tracks_parser.video_track_num(), + tracks_parser.audio_encryption_key_id(), tracks_parser.video_encryption_key_id())); ChangeState(kParsingClusters); @@ -455,4 +469,12 @@ int WebMStreamParser::ParseCluster(const uint8* data, int size) { return bytes_parsed; } +void WebMStreamParser::FireNeedKey(const std::string& key_id) { + int key_id_size = key_id.size(); + 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(key_id_array.Pass(), key_id_size); +} + } // namespace media diff --git a/media/webm/webm_stream_parser.h b/media/webm/webm_stream_parser.h index c5d06d5..3640151 100644 --- a/media/webm/webm_stream_parser.h +++ b/media/webm/webm_stream_parser.h @@ -60,6 +60,9 @@ class WebMStreamParser : public StreamParser { // Returning > 0 indicates success & the number of bytes parsed. int ParseCluster(const uint8* data, int size); + // Fire needkey event through the |need_key_cb_|. + void FireNeedKey(const std::string& key_id); + State state_; InitCB init_cb_; NewConfigCB config_cb_; diff --git a/media/webm/webm_tracks_parser.cc b/media/webm/webm_tracks_parser.cc index bf039bf..7232285 100644 --- a/media/webm/webm_tracks_parser.cc +++ b/media/webm/webm_tracks_parser.cc @@ -25,15 +25,6 @@ WebMTracksParser::WebMTracksParser() WebMTracksParser::~WebMTracksParser() {} -const std::string& WebMTracksParser::video_encryption_key_id() const { - if (!video_content_encodings_client_.get()) - return EmptyString(); - - DCHECK(!video_content_encodings_client_->content_encodings().empty()); - return video_content_encodings_client_->content_encodings()[0]-> - encryption_key_id(); -} - int WebMTracksParser::Parse(const uint8* buf, int size) { track_type_ =-1; track_num_ = -1; @@ -50,7 +41,6 @@ int WebMTracksParser::Parse(const uint8* buf, int size) { return parser.IsParsingComplete() ? result : 0; } - WebMParserClient* WebMTracksParser::OnListStart(int id) { if (id == kWebMIdContentEncodings) { DCHECK(!track_content_encodings_client_.get()); @@ -81,23 +71,29 @@ bool WebMTracksParser::OnListEnd(int id) { return false; } - if (track_type_ == kWebMTrackTypeVideo) { - video_track_num_ = track_num_; - if (track_content_encodings_client_.get()) { - video_content_encodings_client_ = - track_content_encodings_client_.Pass(); - } - } else if (track_type_ == kWebMTrackTypeAudio) { - audio_track_num_ = track_num_; - if (track_content_encodings_client_.get()) { - audio_content_encodings_client_ = - track_content_encodings_client_.Pass(); - } - } else { + if (track_type_ != kWebMTrackTypeAudio && + track_type_ != kWebMTrackTypeVideo) { DVLOG(1) << "Unexpected TrackType " << track_type_; return false; } + std::string encryption_key_id; + if (track_content_encodings_client_.get()) { + DCHECK(!track_content_encodings_client_->content_encodings().empty()); + // If we have multiple ContentEncoding in one track. Always choose the + // key id in the first ContentEncoding as the key id of the track. + encryption_key_id = track_content_encodings_client_-> + content_encodings()[0]->encryption_key_id(); + } + + if (track_type_ == kWebMTrackTypeAudio) { + audio_track_num_ = track_num_; + audio_encryption_key_id_ = encryption_key_id; + } else if (track_type_ == kWebMTrackTypeVideo) { + video_track_num_ = track_num_; + video_encryption_key_id_ = encryption_key_id; + } + track_type_ = -1; track_num_ = -1; track_content_encodings_client_.reset(); diff --git a/media/webm/webm_tracks_parser.h b/media/webm/webm_tracks_parser.h index 24211ed..02d43d6 100644 --- a/media/webm/webm_tracks_parser.h +++ b/media/webm/webm_tracks_parser.h @@ -29,8 +29,12 @@ class WebMTracksParser : public WebMParserClient { int64 audio_track_num() const { return audio_track_num_; } int64 video_track_num() const { return video_track_num_; } - - const std::string& video_encryption_key_id() const; + const std::string& audio_encryption_key_id() const { + return audio_encryption_key_id_; + } + const std::string& video_encryption_key_id() const { + return video_encryption_key_id_; + } private: // WebMParserClient methods @@ -46,10 +50,9 @@ class WebMTracksParser : public WebMParserClient { scoped_ptr<WebMContentEncodingsClient> track_content_encodings_client_; int64 audio_track_num_; - scoped_ptr<WebMContentEncodingsClient> audio_content_encodings_client_; - int64 video_track_num_; - scoped_ptr<WebMContentEncodingsClient> video_content_encodings_client_; + std::string audio_encryption_key_id_; + std::string video_encryption_key_id_; DISALLOW_COPY_AND_ASSIGN(WebMTracksParser); }; |