1// libjingle
2// Copyright 2010 Google Inc.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7//  1. Redistributions of source code must retain the above copyright notice,
8//     this list of conditions and the following disclaimer.
9//  2. Redistributions in binary form must reproduce the above copyright notice,
10//     this list of conditions and the following disclaimer in the documentation
11//     and/or other materials provided with the distribution.
12//  3. The name of the author may not be used to endorse or promote products
13//     derived from this software without specific prior written permission.
14//
15// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
26#include <string>
27
28#include "talk/media/base/fakemediaengine.h"
29#include "talk/media/base/rtpdump.h"
30#include "talk/media/base/testutils.h"
31#include "talk/p2p/base/fakesession.h"
32#include "talk/session/media/channel.h"
33#include "talk/session/media/mediarecorder.h"
34#include "webrtc/base/bytebuffer.h"
35#include "webrtc/base/fileutils.h"
36#include "webrtc/base/gunit.h"
37#include "webrtc/base/pathutils.h"
38#include "webrtc/base/thread.h"
39
40namespace cricket {
41
42rtc::StreamInterface* Open(const std::string& path) {
43  return rtc::Filesystem::OpenFile(
44      rtc::Pathname(path), "wb");
45}
46
47/////////////////////////////////////////////////////////////////////////
48// Test RtpDumpSink
49/////////////////////////////////////////////////////////////////////////
50class RtpDumpSinkTest : public testing::Test {
51 public:
52  virtual void SetUp() {
53    EXPECT_TRUE(rtc::Filesystem::GetTemporaryFolder(path_, true, NULL));
54    path_.SetFilename("sink-test.rtpdump");
55    sink_.reset(new RtpDumpSink(Open(path_.pathname())));
56
57    for (int i = 0; i < ARRAY_SIZE(rtp_buf_); ++i) {
58      RtpTestUtility::kTestRawRtpPackets[i].WriteToByteBuffer(
59          RtpTestUtility::kDefaultSsrc, &rtp_buf_[i]);
60    }
61  }
62
63  virtual void TearDown() {
64    stream_.reset();
65    EXPECT_TRUE(rtc::Filesystem::DeleteFile(path_));
66  }
67
68 protected:
69  void OnRtpPacket(const RawRtpPacket& raw) {
70    rtc::ByteBuffer buf;
71    raw.WriteToByteBuffer(RtpTestUtility::kDefaultSsrc, &buf);
72    sink_->OnPacket(buf.Data(), buf.Length(), false);
73  }
74
75  rtc::StreamResult ReadPacket(RtpDumpPacket* packet) {
76    if (!stream_.get()) {
77      sink_.reset();  // This will close the file. So we can read it.
78      stream_.reset(rtc::Filesystem::OpenFile(path_, "rb"));
79      reader_.reset(new RtpDumpReader(stream_.get()));
80    }
81    return reader_->ReadPacket(packet);
82  }
83
84  rtc::Pathname path_;
85  rtc::scoped_ptr<RtpDumpSink> sink_;
86  rtc::ByteBuffer rtp_buf_[3];
87  rtc::scoped_ptr<rtc::StreamInterface> stream_;
88  rtc::scoped_ptr<RtpDumpReader> reader_;
89};
90
91TEST_F(RtpDumpSinkTest, TestRtpDumpSink) {
92  // By default, the sink is disabled. The 1st packet is not written.
93  EXPECT_FALSE(sink_->IsEnabled());
94  sink_->set_packet_filter(PF_ALL);
95  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[0]);
96
97  // Enable the sink. The 2nd packet is written.
98  EXPECT_TRUE(sink_->Enable(true));
99  EXPECT_TRUE(sink_->IsEnabled());
100  EXPECT_TRUE(rtc::Filesystem::IsFile(path_.pathname()));
101  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[1]);
102
103  // Disable the sink. The 3rd packet is not written.
104  EXPECT_TRUE(sink_->Enable(false));
105  EXPECT_FALSE(sink_->IsEnabled());
106  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[2]);
107
108  // Read the recorded file and verify it contains only the 2nd packet.
109  RtpDumpPacket packet;
110  EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet));
111  EXPECT_TRUE(RtpTestUtility::VerifyPacket(
112      &packet, &RtpTestUtility::kTestRawRtpPackets[1], false));
113  EXPECT_EQ(rtc::SR_EOS, ReadPacket(&packet));
114}
115
116TEST_F(RtpDumpSinkTest, TestRtpDumpSinkMaxSize) {
117  EXPECT_TRUE(sink_->Enable(true));
118  sink_->set_packet_filter(PF_ALL);
119  sink_->SetMaxSize(strlen(RtpDumpFileHeader::kFirstLine) +
120                    RtpDumpFileHeader::kHeaderLength +
121                    RtpDumpPacket::kHeaderLength +
122                    RtpTestUtility::kTestRawRtpPackets[0].size());
123  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[0]);
124
125  // Exceed the limit size: the 2nd and 3rd packets are not written.
126  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[1]);
127  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[2]);
128
129  // Read the recorded file and verify that it contains only the first packet.
130  RtpDumpPacket packet;
131  EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet));
132  EXPECT_TRUE(RtpTestUtility::VerifyPacket(
133      &packet, &RtpTestUtility::kTestRawRtpPackets[0], false));
134  EXPECT_EQ(rtc::SR_EOS, ReadPacket(&packet));
135}
136
137TEST_F(RtpDumpSinkTest, TestRtpDumpSinkFilter) {
138  // The default filter is PF_NONE.
139  EXPECT_EQ(PF_NONE, sink_->packet_filter());
140
141  // Set to PF_RTPHEADER before enable.
142  sink_->set_packet_filter(PF_RTPHEADER);
143  EXPECT_EQ(PF_RTPHEADER, sink_->packet_filter());
144  EXPECT_TRUE(sink_->Enable(true));
145  // We dump only the header of the first packet.
146  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[0]);
147
148  // Set the filter to PF_RTPPACKET. We dump all the second packet.
149  sink_->set_packet_filter(PF_RTPPACKET);
150  EXPECT_EQ(PF_RTPPACKET, sink_->packet_filter());
151  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[1]);
152
153  // Set the filter to PF_NONE. We do not dump the third packet.
154  sink_->set_packet_filter(PF_NONE);
155  EXPECT_EQ(PF_NONE, sink_->packet_filter());
156  OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[2]);
157
158  // Read the recorded file and verify the header of the first packet and
159  // the whole packet for the second packet.
160  RtpDumpPacket packet;
161  EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet));
162  EXPECT_TRUE(RtpTestUtility::VerifyPacket(
163      &packet, &RtpTestUtility::kTestRawRtpPackets[0], true));
164  EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet));
165  EXPECT_TRUE(RtpTestUtility::VerifyPacket(
166      &packet, &RtpTestUtility::kTestRawRtpPackets[1], false));
167  EXPECT_EQ(rtc::SR_EOS, ReadPacket(&packet));
168}
169
170/////////////////////////////////////////////////////////////////////////
171// Test MediaRecorder
172/////////////////////////////////////////////////////////////////////////
173void TestMediaRecorder(BaseChannel* channel,
174                       FakeVideoMediaChannel* video_media_channel,
175                       int filter) {
176  // Create media recorder.
177  rtc::scoped_ptr<MediaRecorder> recorder(new MediaRecorder);
178  // Fail to EnableChannel before AddChannel.
179  EXPECT_FALSE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO));
180  EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO));
181  EXPECT_FALSE(channel->HasRecvSinks(SINK_PRE_CRYPTO));
182  EXPECT_FALSE(channel->HasSendSinks(SINK_POST_CRYPTO));
183  EXPECT_FALSE(channel->HasRecvSinks(SINK_POST_CRYPTO));
184
185  // Add the channel to the recorder.
186  rtc::Pathname path;
187  EXPECT_TRUE(rtc::Filesystem::GetTemporaryFolder(path, true, NULL));
188  path.SetFilename("send.rtpdump");
189  std::string send_file = path.pathname();
190  path.SetFilename("recv.rtpdump");
191  std::string recv_file = path.pathname();
192  if (video_media_channel) {
193    EXPECT_TRUE(recorder->AddChannel(static_cast<VideoChannel*>(channel),
194                                     Open(send_file), Open(recv_file), filter));
195  } else {
196    EXPECT_TRUE(recorder->AddChannel(static_cast<VoiceChannel*>(channel),
197                                     Open(send_file), Open(recv_file), filter));
198  }
199
200  // Enable recording only the sent media.
201  EXPECT_TRUE(recorder->EnableChannel(channel, true, false, SINK_PRE_CRYPTO));
202  EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO));
203  EXPECT_FALSE(channel->HasRecvSinks(SINK_POST_CRYPTO));
204  EXPECT_FALSE(channel->HasSendSinks(SINK_POST_CRYPTO));
205  EXPECT_FALSE(channel->HasRecvSinks(SINK_POST_CRYPTO));
206  if (video_media_channel) {
207    EXPECT_TRUE_WAIT(video_media_channel->sent_intra_frame(), 100);
208  }
209
210  // Enable recording only the received meida.
211  EXPECT_TRUE(recorder->EnableChannel(channel, false, true, SINK_PRE_CRYPTO));
212  EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO));
213  EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO));
214  if (video_media_channel) {
215    EXPECT_TRUE(video_media_channel->requested_intra_frame());
216  }
217
218  // Enable recording both the sent and the received video.
219  EXPECT_TRUE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO));
220  EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO));
221  EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO));
222
223  // Enable recording only headers.
224  if (video_media_channel) {
225    video_media_channel->set_sent_intra_frame(false);
226    video_media_channel->set_requested_intra_frame(false);
227  }
228  EXPECT_TRUE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO));
229  EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO));
230  EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO));
231  if (video_media_channel) {
232    if ((filter & PF_RTPPACKET) == PF_RTPPACKET) {
233      // If record the whole RTP packet, trigers FIR.
234      EXPECT_TRUE(video_media_channel->requested_intra_frame());
235      EXPECT_TRUE(video_media_channel->sent_intra_frame());
236    } else {
237      // If record only the RTP header, does not triger FIR.
238      EXPECT_FALSE(video_media_channel->requested_intra_frame());
239      EXPECT_FALSE(video_media_channel->sent_intra_frame());
240    }
241  }
242
243  // Remove the voice channel from the recorder.
244  recorder->RemoveChannel(channel, SINK_PRE_CRYPTO);
245  EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO));
246  EXPECT_FALSE(channel->HasRecvSinks(SINK_PRE_CRYPTO));
247
248  // Delete all files.
249  recorder.reset();
250  EXPECT_TRUE(rtc::Filesystem::DeleteFile(send_file));
251  EXPECT_TRUE(rtc::Filesystem::DeleteFile(recv_file));
252}
253
254// Fisrt start recording header and then start recording media. Verify that
255// differnt files are created for header and media.
256void TestRecordHeaderAndMedia(BaseChannel* channel,
257                              FakeVideoMediaChannel* video_media_channel) {
258  // Create RTP header recorder.
259  rtc::scoped_ptr<MediaRecorder> header_recorder(new MediaRecorder);
260
261  rtc::Pathname path;
262  EXPECT_TRUE(rtc::Filesystem::GetTemporaryFolder(path, true, NULL));
263  path.SetFilename("send-header.rtpdump");
264  std::string send_header_file = path.pathname();
265  path.SetFilename("recv-header.rtpdump");
266  std::string recv_header_file = path.pathname();
267  if (video_media_channel) {
268    EXPECT_TRUE(header_recorder->AddChannel(
269        static_cast<VideoChannel*>(channel),
270        Open(send_header_file), Open(recv_header_file), PF_RTPHEADER));
271  } else {
272    EXPECT_TRUE(header_recorder->AddChannel(
273        static_cast<VoiceChannel*>(channel),
274        Open(send_header_file), Open(recv_header_file), PF_RTPHEADER));
275  }
276
277  // Enable recording both sent and received.
278  EXPECT_TRUE(
279      header_recorder->EnableChannel(channel, true, true, SINK_POST_CRYPTO));
280  EXPECT_TRUE(channel->HasSendSinks(SINK_POST_CRYPTO));
281  EXPECT_TRUE(channel->HasRecvSinks(SINK_POST_CRYPTO));
282  EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO));
283  EXPECT_FALSE(channel->HasRecvSinks(SINK_PRE_CRYPTO));
284  if (video_media_channel) {
285    EXPECT_FALSE(video_media_channel->sent_intra_frame());
286    EXPECT_FALSE(video_media_channel->requested_intra_frame());
287  }
288
289  // Verify that header files are created.
290  EXPECT_TRUE(rtc::Filesystem::IsFile(send_header_file));
291  EXPECT_TRUE(rtc::Filesystem::IsFile(recv_header_file));
292
293  // Create RTP header recorder.
294  rtc::scoped_ptr<MediaRecorder> recorder(new MediaRecorder);
295  path.SetFilename("send.rtpdump");
296  std::string send_file = path.pathname();
297  path.SetFilename("recv.rtpdump");
298  std::string recv_file = path.pathname();
299  if (video_media_channel) {
300    EXPECT_TRUE(recorder->AddChannel(
301        static_cast<VideoChannel*>(channel),
302        Open(send_file), Open(recv_file), PF_RTPPACKET));
303  } else {
304    EXPECT_TRUE(recorder->AddChannel(
305        static_cast<VoiceChannel*>(channel),
306        Open(send_file), Open(recv_file), PF_RTPPACKET));
307  }
308
309  // Enable recording both sent and received.
310  EXPECT_TRUE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO));
311  EXPECT_TRUE(channel->HasSendSinks(SINK_POST_CRYPTO));
312  EXPECT_TRUE(channel->HasRecvSinks(SINK_POST_CRYPTO));
313  EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO));
314  EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO));
315  if (video_media_channel) {
316    EXPECT_TRUE_WAIT(video_media_channel->sent_intra_frame(), 100);
317    EXPECT_TRUE(video_media_channel->requested_intra_frame());
318  }
319
320  // Verify that media files are created.
321  EXPECT_TRUE(rtc::Filesystem::IsFile(send_file));
322  EXPECT_TRUE(rtc::Filesystem::IsFile(recv_file));
323
324  // Delete all files.
325  header_recorder.reset();
326  recorder.reset();
327  EXPECT_TRUE(rtc::Filesystem::DeleteFile(send_header_file));
328  EXPECT_TRUE(rtc::Filesystem::DeleteFile(recv_header_file));
329  EXPECT_TRUE(rtc::Filesystem::DeleteFile(send_file));
330  EXPECT_TRUE(rtc::Filesystem::DeleteFile(recv_file));
331}
332
333TEST(MediaRecorderTest, TestMediaRecorderVoiceChannel) {
334  // Create the voice channel.
335  FakeSession session(true);
336  FakeMediaEngine media_engine;
337  VoiceChannel channel(rtc::Thread::Current(), &media_engine,
338                       new FakeVoiceMediaChannel(NULL), &session, "", false);
339  EXPECT_TRUE(channel.Init());
340  TestMediaRecorder(&channel, NULL, PF_RTPPACKET);
341  TestMediaRecorder(&channel, NULL, PF_RTPHEADER);
342  TestRecordHeaderAndMedia(&channel, NULL);
343}
344
345TEST(MediaRecorderTest, TestMediaRecorderVideoChannel) {
346  // Create the video channel.
347  FakeSession session(true);
348  FakeMediaEngine media_engine;
349  FakeVideoMediaChannel* media_channel = new FakeVideoMediaChannel(NULL);
350  VideoChannel channel(rtc::Thread::Current(), &media_engine,
351                       media_channel, &session, "", false, NULL);
352  EXPECT_TRUE(channel.Init());
353  TestMediaRecorder(&channel, media_channel, PF_RTPPACKET);
354  TestMediaRecorder(&channel, media_channel, PF_RTPHEADER);
355  TestRecordHeaderAndMedia(&channel, media_channel);
356}
357
358}  // namespace cricket
359