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