Remove media sinks from Channel. Allows removing MediaRecorder which isn't in use apart from channel unittests, along with it unittests for MediaRecorder that are flaky when run in parallel can also go. BUG= R=pthatcher@webrtc.org Review URL: https://codereview.webrtc.org/1219663008 Cr-Commit-Position: refs/heads/master@{#9558}
diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index ed925e3..9de1c9b 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp
@@ -676,8 +676,6 @@ 'session/media/currentspeakermonitor.h', 'session/media/mediamonitor.cc', 'session/media/mediamonitor.h', - 'session/media/mediarecorder.cc', - 'session/media/mediarecorder.h', 'session/media/mediasession.cc', 'session/media/mediasession.h', 'session/media/mediasink.h',
diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp index 571e9ee..d08a236 100755 --- a/talk/libjingle_tests.gyp +++ b/talk/libjingle_tests.gyp
@@ -156,7 +156,6 @@ 'session/media/channel_unittest.cc', 'session/media/channelmanager_unittest.cc', 'session/media/currentspeakermonitor_unittest.cc', - 'session/media/mediarecorder_unittest.cc', 'session/media/mediasession_unittest.cc', 'session/media/rtcpmuxfilter_unittest.cc', 'session/media/srtpfilter_unittest.cc',
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc index 95f5bc1..0134d34 100644 --- a/talk/session/media/channel.cc +++ b/talk/session/media/channel.cc
@@ -502,12 +502,6 @@ return false; } - // Signal to the media sink before protecting the packet. - { - rtc::CritScope cs(&signal_send_packet_cs_); - SignalSendPacketPreCrypto(packet->data(), packet->size(), rtcp); - } - rtc::PacketOptions options(dscp); // Protect if needed. if (srtp_filter_.IsActive()) { @@ -576,12 +570,6 @@ return false; } - // Signal to the media sink after protecting the packet. - { - rtc::CritScope cs(&signal_send_packet_cs_); - SignalSendPacketPostCrypto(packet->data(), packet->size(), rtcp); - } - // Bon voyage. int ret = channel->SendPacket(packet->data<char>(), packet->size(), options, @@ -622,12 +610,6 @@ signaling_thread()->Post(this, MSG_FIRSTPACKETRECEIVED); } - // Signal to the media sink before unprotecting the packet. - { - rtc::CritScope cs(&signal_recv_packet_cs_); - SignalRecvPacketPostCrypto(packet->data(), packet->size(), rtcp); - } - // Unprotect the packet, if needed. if (srtp_filter_.IsActive()) { char* data = packet->data<char>(); @@ -673,12 +655,6 @@ return; } - // Signal to the media sink after unprotecting the packet. - { - rtc::CritScope cs(&signal_recv_packet_cs_); - SignalRecvPacketPreCrypto(packet->data(), packet->size(), rtcp); - } - // Push it down to the media channel. if (!rtcp) { media_channel_->OnPacketReceived(packet, packet_time);
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h index 904777e..bb2dffd 100644 --- a/talk/session/media/channel.h +++ b/talk/session/media/channel.h
@@ -148,72 +148,6 @@ srtp_filter_.set_signal_silent_time(silent_time); } - template <class T> - void RegisterSendSink(T* sink, - void (T::*OnPacket)(const void*, size_t, bool), - SinkType type) { - rtc::CritScope cs(&signal_send_packet_cs_); - if (SINK_POST_CRYPTO == type) { - SignalSendPacketPostCrypto.disconnect(sink); - SignalSendPacketPostCrypto.connect(sink, OnPacket); - } else { - SignalSendPacketPreCrypto.disconnect(sink); - SignalSendPacketPreCrypto.connect(sink, OnPacket); - } - } - - void UnregisterSendSink(sigslot::has_slots<>* sink, - SinkType type) { - rtc::CritScope cs(&signal_send_packet_cs_); - if (SINK_POST_CRYPTO == type) { - SignalSendPacketPostCrypto.disconnect(sink); - } else { - SignalSendPacketPreCrypto.disconnect(sink); - } - } - - bool HasSendSinks(SinkType type) { - rtc::CritScope cs(&signal_send_packet_cs_); - if (SINK_POST_CRYPTO == type) { - return !SignalSendPacketPostCrypto.is_empty(); - } else { - return !SignalSendPacketPreCrypto.is_empty(); - } - } - - template <class T> - void RegisterRecvSink(T* sink, - void (T::*OnPacket)(const void*, size_t, bool), - SinkType type) { - rtc::CritScope cs(&signal_recv_packet_cs_); - if (SINK_POST_CRYPTO == type) { - SignalRecvPacketPostCrypto.disconnect(sink); - SignalRecvPacketPostCrypto.connect(sink, OnPacket); - } else { - SignalRecvPacketPreCrypto.disconnect(sink); - SignalRecvPacketPreCrypto.connect(sink, OnPacket); - } - } - - void UnregisterRecvSink(sigslot::has_slots<>* sink, - SinkType type) { - rtc::CritScope cs(&signal_recv_packet_cs_); - if (SINK_POST_CRYPTO == type) { - SignalRecvPacketPostCrypto.disconnect(sink); - } else { - SignalRecvPacketPreCrypto.disconnect(sink); - } - } - - bool HasRecvSinks(SinkType type) { - rtc::CritScope cs(&signal_recv_packet_cs_); - if (SINK_POST_CRYPTO == type) { - return !SignalRecvPacketPostCrypto.is_empty(); - } else { - return !SignalRecvPacketPreCrypto.is_empty(); - } - } - BundleFilter* bundle_filter() { return &bundle_filter_; } const std::vector<StreamParams>& local_streams() const { @@ -377,13 +311,6 @@ } private: - sigslot::signal3<const void*, size_t, bool> SignalSendPacketPreCrypto; - sigslot::signal3<const void*, size_t, bool> SignalSendPacketPostCrypto; - sigslot::signal3<const void*, size_t, bool> SignalRecvPacketPreCrypto; - sigslot::signal3<const void*, size_t, bool> SignalRecvPacketPostCrypto; - rtc::CriticalSection signal_send_packet_cs_; - rtc::CriticalSection signal_recv_packet_cs_; - rtc::Thread* worker_thread_; MediaEngineInterface* media_engine_; BaseSession* session_;
diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc index 8128190..4a82f55 100644 --- a/talk/session/media/channel_unittest.cc +++ b/talk/session/media/channel_unittest.cc
@@ -35,7 +35,6 @@ #include "talk/media/base/testutils.h" #include "webrtc/p2p/base/fakesession.h" #include "talk/session/media/channel.h" -#include "talk/session/media/mediarecorder.h" #include "talk/session/media/typingmonitor.h" #include "webrtc/base/fileutils.h" #include "webrtc/base/gunit.h" @@ -1558,67 +1557,6 @@ channel1_->StopMediaMonitor(); } - void TestMediaSinks() { - CreateChannels(0, 0); - EXPECT_TRUE(SendInitiate()); - EXPECT_TRUE(SendAccept()); - EXPECT_FALSE(channel1_->HasSendSinks(cricket::SINK_POST_CRYPTO)); - EXPECT_FALSE(channel1_->HasRecvSinks(cricket::SINK_POST_CRYPTO)); - EXPECT_FALSE(channel1_->HasSendSinks(cricket::SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel1_->HasRecvSinks(cricket::SINK_PRE_CRYPTO)); - - rtc::Pathname path; - EXPECT_TRUE(rtc::Filesystem::GetTemporaryFolder(path, true, NULL)); - path.SetFilename("sink-test.rtpdump"); - rtc::scoped_ptr<cricket::RtpDumpSink> sink( - new cricket::RtpDumpSink(Open(path.pathname()))); - sink->set_packet_filter(cricket::PF_ALL); - EXPECT_TRUE(sink->Enable(true)); - channel1_->RegisterSendSink( - sink.get(), &cricket::RtpDumpSink::OnPacket, cricket::SINK_POST_CRYPTO); - EXPECT_TRUE(channel1_->HasSendSinks(cricket::SINK_POST_CRYPTO)); - EXPECT_FALSE(channel1_->HasRecvSinks(cricket::SINK_POST_CRYPTO)); - EXPECT_FALSE(channel1_->HasSendSinks(cricket::SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel1_->HasRecvSinks(cricket::SINK_PRE_CRYPTO)); - - // The first packet is recorded with header + data. - EXPECT_TRUE(SendRtp1()); - // The second packet is recorded with header only. - sink->set_packet_filter(cricket::PF_RTPHEADER); - EXPECT_TRUE(SendRtp1()); - // The third packet is not recorded since sink is disabled. - EXPECT_TRUE(sink->Enable(false)); - EXPECT_TRUE(SendRtp1()); - // The fourth packet is not recorded since sink is unregistered. - EXPECT_TRUE(sink->Enable(true)); - channel1_->UnregisterSendSink(sink.get(), cricket::SINK_POST_CRYPTO); - EXPECT_TRUE(SendRtp1()); - sink.reset(); // This will close the file. - - // Read the recorded file and verify two packets. - rtc::scoped_ptr<rtc::StreamInterface> stream( - rtc::Filesystem::OpenFile(path, "rb")); - - cricket::RtpDumpReader reader(stream.get()); - cricket::RtpDumpPacket packet; - EXPECT_EQ(rtc::SR_SUCCESS, reader.ReadPacket(&packet)); - std::string read_packet(reinterpret_cast<const char*>(&packet.data[0]), - packet.data.size()); - EXPECT_EQ(rtp_packet_, read_packet); - - EXPECT_EQ(rtc::SR_SUCCESS, reader.ReadPacket(&packet)); - size_t len = 0; - packet.GetRtpHeaderLen(&len); - EXPECT_EQ(len, packet.data.size()); - EXPECT_EQ(0, memcmp(&packet.data[0], rtp_packet_.c_str(), len)); - - EXPECT_EQ(rtc::SR_EOS, reader.ReadPacket(&packet)); - - // Delete the file for media recording. - stream.reset(); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(path)); - } - void TestSetContentFailure() { CreateChannels(0, 0); typename T::Content content; @@ -2316,10 +2254,6 @@ 3, 7, 120, cricket::DF_PLAY | cricket::DF_SEND)); } -TEST_F(VoiceChannelTest, TestMediaSinks) { - Base::TestMediaSinks(); -} - TEST_F(VoiceChannelTest, TestSetContentFailure) { Base::TestSetContentFailure(); } @@ -2677,10 +2611,6 @@ Base::TestMediaMonitor(); } -TEST_F(VideoChannelTest, TestMediaSinks) { - Base::TestMediaSinks(); -} - TEST_F(VideoChannelTest, TestSetContentFailure) { Base::TestSetContentFailure(); }
diff --git a/talk/session/media/mediarecorder.cc b/talk/session/media/mediarecorder.cc deleted file mode 100644 index 9ce84f3..0000000 --- a/talk/session/media/mediarecorder.cc +++ /dev/null
@@ -1,224 +0,0 @@ -/* - * libjingle - * Copyright 2010 Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "talk/session/media/mediarecorder.h" - -#include <limits.h> - -#include <string> - -#include "talk/media/base/rtpdump.h" -#include "webrtc/base/fileutils.h" -#include "webrtc/base/logging.h" -#include "webrtc/base/pathutils.h" - - -namespace cricket { - -/////////////////////////////////////////////////////////////////////////// -// Implementation of RtpDumpSink. -/////////////////////////////////////////////////////////////////////////// -RtpDumpSink::RtpDumpSink(rtc::StreamInterface* stream) - : max_size_(INT_MAX), - recording_(false), - packet_filter_(PF_NONE) { - stream_.reset(stream); -} - -RtpDumpSink::~RtpDumpSink() {} - -void RtpDumpSink::SetMaxSize(size_t size) { - rtc::CritScope cs(&critical_section_); - max_size_ = size; -} - -bool RtpDumpSink::Enable(bool enable) { - rtc::CritScope cs(&critical_section_); - - recording_ = enable; - - // Create a file and the RTP writer if we have not done yet. - if (recording_ && !writer_) { - if (!stream_) { - return false; - } - writer_.reset(new RtpDumpWriter(stream_.get())); - writer_->set_packet_filter(packet_filter_); - } else if (!recording_ && stream_) { - stream_->Flush(); - } - return true; -} - -void RtpDumpSink::OnPacket(const void* data, size_t size, bool rtcp) { - rtc::CritScope cs(&critical_section_); - - if (recording_ && writer_) { - size_t current_size; - if (writer_->GetDumpSize(¤t_size) && - current_size + RtpDumpPacket::kHeaderLength + size <= max_size_) { - if (!rtcp) { - writer_->WriteRtpPacket(data, size); - } else { - // TODO(whyuan): Enable recording RTCP. - } - } - } -} - -void RtpDumpSink::set_packet_filter(int filter) { - rtc::CritScope cs(&critical_section_); - packet_filter_ = filter; - if (writer_) { - writer_->set_packet_filter(packet_filter_); - } -} - -void RtpDumpSink::Flush() { - rtc::CritScope cs(&critical_section_); - if (stream_) { - stream_->Flush(); - } -} - -/////////////////////////////////////////////////////////////////////////// -// Implementation of MediaRecorder. -/////////////////////////////////////////////////////////////////////////// -MediaRecorder::MediaRecorder() {} - -MediaRecorder::~MediaRecorder() { - rtc::CritScope cs(&critical_section_); - std::map<BaseChannel*, SinkPair*>::iterator itr; - for (itr = sinks_.begin(); itr != sinks_.end(); ++itr) { - delete itr->second; - } -} - -bool MediaRecorder::AddChannel(VoiceChannel* channel, - rtc::StreamInterface* send_stream, - rtc::StreamInterface* recv_stream, - int filter) { - return InternalAddChannel(channel, false, send_stream, recv_stream, - filter); -} -bool MediaRecorder::AddChannel(VideoChannel* channel, - rtc::StreamInterface* send_stream, - rtc::StreamInterface* recv_stream, - int filter) { - return InternalAddChannel(channel, true, send_stream, recv_stream, - filter); -} - -bool MediaRecorder::InternalAddChannel(BaseChannel* channel, - bool video_channel, - rtc::StreamInterface* send_stream, - rtc::StreamInterface* recv_stream, - int filter) { - if (!channel) { - return false; - } - - rtc::CritScope cs(&critical_section_); - if (sinks_.end() != sinks_.find(channel)) { - return false; // The channel was added already. - } - - SinkPair* sink_pair = new SinkPair; - sink_pair->video_channel = video_channel; - sink_pair->filter = filter; - sink_pair->send_sink.reset(new RtpDumpSink(send_stream)); - sink_pair->send_sink->set_packet_filter(filter); - sink_pair->recv_sink.reset(new RtpDumpSink(recv_stream)); - sink_pair->recv_sink->set_packet_filter(filter); - sinks_[channel] = sink_pair; - - return true; -} - -void MediaRecorder::RemoveChannel(BaseChannel* channel, - SinkType type) { - rtc::CritScope cs(&critical_section_); - std::map<BaseChannel*, SinkPair*>::iterator itr = sinks_.find(channel); - if (sinks_.end() != itr) { - channel->UnregisterSendSink(itr->second->send_sink.get(), type); - channel->UnregisterRecvSink(itr->second->recv_sink.get(), type); - delete itr->second; - sinks_.erase(itr); - } -} - -bool MediaRecorder::EnableChannel( - BaseChannel* channel, bool enable_send, bool enable_recv, - SinkType type) { - rtc::CritScope cs(&critical_section_); - std::map<BaseChannel*, SinkPair*>::iterator itr = sinks_.find(channel); - if (sinks_.end() == itr) { - return false; - } - - SinkPair* sink_pair = itr->second; - RtpDumpSink* sink = sink_pair->send_sink.get(); - sink->Enable(enable_send); - if (enable_send) { - channel->RegisterSendSink(sink, &RtpDumpSink::OnPacket, type); - } else { - channel->UnregisterSendSink(sink, type); - } - - sink = sink_pair->recv_sink.get(); - sink->Enable(enable_recv); - if (enable_recv) { - channel->RegisterRecvSink(sink, &RtpDumpSink::OnPacket, type); - } else { - channel->UnregisterRecvSink(sink, type); - } - - if (sink_pair->video_channel && - (sink_pair->filter & PF_RTPPACKET) == PF_RTPPACKET) { - // Request a full intra frame. - VideoChannel* video_channel = static_cast<VideoChannel*>(channel); - if (enable_send) { - video_channel->SendIntraFrame(); - } - if (enable_recv) { - video_channel->RequestIntraFrame(); - } - } - - return true; -} - -void MediaRecorder::FlushSinks() { - rtc::CritScope cs(&critical_section_); - std::map<BaseChannel*, SinkPair*>::iterator itr; - for (itr = sinks_.begin(); itr != sinks_.end(); ++itr) { - itr->second->send_sink->Flush(); - itr->second->recv_sink->Flush(); - } -} - -} // namespace cricket
diff --git a/talk/session/media/mediarecorder.h b/talk/session/media/mediarecorder.h deleted file mode 100644 index 095f355..0000000 --- a/talk/session/media/mediarecorder.h +++ /dev/null
@@ -1,119 +0,0 @@ -/* - * libjingle - * Copyright 2010 Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef TALK_SESSION_MEDIA_MEDIARECORDER_H_ -#define TALK_SESSION_MEDIA_MEDIARECORDER_H_ - -#include <map> -#include <string> - -#include "talk/session/media/channel.h" -#include "talk/session/media/mediasink.h" -#include "webrtc/base/criticalsection.h" -#include "webrtc/base/scoped_ptr.h" -#include "webrtc/base/sigslot.h" - -namespace rtc { -class Pathname; -class FileStream; -} - -namespace cricket { - -class BaseChannel; -class VideoChannel; -class VoiceChannel; -class RtpDumpWriter; - -// RtpDumpSink implements MediaSinkInterface by dumping the RTP/RTCP packets to -// a file. -class RtpDumpSink : public MediaSinkInterface, public sigslot::has_slots<> { - public: - // Takes ownership of stream. - explicit RtpDumpSink(rtc::StreamInterface* stream); - virtual ~RtpDumpSink(); - - virtual void SetMaxSize(size_t size); - virtual bool Enable(bool enable); - virtual bool IsEnabled() const { return recording_; } - virtual void OnPacket(const void* data, size_t size, bool rtcp); - virtual void set_packet_filter(int filter); - int packet_filter() const { return packet_filter_; } - void Flush(); - - private: - size_t max_size_; - bool recording_; - int packet_filter_; - rtc::scoped_ptr<rtc::StreamInterface> stream_; - rtc::scoped_ptr<RtpDumpWriter> writer_; - rtc::CriticalSection critical_section_; - - DISALLOW_COPY_AND_ASSIGN(RtpDumpSink); -}; - -class MediaRecorder { - public: - MediaRecorder(); - virtual ~MediaRecorder(); - - bool AddChannel(VoiceChannel* channel, - rtc::StreamInterface* send_stream, - rtc::StreamInterface* recv_stream, - int filter); - bool AddChannel(VideoChannel* channel, - rtc::StreamInterface* send_stream, - rtc::StreamInterface* recv_stream, - int filter); - void RemoveChannel(BaseChannel* channel, SinkType type); - bool EnableChannel(BaseChannel* channel, bool enable_send, bool enable_recv, - SinkType type); - void FlushSinks(); - - private: - struct SinkPair { - bool video_channel; - int filter; - rtc::scoped_ptr<RtpDumpSink> send_sink; - rtc::scoped_ptr<RtpDumpSink> recv_sink; - }; - - bool InternalAddChannel(BaseChannel* channel, - bool video_channel, - rtc::StreamInterface* send_stream, - rtc::StreamInterface* recv_stream, - int filter); - - std::map<BaseChannel*, SinkPair*> sinks_; - rtc::CriticalSection critical_section_; - - DISALLOW_COPY_AND_ASSIGN(MediaRecorder); -}; - -} // namespace cricket - -#endif // TALK_SESSION_MEDIA_MEDIARECORDER_H_
diff --git a/talk/session/media/mediarecorder_unittest.cc b/talk/session/media/mediarecorder_unittest.cc deleted file mode 100644 index 52a9245..0000000 --- a/talk/session/media/mediarecorder_unittest.cc +++ /dev/null
@@ -1,356 +0,0 @@ -/* - * libjingle - * Copyright 2010 Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <string> - -#include "talk/media/base/fakemediaengine.h" -#include "talk/media/base/rtpdump.h" -#include "talk/media/base/testutils.h" -#include "webrtc/p2p/base/fakesession.h" -#include "talk/session/media/channel.h" -#include "talk/session/media/mediarecorder.h" -#include "webrtc/base/bytebuffer.h" -#include "webrtc/base/fileutils.h" -#include "webrtc/base/gunit.h" -#include "webrtc/base/pathutils.h" -#include "webrtc/base/thread.h" - -namespace cricket { - -rtc::StreamInterface* Open(const std::string& path) { - return rtc::Filesystem::OpenFile( - rtc::Pathname(path), "wb"); -} - -///////////////////////////////////////////////////////////////////////// -// Test RtpDumpSink -///////////////////////////////////////////////////////////////////////// -class RtpDumpSinkTest : public testing::Test { - public: - virtual void SetUp() { - EXPECT_TRUE(rtc::Filesystem::GetTemporaryFolder(path_, true, NULL)); - path_.SetPathname(rtc::Filesystem::TempFilename(path_, "sink-test")); - sink_.reset(new RtpDumpSink(Open(path_.pathname()))); - - for (int i = 0; i < ARRAY_SIZE(rtp_buf_); ++i) { - RtpTestUtility::kTestRawRtpPackets[i].WriteToByteBuffer( - RtpTestUtility::kDefaultSsrc, &rtp_buf_[i]); - } - } - - virtual void TearDown() { - stream_.reset(); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(path_)); - } - - protected: - void OnRtpPacket(const RawRtpPacket& raw) { - rtc::ByteBuffer buf; - raw.WriteToByteBuffer(RtpTestUtility::kDefaultSsrc, &buf); - sink_->OnPacket(buf.Data(), buf.Length(), false); - } - - rtc::StreamResult ReadPacket(RtpDumpPacket* packet) { - if (!stream_.get()) { - sink_.reset(); // This will close the file. So we can read it. - stream_.reset(rtc::Filesystem::OpenFile(path_, "rb")); - reader_.reset(new RtpDumpReader(stream_.get())); - } - return reader_->ReadPacket(packet); - } - - rtc::Pathname path_; - rtc::scoped_ptr<RtpDumpSink> sink_; - rtc::ByteBuffer rtp_buf_[3]; - rtc::scoped_ptr<rtc::StreamInterface> stream_; - rtc::scoped_ptr<RtpDumpReader> reader_; -}; - -TEST_F(RtpDumpSinkTest, TestRtpDumpSink) { - // By default, the sink is disabled. The 1st packet is not written. - EXPECT_FALSE(sink_->IsEnabled()); - sink_->set_packet_filter(PF_ALL); - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[0]); - - // Enable the sink. The 2nd packet is written. - EXPECT_TRUE(sink_->Enable(true)); - EXPECT_TRUE(sink_->IsEnabled()); - EXPECT_TRUE(rtc::Filesystem::IsFile(path_.pathname())); - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[1]); - - // Disable the sink. The 3rd packet is not written. - EXPECT_TRUE(sink_->Enable(false)); - EXPECT_FALSE(sink_->IsEnabled()); - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[2]); - - // Read the recorded file and verify it contains only the 2nd packet. - RtpDumpPacket packet; - EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet)); - EXPECT_TRUE(RtpTestUtility::VerifyPacket( - &packet, &RtpTestUtility::kTestRawRtpPackets[1], false)); - EXPECT_EQ(rtc::SR_EOS, ReadPacket(&packet)); -} - -TEST_F(RtpDumpSinkTest, TestRtpDumpSinkMaxSize) { - EXPECT_TRUE(sink_->Enable(true)); - sink_->set_packet_filter(PF_ALL); - sink_->SetMaxSize(strlen(RtpDumpFileHeader::kFirstLine) + - RtpDumpFileHeader::kHeaderLength + - RtpDumpPacket::kHeaderLength + - RtpTestUtility::kTestRawRtpPackets[0].size()); - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[0]); - - // Exceed the limit size: the 2nd and 3rd packets are not written. - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[1]); - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[2]); - - // Read the recorded file and verify that it contains only the first packet. - RtpDumpPacket packet; - EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet)); - EXPECT_TRUE(RtpTestUtility::VerifyPacket( - &packet, &RtpTestUtility::kTestRawRtpPackets[0], false)); - EXPECT_EQ(rtc::SR_EOS, ReadPacket(&packet)); -} - -TEST_F(RtpDumpSinkTest, TestRtpDumpSinkFilter) { - // The default filter is PF_NONE. - EXPECT_EQ(PF_NONE, sink_->packet_filter()); - - // Set to PF_RTPHEADER before enable. - sink_->set_packet_filter(PF_RTPHEADER); - EXPECT_EQ(PF_RTPHEADER, sink_->packet_filter()); - EXPECT_TRUE(sink_->Enable(true)); - // We dump only the header of the first packet. - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[0]); - - // Set the filter to PF_RTPPACKET. We dump all the second packet. - sink_->set_packet_filter(PF_RTPPACKET); - EXPECT_EQ(PF_RTPPACKET, sink_->packet_filter()); - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[1]); - - // Set the filter to PF_NONE. We do not dump the third packet. - sink_->set_packet_filter(PF_NONE); - EXPECT_EQ(PF_NONE, sink_->packet_filter()); - OnRtpPacket(RtpTestUtility::kTestRawRtpPackets[2]); - - // Read the recorded file and verify the header of the first packet and - // the whole packet for the second packet. - RtpDumpPacket packet; - EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet)); - EXPECT_TRUE(RtpTestUtility::VerifyPacket( - &packet, &RtpTestUtility::kTestRawRtpPackets[0], true)); - EXPECT_EQ(rtc::SR_SUCCESS, ReadPacket(&packet)); - EXPECT_TRUE(RtpTestUtility::VerifyPacket( - &packet, &RtpTestUtility::kTestRawRtpPackets[1], false)); - EXPECT_EQ(rtc::SR_EOS, ReadPacket(&packet)); -} - -///////////////////////////////////////////////////////////////////////// -// Test MediaRecorder -///////////////////////////////////////////////////////////////////////// -void TestMediaRecorder(BaseChannel* channel, - FakeVideoMediaChannel* video_media_channel, - int filter) { - // Create media recorder. - rtc::scoped_ptr<MediaRecorder> recorder(new MediaRecorder); - // Fail to EnableChannel before AddChannel. - EXPECT_FALSE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel->HasRecvSinks(SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel->HasSendSinks(SINK_POST_CRYPTO)); - EXPECT_FALSE(channel->HasRecvSinks(SINK_POST_CRYPTO)); - - // Add the channel to the recorder. - rtc::Pathname path; - EXPECT_TRUE(rtc::Filesystem::GetTemporaryFolder(path, true, NULL)); - std::string send_file = - rtc::Filesystem::TempFilename(path, "send"); - std::string recv_file = - rtc::Filesystem::TempFilename(path, "recv"); - if (video_media_channel) { - EXPECT_TRUE(recorder->AddChannel(static_cast<VideoChannel*>(channel), - Open(send_file), Open(recv_file), filter)); - } else { - EXPECT_TRUE(recorder->AddChannel(static_cast<VoiceChannel*>(channel), - Open(send_file), Open(recv_file), filter)); - } - - // Enable recording only the sent media. - EXPECT_TRUE(recorder->EnableChannel(channel, true, false, SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel->HasRecvSinks(SINK_POST_CRYPTO)); - EXPECT_FALSE(channel->HasSendSinks(SINK_POST_CRYPTO)); - EXPECT_FALSE(channel->HasRecvSinks(SINK_POST_CRYPTO)); - if (video_media_channel) { - EXPECT_TRUE_WAIT(video_media_channel->sent_intra_frame(), 100); - } - - // Enable recording only the received meida. - EXPECT_TRUE(recorder->EnableChannel(channel, false, true, SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO)); - if (video_media_channel) { - EXPECT_TRUE(video_media_channel->requested_intra_frame()); - } - - // Enable recording both the sent and the received video. - EXPECT_TRUE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO)); - - // Enable recording only headers. - if (video_media_channel) { - video_media_channel->set_sent_intra_frame(false); - video_media_channel->set_requested_intra_frame(false); - } - EXPECT_TRUE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO)); - if (video_media_channel) { - if ((filter & PF_RTPPACKET) == PF_RTPPACKET) { - // If record the whole RTP packet, trigers FIR. - EXPECT_TRUE(video_media_channel->requested_intra_frame()); - EXPECT_TRUE(video_media_channel->sent_intra_frame()); - } else { - // If record only the RTP header, does not triger FIR. - EXPECT_FALSE(video_media_channel->requested_intra_frame()); - EXPECT_FALSE(video_media_channel->sent_intra_frame()); - } - } - - // Remove the voice channel from the recorder. - recorder->RemoveChannel(channel, SINK_PRE_CRYPTO); - EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel->HasRecvSinks(SINK_PRE_CRYPTO)); - - // Delete all files. - recorder.reset(); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(send_file)); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(recv_file)); -} - -// Fisrt start recording header and then start recording media. Verify that -// differnt files are created for header and media. -void TestRecordHeaderAndMedia(BaseChannel* channel, - FakeVideoMediaChannel* video_media_channel) { - // Create RTP header recorder. - rtc::scoped_ptr<MediaRecorder> header_recorder(new MediaRecorder); - - rtc::Pathname path; - EXPECT_TRUE(rtc::Filesystem::GetTemporaryFolder(path, true, NULL)); - std::string send_header_file = - rtc::Filesystem::TempFilename(path, "send-header"); - std::string recv_header_file = - rtc::Filesystem::TempFilename(path, "recv-header"); - if (video_media_channel) { - EXPECT_TRUE(header_recorder->AddChannel( - static_cast<VideoChannel*>(channel), - Open(send_header_file), Open(recv_header_file), PF_RTPHEADER)); - } else { - EXPECT_TRUE(header_recorder->AddChannel( - static_cast<VoiceChannel*>(channel), - Open(send_header_file), Open(recv_header_file), PF_RTPHEADER)); - } - - // Enable recording both sent and received. - EXPECT_TRUE( - header_recorder->EnableChannel(channel, true, true, SINK_POST_CRYPTO)); - EXPECT_TRUE(channel->HasSendSinks(SINK_POST_CRYPTO)); - EXPECT_TRUE(channel->HasRecvSinks(SINK_POST_CRYPTO)); - EXPECT_FALSE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_FALSE(channel->HasRecvSinks(SINK_PRE_CRYPTO)); - if (video_media_channel) { - EXPECT_FALSE(video_media_channel->sent_intra_frame()); - EXPECT_FALSE(video_media_channel->requested_intra_frame()); - } - - // Verify that header files are created. - EXPECT_TRUE(rtc::Filesystem::IsFile(send_header_file)); - EXPECT_TRUE(rtc::Filesystem::IsFile(recv_header_file)); - - // Create RTP header recorder. - rtc::scoped_ptr<MediaRecorder> recorder(new MediaRecorder); - std::string send_file = - rtc::Filesystem::TempFilename(path, "send"); - std::string recv_file = - rtc::Filesystem::TempFilename(path, "recv"); - if (video_media_channel) { - EXPECT_TRUE(recorder->AddChannel( - static_cast<VideoChannel*>(channel), - Open(send_file), Open(recv_file), PF_RTPPACKET)); - } else { - EXPECT_TRUE(recorder->AddChannel( - static_cast<VoiceChannel*>(channel), - Open(send_file), Open(recv_file), PF_RTPPACKET)); - } - - // Enable recording both sent and received. - EXPECT_TRUE(recorder->EnableChannel(channel, true, true, SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasSendSinks(SINK_POST_CRYPTO)); - EXPECT_TRUE(channel->HasRecvSinks(SINK_POST_CRYPTO)); - EXPECT_TRUE(channel->HasSendSinks(SINK_PRE_CRYPTO)); - EXPECT_TRUE(channel->HasRecvSinks(SINK_PRE_CRYPTO)); - if (video_media_channel) { - EXPECT_TRUE_WAIT(video_media_channel->sent_intra_frame(), 100); - EXPECT_TRUE(video_media_channel->requested_intra_frame()); - } - - // Verify that media files are created. - EXPECT_TRUE(rtc::Filesystem::IsFile(send_file)); - EXPECT_TRUE(rtc::Filesystem::IsFile(recv_file)); - - // Delete all files. - header_recorder.reset(); - recorder.reset(); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(send_header_file)); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(recv_header_file)); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(send_file)); - EXPECT_TRUE(rtc::Filesystem::DeleteFile(recv_file)); -} - -TEST(MediaRecorderTest, TestMediaRecorderVoiceChannel) { - // Create the voice channel. - FakeMediaEngine media_engine; - VoiceChannel channel(rtc::Thread::Current(), &media_engine, - new FakeVoiceMediaChannel(NULL), NULL, "", false); - TestMediaRecorder(&channel, NULL, PF_RTPPACKET); - TestMediaRecorder(&channel, NULL, PF_RTPHEADER); - TestRecordHeaderAndMedia(&channel, NULL); -} - -TEST(MediaRecorderTest, TestMediaRecorderVideoChannel) { - // Create the video channel. - FakeMediaEngine media_engine; - FakeVideoMediaChannel* media_channel = new FakeVideoMediaChannel(NULL); - VideoChannel channel(rtc::Thread::Current(), &media_engine, - media_channel, NULL, "", false); - TestMediaRecorder(&channel, media_channel, PF_RTPPACKET); - TestMediaRecorder(&channel, media_channel, PF_RTPHEADER); - TestRecordHeaderAndMedia(&channel, media_channel); -} - -} // namespace cricket