// 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 "base/logging.h"
#include "base/message_loop/message_loop_proxy.h"
#include "content/public/renderer/p2p_socket_client.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/logging_defines.h"
using media::cast::AudioSenderConfig;
using media::cast::CastEnvironment;
using media::cast::CastSender;
using media::cast::VideoSenderConfig;
CastSessionDelegate::CastSessionDelegate()
: audio_encode_thread_("CastAudioEncodeThread"),
video_encode_thread_("CastVideoEncodeThread"),
audio_configured_(false),
video_configured_(false),
io_message_loop_proxy_(
content::RenderThread::Get()->GetIOMessageLoopProxy()) {
}
CastSessionDelegate::~CastSessionDelegate() {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
}
void CastSessionDelegate::SetSocketFactory(
scoped_ptr<CastSession::P2PSocketFactory> socket_factory,
const net::IPEndPoint& remote_address) {
socket_factory_ = socket_factory.Pass();
remote_address_ = remote_address;
}
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);
MaybeStartSending();
}
void CastSessionDelegate::StartVideo(
const VideoSenderConfig& config,
const FrameInputAvailableCallback& callback) {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
video_configured_ = true;
video_config_ = config;
frame_input_available_callbacks_.push_back(callback);
MaybeStartSending();
}
void CastSessionDelegate::StartSending() {
DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
if (cast_environment_)
return;
if (!socket_factory_) {
// TODO(hubbe): Post an error back to the user
return;
}
socket_ = socket_factory_->Create();
socket_->SetDelegate(this);
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(
&clock_,
base::MessageLoopProxy::current(),
audio_encode_thread_.message_loop_proxy(),
NULL,
video_encode_thread_.message_loop_proxy(),
NULL,
media::cast::GetDefaultCastLoggingConfig());
// TODO(hclam): Implement VideoEncoderController to configure hardware
// encoder.
cast_sender_.reset(CastSender::CreateCastSender(
cast_environment_,
audio_config_,
video_config_,
NULL,
this));
for (size_t i = 0; i < frame_input_available_callbacks_.size(); ++i) {
frame_input_available_callbacks_[i].Run(
cast_sender_->frame_input());
}
frame_input_available_callbacks_.clear();
}
// media::cast::PacketSender Implementation
bool CastSessionDelegate::SendPacket(
const media::cast::Packet& packet) {
// TODO(hubbe): Make sure audio and video packets gets the right DSCP.
socket_->SendWithDscp(
remote_address_,
*reinterpret_cast<const std::vector<char> *>(&packet),
net::DSCP_AF41);
return true;
}
bool CastSessionDelegate::SendPackets(
const media::cast::PacketList& packets) {
// TODO(hubbe): Add ability to send multiple packets in one IPC message.
for (size_t i = 0; i < packets.size(); i++) {
SendPacket(packets[i]);
}
return true;
}
// content::P2PSocketClient::Delegate Implementation
void CastSessionDelegate::OnOpen(
const net::IPEndPoint& address) {
// Called once Init completes. Ignored.
}
void CastSessionDelegate::OnIncomingTcpConnection(
const net::IPEndPoint& address,
content::P2PSocketClient* client) {
// We only support UDP sockets. This function should not be called
// for UDP sockets.
NOTREACHED();
}
void CastSessionDelegate::OnSendComplete() {
// Ignored for now.
}
void CastSessionDelegate::OnError() {
// TODO(hubbe): Report this back to the user.
}
void CastSessionDelegate::OnDataReceived(const net::IPEndPoint& address,
const std::vector<char>& data,
const base::TimeTicks& timestamp) {
uint8 *packet_copy = new uint8[data.size()];
memcpy(packet_copy, &data[0], data.size());
cast_sender_->packet_receiver()->ReceivedPacket(
packet_copy,
data.size(),
base::Bind(&media::cast::PacketReceiver::DeletePacket,
packet_copy));
}
void CastSessionDelegate::MaybeStartSending() {
if (!audio_configured_ || !video_configured_)
return;
StartSending();
}