Commit 8737204a authored by Ryan Keane's avatar Ryan Keane Committed by Chromium LUCI CQ

[Cast] Add Translation Layer between CmaBackend and gRPC Channel

This CL defines a layer in between the gRPC Channel and the CmaBackend,
which is responsible for converting between internal and external
entity types, and ensuring that calls to the CmaBackend occur on the
proper thread.

Bug: b/167426091
Change-Id: I7bf46475f147a62500f155eb670b4983de4ba824
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2519156
Commit-Queue: Ryan Keane <rwkeane@google.com>
Reviewed-by: default avatarmark a. foltz <mfoltz@chromium.org>
Reviewed-by: default avatarYuchen Liu <yucliu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834565}
parent 95cc415d
......@@ -81,6 +81,7 @@ cast_source_set("unittests") {
if (!is_android) {
sources += [
"backend/proxy/cma_backend_proxy_unittest.cc",
"backend/proxy/proxy_call_translator_unittest.cc",
"backend/proxy/push_buffer_queue_unittest.cc",
]
deps += [
......
......@@ -38,9 +38,12 @@ cast_source_set("proxy") {
"cast_runtime_audio_channel_broker.h",
"cma_backend_proxy.cc",
"cma_backend_proxy.h",
"cma_proxy_handler.h",
"multizone_audio_decoder_proxy.h",
"multizone_audio_decoder_proxy_impl.cc",
"multizone_audio_decoder_proxy_impl.h",
"proxy_call_translator.cc",
"proxy_call_translator.h",
"push_buffer_queue.cc",
"push_buffer_queue.h",
]
......@@ -51,6 +54,7 @@ cast_source_set("proxy") {
"//chromecast/base",
"//chromecast/media/api",
"//chromecast/public/media",
"//third_party/grpc:grpc++",
"//third_party/protobuf:protobuf_full",
]
}
// Copyright 2020 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 CHROMECAST_MEDIA_CMA_BACKEND_PROXY_CMA_PROXY_HANDLER_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_PROXY_CMA_PROXY_HANDLER_H_
#include "base/memory/ref_counted.h"
namespace chromecast {
class TaskRunner;
namespace media {
struct AudioConfig;
class CmaProxyHandler {
public:
// The mode in which Cast Core should operate.
enum class AudioDecoderOperationMode {
// Both multiroom and audio rendering is enabled.
kAll = 0,
// Only multiroom is enabled and audio rendering is disabled. This should
// be used if the runtime is taking over responsibility for rendering audio.
kMultiroomOnly = 1,
// Only audio rendering is enabled and multiroom is disabled.
kAudioOnly = 2
};
// The current state of the remote CMA backend.
enum class PipelineState {
kUninitialized = 0,
kStopped = 1,
kPlaying = 2,
kPaused = 3,
};
// Observer for changes on the remote client.
class Client {
public:
virtual ~Client() = default;
// Called when an error occurs upon calling any gRPC Method.
virtual void OnError() = 0;
// Called when the Start(), Stop(), Pause(), or Resume() methods
// successfully change the current pipeline state.
virtual void OnPipelineStateChange(PipelineState state) = 0;
// Called following the successful processing of a batch of PushBuffer
// calls.
virtual void OnBytesDecoded(int64_t decoded_byte_count) = 0;
};
virtual ~CmaProxyHandler() = default;
// Create a new implementation-specific CmaProxyHandler. Each provided
// object must exist for the duration of the created instance's lifetime, and
// all callbacks for |client| will be called on |task_runner|.
static std::unique_ptr<CmaProxyHandler> Create(TaskRunner* task_runner,
Client* client);
// Calls to the corresponding gRPC Methods. These functions may be called from
// any thread.
virtual void Initialize(const std::string& cast_session_id,
AudioDecoderOperationMode decoder_mode) = 0;
virtual void Start(int64_t start_pts) = 0;
virtual void Stop() = 0;
virtual void Pause() = 0;
virtual void Resume() = 0;
virtual void SetPlaybackRate(float rate) = 0;
virtual void SetVolume(float multiplier) = 0;
// Push the provided data or config to a queue, for processing at a later
// point when resources are available. Returns true if the data was
// successfully pushed to the queue and false otherwise. These functions may
// be called from any thread.
//
// NOTES:
// - SetConfig is expected to be called prior to any PushBuffer calls.
// - SetConfig may be called later on as-well, after which time the new config
// will be used for all following PushBuffer calls.
virtual bool SetConfig(const AudioConfig& config) = 0;
virtual bool PushBuffer(scoped_refptr<DecoderBufferBase> buffer) = 0;
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_PROXY_CMA_PROXY_HANDLER_H_
// Copyright 2020 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 "chromecast/media/cma/backend/proxy/proxy_call_translator.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_forward.h"
#include "base/time/time.h"
#include "chromecast/public/media/decoder_config.h"
#include "chromecast/public/task_runner.h"
#include "third_party/openscreen/src/cast/cast_core/api/runtime/cast_audio_decoder_service.grpc.pb.h"
#include "third_party/protobuf/src/google/protobuf/util/time_util.h"
namespace chromecast {
namespace media {
namespace {
CmaProxyHandler::PipelineState ToClientTypes(
CastRuntimeAudioChannelBroker::Handler::PipelineState state) {
switch (state) {
case CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_UNINITIALIZED:
return CmaProxyHandler::PipelineState::kUninitialized;
case CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_STOPPED:
return CmaProxyHandler::PipelineState::kStopped;
case CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_PLAYING:
return CmaProxyHandler::PipelineState::kPlaying;
case CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_PAUSED:
return CmaProxyHandler::PipelineState::kPaused;
default:
NOTREACHED();
return CmaProxyHandler::PipelineState::kUninitialized;
}
}
cast::media::AudioConfiguration_AudioCodec ToGrpcTypes(AudioCodec codec) {
switch (codec) {
case kAudioCodecUnknown:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_UNKNOWN;
case kCodecAAC:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_AAC;
case kCodecMP3:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_MP3;
case kCodecPCM:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_PCM;
case kCodecPCM_S16BE:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_PCM_S16BE;
case kCodecVorbis:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_VORBIS;
case kCodecOpus:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_OPUS;
case kCodecEAC3:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_EAC3;
case kCodecAC3:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_AC3;
case kCodecDTS:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_DTS;
case kCodecFLAC:
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_FLAC;
case kCodecMpegHAudio:
return cast::media::
AudioConfiguration_AudioCodec_AUDIO_CODEC_MPEG_H_AUDIO;
default:
NOTREACHED();
return cast::media::AudioConfiguration_AudioCodec_AUDIO_CODEC_UNKNOWN;
}
}
cast::media::AudioConfiguration_ChannelLayout ToGrpcTypes(
ChannelLayout channel_layout) {
switch (channel_layout) {
case ChannelLayout::UNSUPPORTED:
return cast::media::
AudioConfiguration_ChannelLayout_CHANNEL_LAYOUT_UNSUPPORTED;
case ChannelLayout::MONO:
return cast::media::AudioConfiguration_ChannelLayout_CHANNEL_LAYOUT_MONO;
case ChannelLayout::STEREO:
return cast::media::
AudioConfiguration_ChannelLayout_CHANNEL_LAYOUT_STEREO;
case ChannelLayout::SURROUND_5_1:
return cast::media::
AudioConfiguration_ChannelLayout_CHANNEL_LAYOUT_SURROUND_5_1;
case ChannelLayout::BITSTREAM:
return cast::media::
AudioConfiguration_ChannelLayout_CHANNEL_LAYOUT_BITSTREAM;
case ChannelLayout::DISCRETE:
return cast::media::
AudioConfiguration_ChannelLayout_CHANNEL_LAYOUT_DISCRETE;
default:
NOTREACHED();
return cast::media::
AudioConfiguration_ChannelLayout_CHANNEL_LAYOUT_UNSUPPORTED;
}
}
cast::media::AudioConfiguration_SampleFormat ToGrpcTypes(
SampleFormat sample_format) {
switch (sample_format) {
case kUnknownSampleFormat:
return cast::media::AudioConfiguration_SampleFormat_SAMPLE_FORMAT_UNKNOWN;
case kSampleFormatU8:
return cast::media::AudioConfiguration_SampleFormat_SAMPLE_FORMAT_U8;
case kSampleFormatS16:
return cast::media::AudioConfiguration_SampleFormat_SAMPLE_FORMAT_S16;
case kSampleFormatS32:
return cast::media::AudioConfiguration_SampleFormat_SAMPLE_FORMAT_S32;
case kSampleFormatF32:
return cast::media::AudioConfiguration_SampleFormat_SAMPLE_FORMAT_F32;
case kSampleFormatPlanarS16:
return cast::media::
AudioConfiguration_SampleFormat_SAMPLE_FORMAT_PLANAR_S16;
case kSampleFormatPlanarF32:
return cast::media::
AudioConfiguration_SampleFormat_SAMPLE_FORMAT_PLANAR_F32;
case kSampleFormatPlanarS32:
return cast::media::
AudioConfiguration_SampleFormat_SAMPLE_FORMAT_PLANAR_S32;
case kSampleFormatS24:
return cast::media::AudioConfiguration_SampleFormat_SAMPLE_FORMAT_S24;
default:
NOTREACHED();
return cast::media::AudioConfiguration_SampleFormat_SAMPLE_FORMAT_UNKNOWN;
}
}
cast::media::CastAudioDecoderMode ToGrpcTypes(
CmaProxyHandler::AudioDecoderOperationMode operation_mode) {
switch (operation_mode) {
case CmaProxyHandler::AudioDecoderOperationMode::kAll:
return cast::media::CAST_AUDIO_DECODER_MODE_ALL;
case CmaProxyHandler::AudioDecoderOperationMode::kMultiroomOnly:
return cast::media::CAST_AUDIO_DECODER_MODE_MULTIROOM_ONLY;
case CmaProxyHandler::AudioDecoderOperationMode::kAudioOnly:
return cast::media::CAST_AUDIO_DECODER_MODE_AUDIO_ONLY;
}
}
CastRuntimeAudioChannelBroker::Handler::PushBufferRequest ToGrpcTypes(
scoped_refptr<DecoderBufferBase> buffer) {
auto* decode_buffer = new cast::media::AudioDecoderBuffer;
decode_buffer->set_pts_micros(buffer->timestamp());
decode_buffer->set_data(buffer->data(), buffer->data_size());
decode_buffer->set_end_of_stream(buffer->end_of_stream());
CastRuntimeAudioChannelBroker::Handler::PushBufferRequest request;
// NOTE: This transfers ownership of |decode_buffer| to |request|.
request.set_allocated_buffer(decode_buffer);
return request;
}
CastRuntimeAudioChannelBroker::Handler::PushBufferRequest ToGrpcTypes(
const AudioConfig& audio_config) {
auto* audio_config_internal = new cast::media::AudioConfiguration;
audio_config_internal->set_codec(ToGrpcTypes(audio_config.codec));
audio_config_internal->set_channel_layout(
ToGrpcTypes(audio_config.channel_layout));
audio_config_internal->set_sample_format(
ToGrpcTypes(audio_config.sample_format));
audio_config_internal->set_bytes_per_channel(audio_config.bytes_per_channel);
audio_config_internal->set_channel_number(audio_config.channel_number);
audio_config_internal->set_samples_per_second(
audio_config.samples_per_second);
// NOTE: This copies the data.
audio_config_internal->set_extra_data(audio_config.extra_data.data(),
audio_config.extra_data.size());
CastRuntimeAudioChannelBroker::Handler::PushBufferRequest request;
// NOTE: This transfers ownership of |audio_config_internal| to |request|.
request.set_allocated_audio_config(audio_config_internal);
return request;
}
// Helper to convert from Chromium callback type (OnceCallback) to Chromecast's
// TaskRunner's Task type.
class OnceCallbackTask : public TaskRunner::Task {
public:
OnceCallbackTask(base::OnceClosure callback)
: callback_(std::move(callback)) {}
~OnceCallbackTask() override = default;
private:
// TaskRunner::Task overrides:
void Run() override { std::move(callback_).Run(); }
base::OnceClosure callback_;
};
} // namespace
// static
std::unique_ptr<CmaProxyHandler> CmaProxyHandler::Create(
TaskRunner* task_runner,
Client* client) {
return std::make_unique<ProxyCallTranslator>(task_runner, client);
}
ProxyCallTranslator::ProxyCallTranslator(TaskRunner* client_task_runner,
CmaProxyHandler::Client* client)
: ProxyCallTranslator(client_task_runner,
client,
CastRuntimeAudioChannelBroker::Create(this)) {}
ProxyCallTranslator::ProxyCallTranslator(
TaskRunner* client_task_runner,
CmaProxyHandler::Client* client,
std::unique_ptr<CastRuntimeAudioChannelBroker> decoder_channel)
: decoder_channel_(std::move(decoder_channel)),
client_task_runner_(client_task_runner),
client_(client),
weak_factory_(this) {
DCHECK(decoder_channel_.get());
DCHECK(client_task_runner_);
DCHECK(client_);
}
ProxyCallTranslator::~ProxyCallTranslator() = default;
void ProxyCallTranslator::Initialize(
const std::string& cast_session_id,
CmaProxyHandler::AudioDecoderOperationMode decoder_mode) {
decoder_channel_->InitializeAsync(cast_session_id, ToGrpcTypes(decoder_mode));
}
void ProxyCallTranslator::Start(int64_t start_pts) {
decoder_channel_->StartAsync(start_pts);
}
void ProxyCallTranslator::Stop() {
decoder_channel_->StopAsync();
}
void ProxyCallTranslator::Pause() {
decoder_channel_->PauseAsync();
}
void ProxyCallTranslator::Resume() {
decoder_channel_->ResumeAsync();
}
void ProxyCallTranslator::SetPlaybackRate(float rate) {
decoder_channel_->SetPlaybackAsync(rate);
}
void ProxyCallTranslator::SetVolume(float multiplier) {
decoder_channel_->SetVolumeAsync(multiplier);
}
bool ProxyCallTranslator::SetConfig(const AudioConfig& config) {
return ProcessPushBufferRequest(ToGrpcTypes(config));
}
bool ProxyCallTranslator::PushBuffer(scoped_refptr<DecoderBufferBase> buffer) {
return ProcessPushBufferRequest(ToGrpcTypes(std::move(buffer)));
}
bool ProxyCallTranslator::ProcessPushBufferRequest(PushBufferRequest request) {
// TODO(rwkeane)
NOTIMPLEMENTED();
return false;
}
ProxyCallTranslator::PushBufferRequest ProxyCallTranslator::GetBufferedData() {
// TODO(rwkeane)
NOTIMPLEMENTED();
return PushBufferRequest{};
}
bool ProxyCallTranslator::HasBufferedData() {
// TODO(rwkeane)
NOTIMPLEMENTED();
return false;
}
void ProxyCallTranslator::HandleInitializeResponse(
CastRuntimeAudioChannelBroker::StatusCode status) {
HandleError(status);
}
void ProxyCallTranslator::HandleSetVolumeResponse(
CastRuntimeAudioChannelBroker::StatusCode status) {
HandleError(status);
}
void ProxyCallTranslator::HandleSetPlaybackResponse(
CastRuntimeAudioChannelBroker::StatusCode status) {
HandleError(status);
}
void ProxyCallTranslator::HandleStateChangeResponse(
CastRuntimeAudioChannelBroker::Handler::PipelineState state,
CastRuntimeAudioChannelBroker::StatusCode status) {
if (!HandleError(status)) {
return;
}
auto* task = new OnceCallbackTask(
base::BindOnce(&ProxyCallTranslator::OnPipelineStateChangeTask,
weak_factory_.GetWeakPtr(), ToClientTypes(state)));
client_task_runner_->PostTask(task, 0);
}
void ProxyCallTranslator::HandlePushBufferResponse(
int64_t decoded_bytes,
CastRuntimeAudioChannelBroker::StatusCode status) {
if (!HandleError(status)) {
return;
}
auto* task = new OnceCallbackTask(
base::BindOnce(&ProxyCallTranslator::OnBytesDecodedTask,
weak_factory_.GetWeakPtr(), decoded_bytes));
client_task_runner_->PostTask(task, 0);
}
void ProxyCallTranslator::HandleGetMediaTimeResponse(
base::Optional<MediaTime> time,
CastRuntimeAudioChannelBroker::StatusCode status) {
NOTREACHED();
}
bool ProxyCallTranslator::HandleError(
CastRuntimeAudioChannelBroker::StatusCode status) {
if (status == CastRuntimeAudioChannelBroker::StatusCode::kOk) {
return true;
}
auto* task = new OnceCallbackTask(base::BindOnce(
&ProxyCallTranslator::OnErrorTask, weak_factory_.GetWeakPtr()));
client_task_runner_->PostTask(task, 0);
return false;
}
void ProxyCallTranslator::OnErrorTask() {
client_->OnError();
}
void ProxyCallTranslator::OnPipelineStateChangeTask(
CmaProxyHandler::PipelineState state) {
client_->OnPipelineStateChange(state);
}
void ProxyCallTranslator::OnBytesDecodedTask(int64_t decoded_byte_count) {
client_->OnBytesDecoded(decoded_byte_count);
}
} // namespace media
} // namespace chromecast
// Copyright 2020 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 CHROMECAST_MEDIA_CMA_BACKEND_PROXY_PROXY_CALL_TRANSLATOR_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_PROXY_PROXY_CALL_TRANSLATOR_H_
#include <memory>
#include "base/memory/ref_counted.h"
#include "base/optional.h"
#include "chromecast/media/api/decoder_buffer_base.h"
#include "chromecast/media/cma/backend/proxy/cast_runtime_audio_channel_broker.h"
#include "chromecast/media/cma/backend/proxy/cma_proxy_handler.h"
namespace chromecast {
class TaskRunner;
namespace media {
struct AudioConfig;
// This class is responsible for translating between entities used by the
// client CmaBackend and entities used by the internal gRPC Channel.
// Calls made to all methods of this class may be made from any thread.
class ProxyCallTranslator : public CmaProxyHandler,
public CastRuntimeAudioChannelBroker::Handler {
public:
// Creates a new ProxyCallTranslator. All provided entities must exist for the
// duration of this instance's lifetime. All calls to |client| will be made
// on |client_task_runner|.
ProxyCallTranslator(TaskRunner* client_task_runner,
CmaProxyHandler::Client* client);
~ProxyCallTranslator() override;
// CmaProxyHandler overrides:
void Initialize(
const std::string& cast_session_id,
CmaProxyHandler::AudioDecoderOperationMode decoder_mode) override;
void Start(int64_t start_pts) override;
void Stop() override;
void Pause() override;
void Resume() override;
void SetPlaybackRate(float rate) override;
void SetVolume(float multiplier) override;
bool SetConfig(const AudioConfig& config) override;
bool PushBuffer(scoped_refptr<DecoderBufferBase> buffer) override;
private:
friend class ProxyCallTranslatorTest;
using MediaTime = CastRuntimeAudioChannelBroker::Handler::MediaTime;
using PipelineState = CastRuntimeAudioChannelBroker::Handler::PipelineState;
using PushBufferRequest =
CastRuntimeAudioChannelBroker::Handler::PushBufferRequest;
ProxyCallTranslator(
TaskRunner* client_task_runner,
CmaProxyHandler::Client* client,
std::unique_ptr<CastRuntimeAudioChannelBroker> decoder_channel);
// CastRuntimeAudioChannelBroker::Handler overrides:
PushBufferRequest GetBufferedData() override;
bool HasBufferedData() override;
void HandleInitializeResponse(
CastRuntimeAudioChannelBroker::StatusCode status) override;
void HandleStateChangeResponse(
PipelineState state,
CastRuntimeAudioChannelBroker::StatusCode status) override;
void HandleSetVolumeResponse(
CastRuntimeAudioChannelBroker::StatusCode status) override;
void HandleSetPlaybackResponse(
CastRuntimeAudioChannelBroker::StatusCode status) override;
void HandlePushBufferResponse(
int64_t decoded_bytes,
CastRuntimeAudioChannelBroker::StatusCode status) override;
void HandleGetMediaTimeResponse(
base::Optional<MediaTime> time,
CastRuntimeAudioChannelBroker::StatusCode status) override;
bool ProcessPushBufferRequest(PushBufferRequest request);
// Helper to share error handling code.
bool HandleError(CastRuntimeAudioChannelBroker::StatusCode status);
// Helpers to simplify use of callbacks for tasks posted to
// |client_task_runner_|.
void OnErrorTask();
void OnPipelineStateChangeTask(CmaProxyHandler::PipelineState state);
void OnBytesDecodedTask(int64_t decoded_byte_count);
std::unique_ptr<CastRuntimeAudioChannelBroker> decoder_channel_;
TaskRunner* const client_task_runner_;
CmaProxyHandler::Client* const client_;
// NOTE: All weak_ptrs created from this factory must be dereferenced on
// |client_task_runner_|. Unfortunately, due to the structure of the
// chromecast::TaskRunner class, weak_ptr validation is not guaranteed so this
// assumption cannot be validated outside of the WeakPtr class.
base::WeakPtrFactory<ProxyCallTranslator> weak_factory_;
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_CMA_BACKEND_PROXY_PROXY_CALL_TRANSLATOR_H_
// Copyright 2020 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 "chromecast/media/cma/backend/proxy/proxy_call_translator.h"
#include <memory>
#include "base/memory/scoped_refptr.h"
#include "base/test/test_simple_task_runner.h"
#include "chromecast/base/task_runner_impl.h"
#include "chromecast/media/cma/backend/proxy/cast_runtime_audio_channel_broker.h"
#include "chromecast/media/cma/backend/proxy/cma_proxy_handler.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chromecast {
namespace media {
namespace {
class MockTranslatorClient : public CmaProxyHandler::Client {
public:
~MockTranslatorClient() override = default;
MOCK_METHOD0(OnError, void());
MOCK_METHOD1(OnPipelineStateChange, void(CmaProxyHandler::PipelineState));
MOCK_METHOD1(OnBytesDecoded, void(int64_t));
MOCK_METHOD2(OnMediaTimeUpdate, void(int64_t, base::TimeTicks));
MOCK_METHOD0(OnEndOfStream, void());
};
class MockDecoderChannel : public CastRuntimeAudioChannelBroker {
public:
~MockDecoderChannel() override = default;
MOCK_METHOD2(
InitializeAsync,
void(const std::string&,
CastRuntimeAudioChannelBroker::CastAudioDecoderMode decoder_mode));
MOCK_METHOD1(SetVolumeAsync, void(float));
MOCK_METHOD1(SetPlaybackAsync, void(double));
MOCK_METHOD0(GetMediaTimeAsync, void());
MOCK_METHOD1(StartAsync, void(int64_t));
MOCK_METHOD0(StopAsync, void());
MOCK_METHOD0(PauseAsync, void());
MOCK_METHOD0(ResumeAsync, void());
};
} // namespace
class ProxyCallTranslatorTest : public testing::Test {
public:
ProxyCallTranslatorTest()
: task_runner_(new base::TestSimpleTaskRunner()),
chromecast_task_runner_(task_runner_),
decoder_channel_not_owned_(
std::make_unique<testing::StrictMock<MockDecoderChannel>>()),
decoder_channel_(decoder_channel_not_owned_.get()),
translator_(&chromecast_task_runner_,
&translator_client_,
std::move(decoder_channel_not_owned_)),
translator_as_handler_(
static_cast<CastRuntimeAudioChannelBroker::Handler*>(
&translator_)) {}
~ProxyCallTranslatorTest() override = default;
protected:
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
TaskRunnerImpl chromecast_task_runner_;
testing::StrictMock<MockTranslatorClient> translator_client_;
std::unique_ptr<testing::StrictMock<MockDecoderChannel>>
decoder_channel_not_owned_;
testing::StrictMock<MockDecoderChannel>* decoder_channel_;
ProxyCallTranslator translator_;
CastRuntimeAudioChannelBroker::Handler* translator_as_handler_;
const CastRuntimeAudioChannelBroker::StatusCode failure_status_ =
CastRuntimeAudioChannelBroker::StatusCode::kUnknown;
const CastRuntimeAudioChannelBroker::StatusCode success_status_ =
CastRuntimeAudioChannelBroker::StatusCode::kOk;
};
TEST_F(ProxyCallTranslatorTest, TestExternalInitialize) {
std::string session_id = "foo";
EXPECT_CALL(
*decoder_channel_,
InitializeAsync(session_id, cast::media::CAST_AUDIO_DECODER_MODE_ALL));
translator_.Initialize(session_id,
CmaProxyHandler::AudioDecoderOperationMode::kAll);
session_id = "bar";
EXPECT_CALL(
*decoder_channel_,
InitializeAsync(session_id,
cast::media::CAST_AUDIO_DECODER_MODE_MULTIROOM_ONLY));
translator_.Initialize(
session_id, CmaProxyHandler::AudioDecoderOperationMode::kMultiroomOnly);
session_id = "foobar";
EXPECT_CALL(*decoder_channel_,
InitializeAsync(session_id,
cast::media::CAST_AUDIO_DECODER_MODE_AUDIO_ONLY));
translator_.Initialize(
session_id, CmaProxyHandler::AudioDecoderOperationMode::kAudioOnly);
}
TEST_F(ProxyCallTranslatorTest, TestExternalStart) {
constexpr int64_t start_pts = 42;
EXPECT_CALL(*decoder_channel_, StartAsync(start_pts));
translator_.Start(start_pts);
}
TEST_F(ProxyCallTranslatorTest, TestExternalStop) {
EXPECT_CALL(*decoder_channel_, StopAsync());
translator_.Stop();
}
TEST_F(ProxyCallTranslatorTest, TestExternalPause) {
EXPECT_CALL(*decoder_channel_, PauseAsync());
translator_.Pause();
}
TEST_F(ProxyCallTranslatorTest, TestExternalResume) {
EXPECT_CALL(*decoder_channel_, ResumeAsync());
translator_.Resume();
}
TEST_F(ProxyCallTranslatorTest, TestExternalSetPlaybackRate) {
constexpr float rate = 42;
EXPECT_CALL(*decoder_channel_, SetPlaybackAsync(rate));
translator_.SetPlaybackRate(rate);
}
TEST_F(ProxyCallTranslatorTest, TestExternalSetVolume) {
constexpr float multiplier = 42;
EXPECT_CALL(*decoder_channel_, SetVolumeAsync(multiplier));
translator_.SetVolume(multiplier);
}
TEST_F(ProxyCallTranslatorTest, TestInternalHandleInitializeFailure) {
EXPECT_CALL(translator_client_, OnError());
translator_as_handler_->HandleInitializeResponse(failure_status_);
task_runner_->RunPendingTasks();
}
TEST_F(ProxyCallTranslatorTest, TestHandleStateChangeFailure) {
EXPECT_CALL(translator_client_, OnError());
translator_as_handler_->HandleStateChangeResponse(
CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_UNINITIALIZED,
failure_status_);
task_runner_->RunPendingTasks();
}
TEST_F(ProxyCallTranslatorTest, TestHandleSetVolumeFailure) {
EXPECT_CALL(translator_client_, OnError());
translator_as_handler_->HandleSetVolumeResponse(failure_status_);
task_runner_->RunPendingTasks();
}
TEST_F(ProxyCallTranslatorTest, TesSetPlaybackFailure) {
EXPECT_CALL(translator_client_, OnError());
translator_as_handler_->HandleSetPlaybackResponse(failure_status_);
task_runner_->RunPendingTasks();
}
TEST_F(ProxyCallTranslatorTest, TestPushBufferFailure) {
EXPECT_CALL(translator_client_, OnError());
translator_as_handler_->HandlePushBufferResponse(42, failure_status_);
task_runner_->RunPendingTasks();
}
TEST_F(ProxyCallTranslatorTest, TestStateChangeSuccess) {
EXPECT_CALL(
translator_client_,
OnPipelineStateChange(CmaProxyHandler::PipelineState::kUninitialized));
translator_as_handler_->HandleStateChangeResponse(
CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_UNINITIALIZED,
success_status_);
task_runner_->RunPendingTasks();
EXPECT_CALL(translator_client_,
OnPipelineStateChange(CmaProxyHandler::PipelineState::kStopped));
translator_as_handler_->HandleStateChangeResponse(
CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_STOPPED,
success_status_);
task_runner_->RunPendingTasks();
EXPECT_CALL(translator_client_,
OnPipelineStateChange(CmaProxyHandler::PipelineState::kPlaying));
translator_as_handler_->HandleStateChangeResponse(
CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_PLAYING,
success_status_);
task_runner_->RunPendingTasks();
EXPECT_CALL(translator_client_,
OnPipelineStateChange(CmaProxyHandler::PipelineState::kPaused));
translator_as_handler_->HandleStateChangeResponse(
CastRuntimeAudioChannelBroker::Handler::PipelineState::
PIPELINE_STATE_PAUSED,
success_status_);
task_runner_->RunPendingTasks();
}
TEST_F(ProxyCallTranslatorTest, TestPushBufferSuccess) {
EXPECT_CALL(translator_client_, OnBytesDecoded(42));
translator_as_handler_->HandlePushBufferResponse(42, success_status_);
task_runner_->RunPendingTasks();
EXPECT_CALL(translator_client_, OnBytesDecoded(112358));
translator_as_handler_->HandlePushBufferResponse(112358, success_status_);
task_runner_->RunPendingTasks();
}
} // namespace media
} // namespace chromecast
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment