1 /*
2 * libjingle
3 * Copyright 2004--2007, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "talk/session/phone/channel.h"
29
30 #include "talk/base/buffer.h"
31 #include "talk/base/byteorder.h"
32 #include "talk/base/common.h"
33 #include "talk/base/logging.h"
34 #include "talk/p2p/base/transportchannel.h"
35 #include "talk/session/phone/channelmanager.h"
36 #include "talk/session/phone/mediasessionclient.h"
37 #include "talk/session/phone/mediasink.h"
38 #include "talk/session/phone/rtcpmuxfilter.h"
39
40 namespace cricket {
41
42 struct PacketMessageData : public talk_base::MessageData {
43 talk_base::Buffer packet;
44 };
45
46 struct VoiceChannelErrorMessageData : public talk_base::MessageData {
VoiceChannelErrorMessageDatacricket::VoiceChannelErrorMessageData47 VoiceChannelErrorMessageData(uint32 in_ssrc,
48 VoiceMediaChannel::Error in_error)
49 : ssrc(in_ssrc),
50 error(in_error) {}
51 uint32 ssrc;
52 VoiceMediaChannel::Error error;
53 };
54
55 struct VideoChannelErrorMessageData : public talk_base::MessageData {
VideoChannelErrorMessageDatacricket::VideoChannelErrorMessageData56 VideoChannelErrorMessageData(uint32 in_ssrc,
57 VideoMediaChannel::Error in_error)
58 : ssrc(in_ssrc),
59 error(in_error) {}
60 uint32 ssrc;
61 VideoMediaChannel::Error error;
62 };
63
PacketType(bool rtcp)64 static const char* PacketType(bool rtcp) {
65 return (!rtcp) ? "RTP" : "RTCP";
66 }
67
ValidPacket(bool rtcp,const talk_base::Buffer * packet)68 static bool ValidPacket(bool rtcp, const talk_base::Buffer* packet) {
69 // Check the packet size. We could check the header too if needed.
70 return (packet &&
71 packet->length() >= (!rtcp ? kMinRtpPacketLen : kMinRtcpPacketLen) &&
72 packet->length() <= kMaxRtpPacketLen);
73 }
74
GetRtpSeqNum(const talk_base::Buffer * packet)75 static uint16 GetRtpSeqNum(const talk_base::Buffer* packet) {
76 return (packet->length() >= kMinRtpPacketLen) ?
77 talk_base::GetBE16(packet->data() + 2) : 0;
78 }
79
GetRtpSsrc(const talk_base::Buffer * packet)80 static uint32 GetRtpSsrc(const talk_base::Buffer* packet) {
81 return (packet->length() >= kMinRtpPacketLen) ?
82 talk_base::GetBE32(packet->data() + 8) : 0;
83 }
84
GetRtcpType(const talk_base::Buffer * packet)85 static int GetRtcpType(const talk_base::Buffer* packet) {
86 return (packet->length() >= kMinRtcpPacketLen) ?
87 static_cast<int>(packet->data()[1]) : 0;
88 }
89
BaseChannel(talk_base::Thread * thread,MediaEngine * media_engine,MediaChannel * media_channel,BaseSession * session,const std::string & content_name,TransportChannel * transport_channel)90 BaseChannel::BaseChannel(talk_base::Thread* thread, MediaEngine* media_engine,
91 MediaChannel* media_channel, BaseSession* session,
92 const std::string& content_name,
93 TransportChannel* transport_channel)
94 : worker_thread_(thread),
95 media_engine_(media_engine),
96 session_(session),
97 media_channel_(media_channel),
98 received_media_sink_(NULL),
99 sent_media_sink_(NULL),
100 content_name_(content_name),
101 transport_channel_(transport_channel),
102 rtcp_transport_channel_(NULL),
103 enabled_(false),
104 writable_(false),
105 has_codec_(false),
106 muted_(false) {
107 ASSERT(worker_thread_ == talk_base::Thread::Current());
108 media_channel_->SetInterface(this);
109 transport_channel_->SignalWritableState.connect(
110 this, &BaseChannel::OnWritableState);
111 transport_channel_->SignalReadPacket.connect(
112 this, &BaseChannel::OnChannelRead);
113
114 LOG(LS_INFO) << "Created channel";
115
116 session->SignalState.connect(this, &BaseChannel::OnSessionState);
117 }
118
~BaseChannel()119 BaseChannel::~BaseChannel() {
120 ASSERT(worker_thread_ == talk_base::Thread::Current());
121 StopConnectionMonitor();
122 FlushRtcpMessages(); // Send any outstanding RTCP packets.
123 Clear(); // eats any outstanding messages or packets
124 // We must destroy the media channel before the transport channel, otherwise
125 // the media channel may try to send on the dead transport channel. NULLing
126 // is not an effective strategy since the sends will come on another thread.
127 delete media_channel_;
128 set_rtcp_transport_channel(NULL);
129 if (transport_channel_ != NULL)
130 session_->DestroyChannel(content_name_, transport_channel_->name());
131 LOG(LS_INFO) << "Destroyed channel";
132 }
133
Enable(bool enable)134 bool BaseChannel::Enable(bool enable) {
135 // Can be called from thread other than worker thread
136 Send(enable ? MSG_ENABLE : MSG_DISABLE);
137 return true;
138 }
139
Mute(bool mute)140 bool BaseChannel::Mute(bool mute) {
141 // Can be called from thread other than worker thread
142 Send(mute ? MSG_MUTE : MSG_UNMUTE);
143 return true;
144 }
145
RemoveStream(uint32 ssrc)146 bool BaseChannel::RemoveStream(uint32 ssrc) {
147 StreamMessageData data(ssrc, 0);
148 Send(MSG_REMOVESTREAM, &data);
149 return true;
150 }
151
SetRtcpCName(const std::string & cname)152 bool BaseChannel::SetRtcpCName(const std::string& cname) {
153 SetRtcpCNameData data(cname);
154 Send(MSG_SETRTCPCNAME, &data);
155 return data.result;
156 }
157
SetLocalContent(const MediaContentDescription * content,ContentAction action)158 bool BaseChannel::SetLocalContent(const MediaContentDescription* content,
159 ContentAction action) {
160 SetContentData data(content, action);
161 Send(MSG_SETLOCALCONTENT, &data);
162 return data.result;
163 }
164
SetRemoteContent(const MediaContentDescription * content,ContentAction action)165 bool BaseChannel::SetRemoteContent(const MediaContentDescription* content,
166 ContentAction action) {
167 SetContentData data(content, action);
168 Send(MSG_SETREMOTECONTENT, &data);
169 return data.result;
170 }
171
SetMaxSendBandwidth(int max_bandwidth)172 bool BaseChannel::SetMaxSendBandwidth(int max_bandwidth) {
173 SetBandwidthData data(max_bandwidth);
174 Send(MSG_SETMAXSENDBANDWIDTH, &data);
175 return data.result;
176 }
177
StartConnectionMonitor(int cms)178 void BaseChannel::StartConnectionMonitor(int cms) {
179 socket_monitor_.reset(new SocketMonitor(transport_channel_,
180 worker_thread(),
181 talk_base::Thread::Current()));
182 socket_monitor_->SignalUpdate.connect(
183 this, &BaseChannel::OnConnectionMonitorUpdate);
184 socket_monitor_->Start(cms);
185 }
186
StopConnectionMonitor()187 void BaseChannel::StopConnectionMonitor() {
188 if (socket_monitor_.get()) {
189 socket_monitor_->Stop();
190 socket_monitor_.reset();
191 }
192 }
193
set_rtcp_transport_channel(TransportChannel * channel)194 void BaseChannel::set_rtcp_transport_channel(TransportChannel* channel) {
195 if (rtcp_transport_channel_ != channel) {
196 if (rtcp_transport_channel_) {
197 session_->DestroyChannel(content_name_, rtcp_transport_channel_->name());
198 }
199 rtcp_transport_channel_ = channel;
200 if (rtcp_transport_channel_) {
201 rtcp_transport_channel_->SignalWritableState.connect(
202 this, &BaseChannel::OnWritableState);
203 rtcp_transport_channel_->SignalReadPacket.connect(
204 this, &BaseChannel::OnChannelRead);
205 }
206 }
207 }
208
SendPacket(talk_base::Buffer * packet)209 bool BaseChannel::SendPacket(talk_base::Buffer* packet) {
210 return SendPacket(false, packet);
211 }
212
SendRtcp(talk_base::Buffer * packet)213 bool BaseChannel::SendRtcp(talk_base::Buffer* packet) {
214 return SendPacket(true, packet);
215 }
216
SetOption(SocketType type,talk_base::Socket::Option opt,int value)217 int BaseChannel::SetOption(SocketType type, talk_base::Socket::Option opt,
218 int value) {
219 switch (type) {
220 case ST_RTP: return transport_channel_->SetOption(opt, value);
221 case ST_RTCP: return rtcp_transport_channel_->SetOption(opt, value);
222 default: return -1;
223 }
224 }
225
OnWritableState(TransportChannel * channel)226 void BaseChannel::OnWritableState(TransportChannel* channel) {
227 ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
228 if (transport_channel_->writable()
229 && (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) {
230 ChannelWritable_w();
231 } else {
232 ChannelNotWritable_w();
233 }
234 }
235
OnChannelRead(TransportChannel * channel,const char * data,size_t len)236 void BaseChannel::OnChannelRead(TransportChannel* channel,
237 const char* data, size_t len) {
238 // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine
239 ASSERT(worker_thread_ == talk_base::Thread::Current());
240
241 talk_base::Buffer packet(data, len);
242 // When using RTCP multiplexing we might get RTCP packets on the RTP
243 // transport. We feed RTP traffic into the demuxer to determine if it is RTCP.
244 bool rtcp = (channel == rtcp_transport_channel_ ||
245 rtcp_mux_filter_.DemuxRtcp(packet.data(), packet.length()));
246 HandlePacket(rtcp, &packet);
247 }
248
SendPacket(bool rtcp,talk_base::Buffer * packet)249 bool BaseChannel::SendPacket(bool rtcp, talk_base::Buffer* packet) {
250 // SendPacket gets called from MediaEngine, typically on an encoder thread.
251 // If the thread is not our worker thread, we will post to our worker
252 // so that the real work happens on our worker. This avoids us having to
253 // synchronize access to all the pieces of the send path, including
254 // SRTP and the inner workings of the transport channels.
255 // The only downside is that we can't return a proper failure code if
256 // needed. Since UDP is unreliable anyway, this should be a non-issue.
257 if (talk_base::Thread::Current() != worker_thread_) {
258 // Avoid a copy by transferring the ownership of the packet data.
259 int message_id = (!rtcp) ? MSG_RTPPACKET : MSG_RTCPPACKET;
260 PacketMessageData* data = new PacketMessageData;
261 packet->TransferTo(&data->packet);
262 worker_thread_->Post(this, message_id, data);
263 return true;
264 }
265
266 // Make sure we have a place to send this packet before doing anything.
267 // (We might get RTCP packets that we don't intend to send.)
268 // If we've negotiated RTCP mux, send RTCP over the RTP transport.
269 TransportChannel* channel = (!rtcp || rtcp_mux_filter_.IsActive()) ?
270 transport_channel_ : rtcp_transport_channel_;
271 if (!channel) {
272 return false;
273 }
274
275 // Protect ourselves against crazy data.
276 if (!ValidPacket(rtcp, packet)) {
277 LOG(LS_ERROR) << "Dropping outgoing " << content_name_ << " "
278 << PacketType(rtcp) << " packet: wrong size="
279 << packet->length();
280 return false;
281 }
282
283 // Push the packet down to the media sink.
284 // Need to do this before protecting the packet.
285 {
286 talk_base::CritScope cs(&sink_critical_section_);
287 if (sent_media_sink_) {
288 if (!rtcp) {
289 sent_media_sink_->OnRtpPacket(packet->data(), packet->length());
290 } else {
291 sent_media_sink_->OnRtcpPacket(packet->data(), packet->length());
292 }
293 }
294 }
295
296 // Protect if needed.
297 if (srtp_filter_.IsActive()) {
298 bool res;
299 char* data = packet->data();
300 int len = packet->length();
301 if (!rtcp) {
302 res = srtp_filter_.ProtectRtp(data, len, packet->capacity(), &len);
303 if (!res) {
304 LOG(LS_ERROR) << "Failed to protect " << content_name_
305 << " RTP packet: size=" << len
306 << ", seqnum=" << GetRtpSeqNum(packet)
307 << ", SSRC=" << GetRtpSsrc(packet);
308 return false;
309 }
310 } else {
311 res = srtp_filter_.ProtectRtcp(data, len, packet->capacity(), &len);
312 if (!res) {
313 LOG(LS_ERROR) << "Failed to protect " << content_name_
314 << " RTCP packet: size=" << len
315 << ", type=" << GetRtcpType(packet);
316 return false;
317 }
318 }
319
320 // Update the length of the packet now that we've added the auth tag.
321 packet->SetLength(len);
322 }
323
324 // Bon voyage.
325 return (channel->SendPacket(packet->data(), packet->length())
326 == static_cast<int>(packet->length()));
327 }
328
HandlePacket(bool rtcp,talk_base::Buffer * packet)329 void BaseChannel::HandlePacket(bool rtcp, talk_base::Buffer* packet) {
330 // Protect ourselvs against crazy data.
331 if (!ValidPacket(rtcp, packet)) {
332 LOG(LS_ERROR) << "Dropping incoming " << content_name_ << " "
333 << PacketType(rtcp) << " packet: wrong size="
334 << packet->length();
335 return;
336 }
337
338 // Unprotect the packet, if needed.
339 if (srtp_filter_.IsActive()) {
340 char* data = packet->data();
341 int len = packet->length();
342 bool res;
343 if (!rtcp) {
344 res = srtp_filter_.UnprotectRtp(data, len, &len);
345 if (!res) {
346 LOG(LS_ERROR) << "Failed to unprotect " << content_name_
347 << " RTP packet: size=" << len
348 << ", seqnum=" << GetRtpSeqNum(packet)
349 << ", SSRC=" << GetRtpSsrc(packet);
350 return;
351 }
352 } else {
353 res = srtp_filter_.UnprotectRtcp(data, len, &len);
354 if (!res) {
355 LOG(LS_ERROR) << "Failed to unprotect " << content_name_
356 << " RTCP packet: size=" << len
357 << ", type=" << GetRtcpType(packet);
358 return;
359 }
360 }
361
362 packet->SetLength(len);
363 }
364
365 // Push it down to the media channel.
366 if (!rtcp) {
367 media_channel_->OnPacketReceived(packet);
368 } else {
369 media_channel_->OnRtcpReceived(packet);
370 }
371
372 // Push it down to the media sink.
373 {
374 talk_base::CritScope cs(&sink_critical_section_);
375 if (received_media_sink_) {
376 if (!rtcp) {
377 received_media_sink_->OnRtpPacket(packet->data(), packet->length());
378 } else {
379 received_media_sink_->OnRtcpPacket(packet->data(), packet->length());
380 }
381 }
382 }
383 }
384
OnSessionState(BaseSession * session,BaseSession::State state)385 void BaseChannel::OnSessionState(BaseSession* session,
386 BaseSession::State state) {
387 const MediaContentDescription* content = NULL;
388 switch (state) {
389 case Session::STATE_SENTINITIATE:
390 content = GetFirstContent(session->local_description());
391 if (content && !SetLocalContent(content, CA_OFFER)) {
392 LOG(LS_ERROR) << "Failure in SetLocalContent with CA_OFFER";
393 session->SetError(BaseSession::ERROR_CONTENT);
394 }
395 break;
396 case Session::STATE_SENTACCEPT:
397 content = GetFirstContent(session->local_description());
398 if (content && !SetLocalContent(content, CA_ANSWER)) {
399 LOG(LS_ERROR) << "Failure in SetLocalContent with CA_ANSWER";
400 session->SetError(BaseSession::ERROR_CONTENT);
401 }
402 break;
403 case Session::STATE_RECEIVEDINITIATE:
404 content = GetFirstContent(session->remote_description());
405 if (content && !SetRemoteContent(content, CA_OFFER)) {
406 LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_OFFER";
407 session->SetError(BaseSession::ERROR_CONTENT);
408 }
409 break;
410 case Session::STATE_RECEIVEDACCEPT:
411 content = GetFirstContent(session->remote_description());
412 if (content && !SetRemoteContent(content, CA_ANSWER)) {
413 LOG(LS_ERROR) << "Failure in SetRemoteContent with CA_ANSWER";
414 session->SetError(BaseSession::ERROR_CONTENT);
415 }
416 break;
417 default:
418 break;
419 }
420 }
421
EnableMedia_w()422 void BaseChannel::EnableMedia_w() {
423 ASSERT(worker_thread_ == talk_base::Thread::Current());
424 if (enabled_)
425 return;
426
427 LOG(LS_INFO) << "Channel enabled";
428 enabled_ = true;
429 ChangeState();
430 }
431
DisableMedia_w()432 void BaseChannel::DisableMedia_w() {
433 ASSERT(worker_thread_ == talk_base::Thread::Current());
434 if (!enabled_)
435 return;
436
437 LOG(LS_INFO) << "Channel disabled";
438 enabled_ = false;
439 ChangeState();
440 }
441
MuteMedia_w()442 void BaseChannel::MuteMedia_w() {
443 ASSERT(worker_thread_ == talk_base::Thread::Current());
444 if (muted_)
445 return;
446
447 if (media_channel()->Mute(true)) {
448 LOG(LS_INFO) << "Channel muted";
449 muted_ = true;
450 }
451 }
452
UnmuteMedia_w()453 void BaseChannel::UnmuteMedia_w() {
454 ASSERT(worker_thread_ == talk_base::Thread::Current());
455 if (!muted_)
456 return;
457
458 if (media_channel()->Mute(false)) {
459 LOG(LS_INFO) << "Channel unmuted";
460 muted_ = false;
461 }
462 }
463
ChannelWritable_w()464 void BaseChannel::ChannelWritable_w() {
465 ASSERT(worker_thread_ == talk_base::Thread::Current());
466 if (writable_)
467 return;
468 LOG(LS_INFO) << "Channel socket writable ("
469 << transport_channel_->name().c_str() << ")";
470 writable_ = true;
471 ChangeState();
472 }
473
ChannelNotWritable_w()474 void BaseChannel::ChannelNotWritable_w() {
475 ASSERT(worker_thread_ == talk_base::Thread::Current());
476 if (!writable_)
477 return;
478
479 LOG(LS_INFO) << "Channel socket not writable ("
480 << transport_channel_->name().c_str() << ")";
481 writable_ = false;
482 ChangeState();
483 }
484
485 // Sets the maximum video bandwidth for automatic bandwidth adjustment.
SetMaxSendBandwidth_w(int max_bandwidth)486 bool BaseChannel::SetMaxSendBandwidth_w(int max_bandwidth) {
487 return media_channel()->SetSendBandwidth(true, max_bandwidth);
488 }
489
SetRtcpCName_w(const std::string & cname)490 bool BaseChannel::SetRtcpCName_w(const std::string& cname) {
491 return media_channel()->SetRtcpCName(cname);
492 }
493
SetSrtp_w(const std::vector<CryptoParams> & cryptos,ContentAction action,ContentSource src)494 bool BaseChannel::SetSrtp_w(const std::vector<CryptoParams>& cryptos,
495 ContentAction action, ContentSource src) {
496 bool ret;
497 if (action == CA_OFFER) {
498 ret = srtp_filter_.SetOffer(cryptos, src);
499 } else if (action == CA_ANSWER) {
500 ret = srtp_filter_.SetAnswer(cryptos, src);
501 } else {
502 // CA_UPDATE, no crypto params.
503 ret = true;
504 }
505 return ret;
506 }
507
SetRtcpMux_w(bool enable,ContentAction action,ContentSource src)508 bool BaseChannel::SetRtcpMux_w(bool enable, ContentAction action,
509 ContentSource src) {
510 bool ret;
511 if (action == CA_OFFER) {
512 ret = rtcp_mux_filter_.SetOffer(enable, src);
513 } else if (action == CA_ANSWER) {
514 ret = rtcp_mux_filter_.SetAnswer(enable, src);
515 if (ret && rtcp_mux_filter_.IsActive()) {
516 // We activated RTCP mux, close down the RTCP transport.
517 set_rtcp_transport_channel(NULL);
518 // If the RTP transport is already writable, then so are we.
519 if (transport_channel_->writable()) {
520 ChannelWritable_w();
521 }
522 }
523 } else {
524 // CA_UPDATE, no RTCP mux info.
525 ret = true;
526 }
527 return ret;
528 }
529
OnMessage(talk_base::Message * pmsg)530 void BaseChannel::OnMessage(talk_base::Message *pmsg) {
531 switch (pmsg->message_id) {
532 case MSG_ENABLE:
533 EnableMedia_w();
534 break;
535 case MSG_DISABLE:
536 DisableMedia_w();
537 break;
538
539 case MSG_MUTE:
540 MuteMedia_w();
541 break;
542 case MSG_UNMUTE:
543 UnmuteMedia_w();
544 break;
545
546 case MSG_SETRTCPCNAME: {
547 SetRtcpCNameData* data = static_cast<SetRtcpCNameData*>(pmsg->pdata);
548 data->result = SetRtcpCName_w(data->cname);
549 break;
550 }
551
552 case MSG_SETLOCALCONTENT: {
553 SetContentData* data = static_cast<SetContentData*>(pmsg->pdata);
554 data->result = SetLocalContent_w(data->content, data->action);
555 break;
556 }
557 case MSG_SETREMOTECONTENT: {
558 SetContentData* data = static_cast<SetContentData*>(pmsg->pdata);
559 data->result = SetRemoteContent_w(data->content, data->action);
560 break;
561 }
562
563 case MSG_REMOVESTREAM: {
564 StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
565 RemoveStream_w(data->ssrc1);
566 break;
567 }
568
569 case MSG_SETMAXSENDBANDWIDTH: {
570 SetBandwidthData* data = static_cast<SetBandwidthData*>(pmsg->pdata);
571 data->result = SetMaxSendBandwidth_w(data->value);
572 break;
573 }
574
575 case MSG_RTPPACKET:
576 case MSG_RTCPPACKET: {
577 PacketMessageData* data = static_cast<PacketMessageData*>(pmsg->pdata);
578 SendPacket(pmsg->message_id == MSG_RTCPPACKET, &data->packet);
579 delete data; // because it is Posted
580 break;
581 }
582 }
583 }
584
Send(uint32 id,talk_base::MessageData * pdata)585 void BaseChannel::Send(uint32 id, talk_base::MessageData *pdata) {
586 worker_thread_->Send(this, id, pdata);
587 }
588
Post(uint32 id,talk_base::MessageData * pdata)589 void BaseChannel::Post(uint32 id, talk_base::MessageData *pdata) {
590 worker_thread_->Post(this, id, pdata);
591 }
592
PostDelayed(int cmsDelay,uint32 id,talk_base::MessageData * pdata)593 void BaseChannel::PostDelayed(int cmsDelay, uint32 id,
594 talk_base::MessageData *pdata) {
595 worker_thread_->PostDelayed(cmsDelay, this, id, pdata);
596 }
597
Clear(uint32 id,talk_base::MessageList * removed)598 void BaseChannel::Clear(uint32 id, talk_base::MessageList* removed) {
599 worker_thread_->Clear(this, id, removed);
600 }
601
FlushRtcpMessages()602 void BaseChannel::FlushRtcpMessages() {
603 // Flush all remaining RTCP messages. This should only be called in
604 // destructor.
605 ASSERT(talk_base::Thread::Current() == worker_thread_);
606 talk_base::MessageList rtcp_messages;
607 Clear(MSG_RTCPPACKET, &rtcp_messages);
608 for (talk_base::MessageList::iterator it = rtcp_messages.begin();
609 it != rtcp_messages.end(); ++it) {
610 Send(MSG_RTCPPACKET, it->pdata);
611 }
612 }
613
VoiceChannel(talk_base::Thread * thread,MediaEngine * media_engine,VoiceMediaChannel * media_channel,BaseSession * session,const std::string & content_name,bool rtcp)614 VoiceChannel::VoiceChannel(talk_base::Thread* thread,
615 MediaEngine* media_engine,
616 VoiceMediaChannel* media_channel,
617 BaseSession* session,
618 const std::string& content_name,
619 bool rtcp)
620 : BaseChannel(thread, media_engine, media_channel, session, content_name,
621 session->CreateChannel(content_name, "rtp")),
622 received_media_(false) {
623 if (rtcp) {
624 set_rtcp_transport_channel(session->CreateChannel(content_name, "rtcp"));
625 }
626 // Can't go in BaseChannel because certain session states will
627 // trigger pure virtual functions, such as GetFirstContent().
628 OnSessionState(session, session->state());
629
630 media_channel->SignalMediaError.connect(
631 this, &VoiceChannel::OnVoiceChannelError);
632 }
633
~VoiceChannel()634 VoiceChannel::~VoiceChannel() {
635 StopAudioMonitor();
636 StopMediaMonitor();
637 // this can't be done in the base class, since it calls a virtual
638 DisableMedia_w();
639 }
640
AddStream(uint32 ssrc)641 bool VoiceChannel::AddStream(uint32 ssrc) {
642 StreamMessageData data(ssrc, 0);
643 Send(MSG_ADDSTREAM, &data);
644 return true;
645 }
646
SetRingbackTone(const void * buf,int len)647 bool VoiceChannel::SetRingbackTone(const void* buf, int len) {
648 SetRingbackToneMessageData data(buf, len);
649 Send(MSG_SETRINGBACKTONE, &data);
650 return true;
651 }
652
653 // TODO: Handle early media the right way. We should get an explicit
654 // ringing message telling us to start playing local ringback, which we cancel
655 // if any early media actually arrives. For now, we do the opposite, which is
656 // to wait 1 second for early media, and start playing local ringback if none
657 // arrives.
SetEarlyMedia(bool enable)658 void VoiceChannel::SetEarlyMedia(bool enable) {
659 if (enable) {
660 // Start the early media timeout
661 PostDelayed(kEarlyMediaTimeout, MSG_EARLYMEDIATIMEOUT);
662 } else {
663 // Stop the timeout if currently going.
664 Clear(MSG_EARLYMEDIATIMEOUT);
665 }
666 }
667
PlayRingbackTone(bool play,bool loop)668 bool VoiceChannel::PlayRingbackTone(bool play, bool loop) {
669 PlayRingbackToneMessageData data(play, loop);
670 Send(MSG_PLAYRINGBACKTONE, &data);
671 return data.result;
672 }
673
PressDTMF(int digit,bool playout)674 bool VoiceChannel::PressDTMF(int digit, bool playout) {
675 DtmfMessageData data(digit, playout);
676 Send(MSG_PRESSDTMF, &data);
677 return data.result;
678 }
679
StartMediaMonitor(int cms)680 void VoiceChannel::StartMediaMonitor(int cms) {
681 media_monitor_.reset(new VoiceMediaMonitor(media_channel(), worker_thread(),
682 talk_base::Thread::Current()));
683 media_monitor_->SignalUpdate.connect(
684 this, &VoiceChannel::OnMediaMonitorUpdate);
685 media_monitor_->Start(cms);
686 }
687
StopMediaMonitor()688 void VoiceChannel::StopMediaMonitor() {
689 if (media_monitor_.get()) {
690 media_monitor_->Stop();
691 media_monitor_->SignalUpdate.disconnect(this);
692 media_monitor_.reset();
693 }
694 }
695
StartAudioMonitor(int cms)696 void VoiceChannel::StartAudioMonitor(int cms) {
697 audio_monitor_.reset(new AudioMonitor(this, talk_base::Thread::Current()));
698 audio_monitor_
699 ->SignalUpdate.connect(this, &VoiceChannel::OnAudioMonitorUpdate);
700 audio_monitor_->Start(cms);
701 }
702
StopAudioMonitor()703 void VoiceChannel::StopAudioMonitor() {
704 if (audio_monitor_.get()) {
705 audio_monitor_->Stop();
706 audio_monitor_.reset();
707 }
708 }
709
GetInputLevel_w()710 int VoiceChannel::GetInputLevel_w() {
711 return media_engine()->GetInputLevel();
712 }
713
GetOutputLevel_w()714 int VoiceChannel::GetOutputLevel_w() {
715 return media_channel()->GetOutputLevel();
716 }
717
GetActiveStreams_w(AudioInfo::StreamList * actives)718 void VoiceChannel::GetActiveStreams_w(AudioInfo::StreamList* actives) {
719 media_channel()->GetActiveStreams(actives);
720 }
721
OnChannelRead(TransportChannel * channel,const char * data,size_t len)722 void VoiceChannel::OnChannelRead(TransportChannel* channel,
723 const char* data, size_t len) {
724 BaseChannel::OnChannelRead(channel, data, len);
725
726 // Set a flag when we've received an RTP packet. If we're waiting for early
727 // media, this will disable the timeout.
728 // If we were playing out our local ringback, make sure it is stopped to
729 // prevent it from interfering with the incoming media.
730 if (!received_media_) {
731 if (!PlayRingbackTone_w(false, false)) {
732 LOG(LS_ERROR) << "Failed to stop ringback tone.";
733 SendLastMediaError();
734 }
735 }
736 }
737
ChangeState()738 void VoiceChannel::ChangeState() {
739 // render incoming data if we are the active call
740 // we receive data on the default channel and multiplexed streams
741 bool recv = enabled();
742 if (!media_channel()->SetPlayout(recv)) {
743 SendLastMediaError();
744 }
745
746 // send outgoing data if we are the active call, have the
747 // remote party's codec, and have a writable transport
748 // we only send data on the default channel
749 bool send = enabled() && has_codec() && writable();
750 SendFlags send_flag = send ? SEND_MICROPHONE : SEND_NOTHING;
751 if (!media_channel()->SetSend(send_flag)) {
752 LOG(LS_ERROR) << "Failed to SetSend " << send_flag << " on voice channel";
753 SendLastMediaError();
754 }
755
756 LOG(LS_INFO) << "Changing voice state, recv=" << recv << " send=" << send;
757 }
758
GetFirstContent(const SessionDescription * sdesc)759 const MediaContentDescription* VoiceChannel::GetFirstContent(
760 const SessionDescription* sdesc) {
761 const ContentInfo* cinfo = GetFirstAudioContent(sdesc);
762 if (cinfo == NULL)
763 return NULL;
764
765 return static_cast<const MediaContentDescription*>(cinfo->description);
766 }
767
SetLocalContent_w(const MediaContentDescription * content,ContentAction action)768 bool VoiceChannel::SetLocalContent_w(const MediaContentDescription* content,
769 ContentAction action) {
770 ASSERT(worker_thread() == talk_base::Thread::Current());
771 LOG(LS_INFO) << "Setting local voice description";
772
773 const AudioContentDescription* audio =
774 static_cast<const AudioContentDescription*>(content);
775 ASSERT(audio != NULL);
776
777 bool ret;
778 // set SRTP
779 ret = SetSrtp_w(audio->cryptos(), action, CS_LOCAL);
780 // set RTCP mux
781 if (ret) {
782 ret = SetRtcpMux_w(audio->rtcp_mux(), action, CS_LOCAL);
783 }
784 // set payload type and config for voice codecs
785 if (ret) {
786 ret = media_channel()->SetRecvCodecs(audio->codecs());
787 }
788 return ret;
789 }
790
SetRemoteContent_w(const MediaContentDescription * content,ContentAction action)791 bool VoiceChannel::SetRemoteContent_w(const MediaContentDescription* content,
792 ContentAction action) {
793 ASSERT(worker_thread() == talk_base::Thread::Current());
794 LOG(LS_INFO) << "Setting remote voice description";
795
796 const AudioContentDescription* audio =
797 static_cast<const AudioContentDescription*>(content);
798 ASSERT(audio != NULL);
799
800 bool ret;
801 // set the sending SSRC, if the remote side gave us one
802 if (audio->ssrc_set()) {
803 media_channel()->SetSendSsrc(audio->ssrc());
804 }
805 // set SRTP
806 ret = SetSrtp_w(audio->cryptos(), action, CS_REMOTE);
807 // set RTCP mux
808 if (ret) {
809 ret = SetRtcpMux_w(audio->rtcp_mux(), action, CS_REMOTE);
810 }
811 // set codecs and payload types
812 if (ret) {
813 ret = media_channel()->SetSendCodecs(audio->codecs());
814 }
815
816 int audio_options = 0;
817 if (audio->conference_mode()) {
818 audio_options |= OPT_CONFERENCE;
819 }
820 if (!media_channel()->SetOptions(audio_options)) {
821 // Log an error on failure, but don't abort the call.
822 LOG(LS_ERROR) << "Failed to set voice channel options";
823 }
824
825 // update state
826 if (ret) {
827 set_has_codec(true);
828 ChangeState();
829 }
830 return ret;
831 }
832
AddStream_w(uint32 ssrc)833 void VoiceChannel::AddStream_w(uint32 ssrc) {
834 ASSERT(worker_thread() == talk_base::Thread::Current());
835 media_channel()->AddStream(ssrc);
836 }
837
RemoveStream_w(uint32 ssrc)838 void VoiceChannel::RemoveStream_w(uint32 ssrc) {
839 media_channel()->RemoveStream(ssrc);
840 }
841
SetRingbackTone_w(const void * buf,int len)842 void VoiceChannel::SetRingbackTone_w(const void* buf, int len) {
843 ASSERT(worker_thread() == talk_base::Thread::Current());
844 media_channel()->SetRingbackTone(static_cast<const char*>(buf), len);
845 }
846
PlayRingbackTone_w(bool play,bool loop)847 bool VoiceChannel::PlayRingbackTone_w(bool play, bool loop) {
848 ASSERT(worker_thread() == talk_base::Thread::Current());
849 if (play) {
850 LOG(LS_INFO) << "Playing ringback tone, loop=" << loop;
851 } else {
852 LOG(LS_INFO) << "Stopping ringback tone";
853 }
854 return media_channel()->PlayRingbackTone(play, loop);
855 }
856
HandleEarlyMediaTimeout()857 void VoiceChannel::HandleEarlyMediaTimeout() {
858 // This occurs on the main thread, not the worker thread.
859 if (!received_media_) {
860 LOG(LS_INFO) << "No early media received before timeout";
861 SignalEarlyMediaTimeout(this);
862 }
863 }
864
PressDTMF_w(int digit,bool playout)865 bool VoiceChannel::PressDTMF_w(int digit, bool playout) {
866 if (!enabled() || !writable()) {
867 return false;
868 }
869
870 return media_channel()->PressDTMF(digit, playout);
871 }
872
OnMessage(talk_base::Message * pmsg)873 void VoiceChannel::OnMessage(talk_base::Message *pmsg) {
874 switch (pmsg->message_id) {
875 case MSG_ADDSTREAM: {
876 StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
877 AddStream_w(data->ssrc1);
878 break;
879 }
880 case MSG_SETRINGBACKTONE: {
881 SetRingbackToneMessageData* data =
882 static_cast<SetRingbackToneMessageData*>(pmsg->pdata);
883 SetRingbackTone_w(data->buf, data->len);
884 break;
885 }
886 case MSG_PLAYRINGBACKTONE: {
887 PlayRingbackToneMessageData* data =
888 static_cast<PlayRingbackToneMessageData*>(pmsg->pdata);
889 data->result = PlayRingbackTone_w(data->play, data->loop);
890 break;
891 }
892 case MSG_EARLYMEDIATIMEOUT:
893 HandleEarlyMediaTimeout();
894 break;
895 case MSG_PRESSDTMF: {
896 DtmfMessageData* data = static_cast<DtmfMessageData*>(pmsg->pdata);
897 data->result = PressDTMF_w(data->digit, data->playout);
898 break;
899 }
900 case MSG_CHANNEL_ERROR: {
901 VoiceChannelErrorMessageData* data =
902 static_cast<VoiceChannelErrorMessageData*>(pmsg->pdata);
903 SignalMediaError(this, data->ssrc, data->error);
904 delete data;
905 break;
906 }
907
908 default:
909 BaseChannel::OnMessage(pmsg);
910 break;
911 }
912 }
913
OnConnectionMonitorUpdate(SocketMonitor * monitor,const std::vector<ConnectionInfo> & infos)914 void VoiceChannel::OnConnectionMonitorUpdate(
915 SocketMonitor* monitor, const std::vector<ConnectionInfo>& infos) {
916 SignalConnectionMonitor(this, infos);
917 }
918
OnMediaMonitorUpdate(VoiceMediaChannel * media_channel,const VoiceMediaInfo & info)919 void VoiceChannel::OnMediaMonitorUpdate(
920 VoiceMediaChannel* media_channel, const VoiceMediaInfo& info) {
921 ASSERT(media_channel == this->media_channel());
922 SignalMediaMonitor(this, info);
923 }
924
OnAudioMonitorUpdate(AudioMonitor * monitor,const AudioInfo & info)925 void VoiceChannel::OnAudioMonitorUpdate(AudioMonitor* monitor,
926 const AudioInfo& info) {
927 SignalAudioMonitor(this, info);
928 }
929
OnVoiceChannelError(uint32 ssrc,VoiceMediaChannel::Error error)930 void VoiceChannel::OnVoiceChannelError(
931 uint32 ssrc, VoiceMediaChannel::Error error) {
932 VoiceChannelErrorMessageData *data = new VoiceChannelErrorMessageData(
933 ssrc, error);
934 signaling_thread()->Post(this, MSG_CHANNEL_ERROR, data);
935 }
936
VideoChannel(talk_base::Thread * thread,MediaEngine * media_engine,VideoMediaChannel * media_channel,BaseSession * session,const std::string & content_name,bool rtcp,VoiceChannel * voice_channel)937 VideoChannel::VideoChannel(talk_base::Thread* thread,
938 MediaEngine* media_engine,
939 VideoMediaChannel* media_channel,
940 BaseSession* session,
941 const std::string& content_name,
942 bool rtcp,
943 VoiceChannel* voice_channel)
944 : BaseChannel(thread, media_engine, media_channel, session, content_name,
945 session->CreateChannel(content_name, "video_rtp")),
946 voice_channel_(voice_channel), renderer_(NULL) {
947 if (rtcp) {
948 set_rtcp_transport_channel(
949 session->CreateChannel(content_name, "video_rtcp"));
950 }
951 // Can't go in BaseChannel because certain session states will
952 // trigger pure virtual functions, such as GetFirstContent()
953 OnSessionState(session, session->state());
954
955 media_channel->SignalMediaError.connect(
956 this, &VideoChannel::OnVideoChannelError);
957 }
958
SendLastMediaError()959 void VoiceChannel::SendLastMediaError() {
960 uint32 ssrc;
961 VoiceMediaChannel::Error error;
962 media_channel()->GetLastMediaError(&ssrc, &error);
963 SignalMediaError(this, ssrc, error);
964 }
965
~VideoChannel()966 VideoChannel::~VideoChannel() {
967 StopMediaMonitor();
968 // this can't be done in the base class, since it calls a virtual
969 DisableMedia_w();
970 }
971
AddStream(uint32 ssrc,uint32 voice_ssrc)972 bool VideoChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) {
973 StreamMessageData data(ssrc, voice_ssrc);
974 Send(MSG_ADDSTREAM, &data);
975 return true;
976 }
977
SetRenderer(uint32 ssrc,VideoRenderer * renderer)978 bool VideoChannel::SetRenderer(uint32 ssrc, VideoRenderer* renderer) {
979 RenderMessageData data(ssrc, renderer);
980 Send(MSG_SETRENDERER, &data);
981 return true;
982 }
983
984
985
SendIntraFrame()986 bool VideoChannel::SendIntraFrame() {
987 Send(MSG_SENDINTRAFRAME);
988 return true;
989 }
RequestIntraFrame()990 bool VideoChannel::RequestIntraFrame() {
991 Send(MSG_REQUESTINTRAFRAME);
992 return true;
993 }
994
ChangeState()995 void VideoChannel::ChangeState() {
996 // render incoming data if we are the active call
997 // we receive data on the default channel and multiplexed streams
998 bool recv = enabled();
999 if (!media_channel()->SetRender(recv)) {
1000 LOG(LS_ERROR) << "Failed to SetRender on video channel";
1001 // TODO: Report error back to server.
1002 }
1003
1004 // send outgoing data if we are the active call, have the
1005 // remote party's codec, and have a writable transport
1006 // we only send data on the default channel
1007 bool send = enabled() && has_codec() && writable();
1008 if (!media_channel()->SetSend(send)) {
1009 LOG(LS_ERROR) << "Failed to SetSend on video channel";
1010 // TODO: Report error back to server.
1011 }
1012
1013 LOG(LS_INFO) << "Changing video state, recv=" << recv << " send=" << send;
1014 }
1015
StartMediaMonitor(int cms)1016 void VideoChannel::StartMediaMonitor(int cms) {
1017 media_monitor_.reset(new VideoMediaMonitor(media_channel(), worker_thread(),
1018 talk_base::Thread::Current()));
1019 media_monitor_->SignalUpdate.connect(
1020 this, &VideoChannel::OnMediaMonitorUpdate);
1021 media_monitor_->Start(cms);
1022 }
1023
StopMediaMonitor()1024 void VideoChannel::StopMediaMonitor() {
1025 if (media_monitor_.get()) {
1026 media_monitor_->Stop();
1027 media_monitor_.reset();
1028 }
1029 }
1030
GetFirstContent(const SessionDescription * sdesc)1031 const MediaContentDescription* VideoChannel::GetFirstContent(
1032 const SessionDescription* sdesc) {
1033 const ContentInfo* cinfo = GetFirstVideoContent(sdesc);
1034 if (cinfo == NULL)
1035 return NULL;
1036
1037 return static_cast<const MediaContentDescription*>(cinfo->description);
1038 }
1039
SetLocalContent_w(const MediaContentDescription * content,ContentAction action)1040 bool VideoChannel::SetLocalContent_w(const MediaContentDescription* content,
1041 ContentAction action) {
1042 ASSERT(worker_thread() == talk_base::Thread::Current());
1043 LOG(LS_INFO) << "Setting local video description";
1044
1045 const VideoContentDescription* video =
1046 static_cast<const VideoContentDescription*>(content);
1047 ASSERT(video != NULL);
1048
1049 bool ret;
1050 // set SRTP
1051 ret = SetSrtp_w(video->cryptos(), action, CS_LOCAL);
1052 // set RTCP mux
1053 if (ret) {
1054 ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_LOCAL);
1055 }
1056 // set payload types and config for receiving video
1057 if (ret) {
1058 ret = media_channel()->SetRecvCodecs(video->codecs());
1059 }
1060 return ret;
1061 }
1062
SetRemoteContent_w(const MediaContentDescription * content,ContentAction action)1063 bool VideoChannel::SetRemoteContent_w(const MediaContentDescription* content,
1064 ContentAction action) {
1065 ASSERT(worker_thread() == talk_base::Thread::Current());
1066 LOG(LS_INFO) << "Setting remote video description";
1067
1068 const VideoContentDescription* video =
1069 static_cast<const VideoContentDescription*>(content);
1070 ASSERT(video != NULL);
1071
1072 bool ret;
1073 // set the sending SSRC, if the remote side gave us one
1074 // TODO: remove this, since it's not needed.
1075 if (video->ssrc_set()) {
1076 media_channel()->SetSendSsrc(video->ssrc());
1077 }
1078 // set SRTP
1079 ret = SetSrtp_w(video->cryptos(), action, CS_REMOTE);
1080 // set RTCP mux
1081 if (ret) {
1082 ret = SetRtcpMux_w(video->rtcp_mux(), action, CS_REMOTE);
1083 }
1084 // Set video bandwidth parameters.
1085 if (ret) {
1086 int bandwidth_bps = video->bandwidth();
1087 bool auto_bandwidth = (bandwidth_bps == kAutoBandwidth);
1088 ret = media_channel()->SetSendBandwidth(auto_bandwidth, bandwidth_bps);
1089 }
1090 if (ret) {
1091 ret = media_channel()->SetSendCodecs(video->codecs());
1092 }
1093 media_channel()->SetRtpExtensionHeaders(!video->rtp_headers_disabled());
1094 if (ret) {
1095 set_has_codec(true);
1096 ChangeState();
1097 }
1098 return ret;
1099 }
1100
AddStream_w(uint32 ssrc,uint32 voice_ssrc)1101 void VideoChannel::AddStream_w(uint32 ssrc, uint32 voice_ssrc) {
1102 media_channel()->AddStream(ssrc, voice_ssrc);
1103 }
1104
RemoveStream_w(uint32 ssrc)1105 void VideoChannel::RemoveStream_w(uint32 ssrc) {
1106 media_channel()->RemoveStream(ssrc);
1107 }
1108
SetRenderer_w(uint32 ssrc,VideoRenderer * renderer)1109 void VideoChannel::SetRenderer_w(uint32 ssrc, VideoRenderer* renderer) {
1110 media_channel()->SetRenderer(ssrc, renderer);
1111 }
1112
1113
OnMessage(talk_base::Message * pmsg)1114 void VideoChannel::OnMessage(talk_base::Message *pmsg) {
1115 switch (pmsg->message_id) {
1116 case MSG_ADDSTREAM: {
1117 StreamMessageData* data = static_cast<StreamMessageData*>(pmsg->pdata);
1118 AddStream_w(data->ssrc1, data->ssrc2);
1119 break;
1120 }
1121 case MSG_SETRENDERER: {
1122 RenderMessageData* data = static_cast<RenderMessageData*>(pmsg->pdata);
1123 SetRenderer_w(data->ssrc, data->renderer);
1124 break;
1125 }
1126 case MSG_SENDINTRAFRAME:
1127 SendIntraFrame_w();
1128 break;
1129 case MSG_REQUESTINTRAFRAME:
1130 RequestIntraFrame_w();
1131 break;
1132 case MSG_CHANNEL_ERROR: {
1133 const VideoChannelErrorMessageData* data =
1134 static_cast<VideoChannelErrorMessageData*>(pmsg->pdata);
1135 SignalMediaError(this, data->ssrc, data->error);
1136 delete data;
1137 break;
1138 }
1139 default:
1140 BaseChannel::OnMessage(pmsg);
1141 break;
1142 }
1143 }
1144
OnConnectionMonitorUpdate(SocketMonitor * monitor,const std::vector<ConnectionInfo> & infos)1145 void VideoChannel::OnConnectionMonitorUpdate(
1146 SocketMonitor *monitor, const std::vector<ConnectionInfo> &infos) {
1147 SignalConnectionMonitor(this, infos);
1148 }
1149
OnMediaMonitorUpdate(VideoMediaChannel * media_channel,const VideoMediaInfo & info)1150 void VideoChannel::OnMediaMonitorUpdate(
1151 VideoMediaChannel* media_channel, const VideoMediaInfo &info) {
1152 ASSERT(media_channel == this->media_channel());
1153 SignalMediaMonitor(this, info);
1154 }
1155
1156
OnVideoChannelError(uint32 ssrc,VideoMediaChannel::Error error)1157 void VideoChannel::OnVideoChannelError(uint32 ssrc,
1158 VideoMediaChannel::Error error) {
1159 VideoChannelErrorMessageData* data = new VideoChannelErrorMessageData(
1160 ssrc, error);
1161 signaling_thread()->Post(this, MSG_CHANNEL_ERROR, data);
1162 }
1163
1164 } // namespace cricket
1165