9478437fde
1. Constructors, SetData(), and AppendData() now accept uint8_t*, int8_t*, and char*. Previously, they accepted void*, meaning that any kind of pointer was accepted. I think requiring an explicit cast in cases where the input array isn't already of a byte-sized type is a better compromise between convenience and safety. 2. data() can now return a uint8_t* instead of a char*, which seems more appropriate for a byte array, and is harder to mix up with zero-terminated C strings. data<int8_t>() is also available so that callers that want that type instead won't have to cast, as is data<char>() (which remains the default until all existing callers have been fixed). 3. Constructors, SetData(), and AppendData() now accept arrays natively, not just decayed to pointers. The advantage of this is that callers don't have to pass the size separately. 4. There are new constructors that allow setting size and capacity without initializing the array. Previously, this had to be done separately after construction. 5. Instead of TransferTo(), Buffer now supports swap(), and move construction and assignment, and has a Pass() method that works just like std::move(). (The Pass method is modeled after scoped_ptr::Pass().) R=jmarusic@webrtc.org, tommi@webrtc.org Review URL: https://webrtc-codereview.appspot.com/42989004 Cr-Commit-Position: refs/heads/master@{#9033}
377 lines
12 KiB
C++
377 lines
12 KiB
C++
/*
|
|
* libjingle
|
|
* Copyright 2004 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/media/base/filemediaengine.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits.h>
|
|
|
|
#include "talk/media/base/rtpdump.h"
|
|
#include "talk/media/base/rtputils.h"
|
|
#include "talk/media/base/streamparams.h"
|
|
#include "webrtc/base/buffer.h"
|
|
#include "webrtc/base/event.h"
|
|
#include "webrtc/base/logging.h"
|
|
#include "webrtc/base/pathutils.h"
|
|
#include "webrtc/base/stream.h"
|
|
|
|
namespace cricket {
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Implementation of FileMediaEngine.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
int FileMediaEngine::GetCapabilities() {
|
|
int capabilities = 0;
|
|
if (!voice_input_filename_.empty()) {
|
|
capabilities |= AUDIO_SEND;
|
|
}
|
|
if (!voice_output_filename_.empty()) {
|
|
capabilities |= AUDIO_RECV;
|
|
}
|
|
if (!video_input_filename_.empty()) {
|
|
capabilities |= VIDEO_SEND;
|
|
}
|
|
if (!video_output_filename_.empty()) {
|
|
capabilities |= VIDEO_RECV;
|
|
}
|
|
return capabilities;
|
|
}
|
|
|
|
VoiceMediaChannel* FileMediaEngine::CreateChannel() {
|
|
rtc::FileStream* input_file_stream = NULL;
|
|
rtc::FileStream* output_file_stream = NULL;
|
|
|
|
if (voice_input_filename_.empty() && voice_output_filename_.empty())
|
|
return NULL;
|
|
if (!voice_input_filename_.empty()) {
|
|
input_file_stream = rtc::Filesystem::OpenFile(
|
|
rtc::Pathname(voice_input_filename_), "rb");
|
|
if (!input_file_stream) {
|
|
LOG(LS_ERROR) << "Not able to open the input audio stream file.";
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!voice_output_filename_.empty()) {
|
|
output_file_stream = rtc::Filesystem::OpenFile(
|
|
rtc::Pathname(voice_output_filename_), "wb");
|
|
if (!output_file_stream) {
|
|
delete input_file_stream;
|
|
LOG(LS_ERROR) << "Not able to open the output audio stream file.";
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return new FileVoiceChannel(input_file_stream, output_file_stream,
|
|
rtp_sender_thread_);
|
|
}
|
|
|
|
VideoMediaChannel* FileMediaEngine::CreateVideoChannel(
|
|
const VideoOptions& options,
|
|
VoiceMediaChannel* voice_ch) {
|
|
rtc::FileStream* input_file_stream = NULL;
|
|
rtc::FileStream* output_file_stream = NULL;
|
|
|
|
if (video_input_filename_.empty() && video_output_filename_.empty())
|
|
return NULL;
|
|
|
|
if (!video_input_filename_.empty()) {
|
|
input_file_stream = rtc::Filesystem::OpenFile(
|
|
rtc::Pathname(video_input_filename_), "rb");
|
|
if (!input_file_stream) {
|
|
LOG(LS_ERROR) << "Not able to open the input video stream file.";
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (!video_output_filename_.empty()) {
|
|
output_file_stream = rtc::Filesystem::OpenFile(
|
|
rtc::Pathname(video_output_filename_), "wb");
|
|
if (!output_file_stream) {
|
|
delete input_file_stream;
|
|
LOG(LS_ERROR) << "Not able to open the output video stream file.";
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
FileVideoChannel* channel = new FileVideoChannel(
|
|
input_file_stream, output_file_stream, rtp_sender_thread_);
|
|
channel->SetOptions(options);
|
|
return channel;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Definition of RtpSenderReceiver.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
class RtpSenderReceiver : public rtc::MessageHandler {
|
|
public:
|
|
RtpSenderReceiver(MediaChannel* channel,
|
|
rtc::StreamInterface* input_file_stream,
|
|
rtc::StreamInterface* output_file_stream,
|
|
rtc::Thread* sender_thread);
|
|
virtual ~RtpSenderReceiver();
|
|
|
|
// Called by media channel. Context: media channel thread.
|
|
bool SetSend(bool send);
|
|
void SetSendSsrc(uint32 ssrc);
|
|
void OnPacketReceived(rtc::Buffer* packet);
|
|
|
|
// Override virtual method of parent MessageHandler. Context: Worker Thread.
|
|
virtual void OnMessage(rtc::Message* pmsg);
|
|
|
|
private:
|
|
// Read the next RTP dump packet, whose RTP SSRC is the same as first_ssrc_.
|
|
// Return true if successful.
|
|
bool ReadNextPacket(RtpDumpPacket* packet);
|
|
// Send a RTP packet to the network. The input parameter data points to the
|
|
// start of the RTP packet and len is the packet size. Return true if the sent
|
|
// size is equal to len.
|
|
bool SendRtpPacket(const void* data, size_t len);
|
|
|
|
MediaChannel* media_channel_;
|
|
rtc::scoped_ptr<rtc::StreamInterface> input_stream_;
|
|
rtc::scoped_ptr<rtc::StreamInterface> output_stream_;
|
|
rtc::scoped_ptr<RtpDumpLoopReader> rtp_dump_reader_;
|
|
rtc::scoped_ptr<RtpDumpWriter> rtp_dump_writer_;
|
|
rtc::Thread* sender_thread_;
|
|
bool own_sender_thread_;
|
|
// RTP dump packet read from the input stream.
|
|
RtpDumpPacket rtp_dump_packet_;
|
|
uint32 start_send_time_;
|
|
bool sending_;
|
|
bool first_packet_;
|
|
uint32 first_ssrc_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(RtpSenderReceiver);
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Implementation of RtpSenderReceiver.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
RtpSenderReceiver::RtpSenderReceiver(
|
|
MediaChannel* channel,
|
|
rtc::StreamInterface* input_file_stream,
|
|
rtc::StreamInterface* output_file_stream,
|
|
rtc::Thread* sender_thread)
|
|
: media_channel_(channel),
|
|
input_stream_(input_file_stream),
|
|
output_stream_(output_file_stream),
|
|
sending_(false),
|
|
first_packet_(true) {
|
|
if (sender_thread == NULL) {
|
|
sender_thread_ = new rtc::Thread();
|
|
own_sender_thread_ = true;
|
|
} else {
|
|
sender_thread_ = sender_thread;
|
|
own_sender_thread_ = false;
|
|
}
|
|
|
|
if (input_stream_) {
|
|
rtp_dump_reader_.reset(new RtpDumpLoopReader(input_stream_.get()));
|
|
// Start the sender thread, which reads rtp dump records, waits based on
|
|
// the record timestamps, and sends the RTP packets to the network.
|
|
if (own_sender_thread_) {
|
|
sender_thread_->Start();
|
|
}
|
|
}
|
|
|
|
// Create a rtp dump writer for the output RTP dump stream.
|
|
if (output_stream_) {
|
|
rtp_dump_writer_.reset(new RtpDumpWriter(output_stream_.get()));
|
|
}
|
|
}
|
|
|
|
RtpSenderReceiver::~RtpSenderReceiver() {
|
|
if (own_sender_thread_) {
|
|
sender_thread_->Stop();
|
|
delete sender_thread_;
|
|
}
|
|
}
|
|
|
|
bool RtpSenderReceiver::SetSend(bool send) {
|
|
bool was_sending = sending_;
|
|
sending_ = send;
|
|
if (!was_sending && sending_) {
|
|
sender_thread_->PostDelayed(0, this); // Wake up the send thread.
|
|
start_send_time_ = rtc::Time();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void RtpSenderReceiver::SetSendSsrc(uint32 ssrc) {
|
|
if (rtp_dump_reader_) {
|
|
rtp_dump_reader_->SetSsrc(ssrc);
|
|
}
|
|
}
|
|
|
|
void RtpSenderReceiver::OnPacketReceived(rtc::Buffer* packet) {
|
|
if (rtp_dump_writer_) {
|
|
rtp_dump_writer_->WriteRtpPacket(packet->data(), packet->size());
|
|
}
|
|
}
|
|
|
|
void RtpSenderReceiver::OnMessage(rtc::Message* pmsg) {
|
|
if (!sending_) {
|
|
// If the sender thread is not sending, ignore this message. The thread goes
|
|
// to sleep until SetSend(true) wakes it up.
|
|
return;
|
|
}
|
|
if (!first_packet_) {
|
|
// Send the previously read packet.
|
|
SendRtpPacket(&rtp_dump_packet_.data[0], rtp_dump_packet_.data.size());
|
|
}
|
|
|
|
if (ReadNextPacket(&rtp_dump_packet_)) {
|
|
int wait = rtc::TimeUntil(
|
|
start_send_time_ + rtp_dump_packet_.elapsed_time);
|
|
wait = std::max(0, wait);
|
|
sender_thread_->PostDelayed(wait, this);
|
|
} else {
|
|
sender_thread_->Quit();
|
|
}
|
|
}
|
|
|
|
bool RtpSenderReceiver::ReadNextPacket(RtpDumpPacket* packet) {
|
|
while (rtc::SR_SUCCESS == rtp_dump_reader_->ReadPacket(packet)) {
|
|
uint32 ssrc;
|
|
if (!packet->GetRtpSsrc(&ssrc)) {
|
|
return false;
|
|
}
|
|
if (first_packet_) {
|
|
first_packet_ = false;
|
|
first_ssrc_ = ssrc;
|
|
}
|
|
if (ssrc == first_ssrc_) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool RtpSenderReceiver::SendRtpPacket(const void* data, size_t len) {
|
|
if (!media_channel_)
|
|
return false;
|
|
|
|
rtc::Buffer packet(reinterpret_cast<const uint8_t*>(data), len,
|
|
kMaxRtpPacketLen);
|
|
return media_channel_->SendPacket(&packet);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Implementation of FileVoiceChannel.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
FileVoiceChannel::FileVoiceChannel(
|
|
rtc::StreamInterface* input_file_stream,
|
|
rtc::StreamInterface* output_file_stream,
|
|
rtc::Thread* rtp_sender_thread)
|
|
: send_ssrc_(0),
|
|
rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
|
|
output_file_stream,
|
|
rtp_sender_thread)) {}
|
|
|
|
FileVoiceChannel::~FileVoiceChannel() {}
|
|
|
|
bool FileVoiceChannel::SetSendCodecs(const std::vector<AudioCodec>& codecs) {
|
|
// TODO(whyuan): Check the format of RTP dump input.
|
|
return true;
|
|
}
|
|
|
|
bool FileVoiceChannel::SetSend(SendFlags flag) {
|
|
return rtp_sender_receiver_->SetSend(flag != SEND_NOTHING);
|
|
}
|
|
|
|
bool FileVoiceChannel::AddSendStream(const StreamParams& sp) {
|
|
if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) {
|
|
LOG(LS_ERROR) << "FileVoiceChannel only supports one send stream.";
|
|
return false;
|
|
}
|
|
send_ssrc_ = sp.ssrcs[0];
|
|
rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
|
|
return true;
|
|
}
|
|
|
|
bool FileVoiceChannel::RemoveSendStream(uint32 ssrc) {
|
|
if (ssrc != send_ssrc_)
|
|
return false;
|
|
send_ssrc_ = 0;
|
|
rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
|
|
return true;
|
|
}
|
|
|
|
void FileVoiceChannel::OnPacketReceived(
|
|
rtc::Buffer* packet, const rtc::PacketTime& packet_time) {
|
|
rtp_sender_receiver_->OnPacketReceived(packet);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
// Implementation of FileVideoChannel.
|
|
///////////////////////////////////////////////////////////////////////////
|
|
FileVideoChannel::FileVideoChannel(
|
|
rtc::StreamInterface* input_file_stream,
|
|
rtc::StreamInterface* output_file_stream,
|
|
rtc::Thread* rtp_sender_thread)
|
|
: send_ssrc_(0),
|
|
rtp_sender_receiver_(new RtpSenderReceiver(this, input_file_stream,
|
|
output_file_stream,
|
|
rtp_sender_thread)) {}
|
|
|
|
FileVideoChannel::~FileVideoChannel() {}
|
|
|
|
bool FileVideoChannel::SetSendCodecs(const std::vector<VideoCodec>& codecs) {
|
|
// TODO(whyuan): Check the format of RTP dump input.
|
|
return true;
|
|
}
|
|
|
|
bool FileVideoChannel::SetSend(bool send) {
|
|
return rtp_sender_receiver_->SetSend(send);
|
|
}
|
|
|
|
bool FileVideoChannel::AddSendStream(const StreamParams& sp) {
|
|
if (send_ssrc_ != 0 || sp.ssrcs.size() != 1) {
|
|
LOG(LS_ERROR) << "FileVideoChannel only support one send stream.";
|
|
return false;
|
|
}
|
|
send_ssrc_ = sp.ssrcs[0];
|
|
rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
|
|
return true;
|
|
}
|
|
|
|
bool FileVideoChannel::RemoveSendStream(uint32 ssrc) {
|
|
if (ssrc != send_ssrc_)
|
|
return false;
|
|
send_ssrc_ = 0;
|
|
rtp_sender_receiver_->SetSendSsrc(send_ssrc_);
|
|
return true;
|
|
}
|
|
|
|
void FileVideoChannel::OnPacketReceived(
|
|
rtc::Buffer* packet, const rtc::PacketTime& packet_time) {
|
|
rtp_sender_receiver_->OnPacketReceived(packet);
|
|
}
|
|
|
|
} // namespace cricket
|