Chromium Code Reviews| Index: media/remoting/remoting_controller.cc | 
| diff --git a/media/remoting/remoting_controller.cc b/media/remoting/remoting_controller.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..6be0cf16aca6b8d094717fcad589ee39943c8645 | 
| --- /dev/null | 
| +++ b/media/remoting/remoting_controller.cc | 
| @@ -0,0 +1,208 @@ | 
| +// Copyright 2016 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/remoting_controller.h" | 
| + | 
| +#include "base/bind.h" | 
| +#include "base/logging.h" | 
| +#include "base/single_thread_task_runner.h" | 
| + | 
| +namespace media { | 
| + | 
| +RemotingController::RemotingController( | 
| + mojom::RemotingSourceRequest source_request, | 
| + mojom::RemoterPtr remoter) | 
| + : binding_(this, std::move(source_request)), | 
| + remoter_(std::move(remoter)), | 
| + task_runner_(base::ThreadTaskRunnerHandle::Get()), | 
| + weak_factory_(this) {} | 
| + | 
| +RemotingController::~RemotingController() {} | 
| + | 
| +void RemotingController::OnSinkAvailable() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + is_sink_available_ = true; | 
| + UpdateAndMaybeSwitch(); | 
| +} | 
| + | 
| +void RemotingController::OnSinkGone() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + is_sink_available_ = false; | 
| + UpdateAndMaybeSwitch(); | 
| +} | 
| + | 
| +void RemotingController::OnStarted() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + VLOG(1) << "Remoting started successively."; | 
| + if (is_remoting_) | 
| + switch_renderer_cb_.Run(); | 
| + else | 
| + remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | 
| +} | 
| + | 
| +void RemotingController::OnStartFailed(mojom::RemotingStartFailReason reason) { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + VLOG(1) << "Failed to start remoting:" << reason; | 
| + is_remoting_ = false; | 
| +} | 
| + | 
| +void RemotingController::OnMessageFromSink( | 
| + const std::vector<uint8_t>& message) { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + // TODO(xjz): Merge with Eric's CL to handle the RPC messages here. | 
| + NOTIMPLEMENTED(); | 
| +} | 
| + | 
| +void RemotingController::OnStopped(mojom::RemotingStopReason reason) { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + VLOG(1) << "Remoting stopped: " << reason; | 
| + is_remoting_ = false; | 
| +} | 
| + | 
| +void RemotingController::OnEnteredFullscreen() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + is_fullscreen_ = true; | 
| + UpdateAndMaybeSwitch(); | 
| +} | 
| + | 
| +void RemotingController::OnExitedFullscreen() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + is_fullscreen_ = false; | 
| + UpdateAndMaybeSwitch(); | 
| +} | 
| + | 
| +void RemotingController::OnSetCdm(CdmContext* cdm_context) { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + // TODO(xjz): Not implemented. Will add in up-coming change. | 
| + NOTIMPLEMENTED(); | 
| +} | 
| + | 
| +void RemotingController::SetSwitchRendererCallback( | 
| + const SwitchRendererCallback& cb) { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + DCHECK(!cb.is_null()); | 
| + | 
| + switch_renderer_cb_ = cb; | 
| +} | 
| + | 
| +void RemotingController::OnMetadata(const PipelineMetadata& metadata) { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + has_video_ = metadata.has_video; | 
| + has_audio_ = metadata.has_audio; | 
| + if (!has_video_ && !has_audio_) | 
| + return; | 
| + | 
| + if (has_video_) { | 
| + DCHECK(metadata.video_decoder_config.IsValidConfig()); | 
| + video_decoder_config_ = metadata.video_decoder_config; | 
| + } | 
| + if (has_audio_) { | 
| + DCHECK(metadata.audio_decoder_config.IsValidConfig()); | 
| + audio_decoder_config_ = metadata.audio_decoder_config; | 
| + } | 
| + UpdateAndMaybeSwitch(); | 
| +} | 
| + | 
| +bool RemotingController::IsVideoConfigSupported() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + DCHECK(has_video_); | 
| + | 
| + switch (video_decoder_config_.codec()) { | 
| + case VideoCodec::kCodecH264: | 
| + case VideoCodec::kCodecVP8: | 
| + return true; | 
| + default: | 
| + VLOG(2) << "Remoting does not support video codec: " | 
| + << video_decoder_config_.codec(); | 
| + return false; | 
| + } | 
| +} | 
| + | 
| +bool RemotingController::IsAudioConfigSupported() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + DCHECK(has_audio_); | 
| + | 
| + switch (audio_decoder_config_.codec()) { | 
| + case AudioCodec::kCodecAAC: | 
| + case AudioCodec::kCodecMP3: | 
| + case AudioCodec::kCodecPCM: | 
| + case AudioCodec::kCodecVorbis: | 
| + case AudioCodec::kCodecFLAC: | 
| + case AudioCodec::kCodecAMR_NB: | 
| + case AudioCodec::kCodecAMR_WB: | 
| + case AudioCodec::kCodecPCM_MULAW: | 
| + case AudioCodec::kCodecGSM_MS: | 
| + case AudioCodec::kCodecPCM_S16BE: | 
| + case AudioCodec::kCodecPCM_S24BE: | 
| + case AudioCodec::kCodecOpus: | 
| + case AudioCodec::kCodecEAC3: | 
| + case AudioCodec::kCodecPCM_ALAW: | 
| + case AudioCodec::kCodecALAC: | 
| + case AudioCodec::kCodecAC3: | 
| + return true; | 
| + default: | 
| + VLOG(2) << "Remoting does not support audio codec: " | 
| + << audio_decoder_config_.codec(); | 
| + return false; | 
| + } | 
| +} | 
| + | 
| +bool RemotingController::ShouldBeRemoting() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + // TODO(xjz): The control logic for EME will be added in a later CL. | 
| + if (video_decoder_config_.is_encrypted()) | 
| 
 
