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