1 // Copyright (c) 2012 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.
5 #include "content/renderer/media/rtc_data_channel_handler.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/histogram.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/thread_task_runner_handle.h"
22 enum DataChannelCounters
{
31 void IncrementCounter(DataChannelCounters counter
) {
32 UMA_HISTOGRAM_ENUMERATION("WebRTC.DataChannelCounters",
39 // Implementation of DataChannelObserver that receives events on libjingle's
40 // signaling thread and forwards them over to the main thread for handling.
41 // Since the handler's lifetime is scoped potentially narrower than what
42 // the callbacks allow for, we use reference counting here to make sure
43 // all callbacks have a valid pointer but won't do anything if the handler
45 RtcDataChannelHandler::Observer::Observer(
46 RtcDataChannelHandler
* handler
,
47 const scoped_refptr
<base::SingleThreadTaskRunner
>& main_thread
,
48 webrtc::DataChannelInterface
* channel
)
49 : handler_(handler
), main_thread_(main_thread
), channel_(channel
) {
50 channel_
->RegisterObserver(this);
53 RtcDataChannelHandler::Observer::~Observer() {
55 DCHECK(!channel_
.get()) << "Unregister hasn't been called.";
58 const scoped_refptr
<base::SingleThreadTaskRunner
>&
59 RtcDataChannelHandler::Observer::main_thread() const {
63 const scoped_refptr
<webrtc::DataChannelInterface
>&
64 RtcDataChannelHandler::Observer::channel() const {
65 DCHECK(main_thread_
->BelongsToCurrentThread());
69 void RtcDataChannelHandler::Observer::Unregister() {
70 DCHECK(main_thread_
->BelongsToCurrentThread());
73 channel_
->UnregisterObserver();
74 // Now that we're guaranteed to not get further OnStateChange callbacks,
75 // it's safe to release our reference to the channel.
80 void RtcDataChannelHandler::Observer::OnStateChange() {
81 main_thread_
->PostTask(FROM_HERE
, base::Bind(
82 &RtcDataChannelHandler::Observer::OnStateChangeImpl
, this,
86 void RtcDataChannelHandler::Observer::OnBufferedAmountChange(
87 uint64 previous_amount
) {
88 // Optimization: Only post a task if the change is a decrease, because the web
89 // interface does not perform any action when there is an increase.
90 if (previous_amount
> channel_
->buffered_amount()) {
91 main_thread_
->PostTask(FROM_HERE
, base::Bind(
92 &RtcDataChannelHandler::Observer::OnBufferedAmountDecreaseImpl
, this,
97 void RtcDataChannelHandler::Observer::OnMessage(
98 const webrtc::DataBuffer
& buffer
) {
99 // TODO(tommi): Figure out a way to transfer ownership of the buffer without
100 // having to create a copy. See webrtc bug 3967.
101 scoped_ptr
<webrtc::DataBuffer
> new_buffer(new webrtc::DataBuffer(buffer
));
102 main_thread_
->PostTask(FROM_HERE
,
103 base::Bind(&RtcDataChannelHandler::Observer::OnMessageImpl
, this,
104 base::Passed(&new_buffer
)));
107 void RtcDataChannelHandler::Observer::OnStateChangeImpl(
108 webrtc::DataChannelInterface::DataState state
) {
109 DCHECK(main_thread_
->BelongsToCurrentThread());
111 handler_
->OnStateChange(state
);
114 void RtcDataChannelHandler::Observer::OnBufferedAmountDecreaseImpl(
115 unsigned previous_amount
) {
116 DCHECK(main_thread_
->BelongsToCurrentThread());
118 handler_
->OnBufferedAmountDecrease(previous_amount
);
121 void RtcDataChannelHandler::Observer::OnMessageImpl(
122 scoped_ptr
<webrtc::DataBuffer
> buffer
) {
123 DCHECK(main_thread_
->BelongsToCurrentThread());
125 handler_
->OnMessage(buffer
.Pass());
128 RtcDataChannelHandler::RtcDataChannelHandler(
129 const scoped_refptr
<base::SingleThreadTaskRunner
>& main_thread
,
130 webrtc::DataChannelInterface
* channel
)
131 : observer_(new Observer(this, main_thread
, channel
)),
132 webkit_client_(NULL
) {
133 DVLOG(1) << "RtcDataChannelHandler " << channel
->label();
135 // Detach from the ctor thread since we can be constructed on either the main
136 // or signaling threads.
137 thread_checker_
.DetachFromThread();
139 IncrementCounter(CHANNEL_CREATED
);
140 if (channel
->reliable())
141 IncrementCounter(CHANNEL_RELIABLE
);
142 if (channel
->ordered())
143 IncrementCounter(CHANNEL_ORDERED
);
144 if (channel
->negotiated())
145 IncrementCounter(CHANNEL_NEGOTIATED
);
147 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.DataChannelMaxRetransmits",
148 channel
->maxRetransmits(), 0,
149 std::numeric_limits
<unsigned short>::max(), 50);
150 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.DataChannelMaxRetransmitTime",
151 channel
->maxRetransmitTime(), 0,
152 std::numeric_limits
<unsigned short>::max(), 50);
155 RtcDataChannelHandler::~RtcDataChannelHandler() {
156 DCHECK(thread_checker_
.CalledOnValidThread());
157 DVLOG(1) << "::dtor";
158 // setClient might not have been called at all if the data channel was not
159 // passed to Blink. So, we call it here explicitly just to make sure the
160 // observer gets properly unregistered.
161 // See RTCPeerConnectionHandler::OnDataChannel for more.
165 void RtcDataChannelHandler::setClient(
166 blink::WebRTCDataChannelHandlerClient
* client
) {
167 DCHECK(thread_checker_
.CalledOnValidThread());
168 DVLOG(3) << "setClient " << client
;
169 webkit_client_
= client
;
170 if (!client
&& observer_
.get()) {
171 observer_
->Unregister();
176 blink::WebString
RtcDataChannelHandler::label() {
177 DCHECK(thread_checker_
.CalledOnValidThread());
178 return base::UTF8ToUTF16(channel()->label());
181 bool RtcDataChannelHandler::isReliable() {
182 DCHECK(thread_checker_
.CalledOnValidThread());
183 return channel()->reliable();
186 bool RtcDataChannelHandler::ordered() const {
187 DCHECK(thread_checker_
.CalledOnValidThread());
188 return channel()->ordered();
191 unsigned short RtcDataChannelHandler::maxRetransmitTime() const {
192 DCHECK(thread_checker_
.CalledOnValidThread());
193 return channel()->maxRetransmitTime();
196 unsigned short RtcDataChannelHandler::maxRetransmits() const {
197 DCHECK(thread_checker_
.CalledOnValidThread());
198 return channel()->maxRetransmits();
201 blink::WebString
RtcDataChannelHandler::protocol() const {
202 DCHECK(thread_checker_
.CalledOnValidThread());
203 return base::UTF8ToUTF16(channel()->protocol());
206 bool RtcDataChannelHandler::negotiated() const {
207 DCHECK(thread_checker_
.CalledOnValidThread());
208 return channel()->negotiated();
211 unsigned short RtcDataChannelHandler::id() const {
212 DCHECK(thread_checker_
.CalledOnValidThread());
213 return channel()->id();
216 blink::WebRTCDataChannelHandlerClient::ReadyState
convertReadyState(
217 webrtc::DataChannelInterface::DataState state
) {
219 case webrtc::DataChannelInterface::kConnecting
:
220 return blink::WebRTCDataChannelHandlerClient::ReadyStateConnecting
;
222 case webrtc::DataChannelInterface::kOpen
:
223 return blink::WebRTCDataChannelHandlerClient::ReadyStateOpen
;
225 case webrtc::DataChannelInterface::kClosing
:
226 return blink::WebRTCDataChannelHandlerClient::ReadyStateClosing
;
228 case webrtc::DataChannelInterface::kClosed
:
229 return blink::WebRTCDataChannelHandlerClient::ReadyStateClosed
;
233 // MSVC does not respect |NOTREACHED()|, so we need a return value.
234 return blink::WebRTCDataChannelHandlerClient::ReadyStateClosed
;
238 blink::WebRTCDataChannelHandlerClient::ReadyState
239 RtcDataChannelHandler::state() const {
240 DCHECK(thread_checker_
.CalledOnValidThread());
241 if (!observer_
.get()) {
242 return blink::WebRTCDataChannelHandlerClient::ReadyStateConnecting
;
244 return convertReadyState(observer_
->channel()->state());
248 unsigned long RtcDataChannelHandler::bufferedAmount() {
249 DCHECK(thread_checker_
.CalledOnValidThread());
250 return channel()->buffered_amount();
253 bool RtcDataChannelHandler::sendStringData(const blink::WebString
& data
) {
254 DCHECK(thread_checker_
.CalledOnValidThread());
255 std::string utf8_buffer
= base::UTF16ToUTF8(base::StringPiece16(data
));
256 rtc::Buffer
buffer(utf8_buffer
.c_str(), utf8_buffer
.length());
257 webrtc::DataBuffer
data_buffer(buffer
, false);
258 RecordMessageSent(data_buffer
.size());
259 return channel()->Send(data_buffer
);
262 bool RtcDataChannelHandler::sendRawData(const char* data
, size_t length
) {
263 DCHECK(thread_checker_
.CalledOnValidThread());
264 rtc::Buffer
buffer(data
, length
);
265 webrtc::DataBuffer
data_buffer(buffer
, true);
266 RecordMessageSent(data_buffer
.size());
267 return channel()->Send(data_buffer
);
270 void RtcDataChannelHandler::close() {
271 DCHECK(thread_checker_
.CalledOnValidThread());
273 // Note that even though Close() will run synchronously, the readyState has
274 // not changed yet since the state changes that occured on the signaling
275 // thread have been posted to this thread and will be delivered later.
276 // To work around this, we could have a nested loop here and deliver the
277 // callbacks before running from this function, but doing so can cause
278 // undesired side effects in webkit, so we don't, and instead rely on the
279 // user of the API handling readyState notifications.
282 const scoped_refptr
<webrtc::DataChannelInterface
>&
283 RtcDataChannelHandler::channel() const {
284 return observer_
->channel();
287 void RtcDataChannelHandler::OnStateChange(
288 webrtc::DataChannelInterface::DataState state
) {
289 DCHECK(thread_checker_
.CalledOnValidThread());
290 DVLOG(1) << "OnStateChange " << state
;
292 if (!webkit_client_
) {
293 // If this happens, the web application will not get notified of changes.
294 NOTREACHED() << "WebRTCDataChannelHandlerClient not set.";
298 if (state
== webrtc::DataChannelInterface::kOpen
)
299 IncrementCounter(CHANNEL_OPENED
);
301 webkit_client_
->didChangeReadyState(convertReadyState(state
));
304 void RtcDataChannelHandler::OnBufferedAmountDecrease(
305 unsigned previous_amount
) {
306 DCHECK(thread_checker_
.CalledOnValidThread());
307 DVLOG(1) << "OnBufferedAmountDecrease " << previous_amount
;
309 if (!webkit_client_
) {
310 // If this happens, the web application will not get notified of changes.
311 NOTREACHED() << "WebRTCDataChannelHandlerClient not set.";
315 webkit_client_
->didDecreaseBufferedAmount(previous_amount
);
318 void RtcDataChannelHandler::OnMessage(scoped_ptr
<webrtc::DataBuffer
> buffer
) {
319 DCHECK(thread_checker_
.CalledOnValidThread());
320 if (!webkit_client_
) {
321 // If this happens, the web application will not get notified of changes.
322 NOTREACHED() << "WebRTCDataChannelHandlerClient not set.";
326 if (buffer
->binary
) {
327 webkit_client_
->didReceiveRawData(buffer
->data
.data
<char>(),
328 buffer
->data
.size());
330 base::string16 utf16
;
331 if (!base::UTF8ToUTF16(buffer
->data
.data
<char>(), buffer
->data
.size(),
333 LOG(ERROR
) << "Failed convert received data to UTF16";
336 webkit_client_
->didReceiveStringData(utf16
);
340 void RtcDataChannelHandler::RecordMessageSent(size_t num_bytes
) {
341 DCHECK(thread_checker_
.CalledOnValidThread());
342 // Currently, messages are capped at some fairly low limit (16 Kb?)
343 // but we may allow unlimited-size messages at some point, so making
344 // the histogram maximum quite large (100 Mb) to have some
345 // granularity at the higher end in that eventuality. The histogram
346 // buckets are exponentially growing in size, so we'll still have
347 // good granularity at the low end.
349 // This makes the last bucket in the histogram count messages from
350 // 100 Mb to infinity.
351 const int kMaxBucketSize
= 100 * 1024 * 1024;
352 const int kNumBuckets
= 50;
354 if (channel()->reliable()) {
355 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.ReliableDataChannelMessageSize",
357 1, kMaxBucketSize
, kNumBuckets
);
359 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.UnreliableDataChannelMessageSize",
361 1, kMaxBucketSize
, kNumBuckets
);
365 } // namespace content