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 <string>
29
30#include "talk/media/base/constants.h"
31#include "talk/media/base/fakenetworkinterface.h"
32#include "talk/media/base/rtpdataengine.h"
33#include "talk/media/base/rtputils.h"
34#include "webrtc/base/buffer.h"
35#include "webrtc/base/gunit.h"
36#include "webrtc/base/helpers.h"
37#include "webrtc/base/scoped_ptr.h"
38#include "webrtc/base/ssladapter.h"
39#include "webrtc/base/timing.h"
40
41class FakeTiming : public rtc::Timing {
42 public:
43  FakeTiming() : now_(0.0) {}
44
45  virtual double TimerNow() {
46    return now_;
47  }
48
49  void set_now(double now) {
50    now_ = now;
51  }
52
53 private:
54  double now_;
55};
56
57class FakeDataReceiver : public sigslot::has_slots<> {
58 public:
59  FakeDataReceiver() : has_received_data_(false) {}
60
61  void OnDataReceived(
62      const cricket::ReceiveDataParams& params,
63      const char* data, size_t len) {
64    has_received_data_ = true;
65    last_received_data_ = std::string(data, len);
66    last_received_data_len_ = len;
67    last_received_data_params_ = params;
68  }
69
70  bool has_received_data() const { return has_received_data_; }
71  std::string last_received_data() const { return last_received_data_; }
72  size_t last_received_data_len() const { return last_received_data_len_; }
73  cricket::ReceiveDataParams last_received_data_params() const {
74    return last_received_data_params_;
75  }
76
77 private:
78  bool has_received_data_;
79  std::string last_received_data_;
80  size_t last_received_data_len_;
81  cricket::ReceiveDataParams last_received_data_params_;
82};
83
84class RtpDataMediaChannelTest : public testing::Test {
85 protected:
86  virtual void SetUp() {
87    // Seed needed for each test to satisfy expectations.
88    iface_.reset(new cricket::FakeNetworkInterface());
89    timing_ = new FakeTiming();
90    dme_.reset(CreateEngine(timing_));
91    receiver_.reset(new FakeDataReceiver());
92  }
93
94  void SetNow(double now) {
95    timing_->set_now(now);
96  }
97
98  cricket::RtpDataEngine* CreateEngine(FakeTiming* timing) {
99    cricket::RtpDataEngine* dme = new cricket::RtpDataEngine();
100    dme->SetTiming(timing);
101    return dme;
102  }
103
104  cricket::RtpDataMediaChannel* CreateChannel() {
105    return CreateChannel(dme_.get());
106  }
107
108  cricket::RtpDataMediaChannel* CreateChannel(cricket::RtpDataEngine* dme) {
109    cricket::RtpDataMediaChannel* channel =
110        static_cast<cricket::RtpDataMediaChannel*>(dme->CreateChannel(
111            cricket::DCT_RTP));
112    channel->SetInterface(iface_.get());
113    channel->SignalDataReceived.connect(
114        receiver_.get(), &FakeDataReceiver::OnDataReceived);
115    return channel;
116  }
117
118  FakeDataReceiver* receiver() {
119    return receiver_.get();
120  }
121
122  bool HasReceivedData() {
123    return receiver_->has_received_data();
124  }
125
126  std::string GetReceivedData() {
127    return receiver_->last_received_data();
128  }
129
130  size_t GetReceivedDataLen() {
131    return receiver_->last_received_data_len();
132  }
133
134  cricket::ReceiveDataParams GetReceivedDataParams() {
135    return receiver_->last_received_data_params();
136  }
137
138  bool HasSentData(int count) {
139    return (iface_->NumRtpPackets() > count);
140  }
141
142  std::string GetSentData(int index) {
143    // Assume RTP header of length 12
144    rtc::scoped_ptr<const rtc::Buffer> packet(
145        iface_->GetRtpPacket(index));
146    if (packet->size() > 12) {
147      return std::string(packet->data<char>() + 12, packet->size() - 12);
148    } else {
149      return "";
150    }
151  }
152
153  cricket::RtpHeader GetSentDataHeader(int index) {
154    rtc::scoped_ptr<const rtc::Buffer> packet(
155        iface_->GetRtpPacket(index));
156    cricket::RtpHeader header;
157    GetRtpHeader(packet->data(), packet->size(), &header);
158    return header;
159  }
160
161 private:
162  rtc::scoped_ptr<cricket::RtpDataEngine> dme_;
163  // Timing passed into dme_.  Owned by dme_;
164  FakeTiming* timing_;
165  rtc::scoped_ptr<cricket::FakeNetworkInterface> iface_;
166  rtc::scoped_ptr<FakeDataReceiver> receiver_;
167};
168
169TEST_F(RtpDataMediaChannelTest, SetUnknownCodecs) {
170  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
171
172  cricket::DataCodec known_codec;
173  known_codec.id = 103;
174  known_codec.name = "google-data";
175  cricket::DataCodec unknown_codec;
176  unknown_codec.id = 104;
177  unknown_codec.name = "unknown-data";
178
179  cricket::DataSendParameters send_parameters_known;
180  send_parameters_known.codecs.push_back(known_codec);
181  cricket::DataRecvParameters recv_parameters_known;
182  recv_parameters_known.codecs.push_back(known_codec);
183
184  cricket::DataSendParameters send_parameters_unknown;
185  send_parameters_unknown.codecs.push_back(unknown_codec);
186  cricket::DataRecvParameters recv_parameters_unknown;
187  recv_parameters_unknown.codecs.push_back(unknown_codec);
188
189  cricket::DataSendParameters send_parameters_mixed;
190  send_parameters_mixed.codecs.push_back(known_codec);
191  send_parameters_mixed.codecs.push_back(unknown_codec);
192  cricket::DataRecvParameters recv_parameters_mixed;
193  recv_parameters_mixed.codecs.push_back(known_codec);
194  recv_parameters_mixed.codecs.push_back(unknown_codec);
195
196  EXPECT_TRUE(dmc->SetSendParameters(send_parameters_known));
197  EXPECT_FALSE(dmc->SetSendParameters(send_parameters_unknown));
198  EXPECT_TRUE(dmc->SetSendParameters(send_parameters_mixed));
199  EXPECT_TRUE(dmc->SetRecvParameters(recv_parameters_known));
200  EXPECT_FALSE(dmc->SetRecvParameters(recv_parameters_unknown));
201  EXPECT_FALSE(dmc->SetRecvParameters(recv_parameters_mixed));
202}
203
204TEST_F(RtpDataMediaChannelTest, AddRemoveSendStream) {
205  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
206
207  cricket::StreamParams stream1;
208  stream1.add_ssrc(41);
209  EXPECT_TRUE(dmc->AddSendStream(stream1));
210  cricket::StreamParams stream2;
211  stream2.add_ssrc(42);
212  EXPECT_TRUE(dmc->AddSendStream(stream2));
213
214  EXPECT_TRUE(dmc->RemoveSendStream(41));
215  EXPECT_TRUE(dmc->RemoveSendStream(42));
216  EXPECT_FALSE(dmc->RemoveSendStream(43));
217}
218
219TEST_F(RtpDataMediaChannelTest, AddRemoveRecvStream) {
220  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
221
222  cricket::StreamParams stream1;
223  stream1.add_ssrc(41);
224  EXPECT_TRUE(dmc->AddRecvStream(stream1));
225  cricket::StreamParams stream2;
226  stream2.add_ssrc(42);
227  EXPECT_TRUE(dmc->AddRecvStream(stream2));
228  EXPECT_FALSE(dmc->AddRecvStream(stream2));
229
230  EXPECT_TRUE(dmc->RemoveRecvStream(41));
231  EXPECT_TRUE(dmc->RemoveRecvStream(42));
232}
233
234TEST_F(RtpDataMediaChannelTest, SendData) {
235  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
236
237  cricket::SendDataParams params;
238  params.ssrc = 42;
239  unsigned char data[] = "food";
240  rtc::Buffer payload(data, 4);
241  unsigned char padded_data[] = {
242    0x00, 0x00, 0x00, 0x00,
243    'f', 'o', 'o', 'd',
244  };
245  cricket::SendDataResult result;
246
247  // Not sending
248  EXPECT_FALSE(dmc->SendData(params, payload, &result));
249  EXPECT_EQ(cricket::SDR_ERROR, result);
250  EXPECT_FALSE(HasSentData(0));
251  ASSERT_TRUE(dmc->SetSend(true));
252
253  // Unknown stream name.
254  EXPECT_FALSE(dmc->SendData(params, payload, &result));
255  EXPECT_EQ(cricket::SDR_ERROR, result);
256  EXPECT_FALSE(HasSentData(0));
257
258  cricket::StreamParams stream;
259  stream.add_ssrc(42);
260  ASSERT_TRUE(dmc->AddSendStream(stream));
261
262  // Unknown codec;
263  EXPECT_FALSE(dmc->SendData(params, payload, &result));
264  EXPECT_EQ(cricket::SDR_ERROR, result);
265  EXPECT_FALSE(HasSentData(0));
266
267  cricket::DataCodec codec;
268  codec.id = 103;
269  codec.name = cricket::kGoogleRtpDataCodecName;
270  cricket::DataSendParameters parameters;
271  parameters.codecs.push_back(codec);
272  ASSERT_TRUE(dmc->SetSendParameters(parameters));
273
274  // Length too large;
275  std::string x10000(10000, 'x');
276  EXPECT_FALSE(dmc->SendData(
277      params, rtc::Buffer(x10000.data(), x10000.length()), &result));
278  EXPECT_EQ(cricket::SDR_ERROR, result);
279  EXPECT_FALSE(HasSentData(0));
280
281  // Finally works!
282  EXPECT_TRUE(dmc->SendData(params, payload, &result));
283  EXPECT_EQ(cricket::SDR_SUCCESS, result);
284  ASSERT_TRUE(HasSentData(0));
285  EXPECT_EQ(sizeof(padded_data), GetSentData(0).length());
286  EXPECT_EQ(0, memcmp(
287      padded_data, GetSentData(0).data(), sizeof(padded_data)));
288  cricket::RtpHeader header0 = GetSentDataHeader(0);
289  EXPECT_NE(0, header0.seq_num);
290  EXPECT_NE(0U, header0.timestamp);
291  EXPECT_EQ(header0.ssrc, 42U);
292  EXPECT_EQ(header0.payload_type, 103);
293
294  // Should bump timestamp by 180000 because the clock rate is 90khz.
295  SetNow(2);
296
297  EXPECT_TRUE(dmc->SendData(params, payload, &result));
298  ASSERT_TRUE(HasSentData(1));
299  EXPECT_EQ(sizeof(padded_data), GetSentData(1).length());
300  EXPECT_EQ(0, memcmp(
301      padded_data, GetSentData(1).data(), sizeof(padded_data)));
302  cricket::RtpHeader header1 = GetSentDataHeader(1);
303  EXPECT_EQ(header1.ssrc, 42U);
304  EXPECT_EQ(header1.payload_type, 103);
305  EXPECT_EQ(static_cast<uint16_t>(header0.seq_num + 1),
306            static_cast<uint16_t>(header1.seq_num));
307  EXPECT_EQ(header0.timestamp + 180000, header1.timestamp);
308}
309
310TEST_F(RtpDataMediaChannelTest, SendDataMultipleClocks) {
311  // Timings owned by RtpDataEngines.
312  FakeTiming* timing1 = new FakeTiming();
313  rtc::scoped_ptr<cricket::RtpDataEngine> dme1(CreateEngine(timing1));
314  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc1(
315      CreateChannel(dme1.get()));
316  FakeTiming* timing2 = new FakeTiming();
317  rtc::scoped_ptr<cricket::RtpDataEngine> dme2(CreateEngine(timing2));
318  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc2(
319      CreateChannel(dme2.get()));
320
321  ASSERT_TRUE(dmc1->SetSend(true));
322  ASSERT_TRUE(dmc2->SetSend(true));
323
324  cricket::StreamParams stream1;
325  stream1.add_ssrc(41);
326  ASSERT_TRUE(dmc1->AddSendStream(stream1));
327  cricket::StreamParams stream2;
328  stream2.add_ssrc(42);
329  ASSERT_TRUE(dmc2->AddSendStream(stream2));
330
331  cricket::DataCodec codec;
332  codec.id = 103;
333  codec.name = cricket::kGoogleRtpDataCodecName;
334  cricket::DataSendParameters parameters;
335  parameters.codecs.push_back(codec);
336  ASSERT_TRUE(dmc1->SetSendParameters(parameters));
337  ASSERT_TRUE(dmc2->SetSendParameters(parameters));
338
339  cricket::SendDataParams params1;
340  params1.ssrc = 41;
341  cricket::SendDataParams params2;
342  params2.ssrc = 42;
343
344  unsigned char data[] = "foo";
345  rtc::Buffer payload(data, 3);
346  cricket::SendDataResult result;
347
348  EXPECT_TRUE(dmc1->SendData(params1, payload, &result));
349  EXPECT_TRUE(dmc2->SendData(params2, payload, &result));
350
351  // Should bump timestamp by 90000 because the clock rate is 90khz.
352  timing1->set_now(1);
353  // Should bump timestamp by 180000 because the clock rate is 90khz.
354  timing2->set_now(2);
355
356  EXPECT_TRUE(dmc1->SendData(params1, payload, &result));
357  EXPECT_TRUE(dmc2->SendData(params2, payload, &result));
358
359  ASSERT_TRUE(HasSentData(3));
360  cricket::RtpHeader header1a = GetSentDataHeader(0);
361  cricket::RtpHeader header2a = GetSentDataHeader(1);
362  cricket::RtpHeader header1b = GetSentDataHeader(2);
363  cricket::RtpHeader header2b = GetSentDataHeader(3);
364
365  EXPECT_EQ(static_cast<uint16_t>(header1a.seq_num + 1),
366            static_cast<uint16_t>(header1b.seq_num));
367  EXPECT_EQ(header1a.timestamp + 90000, header1b.timestamp);
368  EXPECT_EQ(static_cast<uint16_t>(header2a.seq_num + 1),
369            static_cast<uint16_t>(header2b.seq_num));
370  EXPECT_EQ(header2a.timestamp + 180000, header2b.timestamp);
371}
372
373TEST_F(RtpDataMediaChannelTest, SendDataRate) {
374  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
375
376  ASSERT_TRUE(dmc->SetSend(true));
377
378  cricket::DataCodec codec;
379  codec.id = 103;
380  codec.name = cricket::kGoogleRtpDataCodecName;
381  cricket::DataSendParameters parameters;
382  parameters.codecs.push_back(codec);
383  ASSERT_TRUE(dmc->SetSendParameters(parameters));
384
385  cricket::StreamParams stream;
386  stream.add_ssrc(42);
387  ASSERT_TRUE(dmc->AddSendStream(stream));
388
389  cricket::SendDataParams params;
390  params.ssrc = 42;
391  unsigned char data[] = "food";
392  rtc::Buffer payload(data, 4);
393  cricket::SendDataResult result;
394
395  // With rtp overhead of 32 bytes, each one of our packets is 36
396  // bytes, or 288 bits.  So, a limit of 872bps will allow 3 packets,
397  // but not four.
398  parameters.max_bandwidth_bps = 872;
399  ASSERT_TRUE(dmc->SetSendParameters(parameters));
400
401  EXPECT_TRUE(dmc->SendData(params, payload, &result));
402  EXPECT_TRUE(dmc->SendData(params, payload, &result));
403  EXPECT_TRUE(dmc->SendData(params, payload, &result));
404  EXPECT_FALSE(dmc->SendData(params, payload, &result));
405  EXPECT_FALSE(dmc->SendData(params, payload, &result));
406
407  SetNow(0.9);
408  EXPECT_FALSE(dmc->SendData(params, payload, &result));
409
410  SetNow(1.1);
411  EXPECT_TRUE(dmc->SendData(params, payload, &result));
412  EXPECT_TRUE(dmc->SendData(params, payload, &result));
413  SetNow(1.9);
414  EXPECT_TRUE(dmc->SendData(params, payload, &result));
415
416  SetNow(2.2);
417  EXPECT_TRUE(dmc->SendData(params, payload, &result));
418  EXPECT_TRUE(dmc->SendData(params, payload, &result));
419  EXPECT_TRUE(dmc->SendData(params, payload, &result));
420  EXPECT_FALSE(dmc->SendData(params, payload, &result));
421}
422
423TEST_F(RtpDataMediaChannelTest, ReceiveData) {
424  // PT= 103, SN=2, TS=3, SSRC = 4, data = "abcde"
425  unsigned char data[] = {
426    0x80, 0x67, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2A,
427    0x00, 0x00, 0x00, 0x00,
428    'a', 'b', 'c', 'd', 'e'
429  };
430  rtc::Buffer packet(data, sizeof(data));
431
432  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
433
434  // SetReceived not called.
435  dmc->OnPacketReceived(&packet, rtc::PacketTime());
436  EXPECT_FALSE(HasReceivedData());
437
438  dmc->SetReceive(true);
439
440  // Unknown payload id
441  dmc->OnPacketReceived(&packet, rtc::PacketTime());
442  EXPECT_FALSE(HasReceivedData());
443
444  cricket::DataCodec codec;
445  codec.id = 103;
446  codec.name = cricket::kGoogleRtpDataCodecName;
447  cricket::DataRecvParameters parameters;
448  parameters.codecs.push_back(codec);
449  ASSERT_TRUE(dmc->SetRecvParameters(parameters));
450
451  // Unknown stream
452  dmc->OnPacketReceived(&packet, rtc::PacketTime());
453  EXPECT_FALSE(HasReceivedData());
454
455  cricket::StreamParams stream;
456  stream.add_ssrc(42);
457  ASSERT_TRUE(dmc->AddRecvStream(stream));
458
459  // Finally works!
460  dmc->OnPacketReceived(&packet, rtc::PacketTime());
461  EXPECT_TRUE(HasReceivedData());
462  EXPECT_EQ("abcde", GetReceivedData());
463  EXPECT_EQ(5U, GetReceivedDataLen());
464}
465
466TEST_F(RtpDataMediaChannelTest, InvalidRtpPackets) {
467  unsigned char data[] = {
468    0x80, 0x65, 0x00, 0x02
469  };
470  rtc::Buffer packet(data, sizeof(data));
471
472  rtc::scoped_ptr<cricket::RtpDataMediaChannel> dmc(CreateChannel());
473
474  // Too short
475  dmc->OnPacketReceived(&packet, rtc::PacketTime());
476  EXPECT_FALSE(HasReceivedData());
477}
478