OLD | NEW |
| (Empty) |
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 | |
40 namespace cricket { | |
41 | |
42 /////////////////////////////////////////////////////////////////////////// | |
43 // Implementation of RtpDumpSink. | |
44 /////////////////////////////////////////////////////////////////////////// | |
45 RtpDumpSink::RtpDumpSink(rtc::StreamInterface* stream) | |
46 : max_size_(INT_MAX), | |
47 recording_(false), | |
48 packet_filter_(PF_NONE) { | |
49 stream_.reset(stream); | |
50 } | |
51 | |
52 RtpDumpSink::~RtpDumpSink() {} | |
53 | |
54 void RtpDumpSink::SetMaxSize(size_t size) { | |
55 rtc::CritScope cs(&critical_section_); | |
56 max_size_ = size; | |
57 } | |
58 | |
59 bool 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 | |
77 void 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 | |
93 void 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 | |
101 void RtpDumpSink::Flush() { | |
102 rtc::CritScope cs(&critical_section_); | |
103 if (stream_) { | |
104 stream_->Flush(); | |
105 } | |
106 } | |
107 | |
108 /////////////////////////////////////////////////////////////////////////// | |
109 // Implementation of MediaRecorder. | |
110 /////////////////////////////////////////////////////////////////////////// | |
111 MediaRecorder::MediaRecorder() {} | |
112 | |
113 MediaRecorder::~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 | |
121 bool 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 } | |
128 bool 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 | |
136 bool 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 | |
162 void 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 | |
174 bool 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 | |
215 void 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 | |
OLD | NEW |