blob: 8a8b30d7fbab78a7cc9cee707169715df8fa8de9 [file] [log] [blame]
// 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