1 // libjingle 2 // Copyright 2004 Google Inc. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are met: 6 // 7 // 1. Redistributions of source code must retain the above copyright notice, 8 // this list of conditions and the following disclaimer. 9 // 2. Redistributions in binary form must reproduce the above copyright notice, 10 // this list of conditions and the following disclaimer in the documentation 11 // and/or other materials provided with the distribution. 12 // 3. The name of the author may not be used to endorse or promote products 13 // derived from this software without specific prior written permission. 14 // 15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 26 #include "talk/media/base/filemediaengine.h" 27 28 #include <climits> 29 30 #include "talk/base/buffer.h" 31 #include "talk/base/event.h" 32 #include "talk/base/logging.h" 33 #include "talk/base/pathutils.h" 34 #include "talk/base/stream.h" 35 #include "talk/media/base/rtpdump.h" 36 #include "talk/media/base/rtputils.h" 37 #include "talk/media/base/streamparams.h" 38 39 namespace cricket { 40 41 /////////////////////////////////////////////////////////////////////////// 42 // Implementation of FileMediaEngine. 43 /////////////////////////////////////////////////////////////////////////// 44 int FileMediaEngine::GetCapabilities() { 45 int capabilities = 0; 46 if (!voice_input_filename_.empty()) { 47 capabilities |= AUDIO_SEND; 48 } 49 if (!voice_output_filename_.empty()) { 50 capabilities |= AUDIO_RECV; 51 } 52 if (!video_input_filename_.empty()) { 53 capabilities |= VIDEO_SEND; 54 } 55 if (!video_output_filename_.empty()) { 56 capabilities |= VIDEO_RECV; 57 } 58 return capabilities; 59 } 60 61 VoiceMediaChannel* FileMediaEngine::CreateChannel() { 62 talk_base::FileStream* input_file_stream = NULL; 63 talk_base::FileStream* output_file_stream = NULL; 64 65 if (voice_input_filename_.empty() && voice_output_filename_.empty()) 66 return NULL; 67 if (!voice_input_filename_.empty()) { 68 input_file_stream = talk_base::Filesystem::OpenFile( 69 talk_base::Pathname(voice_input_filename_), "rb"); 70 if (!input_file_stream) { 71 LOG(LS_ERROR) << "Not able to open the input audio stream file."; 72 return NULL; 73 } 74 } 75 76 if (!voice_output_filename_.empty()) { 77 output_file_stream = talk_base::Filesystem::OpenFile( 78 talk_base::Pathname(voice_output_filename_), "wb"); 79 if (!output_file_stream) { 80 delete input_file_stream; 81 LOG(LS_ERROR) << "Not able to open the output audio stream file."; 82 return NULL; 83 } 84 } 85 86 return new FileVoiceChannel(input_file_stream, output_file_stream); 87 } 88 89 VideoMediaChannel* FileMediaEngine::CreateVideoChannel( 90 VoiceMediaChannel* voice_ch) { 91 talk_base::FileStream* input_file_stream = NULL; 92 talk_base::FileStream* output_file_stream = NULL; 93 94 if (video_input_filename_.empty() && video_output_filename_.empty()) 95 return NULL; 96 97 if (!video_input_filename_.empty()) { 98 input_file_stream = talk_base::Filesystem::OpenFile( 99 talk_base::Pathname(video_input_filename_), "rb"); 100 if (!input_file_stream) { 101 LOG(LS_ERROR) << "Not able to open the input video stream file."; 102 return NULL; 103 } 104 } 105 106 if (!video_output_filename_.empty()) { 107 output_file_stream = talk_base::Filesystem::OpenFile( 108 talk_base::Pathname(video_output_filename_), "wb"); 109 if (!output_file_stream) { 110 delete input_file_stream; 111 LOG(LS_ERROR) << "Not able to open the output video stream file."; 112 return NULL; 113 } 114 } 115 116 return new FileVideoChannel(input_file_stream, output_file_stream); 117 } 118 119 /////////////////////////////////////////////////////////////////////////// 120 // Definition of RtpSenderReceiver. 121 /////////////////////////////////////////////////////////////////////////// 122 class RtpSenderReceiver 123 : public talk_base::Thread, public talk_base::MessageHandler { 124 public: 125 RtpSenderReceiver(MediaChannel* channel, 126 talk_base::StreamInterface* input_file_stream, 127 talk_base::StreamInterface* output_file_stream); 128 129 // Called by media channel. Context: media channel thread. 130 bool SetSend(bool send); 131 void SetSendSsrc(uint32 ssrc); 132 void OnPacketReceived(talk_base::Buffer* packet); 133 134 // Override virtual method of parent MessageHandler. Context: Worker Thread. 135 virtual void OnMessage(talk_base::Message* pmsg); 136 137 private: 138 // Read the next RTP dump packet, whose RTP SSRC is the same as first_ssrc_. 139 // Return true if successful. 140 bool ReadNextPacket(RtpDumpPacket* packet); 141 // Send a RTP packet to the network. The input parameter data points to the 142 // start of the RTP packet and len is the packet size. Return true if the sent 143 // size is equal to len. 144 bool SendRtpPacket(const void* data, size_t len); 145 146 MediaChannel* media_channel_; 147 talk_base::scoped_ptr<talk_base::StreamInterface> input_stream_; 148 talk_base::scoped_ptr<talk_base::StreamInterface> output_stream_; 149 talk_base::scoped_ptr<RtpDumpLoopReader> rtp_dump_reader_; 150 talk_base::scoped_ptr<RtpDumpWriter> rtp_dump_writer_; 151 // RTP dump packet read from the input stream. 152 RtpDumpPacket rtp_dump_packet_; 153 uint32 start_send_time_; 154 bool sending_; 155 bool first_packet_; 156 uint32 first_ssrc_; 157 158 DISALLOW_COPY_AND_ASSIGN(RtpSenderReceiver); 159 }; 160 161 /////////////////////////////////////////////////////////////////////////// 162 // Implementation of RtpSenderReceiver. 163 /////////////////////////////////////////////////////////////////////////// 164 RtpSenderReceiver::RtpSenderReceiver( 165 MediaChannel* channel, 166 talk_base::StreamInterface* input_file_stream, 167 talk_base::StreamInterface* output_file_stream) 168 : media_channel_(channel), 169 sending_(false), 170 first_packet_(true) { 171 input_stream_.reset(input_file_stream); 172 if (input_stream_) { 173 rtp_dump_reader_.reset(new RtpDumpLoopReader(input_stream_.get())); 174 // Start the sender thread, which reads rtp dump records, waits based on 175 // the record timestamps, and sends the RTP packets to the network. 176 Thread::Start(); 177 } 178 179 // Create a rtp dump writer for the output RTP dump stream. 180 output_stream_.reset(output_file_stream); 181 if (output_stream_) { 182 rtp_dump_writer_.reset(new RtpDumpWriter(output_stream_.get())); 183 } 184 } 185 186 bool RtpSenderReceiver::SetSend(bool send) { 187 bool was_sending = sending_; 188 sending_ = send; 189 if (!was_sending && sending_) { 190 PostDelayed(0, this); // Wake up the send thread. 191 start_send_time_ = talk_base::Time(); 192 } 193 return true; 194 } 195 196 void RtpSenderReceiver::SetSendSsrc(uint32 ssrc) { 197 if (rtp_dump_reader_) { 198 rtp_dump_reader_->SetSsrc(ssrc); 199 } 200 } 201 202 void RtpSenderReceiver::OnPacketReceived(talk_base::Buffer* packet) { 203 if (rtp_dump_writer_) { 204 rtp_dump_writer_->WriteRtpPacket(packet->data(), packet->length()); 205 } 206 } 207 208 void RtpSenderReceiver::OnMessage(talk_base::Message* pmsg) { 209 if (!sending_) { 210 // If the sender thread is not sending, ignore this message. The thread goes 211 // to sleep until SetSend(true) wakes it up. 212 return; 213 } 214 215 if (!first_packet_) { 216 // Send the previously read packet. 217 SendRtpPacket(&rtp_dump_packet_.data[0], rtp_dump_packet_.data.size()); 218 } 219 220 if (ReadNextPacket(&rtp_dump_packet_)) { 221 int wait = talk_base::TimeUntil( 222 start_send_time_ + rtp_dump_packet_.elapsed_time); 223 wait = talk_base::_max(0, wait); 224 PostDelayed(wait, this); 225 } else { 226 Quit(); 227 } 228 } 229 230 bool RtpSenderReceiver::ReadNextPacket(RtpDumpPacket* packet) { 231 while (talk_base::SR_SUCCESS == rtp_dump_reader_->ReadPacket(packet)) { 232 uint32 ssrc; 233 if (!packet->GetRtpSsrc(&ssrc)) { 234 return false; 235 } 236 if (first_packet_) { 237 first_packet_ = false; 238 first_ssrc_ = ssrc; 239 } 240 if (ssrc == first_ssrc_) { 241 return true; 242 } 243 } 244 return false; 245 } 246 247 bool RtpSenderReceiver::SendRtpPacket(const void* data, size_t len) { 248 if (!media_channel_) 249 return false; 250 251 talk_base::Buffer packet(data, len, kMaxRtpPacketLen); 252 return media_channel_->SendPacket(&packet); 253 } 254 255 /////////////////////////////////////////////////////////////////////////// 256 // Implementation of FileVoiceChannel. 257 /////////////////////////////////////////////////////////////////////////// 258 FileVoiceChannel::FileVoiceChannel( 259 talk_base::StreamInterface* input_file_stream, 260 talk_base::StreamInterface* output_file_stream) 261 : send_ssrc_(0), 262 rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream, 263 output_file_stream)) {} 264 265 FileVoiceChannel::~FileVoiceChannel() {} 266 267 bool FileVoiceChannel::SetSendCodecs(const std::vector<AudioCodec>& codecs) { 268 // TODO(whyuan): Check the format of RTP dump input. 269 return true; 270 } 271 272 bool FileVoiceChannel::SetSend(SendFlags flag) { 273 return rtp_sender_receiver_->SetSend(flag != SEND_NOTHING); 274 } 275 276 bool FileVoiceChannel::AddSendStream(const StreamParams& sp) { 277 if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) { 278 LOG(LS_ERROR) << "FileVoiceChannel only supports one send stream."; 279 return false; 280 } 281 send_ssrc_ = sp.ssrcs[0]; 282 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); 283 return true; 284 } 285 286 bool FileVoiceChannel::RemoveSendStream(uint32 ssrc) { 287 if (ssrc != send_ssrc_) 288 return false; 289 send_ssrc_ = 0; 290 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); 291 return true; 292 } 293 294 void FileVoiceChannel::OnPacketReceived(talk_base::Buffer* packet) { 295 rtp_sender_receiver_->OnPacketReceived(packet); 296 } 297 298 /////////////////////////////////////////////////////////////////////////// 299 // Implementation of FileVideoChannel. 300 /////////////////////////////////////////////////////////////////////////// 301 FileVideoChannel::FileVideoChannel( 302 talk_base::StreamInterface* input_file_stream, 303 talk_base::StreamInterface* output_file_stream) 304 : send_ssrc_(0), 305 rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream, 306 output_file_stream)) {} 307 308 FileVideoChannel::~FileVideoChannel() {} 309 310 bool FileVideoChannel::SetSendCodecs(const std::vector<VideoCodec>& codecs) { 311 // TODO(whyuan): Check the format of RTP dump input. 312 return true; 313 } 314 315 bool FileVideoChannel::SetSend(bool send) { 316 return rtp_sender_receiver_->SetSend(send); 317 } 318 319 bool FileVideoChannel::AddSendStream(const StreamParams& sp) { 320 if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) { 321 LOG(LS_ERROR) << "FileVideoChannel only support one send stream."; 322 return false; 323 } 324 send_ssrc_ = sp.ssrcs[0]; 325 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); 326 return true; 327 } 328 329 bool FileVideoChannel::RemoveSendStream(uint32 ssrc) { 330 if (ssrc != send_ssrc_) 331 return false; 332 send_ssrc_ = 0; 333 rtp_sender_receiver_->SetSendSsrc(send_ssrc_); 334 return true; 335 } 336 337 void FileVideoChannel::OnPacketReceived(talk_base::Buffer* packet) { 338 rtp_sender_receiver_->OnPacketReceived(packet); 339 } 340 341 } // namespace cricket 342