xhwang
2016/10/05 05:27:53
How about make this check part of IsVideoConfigSup
 
xjz
2016/10/05 17:24:23
I would leave this check here for now. It will cha
 
xhwang
2016/10/05 17:36:41
Then please at least check |has_video_| before che
 
xjz
2016/10/05 23:39:01
Done.
 
 | 
| + return false; | 
| + if (audio_decoder_config_.is_encrypted()) | 
| + return false; | 
| + if (!is_sink_available_) | 
| + return false; | 
| + if (!is_fullscreen_) | 
| + return false; | 
| + if (has_video_ && !IsVideoConfigSupported()) | 
| + return false; | 
| + if (has_audio_ && !IsAudioConfigSupported()) | 
| + return false; | 
| + return true; | 
| +} | 
| + | 
| +void RemotingController::UpdateAndMaybeSwitch() { | 
| + DCHECK(task_runner_->BelongsToCurrentThread()); | 
| + | 
| + // TODO(xjz): The switching logic for encrypted content will be added in a | 
| + // later CL. | 
| + | 
| + // Demuxer is not initialized yet. | 
| + if (!has_audio_ && !has_video_) | 
| + return; | 
| + | 
| + DCHECK(!switch_renderer_cb_.is_null()); | 
| + | 
| + bool should_be_remoting = ShouldBeRemoting(); | 
| + if (is_remoting_ == should_be_remoting) | 
| + return; | 
| + | 
| + // Switch between local and remoting. | 
| + is_remoting_ = should_be_remoting; | 
| + if (is_remoting_) { | 
| + // |swithc_renderer_cb_.Run()| will be called after remoting is started | 
| + // successfully. | 
| + remoter_->Start(); | 
| + } else { | 
| + switch_renderer_cb_.Run(); | 
| + remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); | 
| + } | 
| +} | 
| + | 
| +} // namespace media |