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 40 namespace cricket { 41 42 rtc::StreamInterface* Open(const std::string& path) { 43 return rtc::Filesystem::OpenFile( 44 rtc::Pathname(path), "wb"); 45 } 46 47 ///////////////////////////////////////////////////////////////////////// 48 // Test RtpDumpSink 49 ///////////////////////////////////////////////////////////////////////// 50 class 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 91 TEST_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 116 TEST_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 137 TEST_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 ///////////////////////////////////////////////////////////////////////// 173 void 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. 256 void 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 333 TEST(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 345 TEST(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