1/* 2 * libjingle 3 * Copyright 2012 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/media/base/rtpdataengine.h" 29 30#include "talk/base/buffer.h" 31#include "talk/base/helpers.h" 32#include "talk/base/logging.h" 33#include "talk/base/ratelimiter.h" 34#include "talk/base/timing.h" 35#include "talk/media/base/codec.h" 36#include "talk/media/base/constants.h" 37#include "talk/media/base/rtputils.h" 38#include "talk/media/base/streamparams.h" 39 40namespace cricket { 41 42// We want to avoid IP fragmentation. 43static const size_t kDataMaxRtpPacketLen = 1200U; 44// We reserve space after the RTP header for future wiggle room. 45static const unsigned char kReservedSpace[] = { 46 0x00, 0x00, 0x00, 0x00 47}; 48 49// Amount of overhead SRTP may take. We need to leave room in the 50// buffer for it, otherwise SRTP will fail later. If SRTP ever uses 51// more than this, we need to increase this number. 52static const size_t kMaxSrtpHmacOverhead = 16; 53 54RtpDataEngine::RtpDataEngine() { 55 data_codecs_.push_back( 56 DataCodec(kGoogleRtpDataCodecId, 57 kGoogleRtpDataCodecName, 0)); 58 SetTiming(new talk_base::Timing()); 59} 60 61DataMediaChannel* RtpDataEngine::CreateChannel( 62 DataChannelType data_channel_type) { 63 if (data_channel_type != DCT_RTP) { 64 return NULL; 65 } 66 return new RtpDataMediaChannel(timing_.get()); 67} 68 69// TODO(pthatcher): Should we move these find/get functions somewhere 70// common? 71bool FindCodecById(const std::vector<DataCodec>& codecs, 72 int id, DataCodec* codec_out) { 73 std::vector<DataCodec>::const_iterator iter; 74 for (iter = codecs.begin(); iter != codecs.end(); ++iter) { 75 if (iter->id == id) { 76 *codec_out = *iter; 77 return true; 78 } 79 } 80 return false; 81} 82 83bool FindCodecByName(const std::vector<DataCodec>& codecs, 84 const std::string& name, DataCodec* codec_out) { 85 std::vector<DataCodec>::const_iterator iter; 86 for (iter = codecs.begin(); iter != codecs.end(); ++iter) { 87 if (iter->name == name) { 88 *codec_out = *iter; 89 return true; 90 } 91 } 92 return false; 93} 94 95RtpDataMediaChannel::RtpDataMediaChannel(talk_base::Timing* timing) { 96 Construct(timing); 97} 98 99RtpDataMediaChannel::RtpDataMediaChannel() { 100 Construct(NULL); 101} 102 103void RtpDataMediaChannel::Construct(talk_base::Timing* timing) { 104 sending_ = false; 105 receiving_ = false; 106 timing_ = timing; 107 send_limiter_.reset(new talk_base::RateLimiter(kDataMaxBandwidth / 8, 1.0)); 108} 109 110 111RtpDataMediaChannel::~RtpDataMediaChannel() { 112 std::map<uint32, RtpClock*>::const_iterator iter; 113 for (iter = rtp_clock_by_send_ssrc_.begin(); 114 iter != rtp_clock_by_send_ssrc_.end(); 115 ++iter) { 116 delete iter->second; 117 } 118} 119 120void RtpClock::Tick( 121 double now, int* seq_num, uint32* timestamp) { 122 *seq_num = ++last_seq_num_; 123 *timestamp = timestamp_offset_ + static_cast<uint32>(now * clockrate_); 124} 125 126const DataCodec* FindUnknownCodec(const std::vector<DataCodec>& codecs) { 127 DataCodec data_codec(kGoogleRtpDataCodecId, kGoogleRtpDataCodecName, 0); 128 std::vector<DataCodec>::const_iterator iter; 129 for (iter = codecs.begin(); iter != codecs.end(); ++iter) { 130 if (!iter->Matches(data_codec)) { 131 return &(*iter); 132 } 133 } 134 return NULL; 135} 136 137const DataCodec* FindKnownCodec(const std::vector<DataCodec>& codecs) { 138 DataCodec data_codec(kGoogleRtpDataCodecId, kGoogleRtpDataCodecName, 0); 139 std::vector<DataCodec>::const_iterator iter; 140 for (iter = codecs.begin(); iter != codecs.end(); ++iter) { 141 if (iter->Matches(data_codec)) { 142 return &(*iter); 143 } 144 } 145 return NULL; 146} 147 148bool RtpDataMediaChannel::SetRecvCodecs(const std::vector<DataCodec>& codecs) { 149 const DataCodec* unknown_codec = FindUnknownCodec(codecs); 150 if (unknown_codec) { 151 LOG(LS_WARNING) << "Failed to SetRecvCodecs because of unknown codec: " 152 << unknown_codec->ToString(); 153 return false; 154 } 155 156 recv_codecs_ = codecs; 157 return true; 158} 159 160bool RtpDataMediaChannel::SetSendCodecs(const std::vector<DataCodec>& codecs) { 161 const DataCodec* known_codec = FindKnownCodec(codecs); 162 if (!known_codec) { 163 LOG(LS_WARNING) << 164 "Failed to SetSendCodecs because there is no known codec."; 165 return false; 166 } 167 168 send_codecs_ = codecs; 169 return true; 170} 171 172bool RtpDataMediaChannel::AddSendStream(const StreamParams& stream) { 173 if (!stream.has_ssrcs()) { 174 return false; 175 } 176 177 StreamParams found_stream; 178 if (GetStreamBySsrc(send_streams_, stream.first_ssrc(), &found_stream)) { 179 LOG(LS_WARNING) << "Not adding data send stream '" << stream.id 180 << "' with ssrc=" << stream.first_ssrc() 181 << " because stream already exists."; 182 return false; 183 } 184 185 send_streams_.push_back(stream); 186 // TODO(pthatcher): This should be per-stream, not per-ssrc. 187 // And we should probably allow more than one per stream. 188 rtp_clock_by_send_ssrc_[stream.first_ssrc()] = new RtpClock( 189 kDataCodecClockrate, 190 talk_base::CreateRandomNonZeroId(), talk_base::CreateRandomNonZeroId()); 191 192 LOG(LS_INFO) << "Added data send stream '" << stream.id 193 << "' with ssrc=" << stream.first_ssrc(); 194 return true; 195} 196 197bool RtpDataMediaChannel::RemoveSendStream(uint32 ssrc) { 198 StreamParams found_stream; 199 if (!GetStreamBySsrc(send_streams_, ssrc, &found_stream)) { 200 return false; 201 } 202 203 RemoveStreamBySsrc(&send_streams_, ssrc); 204 delete rtp_clock_by_send_ssrc_[ssrc]; 205 rtp_clock_by_send_ssrc_.erase(ssrc); 206 return true; 207} 208 209bool RtpDataMediaChannel::AddRecvStream(const StreamParams& stream) { 210 if (!stream.has_ssrcs()) { 211 return false; 212 } 213 214 StreamParams found_stream; 215 if (GetStreamBySsrc(recv_streams_, stream.first_ssrc(), &found_stream)) { 216 LOG(LS_WARNING) << "Not adding data recv stream '" << stream.id 217 << "' with ssrc=" << stream.first_ssrc() 218 << " because stream already exists."; 219 return false; 220 } 221 222 recv_streams_.push_back(stream); 223 LOG(LS_INFO) << "Added data recv stream '" << stream.id 224 << "' with ssrc=" << stream.first_ssrc(); 225 return true; 226} 227 228bool RtpDataMediaChannel::RemoveRecvStream(uint32 ssrc) { 229 RemoveStreamBySsrc(&recv_streams_, ssrc); 230 return true; 231} 232 233void RtpDataMediaChannel::OnPacketReceived( 234 talk_base::Buffer* packet, const talk_base::PacketTime& packet_time) { 235 RtpHeader header; 236 if (!GetRtpHeader(packet->data(), packet->length(), &header)) { 237 // Don't want to log for every corrupt packet. 238 // LOG(LS_WARNING) << "Could not read rtp header from packet of length " 239 // << packet->length() << "."; 240 return; 241 } 242 243 size_t header_length; 244 if (!GetRtpHeaderLen(packet->data(), packet->length(), &header_length)) { 245 // Don't want to log for every corrupt packet. 246 // LOG(LS_WARNING) << "Could not read rtp header" 247 // << length from packet of length " 248 // << packet->length() << "."; 249 return; 250 } 251 const char* data = packet->data() + header_length + sizeof(kReservedSpace); 252 size_t data_len = packet->length() - header_length - sizeof(kReservedSpace); 253 254 if (!receiving_) { 255 LOG(LS_WARNING) << "Not receiving packet " 256 << header.ssrc << ":" << header.seq_num 257 << " before SetReceive(true) called."; 258 return; 259 } 260 261 DataCodec codec; 262 if (!FindCodecById(recv_codecs_, header.payload_type, &codec)) { 263 // For bundling, this will be logged for every message. 264 // So disable this logging. 265 // LOG(LS_WARNING) << "Not receiving packet " 266 // << header.ssrc << ":" << header.seq_num 267 // << " (" << data_len << ")" 268 // << " because unknown payload id: " << header.payload_type; 269 return; 270 } 271 272 StreamParams found_stream; 273 if (!GetStreamBySsrc(recv_streams_, header.ssrc, &found_stream)) { 274 LOG(LS_WARNING) << "Received packet for unknown ssrc: " << header.ssrc; 275 return; 276 } 277 278 // Uncomment this for easy debugging. 279 // LOG(LS_INFO) << "Received packet" 280 // << " groupid=" << found_stream.groupid 281 // << ", ssrc=" << header.ssrc 282 // << ", seqnum=" << header.seq_num 283 // << ", timestamp=" << header.timestamp 284 // << ", len=" << data_len; 285 286 ReceiveDataParams params; 287 params.ssrc = header.ssrc; 288 params.seq_num = header.seq_num; 289 params.timestamp = header.timestamp; 290 SignalDataReceived(params, data, data_len); 291} 292 293bool RtpDataMediaChannel::SetMaxSendBandwidth(int bps) { 294 if (bps <= 0) { 295 bps = kDataMaxBandwidth; 296 } 297 send_limiter_.reset(new talk_base::RateLimiter(bps / 8, 1.0)); 298 LOG(LS_INFO) << "RtpDataMediaChannel::SetSendBandwidth to " << bps << "bps."; 299 return true; 300} 301 302bool RtpDataMediaChannel::SendData( 303 const SendDataParams& params, 304 const talk_base::Buffer& payload, 305 SendDataResult* result) { 306 if (result) { 307 // If we return true, we'll set this to SDR_SUCCESS. 308 *result = SDR_ERROR; 309 } 310 if (!sending_) { 311 LOG(LS_WARNING) << "Not sending packet with ssrc=" << params.ssrc 312 << " len=" << payload.length() << " before SetSend(true)."; 313 return false; 314 } 315 316 if (params.type != cricket::DMT_TEXT) { 317 LOG(LS_WARNING) << "Not sending data because binary type is unsupported."; 318 return false; 319 } 320 321 StreamParams found_stream; 322 if (!GetStreamBySsrc(send_streams_, params.ssrc, &found_stream)) { 323 LOG(LS_WARNING) << "Not sending data because ssrc is unknown: " 324 << params.ssrc; 325 return false; 326 } 327 328 DataCodec found_codec; 329 if (!FindCodecByName(send_codecs_, kGoogleRtpDataCodecName, &found_codec)) { 330 LOG(LS_WARNING) << "Not sending data because codec is unknown: " 331 << kGoogleRtpDataCodecName; 332 return false; 333 } 334 335 size_t packet_len = (kMinRtpPacketLen + sizeof(kReservedSpace) 336 + payload.length() + kMaxSrtpHmacOverhead); 337 if (packet_len > kDataMaxRtpPacketLen) { 338 return false; 339 } 340 341 double now = timing_->TimerNow(); 342 343 if (!send_limiter_->CanUse(packet_len, now)) { 344 LOG(LS_VERBOSE) << "Dropped data packet of len=" << packet_len 345 << "; already sent " << send_limiter_->used_in_period() 346 << "/" << send_limiter_->max_per_period(); 347 return false; 348 } 349 350 RtpHeader header; 351 header.payload_type = found_codec.id; 352 header.ssrc = params.ssrc; 353 rtp_clock_by_send_ssrc_[header.ssrc]->Tick( 354 now, &header.seq_num, &header.timestamp); 355 356 talk_base::Buffer packet; 357 packet.SetCapacity(packet_len); 358 packet.SetLength(kMinRtpPacketLen); 359 if (!SetRtpHeader(packet.data(), packet.length(), header)) { 360 return false; 361 } 362 packet.AppendData(&kReservedSpace, sizeof(kReservedSpace)); 363 packet.AppendData(payload.data(), payload.length()); 364 365 LOG(LS_VERBOSE) << "Sent RTP data packet: " 366 << " stream=" << found_stream.id 367 << " ssrc=" << header.ssrc 368 << ", seqnum=" << header.seq_num 369 << ", timestamp=" << header.timestamp 370 << ", len=" << payload.length(); 371 372 MediaChannel::SendPacket(&packet); 373 send_limiter_->Use(packet_len, now); 374 if (result) { 375 *result = SDR_SUCCESS; 376 } 377 return true; 378} 379 380} // namespace cricket 381