1/* 2 * libjingle 3 * Copyright 2010 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 "talk/session/media/mediarecorder.h" 29 30#include <limits.h> 31 32#include <string> 33 34#include "talk/media/base/rtpdump.h" 35#include "webrtc/base/fileutils.h" 36#include "webrtc/base/logging.h" 37#include "webrtc/base/pathutils.h" 38 39 40namespace cricket { 41 42/////////////////////////////////////////////////////////////////////////// 43// Implementation of RtpDumpSink. 44/////////////////////////////////////////////////////////////////////////// 45RtpDumpSink::RtpDumpSink(rtc::StreamInterface* stream) 46 : max_size_(INT_MAX), 47 recording_(false), 48 packet_filter_(PF_NONE) { 49 stream_.reset(stream); 50} 51 52RtpDumpSink::~RtpDumpSink() {} 53 54void RtpDumpSink::SetMaxSize(size_t size) { 55 rtc::CritScope cs(&critical_section_); 56 max_size_ = size; 57} 58 59bool RtpDumpSink::Enable(bool enable) { 60 rtc::CritScope cs(&critical_section_); 61 62 recording_ = enable; 63 64 // Create a file and the RTP writer if we have not done yet. 65 if (recording_ && !writer_) { 66 if (!stream_) { 67 return false; 68 } 69 writer_.reset(new RtpDumpWriter(stream_.get())); 70 writer_->set_packet_filter(packet_filter_); 71 } else if (!recording_ && stream_) { 72 stream_->Flush(); 73 } 74 return true; 75} 76 77void RtpDumpSink::OnPacket(const void* data, size_t size, bool rtcp) { 78 rtc::CritScope cs(&critical_section_); 79 80 if (recording_ && writer_) { 81 size_t current_size; 82 if (writer_->GetDumpSize(¤t_size) && 83 current_size + RtpDumpPacket::kHeaderLength + size <= max_size_) { 84 if (!rtcp) { 85 writer_->WriteRtpPacket(data, size); 86 } else { 87 // TODO(whyuan): Enable recording RTCP. 88 } 89 } 90 } 91} 92 93void RtpDumpSink::set_packet_filter(int filter) { 94 rtc::CritScope cs(&critical_section_); 95 packet_filter_ = filter; 96 if (writer_) { 97 writer_->set_packet_filter(packet_filter_); 98 } 99} 100 101void RtpDumpSink::Flush() { 102 rtc::CritScope cs(&critical_section_); 103 if (stream_) { 104 stream_->Flush(); 105 } 106} 107 108/////////////////////////////////////////////////////////////////////////// 109// Implementation of MediaRecorder. 110/////////////////////////////////////////////////////////////////////////// 111MediaRecorder::MediaRecorder() {} 112 113MediaRecorder::~MediaRecorder() { 114 rtc::CritScope cs(&critical_section_); 115 std::map<BaseChannel*, SinkPair*>::iterator itr; 116 for (itr = sinks_.begin(); itr != sinks_.end(); ++itr) { 117 delete itr->second; 118 } 119} 120 121bool MediaRecorder::AddChannel(VoiceChannel* channel, 122 rtc::StreamInterface* send_stream, 123 rtc::StreamInterface* recv_stream, 124 int filter) { 125 return InternalAddChannel(channel, false, send_stream, recv_stream, 126 filter); 127} 128bool MediaRecorder::AddChannel(VideoChannel* channel, 129 rtc::StreamInterface* send_stream, 130 rtc::StreamInterface* recv_stream, 131 int filter) { 132 return InternalAddChannel(channel, true, send_stream, recv_stream, 133 filter); 134} 135 136bool MediaRecorder::InternalAddChannel(BaseChannel* channel, 137 bool video_channel, 138 rtc::StreamInterface* send_stream, 139 rtc::StreamInterface* recv_stream, 140 int filter) { 141 if (!channel) { 142 return false; 143 } 144 145 rtc::CritScope cs(&critical_section_); 146 if (sinks_.end() != sinks_.find(channel)) { 147 return false; // The channel was added already. 148 } 149 150 SinkPair* sink_pair = new SinkPair; 151 sink_pair->video_channel = video_channel; 152 sink_pair->filter = filter; 153 sink_pair->send_sink.reset(new RtpDumpSink(send_stream)); 154 sink_pair->send_sink->set_packet_filter(filter); 155 sink_pair->recv_sink.reset(new RtpDumpSink(recv_stream)); 156 sink_pair->recv_sink->set_packet_filter(filter); 157 sinks_[channel] = sink_pair; 158 159 return true; 160} 161 162void MediaRecorder::RemoveChannel(BaseChannel* channel, 163 SinkType type) { 164 rtc::CritScope cs(&critical_section_); 165 std::map<BaseChannel*, SinkPair*>::iterator itr = sinks_.find(channel); 166 if (sinks_.end() != itr) { 167 channel->UnregisterSendSink(itr->second->send_sink.get(), type); 168 channel->UnregisterRecvSink(itr->second->recv_sink.get(), type); 169 delete itr->second; 170 sinks_.erase(itr); 171 } 172} 173 174bool MediaRecorder::EnableChannel( 175 BaseChannel* channel, bool enable_send, bool enable_recv, 176 SinkType type) { 177 rtc::CritScope cs(&critical_section_); 178 std::map<BaseChannel*, SinkPair*>::iterator itr = sinks_.find(channel); 179 if (sinks_.end() == itr) { 180 return false; 181 } 182 183 SinkPair* sink_pair = itr->second; 184 RtpDumpSink* sink = sink_pair->send_sink.get(); 185 sink->Enable(enable_send); 186 if (enable_send) { 187 channel->RegisterSendSink(sink, &RtpDumpSink::OnPacket, type); 188 } else { 189 channel->UnregisterSendSink(sink, type); 190 } 191 192 sink = sink_pair->recv_sink.get(); 193 sink->Enable(enable_recv); 194 if (enable_recv) { 195 channel->RegisterRecvSink(sink, &RtpDumpSink::OnPacket, type); 196 } else { 197 channel->UnregisterRecvSink(sink, type); 198 } 199 200 if (sink_pair->video_channel && 201 (sink_pair->filter & PF_RTPPACKET) == PF_RTPPACKET) { 202 // Request a full intra frame. 203 VideoChannel* video_channel = static_cast<VideoChannel*>(channel); 204 if (enable_send) { 205 video_channel->SendIntraFrame(); 206 } 207 if (enable_recv) { 208 video_channel->RequestIntraFrame(); 209 } 210 } 211 212 return true; 213} 214 215void MediaRecorder::FlushSinks() { 216 rtc::CritScope cs(&critical_section_); 217 std::map<BaseChannel*, SinkPair*>::iterator itr; 218 for (itr = sinks_.begin(); itr != sinks_.end(); ++itr) { 219 itr->second->send_sink->Flush(); 220 itr->second->recv_sink->Flush(); 221 } 222} 223 224} // namespace cricket 225