blob: 7b06e48b07e148af7ea3c1341979b76e27c69acc [file] [log] [blame]
/*
* libjingle
* Copyright 2010, Google Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "talk/session/phone/mediarecorder.h"
#include <limits.h>
#include <string>
#include "talk/base/fileutils.h"
#include "talk/base/logging.h"
#include "talk/base/pathutils.h"
#include "talk/session/phone/channel.h"
#include "talk/session/phone/rtpdump.h"
namespace cricket {
///////////////////////////////////////////////////////////////////////////
// Implementation of RtpDumpSink.
///////////////////////////////////////////////////////////////////////////
RtpDumpSink::RtpDumpSink(const std::string& filename)
: max_size_(INT_MAX),
recording_(false),
packet_filter_(PF_NONE),
filename_(filename) {
}
RtpDumpSink::~RtpDumpSink() {}
void RtpDumpSink::SetMaxSize(size_t size) {
talk_base::CritScope cs(&critical_section_);
max_size_ = size;
}
bool RtpDumpSink::Enable(bool enable) {
talk_base::CritScope cs(&critical_section_);
recording_ = enable;
// Create a file and the RTP writer if we have not done yet.
if (recording_ && !writer_.get()) {
stream_.reset(talk_base::Filesystem::OpenFile(
talk_base::Pathname(filename_), "wb"));
if (!stream_.get()) {
return false;
}
writer_.reset(new RtpDumpWriter(stream_.get()));
writer_->set_packet_filter(packet_filter_);
} else if (!recording_ && stream_.get()) {
stream_->Flush();
}
return true;
}
void RtpDumpSink::OnPacket(const void* data, size_t size, bool rtcp) {
talk_base::CritScope cs(&critical_section_);
if (recording_ && writer_.get()) {
size_t current_size;
if (writer_->GetDumpSize(&current_size) &&
current_size + RtpDumpPacket::kHeaderLength + size <= max_size_) {
if (!rtcp) {
writer_->WriteRtpPacket(data, size);
} else {
// TODO: Enable recording RTCP.
}
}
}
}
void RtpDumpSink::set_packet_filter(int filter) {
talk_base::CritScope cs(&critical_section_);
packet_filter_ = filter;
if (writer_.get()) {
writer_->set_packet_filter(packet_filter_);
}
}
void RtpDumpSink::Flush() {
talk_base::CritScope cs(&critical_section_);
if (stream_.get()) {
stream_->Flush();
}
}
///////////////////////////////////////////////////////////////////////////
// Implementation of MediaRecorder.
///////////////////////////////////////////////////////////////////////////
MediaRecorder::MediaRecorder() {}
MediaRecorder::~MediaRecorder() {
talk_base::CritScope cs(&critical_section_);
std::map<BaseChannel*, SinkPair*>::iterator itr;
for (itr = sinks_.begin(); itr != sinks_.end(); ++itr) {
delete itr->second;
}
}
bool MediaRecorder::AddChannel(VoiceChannel* channel,
const std::string& send_filename,
const std::string& recv_filename,
int filter) {
return InternalAddChannel(channel, false, send_filename, recv_filename,
filter);
}
bool MediaRecorder::AddChannel(VideoChannel* channel,
const std::string& send_filename,
const std::string& recv_filename,
int filter) {
return InternalAddChannel(channel, true, send_filename, recv_filename,
filter);
}
bool MediaRecorder::InternalAddChannel(BaseChannel* channel,
bool video_channel,
const std::string& send_filename,
const std::string& recv_filename,
int filter) {
if (!channel) {
return false;
}
talk_base::CritScope cs(&critical_section_);
if (sinks_.end() != sinks_.find(channel)) {
return false; // The channel was added already.
}
SinkPair* sink_pair = new SinkPair;
sink_pair->video_channel = video_channel;
sink_pair->filter = filter;
sink_pair->send_sink.reset(new RtpDumpSink(send_filename));
sink_pair->send_sink->set_packet_filter(filter);
sink_pair->recv_sink.reset(new RtpDumpSink(recv_filename));
sink_pair->recv_sink->set_packet_filter(filter);
sinks_[channel] = sink_pair;
return true;
}
void MediaRecorder::RemoveChannel(BaseChannel* channel) {
talk_base::CritScope cs(&critical_section_);
std::map<BaseChannel*, SinkPair*>::iterator itr = sinks_.find(channel);
if (sinks_.end() != itr) {
channel->UnregisterSendSink(itr->second->send_sink.get());
channel->UnregisterRecvSink(itr->second->recv_sink.get());
delete itr->second;
sinks_.erase(itr);
}
}
bool MediaRecorder::EnableChannel(
BaseChannel* channel, bool enable_send, bool enable_recv) {
talk_base::CritScope cs(&critical_section_);
std::map<BaseChannel*, SinkPair*>::iterator itr = sinks_.find(channel);
if (sinks_.end() == itr) {
return false;
}
SinkPair* sink_pair = itr->second;
RtpDumpSink* sink = sink_pair->send_sink.get();
sink->Enable(enable_send);
if (enable_send) {
channel->RegisterSendSink(sink, &RtpDumpSink::OnPacket);
} else {
channel->UnregisterSendSink(sink);
}
sink = sink_pair->recv_sink.get();
sink->Enable(enable_recv);
if (enable_recv) {
channel->RegisterRecvSink(sink, &RtpDumpSink::OnPacket);
} else {
channel->UnregisterRecvSink(sink);
}
if (sink_pair->video_channel &&
(sink_pair->filter & PF_RTPPACKET) == PF_RTPPACKET) {
// Request a full intra frame.
VideoChannel* video_channel = static_cast<VideoChannel*>(channel);
if (enable_send) {
video_channel->SendIntraFrame();
}
if (enable_recv) {
video_channel->RequestIntraFrame();
}
}
return true;
}
void MediaRecorder::FlushSinks() {
talk_base::CritScope cs(&critical_section_);
std::map<BaseChannel*, SinkPair*>::iterator itr;
for (itr = sinks_.begin(); itr != sinks_.end(); ++itr) {
itr->second->send_sink->Flush();
itr->second->recv_sink->Flush();
}
}
} // namespace cricket