OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/media/sctp/sctpdataengine.h" | 11 #include "webrtc/media/sctp/sctpdataengine.h" |
12 | 12 |
13 #include <stdarg.h> | 13 #include <stdarg.h> |
14 #include <stdio.h> | 14 #include <stdio.h> |
15 | 15 |
16 #include <memory> | 16 #include <memory> |
17 #include <sstream> | 17 #include <sstream> |
18 #include <vector> | 18 #include <vector> |
19 | 19 |
20 #include "usrsctplib/usrsctp.h" | 20 #include "usrsctplib/usrsctp.h" |
21 #include "webrtc/base/arraysize.h" | 21 #include "webrtc/base/arraysize.h" |
22 #include "webrtc/base/buffer.h" | 22 #include "webrtc/base/copyonwritebuffer.h" |
23 #include "webrtc/base/helpers.h" | 23 #include "webrtc/base/helpers.h" |
24 #include "webrtc/base/logging.h" | 24 #include "webrtc/base/logging.h" |
25 #include "webrtc/base/safe_conversions.h" | 25 #include "webrtc/base/safe_conversions.h" |
26 #include "webrtc/media/base/codec.h" | 26 #include "webrtc/media/base/codec.h" |
27 #include "webrtc/media/base/mediaconstants.h" | 27 #include "webrtc/media/base/mediaconstants.h" |
28 #include "webrtc/media/base/streamparams.h" | 28 #include "webrtc/media/base/streamparams.h" |
29 | 29 |
30 namespace { | 30 namespace { |
31 typedef cricket::SctpDataMediaChannel::StreamSet StreamSet; | 31 typedef cricket::SctpDataMediaChannel::StreamSet StreamSet; |
32 // Returns a comma-separated, human-readable list of the stream IDs in 's' | 32 // Returns a comma-separated, human-readable list of the stream IDs in 's' |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
82 } else { | 82 } else { |
83 result << array[i]; | 83 result << array[i]; |
84 } | 84 } |
85 } | 85 } |
86 return result.str(); | 86 return result.str(); |
87 } | 87 } |
88 } // namespace | 88 } // namespace |
89 | 89 |
90 namespace cricket { | 90 namespace cricket { |
91 typedef rtc::ScopedMessageData<SctpInboundPacket> InboundPacketMessage; | 91 typedef rtc::ScopedMessageData<SctpInboundPacket> InboundPacketMessage; |
92 typedef rtc::ScopedMessageData<rtc::Buffer> OutboundPacketMessage; | 92 typedef rtc::ScopedMessageData<rtc::CopyOnWriteBuffer> OutboundPacketMessage; |
93 | 93 |
94 // The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280, | 94 // The biggest SCTP packet. Starting from a 'safe' wire MTU value of 1280, |
95 // take off 80 bytes for DTLS/TURN/TCP/IP overhead. | 95 // take off 80 bytes for DTLS/TURN/TCP/IP overhead. |
96 static const size_t kSctpMtu = 1200; | 96 static const size_t kSctpMtu = 1200; |
97 | 97 |
98 // The size of the SCTP association send buffer. 256kB, the usrsctp default. | 98 // The size of the SCTP association send buffer. 256kB, the usrsctp default. |
99 static const int kSendBufferSize = 262144; | 99 static const int kSendBufferSize = 262144; |
100 enum { | 100 enum { |
101 MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket | 101 MSG_SCTPINBOUNDPACKET = 1, // MessageData is SctpInboundPacket |
102 MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is rtc:Buffer | 102 MSG_SCTPOUTBOUNDPACKET = 2, // MessageData is rtc:Buffer |
103 }; | 103 }; |
104 | 104 |
105 struct SctpInboundPacket { | 105 struct SctpInboundPacket { |
106 rtc::Buffer buffer; | 106 rtc::CopyOnWriteBuffer buffer; |
107 ReceiveDataParams params; | 107 ReceiveDataParams params; |
108 // The |flags| parameter is used by SCTP to distinguish notification packets | 108 // The |flags| parameter is used by SCTP to distinguish notification packets |
109 // from other types of packets. | 109 // from other types of packets. |
110 int flags; | 110 int flags; |
111 }; | 111 }; |
112 | 112 |
113 // Helper for logging SCTP messages. | 113 // Helper for logging SCTP messages. |
114 static void debug_sctp_printf(const char *format, ...) { | 114 static void debug_sctp_printf(const char *format, ...) { |
115 char s[255]; | 115 char s[255]; |
116 va_list ap; | 116 va_list ap; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
158 case SctpDataMediaChannel::PPID_NONE: | 158 case SctpDataMediaChannel::PPID_NONE: |
159 *dest = cricket::DMT_NONE; | 159 *dest = cricket::DMT_NONE; |
160 return true; | 160 return true; |
161 | 161 |
162 default: | 162 default: |
163 return false; | 163 return false; |
164 } | 164 } |
165 } | 165 } |
166 | 166 |
167 // Log the packet in text2pcap format, if log level is at LS_VERBOSE. | 167 // Log the packet in text2pcap format, if log level is at LS_VERBOSE. |
168 static void VerboseLogPacket(void *data, size_t length, int direction) { | 168 static void VerboseLogPacket(const void *data, size_t length, int direction) { |
169 if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) { | 169 if (LOG_CHECK_LEVEL(LS_VERBOSE) && length > 0) { |
170 char *dump_buf; | 170 char *dump_buf; |
171 // Some downstream project uses an older version of usrsctp that expects | |
172 // a non-const "void*" as first parameter when dumping the packet, so we | |
173 // need to cast the const away here to avoid a compiler error. | |
171 if ((dump_buf = usrsctp_dumppacket( | 174 if ((dump_buf = usrsctp_dumppacket( |
172 data, length, direction)) != NULL) { | 175 const_cast<void*>(data), length, direction)) != NULL) { |
joachim
2016/03/20 10:14:10
This is the change compared to https://codereview.
| |
173 LOG(LS_VERBOSE) << dump_buf; | 176 LOG(LS_VERBOSE) << dump_buf; |
174 usrsctp_freedumpbuffer(dump_buf); | 177 usrsctp_freedumpbuffer(dump_buf); |
175 } | 178 } |
176 } | 179 } |
177 } | 180 } |
178 | 181 |
179 // This is the callback usrsctp uses when there's data to send on the network | 182 // This is the callback usrsctp uses when there's data to send on the network |
180 // that has been wrapped appropriatly for the SCTP protocol. | 183 // that has been wrapped appropriatly for the SCTP protocol. |
181 static int OnSctpOutboundPacket(void* addr, void* data, size_t length, | 184 static int OnSctpOutboundPacket(void* addr, void* data, size_t length, |
182 uint8_t tos, uint8_t set_df) { | 185 uint8_t tos, uint8_t set_df) { |
183 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr); | 186 SctpDataMediaChannel* channel = static_cast<SctpDataMediaChannel*>(addr); |
184 LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():" | 187 LOG(LS_VERBOSE) << "global OnSctpOutboundPacket():" |
185 << "addr: " << addr << "; length: " << length | 188 << "addr: " << addr << "; length: " << length |
186 << "; tos: " << std::hex << static_cast<int>(tos) | 189 << "; tos: " << std::hex << static_cast<int>(tos) |
187 << "; set_df: " << std::hex << static_cast<int>(set_df); | 190 << "; set_df: " << std::hex << static_cast<int>(set_df); |
188 | 191 |
189 VerboseLogPacket(addr, length, SCTP_DUMP_OUTBOUND); | 192 VerboseLogPacket(addr, length, SCTP_DUMP_OUTBOUND); |
190 // Note: We have to copy the data; the caller will delete it. | 193 // Note: We have to copy the data; the caller will delete it. |
191 auto* msg = new OutboundPacketMessage( | 194 auto* msg = new OutboundPacketMessage( |
192 new rtc::Buffer(reinterpret_cast<uint8_t*>(data), length)); | 195 new rtc::CopyOnWriteBuffer(reinterpret_cast<uint8_t*>(data), length)); |
193 channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, msg); | 196 channel->worker_thread()->Post(channel, MSG_SCTPOUTBOUNDPACKET, msg); |
194 return 0; | 197 return 0; |
195 } | 198 } |
196 | 199 |
197 // This is the callback called from usrsctp when data has been received, after | 200 // This is the callback called from usrsctp when data has been received, after |
198 // a packet has been interpreted and parsed by usrsctp and found to contain | 201 // a packet has been interpreted and parsed by usrsctp and found to contain |
199 // payload data. It is called by a usrsctp thread. It is assumed this function | 202 // payload data. It is called by a usrsctp thread. It is assumed this function |
200 // will free the memory used by 'data'. | 203 // will free the memory used by 'data'. |
201 static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr, | 204 static int OnSctpInboundPacket(struct socket* sock, union sctp_sockstore addr, |
202 void* data, size_t length, | 205 void* data, size_t length, |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
574 | 577 |
575 bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) { | 578 bool SctpDataMediaChannel::RemoveRecvStream(uint32_t ssrc) { |
576 // SCTP DataChannels are always bi-directional and calling RemoveSendStream | 579 // SCTP DataChannels are always bi-directional and calling RemoveSendStream |
577 // will disable both sending and receiving on the stream. So RemoveRecvStream | 580 // will disable both sending and receiving on the stream. So RemoveRecvStream |
578 // is a no-op. | 581 // is a no-op. |
579 return true; | 582 return true; |
580 } | 583 } |
581 | 584 |
582 bool SctpDataMediaChannel::SendData( | 585 bool SctpDataMediaChannel::SendData( |
583 const SendDataParams& params, | 586 const SendDataParams& params, |
584 const rtc::Buffer& payload, | 587 const rtc::CopyOnWriteBuffer& payload, |
585 SendDataResult* result) { | 588 SendDataResult* result) { |
586 if (result) { | 589 if (result) { |
587 // Preset |result| to assume an error. If SendData succeeds, we'll | 590 // Preset |result| to assume an error. If SendData succeeds, we'll |
588 // overwrite |*result| once more at the end. | 591 // overwrite |*result| once more at the end. |
589 *result = SDR_ERROR; | 592 *result = SDR_ERROR; |
590 } | 593 } |
591 | 594 |
592 if (!sending_) { | 595 if (!sending_) { |
593 LOG(LS_WARNING) << debug_name_ << "->SendData(...): " | 596 LOG(LS_WARNING) << debug_name_ << "->SendData(...): " |
594 << "Not sending packet with ssrc=" << params.ssrc | 597 << "Not sending packet with ssrc=" << params.ssrc |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
644 } | 647 } |
645 if (result) { | 648 if (result) { |
646 // Only way out now is success. | 649 // Only way out now is success. |
647 *result = SDR_SUCCESS; | 650 *result = SDR_SUCCESS; |
648 } | 651 } |
649 return true; | 652 return true; |
650 } | 653 } |
651 | 654 |
652 // Called by network interface when a packet has been received. | 655 // Called by network interface when a packet has been received. |
653 void SctpDataMediaChannel::OnPacketReceived( | 656 void SctpDataMediaChannel::OnPacketReceived( |
654 rtc::Buffer* packet, const rtc::PacketTime& packet_time) { | 657 rtc::CopyOnWriteBuffer* packet, const rtc::PacketTime& packet_time) { |
655 RTC_DCHECK(rtc::Thread::Current() == worker_thread_); | 658 RTC_DCHECK(rtc::Thread::Current() == worker_thread_); |
656 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " | 659 LOG(LS_VERBOSE) << debug_name_ << "->OnPacketReceived(...): " |
657 << " length=" << packet->size() << ", sending: " << sending_; | 660 << " length=" << packet->size() << ", sending: " << sending_; |
658 // Only give receiving packets to usrsctp after if connected. This enables two | 661 // Only give receiving packets to usrsctp after if connected. This enables two |
659 // peers to each make a connect call, but for them not to receive an INIT | 662 // peers to each make a connect call, but for them not to receive an INIT |
660 // packet before they have called connect; least the last receiver of the INIT | 663 // packet before they have called connect; least the last receiver of the INIT |
661 // packet will have called connect, and a connection will be established. | 664 // packet will have called connect, and a connection will be established. |
662 if (sending_) { | 665 if (sending_) { |
663 // Pass received packet to SCTP stack. Once processed by usrsctp, the data | 666 // Pass received packet to SCTP stack. Once processed by usrsctp, the data |
664 // will be will be given to the global OnSctpInboundData, and then, | 667 // will be will be given to the global OnSctpInboundData, and then, |
665 // marshalled by a Post and handled with OnMessage. | 668 // marshalled by a Post and handled with OnMessage. |
666 VerboseLogPacket(packet->data(), packet->size(), SCTP_DUMP_INBOUND); | 669 VerboseLogPacket(packet->cdata(), packet->size(), SCTP_DUMP_INBOUND); |
667 usrsctp_conninput(this, packet->data(), packet->size(), 0); | 670 usrsctp_conninput(this, packet->cdata(), packet->size(), 0); |
668 } else { | 671 } else { |
669 // TODO(ldixon): Consider caching the packet for very slightly better | 672 // TODO(ldixon): Consider caching the packet for very slightly better |
670 // reliability. | 673 // reliability. |
671 } | 674 } |
672 } | 675 } |
673 | 676 |
674 void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel( | 677 void SctpDataMediaChannel::OnInboundPacketFromSctpToChannel( |
675 SctpInboundPacket* packet) { | 678 SctpInboundPacket* packet) { |
676 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " | 679 LOG(LS_VERBOSE) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " |
677 << "Received SCTP data:" | 680 << "Received SCTP data:" |
678 << " ssrc=" << packet->params.ssrc | 681 << " ssrc=" << packet->params.ssrc |
679 << " notification: " << (packet->flags & MSG_NOTIFICATION) | 682 << " notification: " << (packet->flags & MSG_NOTIFICATION) |
680 << " length=" << packet->buffer.size(); | 683 << " length=" << packet->buffer.size(); |
681 // Sending a packet with data == NULL (no data) is SCTPs "close the | 684 // Sending a packet with data == NULL (no data) is SCTPs "close the |
682 // connection" message. This sets sock_ = NULL; | 685 // connection" message. This sets sock_ = NULL; |
683 if (!packet->buffer.size() || !packet->buffer.data()) { | 686 if (!packet->buffer.size() || !packet->buffer.data()) { |
684 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " | 687 LOG(LS_INFO) << debug_name_ << "->OnInboundPacketFromSctpToChannel(...): " |
685 "No data, closing."; | 688 "No data, closing."; |
686 return; | 689 return; |
687 } | 690 } |
688 if (packet->flags & MSG_NOTIFICATION) { | 691 if (packet->flags & MSG_NOTIFICATION) { |
689 OnNotificationFromSctp(&packet->buffer); | 692 OnNotificationFromSctp(packet->buffer); |
690 } else { | 693 } else { |
691 OnDataFromSctpToChannel(packet->params, &packet->buffer); | 694 OnDataFromSctpToChannel(packet->params, packet->buffer); |
692 } | 695 } |
693 } | 696 } |
694 | 697 |
695 void SctpDataMediaChannel::OnDataFromSctpToChannel( | 698 void SctpDataMediaChannel::OnDataFromSctpToChannel( |
696 const ReceiveDataParams& params, rtc::Buffer* buffer) { | 699 const ReceiveDataParams& params, const rtc::CopyOnWriteBuffer& buffer) { |
697 if (receiving_) { | 700 if (receiving_) { |
698 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): " | 701 LOG(LS_VERBOSE) << debug_name_ << "->OnDataFromSctpToChannel(...): " |
699 << "Posting with length: " << buffer->size() | 702 << "Posting with length: " << buffer.size() |
700 << " on stream " << params.ssrc; | 703 << " on stream " << params.ssrc; |
701 // Reports all received messages to upper layers, no matter whether the sid | 704 // Reports all received messages to upper layers, no matter whether the sid |
702 // is known. | 705 // is known. |
703 SignalDataReceived(params, buffer->data<char>(), buffer->size()); | 706 SignalDataReceived(params, buffer.data<char>(), buffer.size()); |
704 } else { | 707 } else { |
705 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): " | 708 LOG(LS_WARNING) << debug_name_ << "->OnDataFromSctpToChannel(...): " |
706 << "Not receiving packet with sid=" << params.ssrc | 709 << "Not receiving packet with sid=" << params.ssrc |
707 << " len=" << buffer->size() << " before SetReceive(true)."; | 710 << " len=" << buffer.size() << " before SetReceive(true)."; |
708 } | 711 } |
709 } | 712 } |
710 | 713 |
711 bool SctpDataMediaChannel::AddStream(const StreamParams& stream) { | 714 bool SctpDataMediaChannel::AddStream(const StreamParams& stream) { |
712 if (!stream.has_ssrcs()) { | 715 if (!stream.has_ssrcs()) { |
713 return false; | 716 return false; |
714 } | 717 } |
715 | 718 |
716 const uint32_t ssrc = stream.first_ssrc(); | 719 const uint32_t ssrc = stream.first_ssrc(); |
717 if (ssrc >= cricket::kMaxSctpSid) { | 720 if (ssrc >= cricket::kMaxSctpSid) { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
760 // SendQueuedStreamResets(). | 763 // SendQueuedStreamResets(). |
761 queued_reset_streams_.insert(ssrc); | 764 queued_reset_streams_.insert(ssrc); |
762 | 765 |
763 // Signal our stream-reset logic that it should try to send now, if it can. | 766 // Signal our stream-reset logic that it should try to send now, if it can. |
764 SendQueuedStreamResets(); | 767 SendQueuedStreamResets(); |
765 | 768 |
766 // The stream will actually get removed when we get the acknowledgment. | 769 // The stream will actually get removed when we get the acknowledgment. |
767 return true; | 770 return true; |
768 } | 771 } |
769 | 772 |
770 void SctpDataMediaChannel::OnNotificationFromSctp(rtc::Buffer* buffer) { | 773 void SctpDataMediaChannel::OnNotificationFromSctp( |
774 const rtc::CopyOnWriteBuffer& buffer) { | |
771 const sctp_notification& notification = | 775 const sctp_notification& notification = |
772 reinterpret_cast<const sctp_notification&>(*buffer->data()); | 776 reinterpret_cast<const sctp_notification&>(*buffer.data()); |
773 ASSERT(notification.sn_header.sn_length == buffer->size()); | 777 ASSERT(notification.sn_header.sn_length == buffer.size()); |
774 | 778 |
775 // TODO(ldixon): handle notifications appropriately. | 779 // TODO(ldixon): handle notifications appropriately. |
776 switch (notification.sn_header.sn_type) { | 780 switch (notification.sn_header.sn_type) { |
777 case SCTP_ASSOC_CHANGE: | 781 case SCTP_ASSOC_CHANGE: |
778 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE"; | 782 LOG(LS_VERBOSE) << "SCTP_ASSOC_CHANGE"; |
779 OnNotificationAssocChange(notification.sn_assoc_change); | 783 OnNotificationAssocChange(notification.sn_assoc_change); |
780 break; | 784 break; |
781 case SCTP_REMOTE_ERROR: | 785 case SCTP_REMOTE_ERROR: |
782 LOG(LS_INFO) << "SCTP_REMOTE_ERROR"; | 786 LOG(LS_INFO) << "SCTP_REMOTE_ERROR"; |
783 break; | 787 break; |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
956 &remote_port_); | 960 &remote_port_); |
957 } | 961 } |
958 | 962 |
959 bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) { | 963 bool SctpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) { |
960 return GetCodecIntParameter( | 964 return GetCodecIntParameter( |
961 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort, | 965 codecs, kGoogleSctpDataCodecId, kGoogleSctpDataCodecName, kCodecParamPort, |
962 &local_port_); | 966 &local_port_); |
963 } | 967 } |
964 | 968 |
965 void SctpDataMediaChannel::OnPacketFromSctpToNetwork( | 969 void SctpDataMediaChannel::OnPacketFromSctpToNetwork( |
966 rtc::Buffer* buffer) { | 970 rtc::CopyOnWriteBuffer* buffer) { |
967 // usrsctp seems to interpret the MTU we give it strangely -- it seems to | 971 // usrsctp seems to interpret the MTU we give it strangely -- it seems to |
968 // give us back packets bigger than that MTU, if only by a fixed amount. | 972 // give us back packets bigger than that MTU, if only by a fixed amount. |
969 // This is that amount that we've observed. | 973 // This is that amount that we've observed. |
970 const int kSctpOverhead = 76; | 974 const int kSctpOverhead = 76; |
971 if (buffer->size() > (kSctpOverhead + kSctpMtu)) { | 975 if (buffer->size() > (kSctpOverhead + kSctpMtu)) { |
972 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): " | 976 LOG(LS_ERROR) << debug_name_ << "->OnPacketFromSctpToNetwork(...): " |
973 << "SCTP seems to have made a packet that is bigger " | 977 << "SCTP seems to have made a packet that is bigger " |
974 << "than its official MTU: " << buffer->size() | 978 << "than its official MTU: " << buffer->size() |
975 << " vs max of " << kSctpMtu | 979 << " vs max of " << kSctpMtu |
976 << " even after adding " << kSctpOverhead | 980 << " even after adding " << kSctpOverhead |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1029 } | 1033 } |
1030 case MSG_SCTPOUTBOUNDPACKET: { | 1034 case MSG_SCTPOUTBOUNDPACKET: { |
1031 std::unique_ptr<OutboundPacketMessage> pdata( | 1035 std::unique_ptr<OutboundPacketMessage> pdata( |
1032 static_cast<OutboundPacketMessage*>(msg->pdata)); | 1036 static_cast<OutboundPacketMessage*>(msg->pdata)); |
1033 OnPacketFromSctpToNetwork(pdata->data().get()); | 1037 OnPacketFromSctpToNetwork(pdata->data().get()); |
1034 break; | 1038 break; |
1035 } | 1039 } |
1036 } | 1040 } |
1037 } | 1041 } |
1038 } // namespace cricket | 1042 } // namespace cricket |
OLD | NEW |