Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(373)

Unified Diff: media/filters/decrypting_audio_decoder_unittest.cc

Issue 11198017: Add DecryptingAudioDecoder. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « media/filters/decrypting_audio_decoder.cc ('k') | media/filters/decrypting_video_decoder_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/filters/decrypting_audio_decoder_unittest.cc
diff --git a/media/filters/decrypting_audio_decoder_unittest.cc b/media/filters/decrypting_audio_decoder_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..89d2a64ccae893abb6199b42d2c4957350469529
--- /dev/null
+++ b/media/filters/decrypting_audio_decoder_unittest.cc
@@ -0,0 +1,507 @@
+// Copyright (c) 2012 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 <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/message_loop.h"
+#include "media/base/buffers.h"
+#include "media/base/data_buffer.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/decrypt_config.h"
+#include "media/base/mock_callback.h"
+#include "media/base/mock_filters.h"
+#include "media/filters/decrypting_audio_decoder.h"
+#include "media/filters/ffmpeg_decoder_unittest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using ::testing::_;
+using ::testing::AtMost;
+using ::testing::Invoke;
+using ::testing::IsNull;
+using ::testing::ReturnRef;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
+namespace media {
+
+static const int kFakeAudioFrameSize = 16;
+static const uint8 kFakeKeyId[] = { 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44 };
+static const uint8 kFakeIv[DecryptConfig::kDecryptionKeySize] = { 0 };
+
+// Create a fake non-empty encrypted buffer.
+static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
+ const int buffer_size = 16; // Need a non-empty buffer;
+ scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(buffer_size));
+ buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(new DecryptConfig(
+ std::string(reinterpret_cast<const char*>(kFakeKeyId),
+ arraysize(kFakeKeyId)),
+ std::string(reinterpret_cast<const char*>(kFakeIv), arraysize(kFakeIv)),
+ 0,
+ std::vector<SubsampleEntry>())));
+ return buffer;
+}
+
+// Use anonymous namespace here to prevent the actions to be defined multiple
+// times across multiple test files. Sadly we can't use static for them.
+namespace {
+
+ACTION_P(ReturnBuffer, buffer) {
+ arg0.Run(buffer ? DemuxerStream::kOk : DemuxerStream::kAborted, buffer);
+}
+
+ACTION(ReturnConfigChanged) {
+ arg0.Run(DemuxerStream::kConfigChanged, scoped_refptr<DecoderBuffer>(NULL));
+}
+
+ACTION_P(RunCallback0, param) {
+ if (!arg0.is_null())
+ arg0.Run(param);
+}
+
+ACTION_P(RunCallback1, param) {
+ arg1.Run(param);
+}
+
+ACTION_P2(RunCallback2, param1, param2) {
+ arg1.Run(param1, param2);
+}
+
+ACTION_P2(ResetAndRunCallback, callback, param) {
+ base::ResetAndReturn(callback).Run(param);
+}
+
+MATCHER(IsNullCallback, "") {
+ return (arg.is_null());
+}
+
+} // namespace
+
+class DecryptingAudioDecoderTest : public testing::Test {
+ public:
+ DecryptingAudioDecoderTest()
+ : decoder_(new StrictMock<DecryptingAudioDecoder>(
+ base::Bind(&Identity<scoped_refptr<base::MessageLoopProxy> >,
+ message_loop_.message_loop_proxy()),
+ base::Bind(
+ &DecryptingAudioDecoderTest::RequestDecryptorNotification,
+ base::Unretained(this)))),
+ decryptor_(new StrictMock<MockDecryptor>()),
+ demuxer_(new StrictMock<MockDemuxerStream>()),
+ encrypted_buffer_(CreateFakeEncryptedBuffer()),
+ decoded_frame_(new DataBuffer(kFakeAudioFrameSize)),
+ end_of_stream_frame_(new DataBuffer(0)),
+ decoded_frame_list_(1, decoded_frame_) {
+ }
+
+ void InitializeAndExpectStatus(const AudioDecoderConfig& config,
+ PipelineStatus status) {
+ EXPECT_CALL(*demuxer_, audio_decoder_config())
+ .WillRepeatedly(ReturnRef(config));
+
+ decoder_->Initialize(demuxer_, NewExpectedStatusCB(status),
+ base::Bind(&MockStatisticsCB::OnStatistics,
+ base::Unretained(&statistics_cb_)));
+ message_loop_.RunAllPending();
+ }
+
+ void Initialize() {
+ EXPECT_CALL(*decryptor_, InitializeAudioDecoderMock(_, _, _))
+ .Times(AtMost(1))
+ .WillOnce(DoAll(RunCallback1(true), SaveArg<2>(&key_added_cb_)));
+ EXPECT_CALL(*this, RequestDecryptorNotification(_))
+ .WillOnce(RunCallback0(decryptor_.get()));
+
+ config_.Initialize(kCodecVorbis, 16, CHANNEL_LAYOUT_STEREO, 44100,
+ NULL, 0, true, true);
+
+ InitializeAndExpectStatus(config_, PIPELINE_OK);
+ EXPECT_EQ(16, decoder_->bits_per_channel());
+ EXPECT_EQ(CHANNEL_LAYOUT_STEREO, decoder_->channel_layout());
+ EXPECT_EQ(44100, decoder_->samples_per_second());
+ }
+
+ void ReadAndExpectFrameReadyWith(
+ AudioDecoder::Status status,
+ const scoped_refptr<Buffer>& audio_frame) {
+ if (status != AudioDecoder::kOk)
+ EXPECT_CALL(*this, FrameReady(status, IsNull()));
+ else
+ EXPECT_CALL(*this, FrameReady(status, audio_frame));
+
+ decoder_->Read(base::Bind(&DecryptingAudioDecoderTest::FrameReady,
+ base::Unretained(this)));
+ message_loop_.RunAllPending();
+ }
+
+ // Sets up expectations and actions to put DecryptingAudioDecoder in an
+ // active normal decoding state.
+ void EnterNormalDecodingState() {
+ Decryptor::AudioBuffers end_of_stream_frames_(1, end_of_stream_frame_);
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(encrypted_buffer_))
+ .WillRepeatedly(ReturnBuffer(DecoderBuffer::CreateEOSBuffer()));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillOnce(RunCallback2(Decryptor::kSuccess,
+ decoded_frame_list_))
+ .WillRepeatedly(RunCallback2(Decryptor::kSuccess,
+ end_of_stream_frames_));
+ EXPECT_CALL(statistics_cb_, OnStatistics(_));
+
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_);
+ }
+
+ // Sets up expectations and actions to put DecryptingAudioDecoder in an end
+ // of stream state. This function must be called after
+ // EnterNormalDecodingState() to work.
+ void EnterEndOfStreamState() {
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, end_of_stream_frame_);
+ }
+
+ // Make the read callback pending by saving and not firing it.
+ void EnterPendingReadState() {
+ EXPECT_TRUE(pending_demuxer_read_cb_.is_null());
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(SaveArg<0>(&pending_demuxer_read_cb_));
+ decoder_->Read(base::Bind(&DecryptingAudioDecoderTest::FrameReady,
+ base::Unretained(this)));
+ message_loop_.RunAllPending();
+ // Make sure the Read() on the decoder triggers a Read() on the demuxer.
+ EXPECT_FALSE(pending_demuxer_read_cb_.is_null());
+ }
+
+ // Make the audio decode callback pending by saving and not firing it.
+ void EnterPendingDecodeState() {
+ EXPECT_TRUE(pending_audio_decode_cb_.is_null());
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(encrypted_buffer_, _))
+ .WillOnce(SaveArg<1>(&pending_audio_decode_cb_));
+
+ decoder_->Read(base::Bind(&DecryptingAudioDecoderTest::FrameReady,
+ base::Unretained(this)));
+ message_loop_.RunAllPending();
+ // Make sure the Read() on the decoder triggers a DecryptAndDecode() on the
+ // decryptor.
+ EXPECT_FALSE(pending_audio_decode_cb_.is_null());
+ }
+
+ void EnterWaitingForKeyState() {
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillRepeatedly(RunCallback2(Decryptor::kNoKey,
+ Decryptor::AudioBuffers()));
+ decoder_->Read(base::Bind(&DecryptingAudioDecoderTest::FrameReady,
+ base::Unretained(this)));
+ message_loop_.RunAllPending();
+ }
+
+ void AbortPendingAudioDecodeCB() {
+ if (!pending_audio_decode_cb_.is_null()) {
+ base::ResetAndReturn(&pending_audio_decode_cb_).Run(
+ Decryptor::kSuccess, Decryptor::AudioBuffers());
+ }
+ }
+
+ void Reset() {
+ EXPECT_CALL(*decryptor_, ResetDecoder(Decryptor::kAudio))
+ .WillRepeatedly(InvokeWithoutArgs(
+ this, &DecryptingAudioDecoderTest::AbortPendingAudioDecodeCB));
+
+ decoder_->Reset(NewExpectedClosure());
+ message_loop_.RunAllPending();
+ }
+
+ MOCK_METHOD1(RequestDecryptorNotification,
+ void(const DecryptingAudioDecoder::DecryptorNotificationCB&));
+
+ MOCK_METHOD2(FrameReady, void(AudioDecoder::Status,
+ const scoped_refptr<Buffer>&));
+
+ MessageLoop message_loop_;
+ scoped_refptr<StrictMock<DecryptingAudioDecoder> > decoder_;
+ scoped_ptr<StrictMock<MockDecryptor> > decryptor_;
+ scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_;
+ MockStatisticsCB statistics_cb_;
+ AudioDecoderConfig config_;
+
+ DemuxerStream::ReadCB pending_demuxer_read_cb_;
+ Decryptor::DecoderInitCB pending_init_cb_;
+ Decryptor::KeyAddedCB key_added_cb_;
+ Decryptor::AudioDecodeCB pending_audio_decode_cb_;
+
+ // Constant buffer/frames to be returned by the |demuxer_| and |decryptor_|.
+ scoped_refptr<DecoderBuffer> encrypted_buffer_;
+ scoped_refptr<Buffer> decoded_frame_;
+ scoped_refptr<Buffer> end_of_stream_frame_;
+ Decryptor::AudioBuffers decoded_frame_list_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DecryptingAudioDecoderTest);
+};
+
+TEST_F(DecryptingAudioDecoderTest, Initialize_Normal) {
+ Initialize();
+}
+
+// Ensure that DecryptingAudioDecoder only accepts encrypted audio.
+TEST_F(DecryptingAudioDecoderTest, Initialize_UnencryptedAudioConfig) {
+ AudioDecoderConfig config(kCodecVorbis, 16, CHANNEL_LAYOUT_STEREO, 44100,
+ NULL, 0, false);
+
+ InitializeAndExpectStatus(config, DECODER_ERROR_NOT_SUPPORTED);
+}
+
+// Ensure decoder handles invalid audio configs without crashing.
+TEST_F(DecryptingAudioDecoderTest, Initialize_InvalidAudioConfig) {
+ AudioDecoderConfig config(kUnknownAudioCodec, 0, CHANNEL_LAYOUT_STEREO, 0,
+ NULL, 0, true);
+
+ InitializeAndExpectStatus(config, PIPELINE_ERROR_DECODE);
+}
+
+// Ensure decoder handles unsupported audio configs without crashing.
+TEST_F(DecryptingAudioDecoderTest, Initialize_UnsupportedAudioConfig) {
+ EXPECT_CALL(*decryptor_, InitializeAudioDecoderMock(_, _, _))
+ .WillOnce(RunCallback1(false));
+ EXPECT_CALL(*this, RequestDecryptorNotification(_))
+ .WillOnce(RunCallback0(decryptor_.get()));
+
+ AudioDecoderConfig config(kCodecVorbis, 16, CHANNEL_LAYOUT_STEREO, 44100,
+ NULL, 0, true);
+ InitializeAndExpectStatus(config, DECODER_ERROR_NOT_SUPPORTED);
+}
+
+// Test normal decrypt and decode case.
+TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_Normal) {
+ Initialize();
+ EnterNormalDecodingState();
+}
+
+// Test the case where the decryptor returns error when doing decrypt and
+// decode.
+TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_DecodeError) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillRepeatedly(RunCallback2(Decryptor::kError,
+ Decryptor::AudioBuffers()));
+
+ ReadAndExpectFrameReadyWith(AudioDecoder::kDecodeError, NULL);
+}
+
+// Test the case where the decryptor returns kNeedMoreData to ask for more
+// buffers before it can produce a frame.
+TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_NeedMoreData) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .Times(2)
+ .WillRepeatedly(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillOnce(RunCallback2(Decryptor::kNeedMoreData,
+ Decryptor::AudioBuffers()))
+ .WillRepeatedly(RunCallback2(Decryptor::kSuccess, decoded_frame_list_));
+ EXPECT_CALL(statistics_cb_, OnStatistics(_))
+ .Times(2);
+
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_);
+}
+
+// Test the case where the decryptor returns multiple decoded frames.
+TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_MultipleFrames) {
+ Initialize();
+
+ scoped_refptr<Buffer> decoded_frame_a(new DataBuffer(kFakeAudioFrameSize));
+ scoped_refptr<Buffer> decoded_frame_b(new DataBuffer(kFakeAudioFrameSize));
+ decoded_frame_list_.push_back(decoded_frame_a);
+ decoded_frame_list_.push_back(decoded_frame_b);
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(encrypted_buffer_));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillOnce(RunCallback2(Decryptor::kSuccess, decoded_frame_list_));
+ EXPECT_CALL(statistics_cb_, OnStatistics(_));
+
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_);
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_a);
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_b);
+}
+
+// Test the case where the decryptor receives end-of-stream buffer.
+TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_EndOfStream) {
+ Initialize();
+ EnterNormalDecodingState();
+ EnterEndOfStreamState();
+}
+
+// Test the case where the decryptor returns multiple decoded frames, the last
+// of which is end-of-stream frame.
+TEST_F(DecryptingAudioDecoderTest, DecryptAndDecode_MultipleFramesWithEos) {
+ Initialize();
+
+ scoped_refptr<Buffer> decoded_frame_a(new DataBuffer(kFakeAudioFrameSize));
+ scoped_refptr<Buffer> decoded_frame_b(new DataBuffer(kFakeAudioFrameSize));
+ Decryptor::AudioBuffers second_decoded_frame_list;
+ second_decoded_frame_list.push_back(decoded_frame_a);
+ second_decoded_frame_list.push_back(decoded_frame_b);
+ second_decoded_frame_list.push_back(end_of_stream_frame_);
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(encrypted_buffer_))
+ .WillOnce(ReturnBuffer(DecoderBuffer::CreateEOSBuffer()));
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillOnce(RunCallback2(Decryptor::kSuccess, decoded_frame_list_))
+ .WillOnce(RunCallback2(Decryptor::kSuccess, second_decoded_frame_list));
+ // Expect only one OnStatistics() here because EOS input buffer doesn't
+ // trigger statistics reporting.
+ EXPECT_CALL(statistics_cb_, OnStatistics(_));
+
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_);
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_a);
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, decoded_frame_b);
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, end_of_stream_frame_);
+}
+
+// Test the case where the a key is added when the decryptor is in
+// kWaitingForKey state.
+TEST_F(DecryptingAudioDecoderTest, KeyAdded_DuringWaitingForKey) {
+ Initialize();
+ EnterWaitingForKeyState();
+
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillRepeatedly(RunCallback2(Decryptor::kSuccess, decoded_frame_list_));
+ EXPECT_CALL(statistics_cb_, OnStatistics(_));
+ EXPECT_CALL(*this, FrameReady(AudioDecoder::kOk, decoded_frame_));
+ key_added_cb_.Run();
+ message_loop_.RunAllPending();
+}
+
+// Test the case where the a key is added when the decryptor is in
+// kPendingDecode state.
+TEST_F(DecryptingAudioDecoderTest, KeyAdded_DruingPendingDecode) {
+ Initialize();
+ EnterPendingDecodeState();
+
+ EXPECT_CALL(*decryptor_, DecryptAndDecodeAudio(_, _))
+ .WillRepeatedly(RunCallback2(Decryptor::kSuccess, decoded_frame_list_));
+ EXPECT_CALL(statistics_cb_, OnStatistics(_));
+ EXPECT_CALL(*this, FrameReady(AudioDecoder::kOk, decoded_frame_));
+ // The audio decode callback is returned after the correct decryption key is
+ // added.
+ key_added_cb_.Run();
+ base::ResetAndReturn(&pending_audio_decode_cb_).Run(
+ Decryptor::kNoKey, Decryptor::AudioBuffers());
+ message_loop_.RunAllPending();
+}
+
+// Test resetting when the decoder is in kIdle state but has not decoded any
+// frame.
+TEST_F(DecryptingAudioDecoderTest, Reset_DuringIdleAfterInitialization) {
+ Initialize();
+ Reset();
+}
+
+// Test resetting when the decoder is in kIdle state after it has decoded one
+// frame.
+TEST_F(DecryptingAudioDecoderTest, Reset_DuringIdleAfterDecodedOneFrame) {
+ Initialize();
+ EnterNormalDecodingState();
+ Reset();
+}
+
+// Test resetting when the decoder is in kPendingDemuxerRead state.
+TEST_F(DecryptingAudioDecoderTest, Reset_DuringPendingDemuxerRead) {
+ Initialize();
+ EnterPendingReadState();
+
+ EXPECT_CALL(*this, FrameReady(AudioDecoder::kOk, IsNull()));
+
+ Reset();
+ base::ResetAndReturn(&pending_demuxer_read_cb_).Run(DemuxerStream::kOk,
+ encrypted_buffer_);
+ message_loop_.RunAllPending();
+}
+
+// Test resetting when the decoder is in kPendingDecode state.
+TEST_F(DecryptingAudioDecoderTest, Reset_DuringPendingDecode) {
+ Initialize();
+ EnterPendingDecodeState();
+
+ EXPECT_CALL(*this, FrameReady(AudioDecoder::kOk, IsNull()));
+
+ Reset();
+}
+
+// Test resetting when the decoder is in kWaitingForKey state.
+TEST_F(DecryptingAudioDecoderTest, Reset_DuringWaitingForKey) {
+ Initialize();
+ EnterWaitingForKeyState();
+
+ EXPECT_CALL(*this, FrameReady(AudioDecoder::kOk, IsNull()));
+
+ Reset();
+}
+
+// Test resetting when the decoder has hit end of stream and is in
+// kDecodeFinished state.
+TEST_F(DecryptingAudioDecoderTest, Reset_AfterDecodeFinished) {
+ Initialize();
+ EnterNormalDecodingState();
+ EnterEndOfStreamState();
+ Reset();
+}
+
+// Test resetting after the decoder has been reset.
+TEST_F(DecryptingAudioDecoderTest, Reset_AfterReset) {
+ Initialize();
+ EnterNormalDecodingState();
+ Reset();
+ Reset();
+}
+
+// Test aborted read on the demuxer stream.
+TEST_F(DecryptingAudioDecoderTest, DemuxerRead_Aborted) {
+ Initialize();
+
+ // ReturnBuffer() with NULL triggers aborted demuxer read.
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnBuffer(scoped_refptr<DecoderBuffer>()));
+
+ ReadAndExpectFrameReadyWith(AudioDecoder::kOk, NULL);
+}
+
+// Test aborted read on the demuxer stream when the decoder is being reset.
+TEST_F(DecryptingAudioDecoderTest, DemuxerRead_AbortedDuringReset) {
+ Initialize();
+ EnterPendingReadState();
+
+ // Make sure we get a NULL audio frame returned.
+ EXPECT_CALL(*this, FrameReady(AudioDecoder::kOk, IsNull()));
+
+ Reset();
+ base::ResetAndReturn(&pending_demuxer_read_cb_).Run(DemuxerStream::kAborted,
+ NULL);
+ message_loop_.RunAllPending();
+}
+
+// Test config change on the demuxer stream.
+TEST_F(DecryptingAudioDecoderTest, DemuxerRead_ConfigChanged) {
+ Initialize();
+
+ EXPECT_CALL(*demuxer_, Read(_))
+ .WillOnce(ReturnConfigChanged());
+
+ // TODO(xhwang): Update this test when kConfigChanged is supported in
+ // DecryptingAudioDecoder.
+ ReadAndExpectFrameReadyWith(AudioDecoder::kDecodeError, NULL);
+}
+
+} // namespace media
« no previous file with comments | « media/filters/decrypting_audio_decoder.cc ('k') | media/filters/decrypting_video_decoder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698