blob: bc149ab7224f30eb8696c47d2a47a7840e7d26fa [file] [log] [blame]
// 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 "media/remoting/receiver.h"
#include <utility>
#include "base/check.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/media_util.h"
#include "media/base/mock_filters.h"
#include "media/base/renderer.h"
#include "media/base/test_helpers.h"
#include "media/base/video_decoder_config.h"
#include "media/remoting/mock_receiver_controller.h"
#include "media/remoting/proto_enum_utils.h"
#include "media/remoting/proto_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
using base::test::RunOnceCallback;
using openscreen::cast::RpcMessenger;
using testing::_;
using testing::AtLeast;
using testing::NiceMock;
using testing::StrictMock;
namespace media {
namespace remoting {
class MockSender {
public:
MockSender(RpcMessenger* rpc_messenger, int remote_handle)
: rpc_messenger_(rpc_messenger),
rpc_handle_(rpc_messenger->GetUniqueHandle()),
remote_handle_(remote_handle) {
rpc_messenger_->RegisterMessageReceiverCallback(
rpc_handle_, [this](std::unique_ptr<openscreen::cast::RpcMessage> rpc) {
this->OnReceivedRpc(std::move(rpc));
});
}
MOCK_METHOD(void, AcquireRendererDone, ());
MOCK_METHOD(void, InitializeCallback, (bool));
MOCK_METHOD(void, FlushUntilCallback, ());
MOCK_METHOD(void, OnTimeUpdate, (int64_t, int64_t));
MOCK_METHOD(void, OnBufferingStateChange, (BufferingState));
MOCK_METHOD(void, OnEnded, ());
MOCK_METHOD(void, OnFatalError, ());
MOCK_METHOD(void, OnAudioConfigChange, (AudioDecoderConfig));
MOCK_METHOD(void, OnVideoConfigChange, (VideoDecoderConfig));
MOCK_METHOD(void, OnVideoNaturalSizeChange, (gfx::Size));
MOCK_METHOD(void, OnVideoOpacityChange, (bool));
MOCK_METHOD(void, OnStatisticsUpdate, (PipelineStatistics));
MOCK_METHOD(void, OnWaiting, ());
void OnReceivedRpc(std::unique_ptr<openscreen::cast::RpcMessage> message) {
DCHECK(message);
switch (message->proc()) {
case openscreen::cast::RpcMessage::RPC_ACQUIRE_RENDERER_DONE:
AcquireRendererDone();
break;
case openscreen::cast::RpcMessage::RPC_R_INITIALIZE_CALLBACK:
InitializeCallback(message->boolean_value());
break;
case openscreen::cast::RpcMessage::RPC_R_FLUSHUNTIL_CALLBACK:
FlushUntilCallback();
break;
case openscreen::cast::RpcMessage::RPC_RC_ONTIMEUPDATE: {
DCHECK(message->has_rendererclient_ontimeupdate_rpc());
const int64_t time_usec =
message->rendererclient_ontimeupdate_rpc().time_usec();
const int64_t max_time_usec =
message->rendererclient_ontimeupdate_rpc().max_time_usec();
OnTimeUpdate(time_usec, max_time_usec);
break;
}
case openscreen::cast::RpcMessage::RPC_RC_ONBUFFERINGSTATECHANGE: {
absl::optional<BufferingState> state = ToMediaBufferingState(
message->rendererclient_onbufferingstatechange_rpc().state());
if (state.has_value())
OnBufferingStateChange(state.value());
break;
}
case openscreen::cast::RpcMessage::RPC_RC_ONENDED:
OnEnded();
break;
case openscreen::cast::RpcMessage::RPC_RC_ONERROR:
OnFatalError();
break;
case openscreen::cast::RpcMessage::RPC_RC_ONAUDIOCONFIGCHANGE: {
DCHECK(message->has_rendererclient_onaudioconfigchange_rpc());
const auto* audio_config_message =
message->mutable_rendererclient_onaudioconfigchange_rpc();
const openscreen::cast::AudioDecoderConfig pb_audio_config =
audio_config_message->audio_decoder_config();
AudioDecoderConfig out_audio_config;
ConvertProtoToAudioDecoderConfig(pb_audio_config, &out_audio_config);
DCHECK(out_audio_config.IsValidConfig());
OnAudioConfigChange(out_audio_config);
break;
}
case openscreen::cast::RpcMessage::RPC_RC_ONVIDEOCONFIGCHANGE: {
DCHECK(message->has_rendererclient_onvideoconfigchange_rpc());
const auto* video_config_message =
message->mutable_rendererclient_onvideoconfigchange_rpc();
const openscreen::cast::VideoDecoderConfig pb_video_config =
video_config_message->video_decoder_config();
VideoDecoderConfig out_video_config;
ConvertProtoToVideoDecoderConfig(pb_video_config, &out_video_config);
DCHECK(out_video_config.IsValidConfig());
OnVideoConfigChange(out_video_config);
break;
}
case openscreen::cast::RpcMessage::RPC_RC_ONVIDEONATURALSIZECHANGE: {
DCHECK(message->has_rendererclient_onvideonatualsizechange_rpc());
gfx::Size size(
message->rendererclient_onvideonatualsizechange_rpc().width(),
message->rendererclient_onvideonatualsizechange_rpc().height());
OnVideoNaturalSizeChange(size);
break;
}
case openscreen::cast::RpcMessage::RPC_RC_ONVIDEOOPACITYCHANGE:
OnVideoOpacityChange(message->boolean_value());
break;
case openscreen::cast::RpcMessage::RPC_RC_ONSTATISTICSUPDATE: {
DCHECK(message->has_rendererclient_onstatisticsupdate_rpc());
auto rpc_message = message->rendererclient_onstatisticsupdate_rpc();
PipelineStatistics statistics;
statistics.audio_bytes_decoded = rpc_message.audio_bytes_decoded();
statistics.video_bytes_decoded = rpc_message.video_bytes_decoded();
statistics.video_frames_decoded = rpc_message.video_frames_decoded();
statistics.video_frames_dropped = rpc_message.video_frames_dropped();
statistics.audio_memory_usage = rpc_message.audio_memory_usage();
statistics.video_memory_usage = rpc_message.video_memory_usage();
OnStatisticsUpdate(statistics);
break;
}
default:
VLOG(1) << "Unknown RPC: " << message->proc();
}
}
void SendRpcAcquireRenderer() {
openscreen::cast::RpcMessage rpc;
rpc.set_handle(RpcMessenger::kAcquireRendererHandle);
rpc.set_proc(openscreen::cast::RpcMessage::RPC_ACQUIRE_RENDERER);
rpc.set_integer_value(rpc_handle_);
rpc_messenger_->SendMessageToRemote(rpc);
}
void SendRpcInitialize() {
openscreen::cast::RpcMessage rpc;
rpc.set_handle(remote_handle_);
rpc.set_proc(openscreen::cast::RpcMessage::RPC_R_INITIALIZE);
rpc_messenger_->SendMessageToRemote(rpc);
}
void SendRpcSetPlaybackRate(double playback_rate) {
openscreen::cast::RpcMessage rpc;
rpc.set_handle(remote_handle_);
rpc.set_proc(openscreen::cast::RpcMessage::RPC_R_SETPLAYBACKRATE);
rpc.set_double_value(playback_rate);
rpc_messenger_->SendMessageToRemote(rpc);
}
void SendRpcFlushUntil(uint32_t audio_count, uint32_t video_count) {
openscreen::cast::RpcMessage rpc;
rpc.set_handle(remote_handle_);
rpc.set_proc(openscreen::cast::RpcMessage::RPC_R_FLUSHUNTIL);
openscreen::cast::RendererFlushUntil* message =
rpc.mutable_renderer_flushuntil_rpc();
message->set_audio_count(audio_count);
message->set_video_count(video_count);
message->set_callback_handle(rpc_handle_);
rpc_messenger_->SendMessageToRemote(rpc);
}
void SendRpcStartPlayingFrom(base::TimeDelta time) {
openscreen::cast::RpcMessage rpc;
rpc.set_handle(remote_handle_);
rpc.set_proc(openscreen::cast::RpcMessage::RPC_R_STARTPLAYINGFROM);
rpc.set_integer64_value(time.InMicroseconds());
rpc_messenger_->SendMessageToRemote(rpc);
}
void SendRpcSetVolume(float volume) {
openscreen::cast::RpcMessage rpc;
rpc.set_handle(remote_handle_);
rpc.set_proc(openscreen::cast::RpcMessage::RPC_R_SETVOLUME);
rpc.set_double_value(volume);
rpc_messenger_->SendMessageToRemote(rpc);
}
private:
RpcMessenger* const rpc_messenger_;
const int rpc_handle_;
const int remote_handle_;
};
class ReceiverTest : public ::testing::Test {
public:
ReceiverTest() = default;
void SetUp() override {
mock_controller_ = MockReceiverController::GetInstance();
mock_controller_->Initialize(
mock_controller_->mock_remotee()->BindNewPipeAndPassRemote());
mock_remotee_ = mock_controller_->mock_remotee();
rpc_messenger_ = mock_controller_->rpc_messenger();
receiver_renderer_handle_ = rpc_messenger_->GetUniqueHandle();
mock_sender_ = std::make_unique<StrictMock<MockSender>>(
rpc_messenger_, receiver_renderer_handle_);
rpc_messenger_->RegisterMessageReceiverCallback(
RpcMessenger::kAcquireRendererHandle,
[ptr = weak_factory_.GetWeakPtr()](
std::unique_ptr<openscreen::cast::RpcMessage> message) {
if (ptr) {
ptr->OnReceivedRpc(std::move(message));
}
});
}
void TearDown() override {
rpc_messenger_->UnregisterMessageReceiverCallback(
RpcMessenger::kAcquireRendererHandle);
}
void OnReceivedRpc(std::unique_ptr<openscreen::cast::RpcMessage> message) {
DCHECK(message);
EXPECT_EQ(message->proc(),
openscreen::cast::RpcMessage::RPC_ACQUIRE_RENDERER);
OnAcquireRenderer(std::move(message));
}
void OnAcquireRenderer(
std::unique_ptr<openscreen::cast::RpcMessage> message) {
DCHECK(message->has_integer_value());
DCHECK(message->integer_value() != RpcMessenger::kInvalidHandle);
if (sender_renderer_handle_ == RpcMessenger::kInvalidHandle) {
sender_renderer_handle_ = message->integer_value();
SetRemoteHandle();
}
}
void OnAcquireRendererDone(int receiver_renderer_handle) {
DVLOG(3) << __func__
<< ": Issues RPC_ACQUIRE_RENDERER_DONE RPC message. remote_handle="
<< sender_renderer_handle_
<< " rpc_handle=" << receiver_renderer_handle;
openscreen::cast::RpcMessage rpc;
rpc.set_handle(sender_renderer_handle_);
rpc.set_proc(openscreen::cast::RpcMessage::RPC_ACQUIRE_RENDERER_DONE);
rpc.set_integer_value(receiver_renderer_handle);
rpc_messenger_->SendMessageToRemote(rpc);
}
void CreateReceiver() {
auto renderer = std::make_unique<NiceMock<MockRenderer>>();
mock_renderer_ = renderer.get();
receiver_ = std::make_unique<Receiver>(
receiver_renderer_handle_, sender_renderer_handle_, mock_controller_,
base::ThreadTaskRunnerHandle::Get(), std::move(renderer),
base::BindOnce(&ReceiverTest::OnAcquireRendererDone,
weak_factory_.GetWeakPtr()));
}
void SetRemoteHandle() {
if (!receiver_)
return;
receiver_->SetRemoteHandle(sender_renderer_handle_);
}
void InitializeReceiver() {
receiver_->Initialize(&mock_media_resource_, nullptr,
base::BindOnce(&ReceiverTest::OnRendererInitialized,
weak_factory_.GetWeakPtr()));
}
MOCK_METHOD(void, OnRendererInitialized, (PipelineStatus));
base::test::TaskEnvironment task_environment_;
int sender_renderer_handle_ = RpcMessenger::kInvalidHandle;
int receiver_renderer_handle_ = RpcMessenger::kInvalidHandle;
MockMediaResource mock_media_resource_;
MockRenderer* mock_renderer_ = nullptr;
std::unique_ptr<MockSender> mock_sender_;
RpcMessenger* rpc_messenger_ = nullptr;
MockRemotee* mock_remotee_;
MockReceiverController* mock_controller_ = nullptr;
std::unique_ptr<Receiver> receiver_;
base::WeakPtrFactory<ReceiverTest> weak_factory_{this};
};
TEST_F(ReceiverTest, AcquireRendererBeforeCreateReceiver) {
mock_sender_->SendRpcAcquireRenderer();
EXPECT_CALL(*mock_sender_, AcquireRendererDone()).Times(1);
CreateReceiver();
task_environment_.RunUntilIdle();
}
TEST_F(ReceiverTest, AcquireRendererAfterCreateReceiver) {
CreateReceiver();
EXPECT_CALL(*mock_sender_, AcquireRendererDone()).Times(1);
mock_sender_->SendRpcAcquireRenderer();
task_environment_.RunUntilIdle();
}
// |Receiver::Initialize| will be called by the local pipeline, and the
// |Receiver::RpcInitialize| will be called once it received the
// RPC_R_INITIALIZE messages, so these two initialization functions are possible
// to be called in difference orders.
//
// Call |Receiver::Initialize| first, then send RPC_R_INITIALIZE.
TEST_F(ReceiverTest, InitializeBeforeRpcInitialize) {
EXPECT_CALL(*mock_sender_, AcquireRendererDone()).Times(1);
mock_sender_->SendRpcAcquireRenderer();
CreateReceiver();
EXPECT_CALL(*mock_renderer_,
OnInitialize(&mock_media_resource_, receiver_.get(), _))
.WillOnce(RunOnceCallback<2>(PipelineStatus::PIPELINE_OK));
EXPECT_CALL(*this, OnRendererInitialized(PipelineStatus::PIPELINE_OK))
.Times(1);
EXPECT_CALL(*mock_sender_, InitializeCallback(true)).Times(1);
InitializeReceiver();
mock_sender_->SendRpcInitialize();
task_environment_.RunUntilIdle();
}
// Send RPC_R_INITIALIZE first, then call |Receiver::Initialize|.
TEST_F(ReceiverTest, InitializeAfterRpcInitialize) {
EXPECT_CALL(*mock_sender_, AcquireRendererDone()).Times(1);
mock_sender_->SendRpcAcquireRenderer();
CreateReceiver();
EXPECT_CALL(*mock_renderer_,
OnInitialize(&mock_media_resource_, receiver_.get(), _))
.WillOnce(RunOnceCallback<2>(PipelineStatus::PIPELINE_OK));
EXPECT_CALL(*this, OnRendererInitialized(PipelineStatus::PIPELINE_OK))
.Times(1);
EXPECT_CALL(*mock_sender_, InitializeCallback(true)).Times(1);
mock_sender_->SendRpcInitialize();
InitializeReceiver();
task_environment_.RunUntilIdle();
}
TEST_F(ReceiverTest, RpcRendererMessages) {
EXPECT_CALL(*mock_sender_, AcquireRendererDone()).Times(1);
mock_sender_->SendRpcAcquireRenderer();
CreateReceiver();
mock_sender_->SendRpcInitialize();
InitializeReceiver();
task_environment_.RunUntilIdle();
// SetVolume
const float volume = 0.5;
EXPECT_CALL(*mock_renderer_, SetVolume(volume)).Times(1);
mock_sender_->SendRpcSetVolume(volume);
task_environment_.RunUntilIdle();
EXPECT_CALL(*mock_sender_, OnTimeUpdate(_, _)).Times(AtLeast(1));
// SetPlaybackRate
const double playback_rate = 1.2;
EXPECT_CALL(*mock_renderer_, SetPlaybackRate(playback_rate)).Times(1);
mock_sender_->SendRpcSetPlaybackRate(playback_rate);
task_environment_.RunUntilIdle();
// Flush
const uint32_t flush_audio_count = 10;
const uint32_t flush_video_count = 20;
EXPECT_CALL(*mock_renderer_, OnFlush(_)).WillOnce(RunOnceCallback<0>());
EXPECT_CALL(*mock_sender_, FlushUntilCallback()).Times(1);
mock_sender_->SendRpcFlushUntil(flush_audio_count, flush_video_count);
task_environment_.RunUntilIdle();
EXPECT_EQ(flush_audio_count, mock_remotee_->flush_audio_count());
EXPECT_EQ(flush_video_count, mock_remotee_->flush_video_count());
// StartPlayingFrom
const base::TimeDelta time = base::Seconds(100);
EXPECT_CALL(*mock_renderer_, StartPlayingFrom(time)).Times(1);
mock_sender_->SendRpcStartPlayingFrom(time);
task_environment_.RunUntilIdle();
}
TEST_F(ReceiverTest, RendererClientInterface) {
EXPECT_CALL(*mock_sender_, AcquireRendererDone()).Times(1);
mock_sender_->SendRpcAcquireRenderer();
CreateReceiver();
mock_sender_->SendRpcInitialize();
InitializeReceiver();
task_environment_.RunUntilIdle();
// OnBufferingStateChange
EXPECT_CALL(*mock_sender_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH))
.Times(1);
receiver_->OnBufferingStateChange(BUFFERING_HAVE_ENOUGH,
BUFFERING_CHANGE_REASON_UNKNOWN);
task_environment_.RunUntilIdle();
// OnEnded
EXPECT_CALL(*mock_sender_, OnEnded()).Times(1);
receiver_->OnEnded();
task_environment_.RunUntilIdle();
// OnError
EXPECT_CALL(*mock_sender_, OnFatalError()).Times(1);
receiver_->OnError(PipelineStatus::AUDIO_RENDERER_ERROR);
task_environment_.RunUntilIdle();
// OnAudioConfigChange
const auto kNewAudioConfig = TestAudioConfig::Normal();
EXPECT_CALL(*mock_sender_,
OnAudioConfigChange(DecoderConfigEq(kNewAudioConfig)))
.Times(1);
receiver_->OnAudioConfigChange(kNewAudioConfig);
task_environment_.RunUntilIdle();
// OnVideoConfigChange
const auto kNewVideoConfig = TestVideoConfig::Normal();
EXPECT_CALL(*mock_sender_,
OnVideoConfigChange(DecoderConfigEq(kNewVideoConfig)))
.Times(1);
receiver_->OnVideoConfigChange(kNewVideoConfig);
task_environment_.RunUntilIdle();
// OnVideoNaturalSizeChange
const gfx::Size size(100, 200);
EXPECT_CALL(*mock_sender_, OnVideoNaturalSizeChange(size)).Times(1);
receiver_->OnVideoNaturalSizeChange(size);
task_environment_.RunUntilIdle();
EXPECT_EQ(size, mock_remotee_->changed_size());
// OnVideoOpacityChange
const bool opaque = true;
EXPECT_CALL(*mock_sender_, OnVideoOpacityChange(opaque)).Times(1);
receiver_->OnVideoOpacityChange(opaque);
task_environment_.RunUntilIdle();
// OnStatisticsUpdate
PipelineStatistics statistics;
statistics.audio_bytes_decoded = 100;
statistics.video_bytes_decoded = 200;
statistics.video_frames_decoded = 300;
statistics.video_frames_dropped = 400;
statistics.audio_memory_usage = 500;
statistics.video_memory_usage = 600;
EXPECT_CALL(*mock_sender_, OnStatisticsUpdate(statistics)).Times(1);
receiver_->OnStatisticsUpdate(statistics);
task_environment_.RunUntilIdle();
}
} // namespace remoting
} // namespace media