1 // libjingle 2 // Copyright 2004--2005, 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/session/phone/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/session/phone/rtpdump.h" 36 37 namespace cricket { 38 39 /////////////////////////////////////////////////////////////////////////// 40 // Implementation of FileMediaEngine. 41 /////////////////////////////////////////////////////////////////////////// 42 int FileMediaEngine::GetCapabilities() { 43 int capabilities = 0; 44 if (!voice_input_filename_.empty()) { 45 capabilities |= MediaEngine::AUDIO_SEND; 46 } 47 if (!voice_output_filename_.empty()) { 48 capabilities |= MediaEngine::AUDIO_RECV; 49 } 50 if (!video_input_filename_.empty()) { 51 capabilities |= MediaEngine::VIDEO_SEND; 52 } 53 if (!video_output_filename_.empty()) { 54 capabilities |= MediaEngine::VIDEO_RECV; 55 } 56 return capabilities; 57 } 58 59 VoiceMediaChannel* FileMediaEngine::CreateChannel() { 60 if (!voice_input_filename_.empty() || !voice_output_filename_.empty()) { 61 return new FileVoiceChannel(voice_input_filename_, voice_output_filename_); 62 } else { 63 return NULL; 64 } 65 } 66 67 VideoMediaChannel* FileMediaEngine::CreateVideoChannel( 68 VoiceMediaChannel* voice_ch) { 69 if (!video_input_filename_.empty() || !video_output_filename_.empty()) { 70 return new FileVideoChannel(video_input_filename_, video_output_filename_); 71 } else { 72 return NULL; 73 } 74 } 75 76 /////////////////////////////////////////////////////////////////////////// 77 // Definition of RtpSenderReceiver. 78 /////////////////////////////////////////////////////////////////////////// 79 class RtpSenderReceiver 80 : public talk_base::Thread, public talk_base::MessageHandler { 81 public: 82 RtpSenderReceiver(MediaChannel* channel, const std::string& in_file, 83 const std::string& out_file); 84 85 // Called by media channel. Context: media channel thread. 86 bool SetSend(bool send); 87 void OnPacketReceived(talk_base::Buffer* packet); 88 89 // Override virtual method of parent MessageHandler. Context: Worker Thread. 90 virtual void OnMessage(talk_base::Message* pmsg); 91 92 private: 93 // Read the next RTP dump packet, whose RTP SSRC is the same as first_ssrc_. 94 // Return true if successful. 95 bool ReadNextPacket(RtpDumpPacket* packet); 96 // Send a RTP packet to the network. The input parameter data points to the 97 // start of the RTP packet and len is the packet size. Return true if the sent 98 // size is equal to len. 99 bool SendRtpPacket(const void* data, size_t len); 100 101 MediaChannel* media_channel_; 102 talk_base::scoped_ptr<talk_base::StreamInterface> input_stream_; 103 talk_base::scoped_ptr<talk_base::StreamInterface> output_stream_; 104 talk_base::scoped_ptr<RtpDumpLoopReader> rtp_dump_reader_; 105 talk_base::scoped_ptr<RtpDumpWriter> rtp_dump_writer_; 106 // RTP dump packet read from the input stream. 107 RtpDumpPacket rtp_dump_packet_; 108 uint32 start_send_time_; 109 bool sending_; 110 bool first_packet_; 111 uint32 first_ssrc_; 112 113 DISALLOW_COPY_AND_ASSIGN(RtpSenderReceiver); 114 }; 115 116 /////////////////////////////////////////////////////////////////////////// 117 // Implementation of RtpSenderReceiver. 118 /////////////////////////////////////////////////////////////////////////// 119 RtpSenderReceiver::RtpSenderReceiver(MediaChannel* channel, 120 const std::string& in_file, 121 const std::string& out_file) 122 : media_channel_(channel), 123 sending_(false), 124 first_packet_(true) { 125 input_stream_.reset(talk_base::Filesystem::OpenFile( 126 talk_base::Pathname(in_file), "rb")); 127 if (input_stream_.get()) { 128 rtp_dump_reader_.reset(new RtpDumpLoopReader(input_stream_.get())); 129 // Start the sender thread, which reads rtp dump records, waits based on 130 // the record timestamps, and sends the RTP packets to the network. 131 Thread::Start(); 132 } 133 134 // Create a rtp dump writer for the output RTP dump stream. 135 output_stream_.reset(talk_base::Filesystem::OpenFile( 136 talk_base::Pathname(out_file), "wb")); 137 if (output_stream_.get()) { 138 rtp_dump_writer_.reset(new RtpDumpWriter(output_stream_.get())); 139 } 140 } 141 142 bool RtpSenderReceiver::SetSend(bool send) { 143 bool was_sending = sending_; 144 sending_ = send; 145 if (!was_sending && sending_) { 146 PostDelayed(0, this); // Wake up the send thread. 147 start_send_time_ = talk_base::Time(); 148 } 149 return true; 150 } 151 152 void RtpSenderReceiver::OnPacketReceived(talk_base::Buffer* packet) { 153 if (rtp_dump_writer_.get()) { 154 rtp_dump_writer_->WriteRtpPacket(packet->data(), packet->length()); 155 } 156 } 157 158 void RtpSenderReceiver::OnMessage(talk_base::Message* pmsg) { 159 if (!sending_) { 160 // If the sender thread is not sending, ignore this message. The thread goes 161 // to sleep until SetSend(true) wakes it up. 162 return; 163 } 164 165 if (!first_packet_) { 166 // Send the previously read packet. 167 SendRtpPacket(&rtp_dump_packet_.data[0], rtp_dump_packet_.data.size()); 168 } 169 170 if (ReadNextPacket(&rtp_dump_packet_)) { 171 int wait = talk_base::TimeUntil( 172 start_send_time_ + rtp_dump_packet_.elapsed_time); 173 wait = talk_base::_max(0, wait); 174 PostDelayed(wait, this); 175 } else { 176 Quit(); 177 } 178 } 179 180 bool RtpSenderReceiver::ReadNextPacket(RtpDumpPacket* packet) { 181 while (talk_base::SR_SUCCESS == rtp_dump_reader_->ReadPacket(packet)) { 182 uint32 ssrc; 183 if (!packet->GetRtpSsrc(&ssrc)) { 184 return false; 185 } 186 if (first_packet_) { 187 first_packet_ = false; 188 first_ssrc_ = ssrc; 189 } 190 if (ssrc == first_ssrc_) { 191 return true; 192 } 193 } 194 return false; 195 } 196 197 bool RtpSenderReceiver::SendRtpPacket(const void* data, size_t len) { 198 if (!media_channel_ || !media_channel_->network_interface()) { 199 return false; 200 } 201 202 talk_base::Buffer packet(data, len, kMaxRtpPacketLen); 203 return media_channel_->network_interface()->SendPacket(&packet); 204 } 205 206 /////////////////////////////////////////////////////////////////////////// 207 // Implementation of FileVoiceChannel. 208 /////////////////////////////////////////////////////////////////////////// 209 FileVoiceChannel::FileVoiceChannel(const std::string& in_file, 210 const std::string& out_file) 211 : rtp_sender_receiver_(new RtpSenderReceiver(this, in_file, out_file)) { 212 } 213 214 FileVoiceChannel::~FileVoiceChannel() {} 215 216 bool FileVoiceChannel::SetSendCodecs(const std::vector<AudioCodec>& codecs) { 217 // TODO: Check the format of RTP dump input. 218 return true; 219 } 220 221 bool FileVoiceChannel::SetSend(SendFlags flag) { 222 return rtp_sender_receiver_->SetSend(flag != SEND_NOTHING); 223 } 224 225 void FileVoiceChannel::OnPacketReceived(talk_base::Buffer* packet) { 226 rtp_sender_receiver_->OnPacketReceived(packet); 227 } 228 229 /////////////////////////////////////////////////////////////////////////// 230 // Implementation of FileVideoChannel. 231 /////////////////////////////////////////////////////////////////////////// 232 FileVideoChannel::FileVideoChannel(const std::string& in_file, 233 const std::string& out_file) 234 : rtp_sender_receiver_(new RtpSenderReceiver(this, in_file, out_file)) { 235 } 236 237 FileVideoChannel::~FileVideoChannel() {} 238 239 bool FileVideoChannel::SetSendCodecs(const std::vector<VideoCodec>& codecs) { 240 // TODO: Check the format of RTP dump input. 241 return true; 242 } 243 244 bool FileVideoChannel::SetSend(bool send) { 245 return rtp_sender_receiver_->SetSend(send); 246 } 247 248 void FileVideoChannel::OnPacketReceived(talk_base::Buffer* packet) { 249 rtp_sender_receiver_->OnPacketReceived(packet); 250 } 251 252 } // namespace cricket 253