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 const scoped_refptr<base::SingleThreadTaskRunner>&
56 RtcDataChannelHandler::Observer::main_thread() const {
60 const scoped_refptr<webrtc::DataChannelInterface>&
61 RtcDataChannelHandler::Observer::channel() const {
65 void RtcDataChannelHandler::Observer::ClearHandler() {
66 DCHECK(main_thread_->BelongsToCurrentThread());
70 void RtcDataChannelHandler::Observer::OnStateChange() {
71 main_thread_->PostTask(FROM_HERE, base::Bind(
72 &RtcDataChannelHandler::Observer::OnStateChangeImpl, this,
76 void RtcDataChannelHandler::Observer::OnMessage(
77 const webrtc::DataBuffer& buffer) {
78 // TODO(tommi): Figure out a way to transfer ownership of the buffer without
79 // having to create a copy. See webrtc bug 3967.
80 scoped_ptr<webrtc::DataBuffer> new_buffer(new webrtc::DataBuffer(buffer));
81 main_thread_->PostTask(FROM_HERE,
82 base::Bind(&RtcDataChannelHandler::Observer::OnMessageImpl, this,
83 base::Passed(&new_buffer)));
86 void RtcDataChannelHandler::Observer::OnStateChangeImpl(
87 webrtc::DataChannelInterface::DataState state) {
88 DCHECK(main_thread_->BelongsToCurrentThread());
90 handler_->OnStateChange(state);
93 void RtcDataChannelHandler::Observer::OnMessageImpl(
94 scoped_ptr<webrtc::DataBuffer> buffer) {
95 DCHECK(main_thread_->BelongsToCurrentThread());
97 handler_->OnMessage(buffer.Pass());
100 RtcDataChannelHandler::RtcDataChannelHandler(
101 const scoped_refptr<base::SingleThreadTaskRunner>& main_thread,
102 webrtc::DataChannelInterface* channel)
103 : observer_(new Observer(this, main_thread, channel)),
104 webkit_client_(NULL) {
105 DVLOG(1) << "RtcDataChannelHandler " << channel->label();
107 // Detach from the ctor thread since we can be constructed on either the main
108 // or signaling threads.
109 thread_checker_.DetachFromThread();
111 IncrementCounter(CHANNEL_CREATED);
112 if (channel->reliable())
113 IncrementCounter(CHANNEL_RELIABLE);
114 if (channel->ordered())
115 IncrementCounter(CHANNEL_ORDERED);
116 if (channel->negotiated())
117 IncrementCounter(CHANNEL_NEGOTIATED);
119 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.DataChannelMaxRetransmits",
120 channel->maxRetransmits(), 0,
121 std::numeric_limits<unsigned short>::max(), 50);
122 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.DataChannelMaxRetransmitTime",
123 channel->maxRetransmitTime(), 0,
124 std::numeric_limits<unsigned short>::max(), 50);
127 RtcDataChannelHandler::~RtcDataChannelHandler() {
128 DCHECK(thread_checker_.CalledOnValidThread());
129 DVLOG(1) << "::dtor";
130 observer_->ClearHandler();
133 void RtcDataChannelHandler::setClient(
134 blink::WebRTCDataChannelHandlerClient* client) {
135 DCHECK(thread_checker_.CalledOnValidThread());
136 webkit_client_ = client;
139 blink::WebString RtcDataChannelHandler::label() {
140 DCHECK(thread_checker_.CalledOnValidThread());
141 return base::UTF8ToUTF16(channel()->label());
144 bool RtcDataChannelHandler::isReliable() {
145 DCHECK(thread_checker_.CalledOnValidThread());
146 return channel()->reliable();
149 bool RtcDataChannelHandler::ordered() const {
150 DCHECK(thread_checker_.CalledOnValidThread());
151 return channel()->ordered();
154 unsigned short RtcDataChannelHandler::maxRetransmitTime() const {
155 DCHECK(thread_checker_.CalledOnValidThread());
156 return channel()->maxRetransmitTime();
159 unsigned short RtcDataChannelHandler::maxRetransmits() const {
160 DCHECK(thread_checker_.CalledOnValidThread());
161 return channel()->maxRetransmits();
164 blink::WebString RtcDataChannelHandler::protocol() const {
165 DCHECK(thread_checker_.CalledOnValidThread());
166 return base::UTF8ToUTF16(channel()->protocol());
169 bool RtcDataChannelHandler::negotiated() const {
170 DCHECK(thread_checker_.CalledOnValidThread());
171 return channel()->negotiated();
174 unsigned short RtcDataChannelHandler::id() const {
175 DCHECK(thread_checker_.CalledOnValidThread());
176 return channel()->id();
179 unsigned long RtcDataChannelHandler::bufferedAmount() {
180 DCHECK(thread_checker_.CalledOnValidThread());
181 return channel()->buffered_amount();
184 bool RtcDataChannelHandler::sendStringData(const blink::WebString& data) {
185 DCHECK(thread_checker_.CalledOnValidThread());
186 std::string utf8_buffer = base::UTF16ToUTF8(data);
187 rtc::Buffer buffer(utf8_buffer.c_str(), utf8_buffer.length());
188 webrtc::DataBuffer data_buffer(buffer, false);
189 RecordMessageSent(data_buffer.size());
190 return channel()->Send(data_buffer);
193 bool RtcDataChannelHandler::sendRawData(const char* data, size_t length) {
194 DCHECK(thread_checker_.CalledOnValidThread());
195 rtc::Buffer buffer(data, length);
196 webrtc::DataBuffer data_buffer(buffer, true);
197 RecordMessageSent(data_buffer.size());
198 return channel()->Send(data_buffer);
201 void RtcDataChannelHandler::close() {
202 DCHECK(thread_checker_.CalledOnValidThread());
204 // Note that even though Close() will run synchronously, the readyState has
205 // not changed yet since the state changes that occured on the signaling
206 // thread have been posted to this thread and will be delivered later.
207 // To work around this, we could have a nested loop here and deliver the
208 // callbacks before running from this function, but doing so can cause
209 // undesired side effects in webkit, so we don't, and instead rely on the
210 // user of the API handling readyState notifications.
213 const scoped_refptr<webrtc::DataChannelInterface>&
214 RtcDataChannelHandler::channel() const {
215 return observer_->channel();
218 void RtcDataChannelHandler::OnStateChange(
219 webrtc::DataChannelInterface::DataState state) {
220 DCHECK(thread_checker_.CalledOnValidThread());
221 DVLOG(1) << "OnStateChange " << state;
223 if (!webkit_client_) {
224 // If this happens, the web application will not get notified of changes.
225 NOTREACHED() << "WebRTCDataChannelHandlerClient not set.";
230 case webrtc::DataChannelInterface::kConnecting:
231 webkit_client_->didChangeReadyState(
232 blink::WebRTCDataChannelHandlerClient::ReadyStateConnecting);
234 case webrtc::DataChannelInterface::kOpen:
235 IncrementCounter(CHANNEL_OPENED);
236 webkit_client_->didChangeReadyState(
237 blink::WebRTCDataChannelHandlerClient::ReadyStateOpen);
239 case webrtc::DataChannelInterface::kClosing:
240 webkit_client_->didChangeReadyState(
241 blink::WebRTCDataChannelHandlerClient::ReadyStateClosing);
243 case webrtc::DataChannelInterface::kClosed:
244 webkit_client_->didChangeReadyState(
245 blink::WebRTCDataChannelHandlerClient::ReadyStateClosed);
253 void RtcDataChannelHandler::OnMessage(scoped_ptr<webrtc::DataBuffer> buffer) {
254 DCHECK(thread_checker_.CalledOnValidThread());
255 if (!webkit_client_) {
256 // If this happens, the web application will not get notified of changes.
257 NOTREACHED() << "WebRTCDataChannelHandlerClient not set.";
261 if (buffer->binary) {
262 webkit_client_->didReceiveRawData(buffer->data.data(),
263 buffer->data.length());
265 base::string16 utf16;
266 if (!base::UTF8ToUTF16(buffer->data.data(), buffer->data.length(),
268 LOG(ERROR) << "Failed convert received data to UTF16";
271 webkit_client_->didReceiveStringData(utf16);
275 void RtcDataChannelHandler::RecordMessageSent(size_t num_bytes) {
276 // Currently, messages are capped at some fairly low limit (16 Kb?)
277 // but we may allow unlimited-size messages at some point, so making
278 // the histogram maximum quite large (100 Mb) to have some
279 // granularity at the higher end in that eventuality. The histogram
280 // buckets are exponentially growing in size, so we'll still have
281 // good granularity at the low end.
283 // This makes the last bucket in the histogram count messages from
284 // 100 Mb to infinity.
285 const int kMaxBucketSize = 100 * 1024 * 1024;
286 const int kNumBuckets = 50;
288 if (channel()->reliable()) {
289 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.ReliableDataChannelMessageSize",
291 1, kMaxBucketSize, kNumBuckets);
293 UMA_HISTOGRAM_CUSTOM_COUNTS("WebRTC.UnreliableDataChannelMessageSize",
295 1, kMaxBucketSize, kNumBuckets);
299 } // namespace content