| // Copyright 2014 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/mojo/common/mojo_decoder_buffer_converter.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/run_loop.h" |
| #include "base/test/mock_callback.h" |
| #include "media/base/decoder_buffer.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace media { |
| |
| namespace { |
| |
| uint32_t kDefaultDataPipeCapacityBytes = 1024; |
| |
| MATCHER_P(MatchesDecoderBuffer, buffer, "") { |
| DCHECK(arg); |
| return arg->MatchesForTesting(*buffer); |
| } |
| |
| class MojoDecoderBufferConverter { |
| public: |
| MojoDecoderBufferConverter( |
| uint32_t data_pipe_capacity_bytes = kDefaultDataPipeCapacityBytes) { |
| MojoCreateDataPipeOptions options; |
| options.struct_size = sizeof(MojoCreateDataPipeOptions); |
| options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; |
| options.element_num_bytes = 1; |
| options.capacity_num_bytes = data_pipe_capacity_bytes; |
| mojo::DataPipe data_pipe(options); |
| |
| writer = base::MakeUnique<MojoDecoderBufferWriter>( |
| std::move(data_pipe.producer_handle)); |
| reader = base::MakeUnique<MojoDecoderBufferReader>( |
| std::move(data_pipe.consumer_handle)); |
| } |
| |
| void ConvertAndVerify(const scoped_refptr<DecoderBuffer>& media_buffer) { |
| base::RunLoop run_loop; |
| base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_cb; |
| EXPECT_CALL(mock_cb, Run(MatchesDecoderBuffer(media_buffer))) |
| .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| |
| mojom::DecoderBufferPtr mojo_buffer = |
| writer->WriteDecoderBuffer(media_buffer); |
| reader->ReadDecoderBuffer(std::move(mojo_buffer), mock_cb.Get()); |
| run_loop.Run(); |
| } |
| |
| std::unique_ptr<MojoDecoderBufferWriter> writer; |
| std::unique_ptr<MojoDecoderBufferReader> reader; |
| }; |
| |
| } // namespace |
| |
| TEST(MojoDecoderBufferConverterTest, ConvertDecoderBuffer_Normal) { |
| base::MessageLoop message_loop; |
| const uint8_t kData[] = "hello, world"; |
| const uint8_t kSideData[] = "sideshow bob"; |
| const size_t kDataSize = arraysize(kData); |
| const size_t kSideDataSize = arraysize(kSideData); |
| |
| scoped_refptr<DecoderBuffer> buffer(DecoderBuffer::CopyFrom( |
| reinterpret_cast<const uint8_t*>(&kData), kDataSize, |
| reinterpret_cast<const uint8_t*>(&kSideData), kSideDataSize)); |
| buffer->set_timestamp(base::TimeDelta::FromMilliseconds(123)); |
| buffer->set_duration(base::TimeDelta::FromMilliseconds(456)); |
| buffer->set_splice_timestamp(base::TimeDelta::FromMilliseconds(200)); |
| buffer->set_discard_padding( |
| DecoderBuffer::DiscardPadding(base::TimeDelta::FromMilliseconds(5), |
| base::TimeDelta::FromMilliseconds(6))); |
| |
| MojoDecoderBufferConverter converter; |
| converter.ConvertAndVerify(buffer); |
| } |
| |
| TEST(MojoDecoderBufferConverterTest, ConvertDecoderBuffer_EOS) { |
| base::MessageLoop message_loop; |
| scoped_refptr<DecoderBuffer> buffer(DecoderBuffer::CreateEOSBuffer()); |
| |
| MojoDecoderBufferConverter converter; |
| converter.ConvertAndVerify(buffer); |
| } |
| |
| // TODO(xhwang): Investigate whether we can get rid of zero-byte-buffer. |
| // See http://crbug.com/663438 |
| TEST(MojoDecoderBufferConverterTest, ConvertDecoderBuffer_ZeroByteBuffer) { |
| base::MessageLoop message_loop; |
| scoped_refptr<DecoderBuffer> buffer(new DecoderBuffer(0)); |
| |
| MojoDecoderBufferConverter converter; |
| converter.ConvertAndVerify(buffer); |
| } |
| |
| TEST(MojoDecoderBufferConverterTest, ConvertDecoderBuffer_KeyFrame) { |
| base::MessageLoop message_loop; |
| const uint8_t kData[] = "hello, world"; |
| const size_t kDataSize = arraysize(kData); |
| |
| scoped_refptr<DecoderBuffer> buffer(DecoderBuffer::CopyFrom( |
| reinterpret_cast<const uint8_t*>(&kData), kDataSize)); |
| buffer->set_is_key_frame(true); |
| EXPECT_TRUE(buffer->is_key_frame()); |
| |
| MojoDecoderBufferConverter converter; |
| converter.ConvertAndVerify(buffer); |
| } |
| |
| TEST(MojoDecoderBufferConverterTest, ConvertDecoderBuffer_EncryptedBuffer) { |
| base::MessageLoop message_loop; |
| const uint8_t kData[] = "hello, world"; |
| const size_t kDataSize = arraysize(kData); |
| const char kKeyId[] = "00112233445566778899aabbccddeeff"; |
| const char kIv[] = "0123456789abcdef"; |
| |
| std::vector<SubsampleEntry> subsamples; |
| subsamples.push_back(SubsampleEntry(10, 20)); |
| subsamples.push_back(SubsampleEntry(30, 40)); |
| subsamples.push_back(SubsampleEntry(50, 60)); |
| |
| scoped_refptr<DecoderBuffer> buffer(DecoderBuffer::CopyFrom( |
| reinterpret_cast<const uint8_t*>(&kData), kDataSize)); |
| buffer->set_decrypt_config( |
| base::MakeUnique<DecryptConfig>(kKeyId, kIv, subsamples)); |
| { |
| MojoDecoderBufferConverter converter; |
| converter.ConvertAndVerify(buffer); |
| } |
| |
| // Test empty IV. This is used for clear buffer in an encrypted stream. |
| buffer->set_decrypt_config(base::MakeUnique<DecryptConfig>( |
| kKeyId, "", std::vector<SubsampleEntry>())); |
| { |
| MojoDecoderBufferConverter converter; |
| converter.ConvertAndVerify(buffer); |
| } |
| } |
| |
| // This test verifies that a DecoderBuffer larger than data-pipe capacity |
| // can be transmitted properly. |
| TEST(MojoDecoderBufferConverterTest, Chunked) { |
| base::MessageLoop message_loop; |
| const uint8_t kData[] = "Lorem ipsum dolor sit amet, consectetur cras amet"; |
| const size_t kDataSize = arraysize(kData); |
| scoped_refptr<DecoderBuffer> buffer = |
| DecoderBuffer::CopyFrom(kData, kDataSize); |
| |
| MojoDecoderBufferConverter converter(kDataSize / 3); |
| converter.ConvertAndVerify(buffer); |
| } |
| |
| // This test verifies that MojoDecoderBufferWriter returns NULL if data pipe |
| // is already closed. |
| TEST(MojoDecoderBufferConverterTest, ReaderSidePipeError) { |
| base::MessageLoop message_loop; |
| const uint8_t kData[] = "Hello, world"; |
| const size_t kDataSize = arraysize(kData); |
| scoped_refptr<DecoderBuffer> media_buffer = |
| DecoderBuffer::CopyFrom(kData, kDataSize); |
| |
| MojoDecoderBufferConverter converter; |
| // Before sending the buffer, close the handle on reader side. |
| converter.reader.reset(); |
| mojom::DecoderBufferPtr mojo_buffer = |
| converter.writer->WriteDecoderBuffer(media_buffer); |
| EXPECT_TRUE(mojo_buffer.is_null()); |
| } |
| |
| // This test verifies that MojoDecoderBufferReader::ReadCB is called with a |
| // NULL DecoderBuffer if data pipe is closed during transmission. |
| TEST(MojoDecoderBufferConverterTest, WriterSidePipeError) { |
| base::MessageLoop message_loop; |
| const uint8_t kData[] = "Lorem ipsum dolor sit amet, consectetur cras amet"; |
| const size_t kDataSize = arraysize(kData); |
| scoped_refptr<DecoderBuffer> media_buffer = |
| DecoderBuffer::CopyFrom(kData, kDataSize); |
| |
| // Verify that ReadCB is called with a NULL decoder buffer. |
| base::RunLoop run_loop; |
| base::MockCallback<MojoDecoderBufferReader::ReadCB> mock_cb; |
| EXPECT_CALL(mock_cb, Run(testing::IsNull())) |
| .WillOnce(testing::InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit)); |
| |
| // Make data pipe with capacity smaller than decoder buffer so that only |
| // partial data is written. |
| MojoDecoderBufferConverter converter(kDataSize / 2); |
| mojom::DecoderBufferPtr mojo_buffer = |
| converter.writer->WriteDecoderBuffer(media_buffer); |
| converter.reader->ReadDecoderBuffer(std::move(mojo_buffer), mock_cb.Get()); |
| |
| // Before the entire data is transmitted, close the handle on writer side. |
| // The reader side will get notified and report the error. |
| converter.writer.reset(); |
| run_loop.Run(); |
| } |
| |
| } // namespace media |