#include "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "chrome/renderer/media/cast_transport_sender_ipc.h"
#include "content/public/renderer/p2p_socket_client.h"
#include "content/public/renderer/render_thread.h"
#include "media/cast/cast_config.h"
using media::cast::CastSender;
using media::cast::VideoSenderConfig;
-namespace {
-
-// This is a dummy class that does nothing. This is needed temporarily
-// to enable tests for cast.streaming extension APIs.
-// The real implementation of CastTransportSender is to use IPC to send
-// data to the browser process.
-// See crbug.com/327482 for more details.
-class DummyTransport : public media::cast::transport::CastTransportSender {
- public:
- DummyTransport() {}
- virtual ~DummyTransport() {}
-
- // CastTransportSender implementations.
- virtual void SetPacketReceiver(
- scoped_refptr<
- media::cast::transport::PacketReceiver> packet_receiver) OVERRIDE {}
- virtual void InsertCodedAudioFrame(
- const media::cast::transport::EncodedAudioFrame* audio_frame,
- const base::TimeTicks& recorded_time) OVERRIDE {}
- virtual void InsertCodedVideoFrame(
- const media::cast::transport::EncodedVideoFrame* video_frame,
- const base::TimeTicks& capture_time) OVERRIDE {}
- virtual void SendRtcpFromRtpSender(
- uint32 packet_type_flags,
- const media::cast::transport::RtcpSenderInfo& sender_info,
- const media::cast::transport::RtcpDlrrReportBlock& dlrr,
- const media::cast::transport::RtcpSenderLogMessage& sender_log,
- uint32 sending_ssrc,
- const std::string& c_name) OVERRIDE {}
- virtual void ResendPackets(
- bool is_audio,
- const media::cast::transport::MissingFramesAndPacketsMap& missing_packets)
- OVERRIDE {}
- virtual void RtpAudioStatistics(
- const base::TimeTicks& now,
- media::cast::transport::RtcpSenderInfo* sender_info) OVERRIDE {}
- virtual void RtpVideoStatistics(
- const base::TimeTicks& now,
- media::cast::transport::RtcpSenderInfo* sender_info) OVERRIDE {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DummyTransport);
-};
-
-} // namespace
-
CastSessionDelegate::CastSessionDelegate()
: audio_encode_thread_("CastAudioEncodeThread"),
video_encode_thread_("CastVideoEncodeThread"),
- audio_configured_(false),
- video_configured_(false),
+ transport_configured_(false),
io_message_loop_proxy_(
content::RenderThread::Get()->GetIOMessageLoopProxy()) {
+ DCHECK(io_message_loop_proxy_);
}
CastSessionDelegate::~CastSessionDelegate() {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
}
+void CastSessionDelegate::Initialize() {
+ if (cast_environment_)
+ return; // Already initialized.
+
+ audio_encode_thread_.Start();
+ video_encode_thread_.Start();
+
+ // CastSender uses the renderer's IO thread as the main thread. This reduces
+ // thread hopping for incoming video frames and outgoing network packets.
+ // There's no need to decode so no thread assigned for decoding.
+ // Get default logging: All disabled.
+ cast_environment_ = new CastEnvironment(
+ scoped_ptr<base::TickClock>(new base::DefaultTickClock()).Pass(),
+ base::MessageLoopProxy::current(),
+ audio_encode_thread_.message_loop_proxy(),
+ NULL,
+ video_encode_thread_.message_loop_proxy(),
+ NULL,
+ base::MessageLoopProxy::current(),
+ media::cast::GetDefaultCastSenderLoggingConfig());
+}
+
void CastSessionDelegate::StartAudio(
const AudioSenderConfig& config,
const FrameInputAvailableCallback& callback) {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
- audio_configured_ = true;
- audio_config_ = config;
- frame_input_available_callbacks_.push_back(callback);
+ audio_config_.reset(new AudioSenderConfig(config));
+ video_frame_input_available_callback_ = callback;
StartSendingInternal();
}
const VideoSenderConfig& config,
const FrameInputAvailableCallback& callback) {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ audio_frame_input_available_callback_ = callback;
+
+ video_config_.reset(new VideoSenderConfig(config));
+ StartSendingInternal();
+}
- video_configured_ = true;
- video_config_ = config;
- frame_input_available_callbacks_.push_back(callback);
+void CastSessionDelegate::StartUDP(
+ const net::IPEndPoint& local_endpoint,
+ const net::IPEndPoint& remote_endpoint) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ transport_configured_ = true;
+ local_endpoint_ = local_endpoint;
+ remote_endpoint_ = remote_endpoint;
StartSendingInternal();
}
+void CastSessionDelegate::StatusNotificationCB(
+ media::cast::transport::CastTransportStatus unused_status) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ // TODO(hubbe): Call javascript UDPTransport error function.
+}
+
void CastSessionDelegate::StartSendingInternal() {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
- if (cast_environment_)
+ // No transport, wait.
+ if (!transport_configured_)
return;
- if (!audio_configured_ || !video_configured_)
+
+ // No audio or video, wait.
+ if (!audio_config_ && !video_config_)
return;
- cast_transport_.reset(new DummyTransport());
- audio_encode_thread_.Start();
- video_encode_thread_.Start();
+ Initialize();
- // CastSender uses the renderer's IO thread as the main thread. This reduces
- // thread hopping for incoming video frames and outgoing network packets.
- // There's no need to decode so no thread assigned for decoding.
- // Get default logging: All disabled.
- cast_environment_ = new CastEnvironment(
- scoped_ptr<base::TickClock>(new base::DefaultTickClock()).Pass(),
- base::MessageLoopProxy::current(),
- audio_encode_thread_.message_loop_proxy(),
- NULL,
- video_encode_thread_.message_loop_proxy(),
- NULL,
- base::MessageLoopProxy::current(),
- media::cast::GetDefaultCastSenderLoggingConfig());
+ media::cast::transport::CastTransportConfig config;
+
+ // TODO(hubbe): set config.aes_key and config.aes_iv_mask.
+ config.local_endpoint = local_endpoint_;
+ config.receiver_endpoint = remote_endpoint_;
+ if (audio_config_) {
+ config.audio_ssrc = audio_config_->sender_ssrc;
+ config.audio_codec = audio_config_->codec;
+ config.audio_rtp_config = audio_config_->rtp_config;
+ config.audio_frequency = audio_config_->frequency;
+ config.audio_channels = audio_config_->channels;
+ }
+ if (video_config_) {
+ config.video_ssrc = video_config_->sender_ssrc;
+ config.video_codec = video_config_->codec;
+ config.video_rtp_config = video_config_->rtp_config;
+ }
+
+ cast_transport_.reset(new CastTransportSenderIPC(
+ config,
+ base::Bind(&CastSessionDelegate::StatusNotificationCB,
+ base::Unretained(this))));
cast_sender_.reset(CastSender::CreateCastSender(
cast_environment_,
- audio_config_,
- video_config_,
- NULL,
+ audio_config_.get(),
+ video_config_.get(),
+ NULL, // GPU.
+ base::Bind(&CastSessionDelegate::InitializationResult,
+ base::Unretained(this)),
cast_transport_.get()));
+ cast_transport_->SetPacketReceiver(cast_sender_->packet_receiver());
+}
- for (size_t i = 0; i < frame_input_available_callbacks_.size(); ++i) {
- frame_input_available_callbacks_[i].Run(
- cast_sender_->frame_input());
+void CastSessionDelegate::InitializationResult(
+ media::cast::CastInitializationStatus result) const {
+ DCHECK(cast_sender_);
+
+ // TODO(pwestin): handle the error codes.
+ if (result == media::cast::STATUS_INITIALIZED) {
+ if (!audio_frame_input_available_callback_.is_null()) {
+ audio_frame_input_available_callback_.Run(cast_sender_->frame_input());
+ }
+ if (!video_frame_input_available_callback_.is_null()) {
+ video_frame_input_available_callback_.Run(cast_sender_->frame_input());
+ }
}
- frame_input_available_callbacks_.clear();
}