OLD | NEW |
| (Empty) |
1 /* | |
2 * libjingle | |
3 * Copyright 2004 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/media/base/filemediaengine.h" | |
29 | |
30 #include <algorithm> | |
31 #include <limits.h> | |
32 | |
33 #include "talk/media/base/rtpdump.h" | |
34 #include "talk/media/base/rtputils.h" | |
35 #include "talk/media/base/streamparams.h" | |
36 #include "webrtc/base/buffer.h" | |
37 #include "webrtc/base/event.h" | |
38 #include "webrtc/base/logging.h" | |
39 #include "webrtc/base/pathutils.h" | |
40 #include "webrtc/base/stream.h" | |
41 | |
42 namespace cricket { | |
43 | |
44 /////////////////////////////////////////////////////////////////////////// | |
45 // Implementation of FileMediaEngine. | |
46 /////////////////////////////////////////////////////////////////////////// | |
47 int FileMediaEngine::GetCapabilities() { | |
48 int capabilities = 0; | |
49 if (!voice_input_filename_.empty()) { | |
50 capabilities |= AUDIO_SEND; | |
51 } | |
52 if (!voice_output_filename_.empty()) { | |
53 capabilities |= AUDIO_RECV; | |
54 } | |
55 if (!video_input_filename_.empty()) { | |
56 capabilities |= VIDEO_SEND; | |
57 } | |
58 if (!video_output_filename_.empty()) { | |
59 capabilities |= VIDEO_RECV; | |
60 } | |
61 return capabilities; | |
62 } | |
63 | |
64 VoiceMediaChannel* FileMediaEngine::CreateChannel(const AudioOptions& options) { | |
65 rtc::FileStream* input_file_stream = nullptr; | |
66 rtc::FileStream* output_file_stream = nullptr; | |
67 | |
68 if (voice_input_filename_.empty() && voice_output_filename_.empty()) | |
69 return nullptr; | |
70 if (!voice_input_filename_.empty()) { | |
71 input_file_stream = rtc::Filesystem::OpenFile( | |
72 rtc::Pathname(voice_input_filename_), "rb"); | |
73 if (!input_file_stream) { | |
74 LOG(LS_ERROR) << "Not able to open the input audio stream file."; | |
75 return nullptr; | |
76 } | |
77 } | |
78 | |
79 if (!voice_output_filename_.empty()) { | |
80 output_file_stream = rtc::Filesystem::OpenFile( | |
81 rtc::Pathname(voice_output_filename_), "wb"); | |
82 if (!output_file_stream) { | |
83 delete input_file_stream; | |
84 LOG(LS_ERROR) << "Not able to open the output audio stream file."; | |
85 return nullptr; | |
86 } | |
87 } | |
88 | |
89 FileVoiceChannel* channel = new FileVoiceChannel( | |
90 input_file_stream, output_file_stream, rtp_sender_thread_); | |
91 channel->SetOptions(options); | |
92 return channel; | |
93 } | |
94 | |
95 VideoMediaChannel* FileMediaEngine::CreateVideoChannel( | |
96 const VideoOptions& options, | |
97 VoiceMediaChannel* voice_ch) { | |
98 rtc::FileStream* input_file_stream = NULL; | |
99 rtc::FileStream* output_file_stream = NULL; | |
100 | |
101 if (video_input_filename_.empty() && video_output_filename_.empty()) | |
102 return NULL; | |
103 | |
104 if (!video_input_filename_.empty()) { | |
105 input_file_stream = rtc::Filesystem::OpenFile( | |
106 rtc::Pathname(video_input_filename_), "rb"); | |
107 if (!input_file_stream) { | |
108 LOG(LS_ERROR) << "Not able to open the input video stream file."; | |
109 return NULL; | |
110 } | |
111 } | |
112 | |
113 if (!video_output_filename_.empty()) { | |
114 output_file_stream = rtc::Filesystem::OpenFile( | |
115 rtc::Pathname(video_output_filename_), "wb"); | |
116 if (!output_file_stream) { | |
117 delete input_file_stream; | |
118 LOG(LS_ERROR) << "Not able to open the output video stream file."; | |
119 return NULL; | |
120 } | |
121 } | |
122 | |
123 FileVideoChannel* channel = new FileVideoChannel( | |
124 input_file_stream, output_file_stream, rtp_sender_thread_); | |
125 channel->SetOptions(options); | |
126 return channel; | |
127 } | |
128 | |
129 /////////////////////////////////////////////////////////////////////////// | |
130 // Definition of RtpSenderReceiver. | |
131 /////////////////////////////////////////////////////////////////////////// | |
132 class RtpSenderReceiver : public rtc::MessageHandler { | |
133 public: | |
134 RtpSenderReceiver(MediaChannel* channel, | |
135 rtc::StreamInterface* input_file_stream, | |
136 rtc::StreamInterface* output_file_stream, | |
137 rtc::Thread* sender_thread); | |
138 virtual ~RtpSenderReceiver(); | |
139 | |
140 // Called by media channel. Context: media channel thread. | |
141 bool SetSend(bool send); | |
142 void SetSendSsrc(uint32 ssrc); | |
143 void OnPacketReceived(rtc::Buffer* packet); | |
144 | |
145 // Override virtual method of parent MessageHandler. Context: Worker Thread. | |
146 virtual void OnMessage(rtc::Message* pmsg); | |
147 | |
148 private: | |
149 // Read the next RTP dump packet, whose RTP SSRC is the same as first_ssrc_. | |
150 // Return true if successful. | |
151 bool ReadNextPacket(RtpDumpPacket* packet); | |
152 // Send a RTP packet to the network. The input parameter data points to the | |
153 // start of the RTP packet and len is the packet size. Return true if the sent | |
154 // size is equal to len. | |
155 bool SendRtpPacket(const void* data, size_t len); | |
156 | |
157 MediaChannel* media_channel_; | |
158 rtc::scoped_ptr<rtc::StreamInterface> input_stream_; | |
159 rtc::scoped_ptr<rtc::StreamInterface> output_stream_; | |
160 rtc::scoped_ptr<RtpDumpLoopReader> rtp_dump_reader_; | |
161 rtc::scoped_ptr<RtpDumpWriter> rtp_dump_writer_; | |
162 rtc::Thread* sender_thread_; | |
163 bool own_sender_thread_; | |
164 // RTP dump packet read from the input stream. | |
165 RtpDumpPacket rtp_dump_packet_; | |
166 uint32 start_send_time_; | |
167 bool sending_; | |
168 bool first_packet_; | |
169 uint32 first_ssrc_; | |
170 | |
171 DISALLOW_COPY_AND_ASSIGN(RtpSenderReceiver); | |
172 }; | |
173 | |
174 /////////////////////////////////////////////////////////////////////////// | |
175 // Implementation of RtpSenderReceiver. | |
176 /////////////////////////////////////////////////////////////////////////// | |
177 RtpSenderReceiver::RtpSenderReceiver( | |
178 MediaChannel* channel, | |
179 rtc::StreamInterface* input_file_stream, | |
180 rtc::StreamInterface* output_file_stream, | |
181 rtc::Thread* sender_thread) | |
182 : media_channel_(channel), | |
183 input_stream_(input_file_stream), | |
184 output_stream_(output_file_stream), | |
185 sending_(false), | |
186 first_packet_(true) { | |
187 if (sender_thread == NULL) { | |
188 sender_thread_ = new rtc::Thread(); | |
189 own_sender_thread_ = true; | |
190 } else { | |
191 sender_thread_ = sender_thread; | |
192 own_sender_thread_ = false; | |
193 } | |
194 | |
195 if (input_stream_) { | |
196 rtp_dump_reader_.reset(new RtpDumpLoopReader(input_stream_.get())); | |
197 // Start the sender thread, which reads rtp dump records, waits based on | |
198 // the record timestamps, and sends the RTP packets to the network. | |
199 if (own_sender_thread_) { | |
200 sender_thread_->Start(); | |
201 } | |
202 } | |
203 | |
204 // Create a rtp dump writer for the output RTP dump stream. | |
205 if (output_stream_) { | |
206 rtp_dump_writer_.reset(new RtpDumpWriter(output_stream_.get())); | |
207 } | |
208 } | |
209 | |
210 RtpSenderReceiver::~RtpSenderReceiver() { | |
211 if (own_sender_thread_) { | |
212 sender_thread_->Stop(); | |
213 delete sender_thread_; | |
214 } | |
215 } | |
216 | |
217 bool RtpSenderReceiver::SetSend(bool send) { | |
218 bool was_sending = sending_; | |
219 sending_ = send; | |
220 if (!was_sending && sending_) { | |
221 sender_thread_->PostDelayed(0, this); // Wake up the send thread. | |
222 start_send_time_ = rtc::Time(); | |
223 } | |
224 return true; | |
225 } | |
226 | |
227 void RtpSenderReceiver::SetSendSsrc(uint32 ssrc) { | |
228 if (rtp_dump_reader_) { | |
229 rtp_dump_reader_->SetSsrc(ssrc); | |
230 } | |
231 } | |
232 | |
233 void RtpSenderReceiver::OnPacketReceived(rtc::Buffer* packet) { | |
234 if (rtp_dump_writer_) { | |
235 rtp_dump_writer_->WriteRtpPacket(packet->data(), packet->size()); | |
236 } | |
237 } | |
238 | |
239 void RtpSenderReceiver::OnMessage(rtc::Message* pmsg) { | |
240 if (!sending_) { | |
241 // If the sender thread is not sending, ignore this message. The thread goes | |
242 // to sleep until SetSend(true) wakes it up. | |
243 return; | |
244 } | |
245 if (!first_packet_) { | |
246 // Send the previously read packet. | |
247 SendRtpPacket(&rtp_dump_packet_.data[0], rtp_dump_packet_.data.size()); | |
248 } | |
249 | |
250 if (ReadNextPacket(&rtp_dump_packet_)) { | |
251 int wait = rtc::TimeUntil( | |
252 start_send_time_ + rtp_dump_packet_.elapsed_time); | |
253 wait = std::max(0, wait); | |
254 sender_thread_->PostDelayed(wait, this); | |
255 } else { | |
256 sender_thread_->Quit(); | |
257 } | |
258 } | |
259 | |
260 bool RtpSenderReceiver::ReadNextPacket(RtpDumpPacket* packet) { | |
261 while (rtc::SR_SUCCESS == rtp_dump_reader_->ReadPacket(packet)) { | |
262 uint32 ssrc; | |
263 if (!packet->GetRtpSsrc(&ssrc)) { | |
264 return false; | |
265 } | |
266 if (first_packet_) { | |
267 first_packet_ = false; | |
268 first_ssrc_ = ssrc; | |
269 } | |
270 if (ssrc == first_ssrc_) { | |
271 return true; | |
272 } | |
273 } | |
274 return false; | |
275 } | |
276 | |
277 bool RtpSenderReceiver::SendRtpPacket(const void* data, size_t len) { | |
278 if (!media_channel_) | |
279 return false; | |
280 | |
281 rtc::Buffer packet(reinterpret_cast<const uint8_t*>(data), len, | |
282 kMaxRtpPacketLen); | |
283 return media_channel_->SendPacket(&packet); | |
284 } | |
285 | |
286 /////////////////////////////////////////////////////////////////////////// | |
287 // Implementation of FileVoiceChannel. | |
288 /////////////////////////////////////////////////////////////////////////// | |
289 FileVoiceChannel::FileVoiceChannel( | |
290 rtc::StreamInterface* input_file_stream, | |
291 rtc::StreamInterface* output_file_stream, | |
292 rtc::Thread* rtp_sender_thread) | |
293 : send_ssrc_(0), | |
294 rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream, | |
295 output_file_stream, | |
296 rtp_sender_thread)) {} | |
297 | |
298 FileVoiceChannel::~FileVoiceChannel() {} | |
299 | |
300 bool FileVoiceChannel::SetSendCodecs(const std::vector<AudioCodec>& codecs) { | |
301 // TODO(whyuan): Check the format of RTP dump input. | |
302 return true; | |
303 } | |
304 | |
305 bool FileVoiceChannel::SetSend(SendFlags flag) { | |
306 return rtp_sender_receiver_->SetSend(flag != SEND_NOTHING); | |
307 } | |
308 | |
309 bool FileVoiceChannel::AddSendStream(const StreamParams& sp) { | |
310 if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) { | |
311 LOG(LS_ERROR) << "FileVoiceChannel only supports one send stream."; | |
312 return false; | |
313 } | |
314 send_ssrc_ = sp.ssrcs[0]; | |
315 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); | |
316 return true; | |
317 } | |
318 | |
319 bool FileVoiceChannel::RemoveSendStream(uint32 ssrc) { | |
320 if (ssrc != send_ssrc_) | |
321 return false; | |
322 send_ssrc_ = 0; | |
323 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); | |
324 return true; | |
325 } | |
326 | |
327 void FileVoiceChannel::OnPacketReceived( | |
328 rtc::Buffer* packet, const rtc::PacketTime& packet_time) { | |
329 rtp_sender_receiver_->OnPacketReceived(packet); | |
330 } | |
331 | |
332 /////////////////////////////////////////////////////////////////////////// | |
333 // Implementation of FileVideoChannel. | |
334 /////////////////////////////////////////////////////////////////////////// | |
335 FileVideoChannel::FileVideoChannel( | |
336 rtc::StreamInterface* input_file_stream, | |
337 rtc::StreamInterface* output_file_stream, | |
338 rtc::Thread* rtp_sender_thread) | |
339 : send_ssrc_(0), | |
340 rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream, | |
341 output_file_stream, | |
342 rtp_sender_thread)) {} | |
343 | |
344 FileVideoChannel::~FileVideoChannel() {} | |
345 | |
346 bool FileVideoChannel::SetSendCodecs(const std::vector<VideoCodec>& codecs) { | |
347 // TODO(whyuan): Check the format of RTP dump input. | |
348 return true; | |
349 } | |
350 | |
351 bool FileVideoChannel::SetSend(bool send) { | |
352 return rtp_sender_receiver_->SetSend(send); | |
353 } | |
354 | |
355 bool FileVideoChannel::AddSendStream(const StreamParams& sp) { | |
356 if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) { | |
357 LOG(LS_ERROR) << "FileVideoChannel only support one send stream."; | |
358 return false; | |
359 } | |
360 send_ssrc_ = sp.ssrcs[0]; | |
361 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); | |
362 return true; | |
363 } | |
364 | |
365 bool FileVideoChannel::RemoveSendStream(uint32 ssrc) { | |
366 if (ssrc != send_ssrc_) | |
367 return false; | |
368 send_ssrc_ = 0; | |
369 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); | |
370 return true; | |
371 } | |
372 | |
373 void FileVideoChannel::OnPacketReceived( | |
374 rtc::Buffer* packet, const rtc::PacketTime& packet_time) { | |
375 rtp_sender_receiver_->OnPacketReceived(packet); | |
376 } | |
377 | |
378 } // namespace cricket | |
OLD | NEW |