• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/base/fileutils.h"
35 #include "talk/base/logging.h"
36 #include "talk/base/pathutils.h"
37 #include "talk/media/base/rtpdump.h"
38 
39 
40 namespace cricket {
41 
42 ///////////////////////////////////////////////////////////////////////////
43 // Implementation of RtpDumpSink.
44 ///////////////////////////////////////////////////////////////////////////
RtpDumpSink(talk_base::StreamInterface * stream)45 RtpDumpSink::RtpDumpSink(talk_base::StreamInterface* stream)
46     : max_size_(INT_MAX),
47       recording_(false),
48       packet_filter_(PF_NONE) {
49   stream_.reset(stream);
50 }
51 
~RtpDumpSink()52 RtpDumpSink::~RtpDumpSink() {}
53 
SetMaxSize(size_t size)54 void RtpDumpSink::SetMaxSize(size_t size) {
55   talk_base::CritScope cs(&critical_section_);
56   max_size_ = size;
57 }
58 
Enable(bool enable)59 bool RtpDumpSink::Enable(bool enable) {
60   talk_base::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 
OnPacket(const void * data,size_t size,bool rtcp)77 void RtpDumpSink::OnPacket(const void* data, size_t size, bool rtcp) {
78   talk_base::CritScope cs(&critical_section_);
79 
80   if (recording_ && writer_) {
81     size_t current_size;
82     if (writer_->GetDumpSize(&current_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 
set_packet_filter(int filter)93 void RtpDumpSink::set_packet_filter(int filter) {
94   talk_base::CritScope cs(&critical_section_);
95   packet_filter_ = filter;
96   if (writer_) {
97     writer_->set_packet_filter(packet_filter_);
98   }
99 }
100 
Flush()101 void RtpDumpSink::Flush() {
102   talk_base::CritScope cs(&critical_section_);
103   if (stream_) {
104     stream_->Flush();
105   }
106 }
107 
108 ///////////////////////////////////////////////////////////////////////////
109 // Implementation of MediaRecorder.
110 ///////////////////////////////////////////////////////////////////////////
MediaRecorder()111 MediaRecorder::MediaRecorder() {}
112 
~MediaRecorder()113 MediaRecorder::~MediaRecorder() {
114   talk_base::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 
AddChannel(VoiceChannel * channel,talk_base::StreamInterface * send_stream,talk_base::StreamInterface * recv_stream,int filter)121 bool MediaRecorder::AddChannel(VoiceChannel* channel,
122                                talk_base::StreamInterface* send_stream,
123                                talk_base::StreamInterface* recv_stream,
124                                int filter) {
125   return InternalAddChannel(channel, false, send_stream, recv_stream,
126                             filter);
127 }
AddChannel(VideoChannel * channel,talk_base::StreamInterface * send_stream,talk_base::StreamInterface * recv_stream,int filter)128 bool MediaRecorder::AddChannel(VideoChannel* channel,
129                                talk_base::StreamInterface* send_stream,
130                                talk_base::StreamInterface* recv_stream,
131                                int filter) {
132   return InternalAddChannel(channel, true, send_stream, recv_stream,
133                             filter);
134 }
135 
InternalAddChannel(BaseChannel * channel,bool video_channel,talk_base::StreamInterface * send_stream,talk_base::StreamInterface * recv_stream,int filter)136 bool MediaRecorder::InternalAddChannel(BaseChannel* channel,
137                                        bool video_channel,
138                                        talk_base::StreamInterface* send_stream,
139                                        talk_base::StreamInterface* recv_stream,
140                                        int filter) {
141   if (!channel) {
142     return false;
143   }
144 
145   talk_base::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 
RemoveChannel(BaseChannel * channel,SinkType type)162 void MediaRecorder::RemoveChannel(BaseChannel* channel,
163                                   SinkType type) {
164   talk_base::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 
EnableChannel(BaseChannel * channel,bool enable_send,bool enable_recv,SinkType type)174 bool MediaRecorder::EnableChannel(
175     BaseChannel* channel, bool enable_send, bool enable_recv,
176     SinkType type) {
177   talk_base::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 
FlushSinks()215 void MediaRecorder::FlushSinks() {
216   talk_base::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