chromium/components/cast_receiver/browser/streaming_receiver_session_client.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/cast_receiver/browser/streaming_receiver_session_client.h"

#include <utility>

#include "base/containers/contains.h"
#include "base/logging.h"
#include "base/ranges/algorithm.h"
#include "base/strings/string_util.h"
#include "base/task/sequenced_task_runner.h"
#include "components/cast/message_port/platform_message_port.h"
#include "components/cast_receiver/browser/streaming_controller_base.h"
#include "components/cast_streaming/common/public/cast_streaming_url.h"
#include "media/base/video_decoder_config.h"

namespace cast_receiver {

constexpr base::TimeDelta
    StreamingReceiverSessionClient::kMaxAVSettingsWaitTime;

StreamingReceiverSessionClient::StreamingReceiverSessionClient(
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    network::NetworkContextGetter network_context_getter,
    std::unique_ptr<cast_api_bindings::MessagePort> message_port,
    content::WebContents* web_contents,
    Handler* handler,
    cast_receiver::StreamingConfigManager* config_manager,
    bool supports_audio,
    bool supports_video)
    : StreamingReceiverSessionClient(
          std::move(task_runner),
          std::move(network_context_getter),
          StreamingControllerBase::Create(std::move(message_port),
                                          web_contents),
          handler,
          config_manager,
          supports_audio,
          supports_video) {}

StreamingReceiverSessionClient::StreamingReceiverSessionClient(
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    network::NetworkContextGetter network_context_getter,
    std::unique_ptr<StreamingController> streaming_controller,
    Handler* handler,
    cast_receiver::StreamingConfigManager* config_manager,
    bool supports_audio,
    bool supports_video)
    : handler_(handler),
      task_runner_(std::move(task_runner)),
      streaming_controller_(std::move(streaming_controller)),
      supports_audio_(supports_audio),
      supports_video_(supports_video),
      weak_factory_(this) {
  DCHECK(handler_);
  DCHECK(task_runner_);
  DCHECK(!network_context_getter.is_null());
  DCHECK(config_manager);

  cast_streaming::SetNetworkContextGetter(std::move(network_context_getter));

  DVLOG(1) << "Streaming Receiver Session start pending...";
  config_manager->AddConfigObserver(*this);
  if (config_manager->has_config()) {
    // TODO(crbug.com/1359568): This may not behave correctly if the config is
    // updated before the pushed task runs.
    task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&StreamingReceiverSessionClient::OnStreamingConfigSet,
                       weak_factory_.GetWeakPtr(), config_manager->config()));
    return;
  }

  task_runner_->PostDelayedTask(
      FROM_HERE,
      base::BindOnce(&StreamingReceiverSessionClient::VerifyAVSettingsReceived,
                     weak_factory_.GetWeakPtr()),
      kMaxAVSettingsWaitTime);
}

StreamingReceiverSessionClient::~StreamingReceiverSessionClient() {
  DVLOG(1) << "StreamingReceiverSessionClient state when destroyed"
           << "\n\tIs Healthy: " << is_healthy()
           << "\n\tLaunch called: " << is_streaming_launch_pending()
           << "\n\tAV Settings Received: " << has_received_av_settings();

  cast_streaming::SetNetworkContextGetter({});
}

StreamingReceiverSessionClient::Handler::~Handler() = default;

void StreamingReceiverSessionClient::LaunchStreamingReceiverAsync() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!is_streaming_launch_pending());

  streaming_state_ |= LaunchState::kLaunchCalled;
  streaming_controller_->StartPlaybackAsync(
      base::BindOnce(&StreamingReceiverSessionClient::OnPlaybackStarted,
                     weak_factory_.GetWeakPtr()));
}

void StreamingReceiverSessionClient::VerifyAVSettingsReceived() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (streaming_state_ & LaunchState::kAVSettingsReceived) {
    return;
  }

  LOG(ERROR) << "AVSettings not received within the allocated amount of time";
  TriggerError();
}

void StreamingReceiverSessionClient::OnPlaybackStarted() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  streaming_state_ |= LaunchState::kLaunched;
  handler_->OnStreamingSessionStarted();
}

void StreamingReceiverSessionClient::OnStreamingConfigSet(
    const cast_streaming::ReceiverConfig& config) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // Make a copy so that it can be mofidied locally.
  cast_streaming::ReceiverConfig constraints = config;

  if (streaming_state_ == LaunchState::kError) {
    LOG(WARNING) << "Config received after an error!";
    return;
  }

  if (!supports_audio_) {
    LOG(WARNING) << "Disallowing audio for this streaming session!";
    constraints.audio_codecs.clear();
    constraints.audio_limits.clear();
  }
  if (!supports_video_) {
    LOG(WARNING) << "Disallowing video for this streaming session!";
    constraints.video_codecs.clear();
    constraints.video_limits.clear();
  }
  if (supports_audio_ && supports_video_) {
    DVLOG(1) << "Allowing both audio and video for this streaming session!";
  }

  streaming_state_ |= LaunchState::kAVSettingsReceived;
  if (!has_streaming_launched()) {
    streaming_controller_->InitializeReceiverSession(config, this);
    return;
  }

  // TODO(crbug.com/1359568): Handle receiving a new config after streaming has
  // already started.
  LOG(WARNING)
      << "Received updated streaming config during an ongoing session!";
}

void StreamingReceiverSessionClient::OnAudioConfigUpdated(
    const ::media::AudioDecoderConfig& audio_config) {}

void StreamingReceiverSessionClient::OnVideoConfigUpdated(
    const ::media::VideoDecoderConfig& video_config) {
  handler_->OnResolutionChanged(video_config.visible_rect(),
                                video_config.video_transformation());
}

void StreamingReceiverSessionClient::OnStreamingSessionEnded() {
  // The streaming session will only "end" (as opposed to being "renegotated"
  // when a new config is sent or the stream changes between mirroring and
  // remoting) when the session completely exits. This occurs either when there
  // is an error in the runtime or the when sender-side ends the streaming
  // session.
  //
  // In either case, the result is an unsupported state for the
  // StreamingRuntimeApplication, so an error.
  TriggerError();
}

void StreamingReceiverSessionClient::TriggerError() {
  if (!is_healthy()) {
    return;
  }

  streaming_state_ |= LaunchState::kError;
  handler_->OnError();
}

}  // namespace cast_receiver