Home | History | Annotate | Download | only in media
      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/base/fileutils.h"
     35 #include "talk/base/logging.h"
     36 #include "talk/base/pathutils.h"
     37 #include "talk/media/base/rtpdump.h"
     38 
     39 
     40 namespace cricket {
     41 
     42 ///////////////////////////////////////////////////////////////////////////
     43 // Implementation of RtpDumpSink.
     44 ///////////////////////////////////////////////////////////////////////////
     45 RtpDumpSink::RtpDumpSink(talk_base::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   talk_base::CritScope cs(&critical_section_);
     56   max_size_ = size;
     57 }
     58 
     59 bool RtpDumpSink::Enable(bool enable) {
     60   talk_base::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   talk_base::CritScope cs(&critical_section_);
     79 
     80   if (recording_ && writer_) {
     81     size_t current_size;
     82     if (writer_->GetDumpSize(&current_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   talk_base::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   talk_base::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   talk_base::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                                talk_base::StreamInterface* send_stream,
    123                                talk_base::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                                talk_base::StreamInterface* send_stream,
    130                                talk_base::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                                        talk_base::StreamInterface* send_stream,
    139                                        talk_base::StreamInterface* recv_stream,
    140                                        int filter) {
    141   if (!channel) {
    142     return false;
    143   }
    144 
    145   talk_base::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   talk_base::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   talk_base::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   talk_base::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
    225