diff options
Diffstat (limited to 'chromium/chrome/renderer/media/cast_session_delegate.cc')
-rw-r--r-- | chromium/chrome/renderer/media/cast_session_delegate.cc | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/chromium/chrome/renderer/media/cast_session_delegate.cc b/chromium/chrome/renderer/media/cast_session_delegate.cc new file mode 100644 index 00000000000..0e24f6aee0b --- /dev/null +++ b/chromium/chrome/renderer/media/cast_session_delegate.cc @@ -0,0 +1,332 @@ +// Copyright 2013 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 "chrome/renderer/media/cast_session_delegate.h" + +#include <memory> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/renderer/media/cast_threads.h" +#include "chrome/renderer/media/cast_transport_ipc.h" +#include "components/version_info/version_info.h" +#include "content/public/renderer/render_thread.h" +#include "media/cast/cast_config.h" +#include "media/cast/cast_environment.h" +#include "media/cast/cast_sender.h" +#include "media/cast/logging/log_serializer.h" +#include "media/cast/logging/logging_defines.h" +#include "media/cast/logging/proto/raw_events.pb.h" +#include "media/cast/logging/raw_event_subscriber_bundle.h" +#include "media/cast/net/cast_transport.h" +#include "media/cast/net/cast_transport_config.h" + +using media::cast::CastEnvironment; +using media::cast::CastSender; +using media::cast::FrameSenderConfig; + +static base::LazyInstance<CastThreads>::DestructorAtExit g_cast_threads = + LAZY_INSTANCE_INITIALIZER; + +CastSessionDelegateBase::CastSessionDelegateBase() + : io_task_runner_(content::RenderThread::Get()->GetIOTaskRunner()) { + DCHECK(io_task_runner_.get()); +#if defined(OS_WIN) + // Note that this also increases the accuracy of PostDelayTask, + // which is is very helpful to cast. + if (!base::Time::ActivateHighResolutionTimer(true)) { + LOG(WARNING) << "Failed to activate high resolution timers for cast."; + } +#endif +} + +CastSessionDelegateBase::~CastSessionDelegateBase() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); +#if defined(OS_WIN) + base::Time::ActivateHighResolutionTimer(false); +#endif +} + +void CastSessionDelegateBase::StartUDP( + const net::IPEndPoint& local_endpoint, + const net::IPEndPoint& remote_endpoint, + std::unique_ptr<base::DictionaryValue> options, + const ErrorCallback& error_callback) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + // CastSender uses the renderer's IO thread as the main thread. This reduces + // thread hopping for incoming video frames and outgoing network packets. + // TODO(hubbe): Create cast environment in ctor instead. + cast_environment_ = + new CastEnvironment(base::DefaultTickClock::GetInstance(), + base::ThreadTaskRunnerHandle::Get(), + g_cast_threads.Get().GetAudioEncodeTaskRunner(), + g_cast_threads.Get().GetVideoEncodeTaskRunner()); + + // Rationale for using unretained: The callback cannot be called after the + // destruction of CastTransportIPC, and they both share the same thread. + cast_transport_.reset(new CastTransportIPC( + local_endpoint, remote_endpoint, std::move(options), + base::Bind(&CastSessionDelegateBase::ReceivePacket, + base::Unretained(this)), + base::Bind(&CastSessionDelegateBase::StatusNotificationCB, + base::Unretained(this), error_callback), + base::Bind(&media::cast::LogEventDispatcher::DispatchBatchOfEvents, + base::Unretained(cast_environment_->logger())))); +} + +void CastSessionDelegateBase::StatusNotificationCB( + const ErrorCallback& error_callback, + media::cast::CastTransportStatus status) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + std::string error_message; + + switch (status) { + case media::cast::TRANSPORT_STREAM_UNINITIALIZED: + case media::cast::TRANSPORT_STREAM_INITIALIZED: + return; // Not errors, do nothing. + case media::cast::TRANSPORT_INVALID_CRYPTO_CONFIG: + error_callback.Run("Invalid encrypt/decrypt configuration."); + break; + case media::cast::TRANSPORT_SOCKET_ERROR: + error_callback.Run("Socket error."); + break; + } +} + +CastSessionDelegate::CastSessionDelegate() { + DCHECK(io_task_runner_.get()); +} + +CastSessionDelegate::~CastSessionDelegate() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); +} + +void CastSessionDelegate::StartAudio( + const FrameSenderConfig& config, + const AudioFrameInputAvailableCallback& callback, + const ErrorCallback& error_callback) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + if (!cast_transport_ || !cast_sender_) { + error_callback.Run("Destination not set."); + return; + } + + audio_frame_input_available_callback_ = callback; + cast_sender_->InitializeAudio( + config, + base::Bind(&CastSessionDelegate::OnOperationalStatusChange, + weak_factory_.GetWeakPtr(), true, error_callback)); +} + +void CastSessionDelegate::StartVideo( + const FrameSenderConfig& config, + const VideoFrameInputAvailableCallback& callback, + const ErrorCallback& error_callback, + const media::cast::CreateVideoEncodeAcceleratorCallback& create_vea_cb, + const media::cast::CreateVideoEncodeMemoryCallback& + create_video_encode_mem_cb) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + if (!cast_transport_ || !cast_sender_) { + error_callback.Run("Destination not set."); + return; + } + + video_frame_input_available_callback_ = callback; + + cast_sender_->InitializeVideo( + config, + base::Bind(&CastSessionDelegate::OnOperationalStatusChange, + weak_factory_.GetWeakPtr(), false, error_callback), + create_vea_cb, + create_video_encode_mem_cb); +} + +void CastSessionDelegate::StartRemotingStream( + int32_t stream_id, + const FrameSenderConfig& config, + const ErrorCallback& error_callback) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + if (!cast_transport_) { + error_callback.Run("Destination not set."); + return; + } + + media::cast::CastTransportRtpConfig transport_config; + transport_config.ssrc = config.sender_ssrc; + transport_config.feedback_ssrc = config.receiver_ssrc; + transport_config.rtp_payload_type = config.rtp_payload_type; + transport_config.rtp_stream_id = stream_id; + transport_config.aes_key = config.aes_key; + transport_config.aes_iv_mask = config.aes_iv_mask; + cast_transport_->InitializeStream(transport_config, nullptr); +} + +void CastSessionDelegate::StartUDP( + const net::IPEndPoint& local_endpoint, + const net::IPEndPoint& remote_endpoint, + std::unique_ptr<base::DictionaryValue> options, + const ErrorCallback& error_callback) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + CastSessionDelegateBase::StartUDP(local_endpoint, remote_endpoint, + std::move(options), error_callback); + event_subscribers_.reset( + new media::cast::RawEventSubscriberBundle(cast_environment_)); + + cast_sender_ = CastSender::Create(cast_environment_, cast_transport_.get()); +} + +void CastSessionDelegate::ToggleLogging(bool is_audio, bool enable) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + if (!event_subscribers_.get()) + return; + + if (enable) + event_subscribers_->AddEventSubscribers(is_audio); + else + event_subscribers_->RemoveEventSubscribers(is_audio); +} + +void CastSessionDelegate::GetEventLogsAndReset( + bool is_audio, + const std::string& extra_data, + const EventLogsCallback& callback) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + if (!event_subscribers_.get()) { + callback.Run(std::make_unique<base::Value>(base::Value::Type::BINARY)); + return; + } + + media::cast::EncodingEventSubscriber* subscriber = + event_subscribers_->GetEncodingEventSubscriber(is_audio); + if (!subscriber) { + callback.Run(std::make_unique<base::Value>(base::Value::Type::BINARY)); + return; + } + + media::cast::proto::LogMetadata metadata; + media::cast::FrameEventList frame_events; + media::cast::PacketEventList packet_events; + + subscriber->GetEventsAndReset(&metadata, &frame_events, &packet_events); + + if (!extra_data.empty()) + metadata.set_extra_data(extra_data); + media::cast::proto::GeneralDescription* gen_desc = + metadata.mutable_general_description(); + gen_desc->set_product(version_info::GetProductName()); + gen_desc->set_product_version(version_info::GetVersionNumber()); + gen_desc->set_os(version_info::GetOSType()); + + std::unique_ptr<char[]> serialized_log( + new char[media::cast::kMaxSerializedBytes]); + int output_bytes; + bool success = media::cast::SerializeEvents(metadata, + frame_events, + packet_events, + true, + media::cast::kMaxSerializedBytes, + serialized_log.get(), + &output_bytes); + + if (!success) { + DVLOG(2) << "Failed to serialize event log."; + callback.Run(std::make_unique<base::Value>(base::Value::Type::BINARY)); + return; + } + + DVLOG(2) << "Serialized log length: " << output_bytes; + + auto blob = std::make_unique<base::Value>(std::vector<char>( + serialized_log.get(), serialized_log.get() + output_bytes)); + callback.Run(std::move(blob)); +} + +void CastSessionDelegate::GetStatsAndReset(bool is_audio, + const StatsCallback& callback) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + if (!event_subscribers_.get()) { + callback.Run(std::make_unique<base::DictionaryValue>()); + return; + } + + media::cast::StatsEventSubscriber* subscriber = + event_subscribers_->GetStatsEventSubscriber(is_audio); + if (!subscriber) { + callback.Run(std::make_unique<base::DictionaryValue>()); + return; + } + + std::unique_ptr<base::DictionaryValue> stats = subscriber->GetStats(); + subscriber->Reset(); + + callback.Run(std::move(stats)); +} + +void CastSessionDelegate::OnOperationalStatusChange( + bool is_for_audio, + const ErrorCallback& error_callback, + media::cast::OperationalStatus status) { + DCHECK(cast_sender_); + + switch (status) { + case media::cast::STATUS_UNINITIALIZED: + case media::cast::STATUS_CODEC_REINIT_PENDING: + // Not an error. + // TODO(miu): As an optimization, signal the client to pause sending more + // frames until the state becomes STATUS_INITIALIZED again. + break; + case media::cast::STATUS_INITIALIZED: + // Once initialized, run the "frame input available" callback to allow the + // client to begin sending frames. If STATUS_INITIALIZED is encountered + // again, do nothing since this is only an indication that the codec has + // successfully re-initialized. + if (is_for_audio) { + if (!audio_frame_input_available_callback_.is_null()) { + std::move(audio_frame_input_available_callback_) + .Run(cast_sender_->audio_frame_input()); + } + } else { + if (!video_frame_input_available_callback_.is_null()) { + std::move(video_frame_input_available_callback_) + .Run(cast_sender_->video_frame_input()); + } + } + break; + case media::cast::STATUS_INVALID_CONFIGURATION: + error_callback.Run(base::StringPrintf("Invalid %s configuration.", + is_for_audio ? "audio" : "video")); + break; + case media::cast::STATUS_UNSUPPORTED_CODEC: + error_callback.Run(base::StringPrintf("%s codec not supported.", + is_for_audio ? "Audio" : "Video")); + break; + case media::cast::STATUS_CODEC_INIT_FAILED: + error_callback.Run(base::StringPrintf("%s codec initialization failed.", + is_for_audio ? "Audio" : "Video")); + break; + case media::cast::STATUS_CODEC_RUNTIME_ERROR: + error_callback.Run(base::StringPrintf("%s codec runtime error.", + is_for_audio ? "Audio" : "Video")); + break; + } +} + +void CastSessionDelegate::ReceivePacket( + std::unique_ptr<media::cast::Packet> packet) { + // Do nothing (frees packet) +} |