diff options
Diffstat (limited to 'cast/standalone_sender')
-rw-r--r-- | cast/standalone_sender/BUILD.gn | 2 | ||||
-rw-r--r-- | cast/standalone_sender/looping_file_cast_agent.cc | 31 | ||||
-rw-r--r-- | cast/standalone_sender/looping_file_cast_agent.h | 21 | ||||
-rw-r--r-- | cast/standalone_sender/remoting_sender.cc | 95 | ||||
-rw-r--r-- | cast/standalone_sender/remoting_sender.h | 67 |
5 files changed, 211 insertions, 5 deletions
diff --git a/cast/standalone_sender/BUILD.gn b/cast/standalone_sender/BUILD.gn index afa73d7d..780e0609 100644 --- a/cast/standalone_sender/BUILD.gn +++ b/cast/standalone_sender/BUILD.gn @@ -49,6 +49,8 @@ if (!build_with_chromium) { "looping_file_sender.h", "receiver_chooser.cc", "receiver_chooser.h", + "remoting_sender.cc", + "remoting_sender.h", "simulated_capturer.cc", "simulated_capturer.h", "streaming_opus_encoder.cc", diff --git a/cast/standalone_sender/looping_file_cast_agent.cc b/cast/standalone_sender/looping_file_cast_agent.cc index ce9826de..bfb970bf 100644 --- a/cast/standalone_sender/looping_file_cast_agent.cc +++ b/cast/standalone_sender/looping_file_cast_agent.cc @@ -270,6 +270,10 @@ void LoopingFileCastAgent::CreateAndStartSession() { OSP_VLOG << "Starting session negotiation."; Error negotiation_error; if (connection_settings_->use_remoting) { + remoting_sender_ = std::make_unique<RemotingSender>( + current_session_->rpc_messenger(), AudioCodec::kOpus, VideoCodec::kVp8, + [this]() { OnRemotingReceiverReady(); }); + negotiation_error = current_session_->NegotiateRemoting(audio_config, video_config); } else { @@ -298,17 +302,17 @@ void LoopingFileCastAgent::OnNegotiated( void LoopingFileCastAgent::OnRemotingNegotiated( const SenderSession* session, SenderSession::RemotingNegotiation negotiation) { - // TODO(jophba): this needs to be hashed out as part of - // figuring out the embedder workflow. if (negotiation.senders.audio_sender == nullptr && negotiation.senders.video_sender == nullptr) { OSP_LOG_ERROR << "Missing both audio and video, so exiting..."; return; } - file_sender_ = std::make_unique<LoopingFileSender>( - environment_.get(), connection_settings_.value(), session, - std::move(negotiation.senders), [this]() { shutdown_callback_(); }); + current_negotiation_ = + std::make_unique<SenderSession::RemotingNegotiation>(negotiation); + if (is_ready_for_remoting_) { + StartRemotingSenders(); + } } void LoopingFileCastAgent::OnError(const SenderSession* session, Error error) { @@ -316,6 +320,23 @@ void LoopingFileCastAgent::OnError(const SenderSession* session, Error error) { Shutdown(); } +void LoopingFileCastAgent::OnRemotingReceiverReady() { + is_ready_for_remoting_ = true; + if (current_negotiation_) { + StartRemotingSenders(); + } +} + +void LoopingFileCastAgent::StartRemotingSenders() { + OSP_DCHECK(current_negotiation_); + file_sender_ = std::make_unique<LoopingFileSender>( + environment_.get(), connection_settings_.value(), current_session_.get(), + std::move(current_negotiation_->senders), + [this]() { shutdown_callback_(); }); + current_negotiation_.reset(); + is_ready_for_remoting_ = false; +} + void LoopingFileCastAgent::Shutdown() { TRACE_DEFAULT_SCOPED(TraceCategory::kStandaloneSender); diff --git a/cast/standalone_sender/looping_file_cast_agent.h b/cast/standalone_sender/looping_file_cast_agent.h index cff0f6ba..895d1324 100644 --- a/cast/standalone_sender/looping_file_cast_agent.h +++ b/cast/standalone_sender/looping_file_cast_agent.h @@ -21,6 +21,7 @@ #include "cast/sender/public/sender_socket_factory.h" #include "cast/standalone_sender/connection_settings.h" #include "cast/standalone_sender/looping_file_sender.h" +#include "cast/standalone_sender/remoting_sender.h" #include "cast/streaming/environment.h" #include "cast/streaming/sender_session.h" #include "platform/api/scoped_wake_lock.h" @@ -134,6 +135,15 @@ class LoopingFileCastAgent final SenderSession::RemotingNegotiation negotiation) override; void OnError(const SenderSession* session, Error error) override; + // Callback for when the RemotingSender indicates that the receiver + // is ready. + void OnRemotingReceiverReady(); + + // Starts the remoting sender. This may occur when remoting is "ready" if the + // session is already negotiated, or upon session negotiation if the receiver + // is already ready. + void StartRemotingSenders(); + // Helper for stopping the current session, and/or unwinding a remote // connection request (pre-session). This ensures LoopingFileCastAgent is in a // terminal shutdown state. @@ -168,6 +178,17 @@ class LoopingFileCastAgent final std::unique_ptr<Environment> environment_; std::unique_ptr<SenderSession> current_session_; std::unique_ptr<LoopingFileSender> file_sender_; + + // Remoting specific member variables. + std::unique_ptr<RemotingSender> remoting_sender_; + + // Set when remoting is successfully negotiated. However, remoting streams + // won't start until |is_ready_for_remoting_| is true. + std::unique_ptr<SenderSession::RemotingNegotiation> current_negotiation_; + + // Set to true when the remoting receiver is ready. However, remoting streams + // won't start until remoting is successfully negotiated. + bool is_ready_for_remoting_ = false; }; } // namespace cast diff --git a/cast/standalone_sender/remoting_sender.cc b/cast/standalone_sender/remoting_sender.cc new file mode 100644 index 00000000..1566f0a7 --- /dev/null +++ b/cast/standalone_sender/remoting_sender.cc @@ -0,0 +1,95 @@ +// Copyright 2021 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 "cast/standalone_sender/remoting_sender.h" + +#include <utility> + +#include "cast/streaming/message_fields.h" + +namespace openscreen { +namespace cast { + +namespace { + +VideoDecoderConfig::Codec ToProtoCodec(VideoCodec value) { + switch (value) { + case VideoCodec::kHevc: + return VideoDecoderConfig_Codec_kCodecHEVC; + case VideoCodec::kH264: + return VideoDecoderConfig_Codec_kCodecH264; + case VideoCodec::kVp8: + return VideoDecoderConfig_Codec_kCodecVP8; + case VideoCodec::kVp9: + return VideoDecoderConfig_Codec_kCodecVP9; + default: + return VideoDecoderConfig_Codec_kUnknownVideoCodec; + } +} + +AudioDecoderConfig::Codec ToProtoCodec(AudioCodec value) { + switch (value) { + case AudioCodec::kAac: + return AudioDecoderConfig_Codec_kCodecAAC; + case AudioCodec::kOpus: + return AudioDecoderConfig_Codec_kCodecOpus; + default: + return AudioDecoderConfig_Codec_kUnknownAudioCodec; + } +} + +} // namespace + +RemotingSender::RemotingSender(RpcMessenger* messenger, + AudioCodec audio_codec, + VideoCodec video_codec, + ReadyCallback ready_cb) + : messenger_(messenger), + audio_codec_(audio_codec), + video_codec_(video_codec), + ready_cb_(std::move(ready_cb)) { + messenger_->RegisterMessageReceiverCallback( + RpcMessenger::kAcquireRendererHandle, + [this](std::unique_ptr<RpcMessage> message) { + OSP_DCHECK(message); + this->OnInitializeMessage(*message); + }); +} + +RemotingSender::~RemotingSender() { + messenger_->UnregisterMessageReceiverCallback( + RpcMessenger::kAcquireRendererHandle); +} + +void RemotingSender::OnInitializeMessage(const RpcMessage& message) { + receiver_handle_ = message.integer_value(); + + RpcMessage callback_message; + callback_message.set_handle(receiver_handle_); + callback_message.set_proc(RpcMessage::RPC_DS_INITIALIZE_CALLBACK); + + auto* callback_body = + callback_message.mutable_demuxerstream_initializecb_rpc(); + + // In Chrome, separate calls are used for the audio and video configs, but + // for simplicity's sake we combine them here. + callback_body->mutable_audio_decoder_config()->set_codec( + ToProtoCodec(audio_codec_)); + callback_body->mutable_video_decoder_config()->set_codec( + ToProtoCodec(video_codec_)); + + OSP_DLOG_INFO << "Initializing receiver handle " << receiver_handle_ + << " with audio codec " << CodecToString(audio_codec_) + << " and video codec " << CodecToString(video_codec_); + messenger_->SendMessageToRemote(callback_message); + + if (ready_cb_) { + ready_cb_(); + } else { + OSP_DLOG_INFO << "Received a ready message, but no ready callback."; + } +} + +} // namespace cast +} // namespace openscreen diff --git a/cast/standalone_sender/remoting_sender.h b/cast/standalone_sender/remoting_sender.h new file mode 100644 index 00000000..d331009b --- /dev/null +++ b/cast/standalone_sender/remoting_sender.h @@ -0,0 +1,67 @@ +// Copyright 2021 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 CAST_STANDALONE_SENDER_REMOTING_SENDER_H_ +#define CAST_STANDALONE_SENDER_REMOTING_SENDER_H_ + +#include <memory> + +#include "cast/streaming/constants.h" +#include "cast/streaming/rpc_messenger.h" + +namespace openscreen { +namespace cast { + +// This class behaves like a pared-down version of Chrome's StreamProvider (see +// https://source.chromium.org/chromium/chromium/src/+/main:media/remoting/stream_provider.h +// ). Instead of fully managing a media::DemuxerStream however, it just provides +// an RPC initialization routine that notifies the standalone receiver's +// SimpleRemotingReceiver instance (if configured) that initialization has been +// complete and what codecs were selected. +// +// Due to the sheer complexity of remoting, we don't have a fully functional +// implementation of remoting in the standalone_* components, instead Chrome is +// the reference implementation and we have these simple classes to exercise +// the public APIs. +class RemotingSender { + public: + using ReadyCallback = std::function<void()>; + RemotingSender(RpcMessenger* messenger, + AudioCodec audio_codec, + VideoCodec video_codec, + ReadyCallback ready_cb); + ~RemotingSender(); + + private: + // When the receiver indicates that it is ready for initialization, it will + // The receiver sends us an "initialization" message that we respond to + // here with an "initialization callback" message that contains codec + // information. + void OnInitializeMessage(const RpcMessage& message); + + // The messenger is the only caller of OnInitializeMessage, so there are no + // lifetime concerns. However, if this class outlives |messenger_|, it will + // no longer receive initialization messages. + RpcMessenger* messenger_; + + // Unlike in Chrome, here we should know the video and audio codecs before any + // of the remoting code gets set up, and for simplicity's sake we can only + // populate the AudioDecoderConfig and VideoDecoderConfig objects with the + // codecs and use the rest of the fields as-is from the OFFER/ANSWER exchange. + const AudioCodec audio_codec_; + const VideoCodec video_codec_; + + // The callback method to be called once we get the initialization message + // from the receiver. + ReadyCallback ready_cb_; + + // The initialization message from the receiver contains the handle the + // callback should go to. + RpcMessenger::Handle receiver_handle_ = RpcMessenger::kInvalidHandle; +}; + +} // namespace cast +} // namespace openscreen + +#endif // CAST_STANDALONE_SENDER_REMOTING_SENDER_H_ |