1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/renderer/media/cast_session_delegate.h"
6
7#include "base/logging.h"
8#include "base/message_loop/message_loop_proxy.h"
9#include "content/public/renderer/p2p_socket_client.h"
10#include "content/public/renderer/render_thread.h"
11#include "media/cast/cast_config.h"
12#include "media/cast/cast_environment.h"
13#include "media/cast/cast_sender.h"
14#include "media/cast/logging/logging_defines.h"
15
16using media::cast::AudioSenderConfig;
17using media::cast::CastEnvironment;
18using media::cast::CastSender;
19using media::cast::VideoSenderConfig;
20
21CastSessionDelegate::CastSessionDelegate()
22    : audio_encode_thread_("CastAudioEncodeThread"),
23      video_encode_thread_("CastVideoEncodeThread"),
24      audio_configured_(false),
25      video_configured_(false),
26      io_message_loop_proxy_(
27          content::RenderThread::Get()->GetIOMessageLoopProxy()) {
28}
29
30CastSessionDelegate::~CastSessionDelegate() {
31  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
32}
33
34void CastSessionDelegate::SetSocketFactory(
35    scoped_ptr<CastSession::P2PSocketFactory> socket_factory,
36    const net::IPEndPoint& remote_address) {
37  socket_factory_ = socket_factory.Pass();
38  remote_address_ = remote_address;
39}
40
41void CastSessionDelegate::StartAudio(
42    const AudioSenderConfig& config,
43    const FrameInputAvailableCallback& callback) {
44  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
45
46  audio_configured_ = true;
47  audio_config_ = config;
48  frame_input_available_callbacks_.push_back(callback);
49  MaybeStartSending();
50}
51
52void CastSessionDelegate::StartVideo(
53    const VideoSenderConfig& config,
54    const FrameInputAvailableCallback& callback) {
55  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
56
57  video_configured_ = true;
58  video_config_ = config;
59  frame_input_available_callbacks_.push_back(callback);
60  MaybeStartSending();
61}
62
63void CastSessionDelegate::StartSending() {
64  DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
65
66  if (cast_environment_)
67    return;
68
69  if (!socket_factory_) {
70    // TODO(hubbe): Post an error back to the user
71    return;
72  }
73
74  socket_ = socket_factory_->Create();
75  socket_->SetDelegate(this);
76
77  audio_encode_thread_.Start();
78  video_encode_thread_.Start();
79
80  // CastSender uses the renderer's IO thread as the main thread. This reduces
81  // thread hopping for incoming video frames and outgoing network packets.
82  // There's no need to decode so no thread assigned for decoding.
83  // Get default logging: All disabled.
84  cast_environment_ = new CastEnvironment(
85      &clock_,
86      base::MessageLoopProxy::current(),
87      audio_encode_thread_.message_loop_proxy(),
88      NULL,
89      video_encode_thread_.message_loop_proxy(),
90      NULL,
91      media::cast::GetDefaultCastLoggingConfig());
92
93  // TODO(hclam): Implement VideoEncoderController to configure hardware
94  // encoder.
95  cast_sender_.reset(CastSender::CreateCastSender(
96      cast_environment_,
97      audio_config_,
98      video_config_,
99      NULL,
100      this));
101
102  for (size_t i = 0; i < frame_input_available_callbacks_.size(); ++i) {
103    frame_input_available_callbacks_[i].Run(
104        cast_sender_->frame_input());
105  }
106  frame_input_available_callbacks_.clear();
107}
108
109  // media::cast::PacketSender Implementation
110bool CastSessionDelegate::SendPacket(
111    const media::cast::Packet& packet) {
112  // TODO(hubbe): Make sure audio and video packets gets the right DSCP.
113  socket_->SendWithDscp(
114      remote_address_,
115      *reinterpret_cast<const std::vector<char> *>(&packet),
116      net::DSCP_AF41);
117  return true;
118}
119
120bool CastSessionDelegate::SendPackets(
121    const media::cast::PacketList& packets) {
122  // TODO(hubbe): Add ability to send multiple packets in one IPC message.
123  for (size_t i = 0; i < packets.size(); i++) {
124    SendPacket(packets[i]);
125  }
126  return true;
127}
128
129// content::P2PSocketClient::Delegate Implementation
130void CastSessionDelegate::OnOpen(
131    const net::IPEndPoint& address) {
132  // Called once Init completes. Ignored.
133}
134
135void CastSessionDelegate::OnIncomingTcpConnection(
136    const net::IPEndPoint& address,
137    content::P2PSocketClient* client) {
138  // We only support UDP sockets. This function should not be called
139  // for UDP sockets.
140  NOTREACHED();
141}
142
143void CastSessionDelegate::OnSendComplete() {
144  // Ignored for now.
145}
146
147void CastSessionDelegate::OnError() {
148  // TODO(hubbe): Report this back to the user.
149}
150
151void CastSessionDelegate::OnDataReceived(const net::IPEndPoint& address,
152                                         const std::vector<char>& data,
153                                         const base::TimeTicks& timestamp) {
154  uint8 *packet_copy = new uint8[data.size()];
155  memcpy(packet_copy, &data[0], data.size());
156  cast_sender_->packet_receiver()->ReceivedPacket(
157      packet_copy,
158      data.size(),
159      base::Bind(&media::cast::PacketReceiver::DeletePacket,
160                 packet_copy));
161}
162
163void CastSessionDelegate::MaybeStartSending() {
164  if (!audio_configured_ || !video_configured_)
165    return;
166  StartSending();
167}
168