summaryrefslogtreecommitdiff
path: root/chromium/chrome/renderer/media/cast_session_delegate.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/renderer/media/cast_session_delegate.cc')
-rw-r--r--chromium/chrome/renderer/media/cast_session_delegate.cc332
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)
+}