ronghuawu@google.com c389aa2615 Fix the bad video issue on Window client by increasing the rtp recv buffer size.
Send buffer doesn't really matter, just to keep the same as talk does.

The same fix is submitted to libjingle for reivew. But I think it's worth to fix it here too as
it may take while for webrtc to get from libjingle. This patch is slightly different then that
one as I don't want to add the webrtcvideoengine.h back to webrtc.
Review URL: http://webrtc-codereview.appspot.com/166002

git-svn-id: http://webrtc.googlecode.com/svn/trunk@634 4adac7df-926f-26a2-2b94-8c16560cd09d
2011-09-21 16:53:45 +00:00

1032 lines
32 KiB
C++

/*
* libjingle
* Copyright 2004--2011, 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.
*/
#ifdef HAVE_WEBRTC_VIDEO
#include "talk/session/phone/webrtcvideoengine.h"
#include "talk/base/common.h"
#include "talk/base/buffer.h"
#include "talk/base/byteorder.h"
#include "talk/base/logging.h"
#include "talk/base/stringutils.h"
#include "talk/session/phone/videorenderer.h"
#include "talk/session/phone/webrtcpassthroughrender.h"
#include "talk/session/phone/webrtcvoiceengine.h"
#include "talk/session/phone/webrtcvideoframe.h"
#include "talk/session/phone/webrtcvie.h"
#include "talk/session/phone/webrtcvoe.h"
namespace cricket {
static const int kDefaultLogSeverity = talk_base::LS_WARNING;
static const int kStartVideoBitrate = 300;
static const int kMaxVideoBitrate = 1000;
static const int kMaxVideoRtpBufferSize = 0xFFFF;
class WebRtcRenderAdapter : public webrtc::ExternalRenderer {
public:
explicit WebRtcRenderAdapter(VideoRenderer* renderer)
: renderer_(renderer) {
}
virtual int FrameSizeChange(unsigned int width, unsigned int height,
unsigned int /*number_of_streams*/) {
if (renderer_ == NULL)
return 0;
width_ = width;
height_ = height;
return renderer_->SetSize(width_, height_, 0) ? 0 : -1;
}
virtual int DeliverFrame(unsigned char* buffer, int buffer_size,
unsigned int time_stamp) {
if (renderer_ == NULL)
return 0;
WebRtcVideoFrame video_frame;
video_frame.Attach(buffer, buffer_size, width_, height_, 0, time_stamp);
int ret = renderer_->RenderFrame(&video_frame) ? 0 : -1;
uint8* buffer_temp;
size_t buffer_size_temp;
video_frame.Detach(&buffer_temp, &buffer_size_temp);
return ret;
}
virtual ~WebRtcRenderAdapter() {}
private:
VideoRenderer* renderer_;
unsigned int width_;
unsigned int height_;
};
const WebRtcVideoEngine::VideoCodecPref
WebRtcVideoEngine::kVideoCodecPrefs[] = {
{"VP8", 120, 0},
};
// The formats are sorted by the descending order of width. We use the order to
// find the next format for CPU and bandwidth adaptation.
const VideoFormat WebRtcVideoEngine::kVideoFormats[] = {
// TODO: Understand why we have problem with 16:9 formats.
VideoFormat(1280, 800, VideoFormat::FpsToInterval(30), FOURCC_ANY),
//VideoFormat(1280, 720, VideoFormat::FpsToInterval(30), FOURCC_ANY),
VideoFormat(960, 600, VideoFormat::FpsToInterval(30), FOURCC_ANY),
//VideoFormat(960, 540, VideoFormat::FpsToInterval(30), FOURCC_ANY),
VideoFormat(640, 400, VideoFormat::FpsToInterval(30), FOURCC_ANY),
//VideoFormat(640, 360, VideoFormat::FpsToInterval(30), FOURCC_ANY),
VideoFormat(480, 300, VideoFormat::FpsToInterval(30), FOURCC_ANY),
//VideoFormat(480, 270, VideoFormat::FpsToInterval(30), FOURCC_ANY),
VideoFormat(320, 200, VideoFormat::FpsToInterval(30), FOURCC_ANY),
//VideoFormat(320, 180, VideoFormat::FpsToInterval(30), FOURCC_ANY),
VideoFormat(240, 150, VideoFormat::FpsToInterval(30), FOURCC_ANY),
//VideoFormat(240, 135, VideoFormat::FpsToInterval(30), FOURCC_ANY),
VideoFormat(160, 100, VideoFormat::FpsToInterval(30), FOURCC_ANY),
//VideoFormat(160, 90, VideoFormat::FpsToInterval(30), FOURCC_ANY),
};
// TODO: Understand why 640x400 is not working.
const VideoFormat WebRtcVideoEngine::kDefaultVideoFormat =
VideoFormat(320, 200, VideoFormat::FpsToInterval(30), FOURCC_ANY);
WebRtcVideoEngine::WebRtcVideoEngine()
: vie_wrapper_(new ViEWrapper()),
voice_engine_(NULL) {
Construct();
}
WebRtcVideoEngine::WebRtcVideoEngine(WebRtcVoiceEngine* voice_engine,
ViEWrapper* vie_wrapper)
: vie_wrapper_(vie_wrapper),
voice_engine_(voice_engine) {
Construct();
}
void WebRtcVideoEngine::Construct() {
initialized_ = false;
capture_id_ = -1;
capture_module_ = NULL;
external_capture_ = false;
log_level_ = kDefaultLogSeverity;
capture_started_ = false;
render_module_.reset(new WebRtcPassthroughRender());
ApplyLogging();
if (vie_wrapper_->engine()->SetTraceCallback(this) != 0) {
LOG_RTCERR1(SetTraceCallback, this);
}
// Set default quality levels for our supported codecs. We override them here
// if we know your cpu performance is low, and they can be updated explicitly
// by calling SetDefaultCodec. For example by a flute preference setting, or
// by the server with a jec in response to our reported system info.
VideoCodec max_codec(kVideoCodecPrefs[0].payload_type,
kVideoCodecPrefs[0].name,
kDefaultVideoFormat.width,
kDefaultVideoFormat.height,
kDefaultVideoFormat.framerate(), 0);
if (!SetDefaultCodec(max_codec)) {
LOG(LS_ERROR) << "Failed to initialize list of supported codec types";
}
}
WebRtcVideoEngine::~WebRtcVideoEngine() {
LOG(LS_INFO) << " WebRtcVideoEngine::~WebRtcVideoEngine";
vie_wrapper_->engine()->SetTraceCallback(NULL);
Terminate();
vie_wrapper_.reset();
if (capture_module_) {
capture_module_->Release();
}
}
bool WebRtcVideoEngine::Init() {
LOG(LS_INFO) << "WebRtcVideoEngine::Init";
bool result = InitVideoEngine();
if (result) {
LOG(LS_INFO) << "VideoEngine Init done";
} else {
LOG(LS_ERROR) << "VideoEngine Init failed, releasing";
Terminate();
}
return result;
}
bool WebRtcVideoEngine::InitVideoEngine() {
LOG(LS_INFO) << "WebRtcVideoEngine::InitVideoEngine";
if (vie_wrapper_->base()->Init() != 0) {
LOG_RTCERR0(Init);
return false;
}
if (!voice_engine_) {
LOG(LS_WARNING) << "NULL voice engine";
} else if ((vie_wrapper_->base()->SetVoiceEngine(
voice_engine_->voe()->engine())) != 0) {
LOG_RTCERR0(SetVoiceEngine);
return false;
}
if ((vie_wrapper_->base()->RegisterObserver(*this)) != 0) {
LOG_RTCERR0(RegisterObserver);
return false;
}
if (vie_wrapper_->render()->RegisterVideoRenderModule(
*render_module_.get()) != 0) {
LOG_RTCERR0(RegisterVideoRenderModule);
return false;
}
std::sort(video_codecs_.begin(), video_codecs_.end(),
&VideoCodec::Preferable);
initialized_ = true;
return true;
}
void WebRtcVideoEngine::PerformanceAlarm(const unsigned int cpu_load) {
LOG(LS_INFO) << "WebRtcVideoEngine::PerformanceAlarm";
}
// Ignore spammy trace messages, mostly from the stats API when we haven't
// gotten RTCP info yet from the remote side.
static bool ShouldIgnoreTrace(const std::string& trace) {
static const char* kTracesToIgnore[] = {
"\tfailed to GetReportBlockInformation",
NULL
};
for (const char* const* p = kTracesToIgnore; *p; ++p) {
if (trace.find(*p) == 0) {
return true;
}
}
return false;
}
void WebRtcVideoEngine::Print(const webrtc::TraceLevel level,
const char* trace, const int length) {
talk_base::LoggingSeverity sev = talk_base::LS_VERBOSE;
if (level == webrtc::kTraceError || level == webrtc::kTraceCritical)
sev = talk_base::LS_ERROR;
else if (level == webrtc::kTraceWarning)
sev = talk_base::LS_WARNING;
else if (level == webrtc::kTraceStateInfo || level == webrtc::kTraceInfo)
sev = talk_base::LS_INFO;
if (sev >= log_level_) {
// Skip past boilerplate prefix text
if (length < 72) {
std::string msg(trace, length);
LOG(LS_ERROR) << "Malformed webrtc log message: ";
LOG_V(sev) << msg;
} else {
std::string msg(trace + 71, length - 72);
if (!ShouldIgnoreTrace(msg)) {
LOG_V(sev) << "WebRtc ViE:" << msg;
}
}
}
}
void WebRtcVideoEngine::ApplyLogging() {
int filter = 0;
switch (log_level_) {
case talk_base::LS_VERBOSE: filter |= webrtc::kTraceAll;
case talk_base::LS_INFO: filter |= webrtc::kTraceStateInfo;
case talk_base::LS_WARNING: filter |= webrtc::kTraceWarning;
case talk_base::LS_ERROR: filter |=
webrtc::kTraceError | webrtc::kTraceCritical;
}
}
// Rebuilds the codec list to be only those that are less intensive
// than the specified codec.
bool WebRtcVideoEngine::RebuildCodecList(const VideoCodec& in_codec) {
if (!FindCodec(in_codec))
return false;
video_codecs_.clear();
bool found = false;
for (size_t i = 0; i < ARRAY_SIZE(kVideoCodecPrefs); ++i) {
const VideoCodecPref& pref(kVideoCodecPrefs[i]);
if (!found)
found = (in_codec.name == pref.name);
if (found) {
VideoCodec codec(pref.payload_type, pref.name,
in_codec.width, in_codec.height, in_codec.framerate,
ARRAY_SIZE(kVideoCodecPrefs) - i);
video_codecs_.push_back(codec);
}
}
ASSERT(found);
return true;
}
void WebRtcVideoEngine::Terminate() {
LOG(LS_INFO) << "WebRtcVideoEngine::Terminate";
initialized_ = false;
SetCapture(false);
if (local_renderer_.get()) {
// If the renderer already set, stop it first
if (vie_wrapper_->render()->StopRender(capture_id_) != 0)
LOG_RTCERR1(StopRender, capture_id_);
}
if (vie_wrapper_->render()->DeRegisterVideoRenderModule(
*render_module_.get()) != 0)
LOG_RTCERR0(DeRegisterVideoRenderModule);
if ((vie_wrapper_->base()->DeregisterObserver()) != 0)
LOG_RTCERR0(DeregisterObserver);
if ((vie_wrapper_->base()->SetVoiceEngine(NULL)) != 0)
LOG_RTCERR0(SetVoiceEngine);
if (vie_wrapper_->engine()->SetTraceCallback(NULL) != 0)
LOG_RTCERR0(SetTraceCallback);
}
int WebRtcVideoEngine::GetCapabilities() {
return VIDEO_RECV | VIDEO_SEND;
}
bool WebRtcVideoEngine::SetOptions(int options) {
return true;
}
bool WebRtcVideoEngine::ReleaseCaptureDevice() {
if (capture_id_ != -1) {
// Stop capture
SetCapture(false);
// DisconnectCaptureDevice
WebRtcVideoMediaChannel* channel;
for (VideoChannels::const_iterator it = channels_.begin();
it != channels_.end(); ++it) {
ASSERT(*it != NULL);
channel = *it;
// Ignore the return value here as the channel may not have connected to
// the capturer yet.
vie_wrapper_->capture()->DisconnectCaptureDevice(
channel->video_channel());
channel->set_connected(false);
}
// ReleaseCaptureDevice
vie_wrapper_->capture()->ReleaseCaptureDevice(capture_id_);
capture_id_ = -1;
}
return true;
}
bool WebRtcVideoEngine::SetCaptureDevice(const Device* cam) {
ASSERT(cam != NULL);
ReleaseCaptureDevice();
webrtc::ViECapture* vie_capture = vie_wrapper_->capture();
// There's an external VCM
if (capture_module_) {
if (vie_capture->AllocateCaptureDevice(*capture_module_, capture_id_) != 0)
ASSERT(capture_id_ == -1);
} else if (!external_capture_) {
char device_name[256], device_id[256];
bool found = false;
for (int i = 0; i < vie_capture->NumberOfCaptureDevices(); ++i) {
if (vie_capture->GetCaptureDevice(i, device_name,
ARRAY_SIZE(device_name),
device_id,
ARRAY_SIZE(device_id)) == 0) {
// TODO: We should only compare the device_id here,
// however the devicemanager and webrtc use different format for th v4l2
// device id. So here we also compare the device_name for now.
// For example "usb-0000:00:1d.7-6" vs "/dev/video0".
if (cam->name.compare(device_name) == 0 ||
cam->id.compare(device_id) == 0) {
LOG(INFO) << "Found video capture device: " << device_name;
found = true;
break;
}
}
}
if (!found) {
return false;
}
if (vie_capture->AllocateCaptureDevice(device_id, strlen(device_id),
capture_id_) != 0) {
ASSERT(capture_id_ == -1);
}
}
if (capture_id_ != -1) {
// Connect to all the channels if there is any.
WebRtcVideoMediaChannel* channel;
for (VideoChannels::const_iterator it = channels_.begin();
it != channels_.end(); ++it) {
ASSERT(*it != NULL);
channel = *it;
// No channel should have been connected yet.
// In case of switching device, all channel connections should have been
// disconnected in ReleaseCaptureDevice() first.
ASSERT(!channel->connected());
if (vie_capture->ConnectCaptureDevice(capture_id_,
channel->video_channel()) == 0) {
channel->set_connected(true);
} else {
LOG(LS_WARNING) << "SetCaptureDevice failed to ConnectCaptureDevice.";
}
}
SetCapture(true);
}
return (capture_id_ != -1);
}
bool WebRtcVideoEngine::SetCaptureModule(webrtc::VideoCaptureModule* vcm) {
ReleaseCaptureDevice();
if (capture_module_) {
capture_module_->Release();
capture_module_ = NULL;
}
if (vcm) {
capture_module_ = vcm;
capture_module_->AddRef();
external_capture_ = true;
} else {
external_capture_ = false;
}
return true;
}
bool WebRtcVideoEngine::SetLocalRenderer(VideoRenderer* renderer) {
if (local_renderer_.get()) {
// If the renderer already set, stop and remove it first
if (vie_wrapper_->render()->StopRender(capture_id_) != 0) {
LOG_RTCERR1(StopRender, capture_id_);
}
if (vie_wrapper_->render()->RemoveRenderer(capture_id_) != 0) {
LOG_RTCERR1(RemoveRenderer, capture_id_);
}
}
local_renderer_.reset(new WebRtcRenderAdapter(renderer));
int ret;
ret = vie_wrapper_->render()->AddRenderer(capture_id_,
webrtc::kVideoI420,
local_renderer_.get());
if (ret != 0)
return false;
ret = vie_wrapper_->render()->StartRender(capture_id_);
return (ret == 0);
}
CaptureResult WebRtcVideoEngine::SetCapture(bool capture) {
if ((capture_started_ != capture) && (capture_id_ != -1)) {
int ret;
if (capture)
ret = vie_wrapper_->capture()->StartCapture(capture_id_);
else
ret = vie_wrapper_->capture()->StopCapture(capture_id_);
if (ret != 0)
return CR_NO_DEVICE;
capture_started_ = capture;
}
return CR_SUCCESS;
}
const std::vector<VideoCodec>& WebRtcVideoEngine::codecs() const {
return video_codecs_;
}
void WebRtcVideoEngine::SetLogging(int min_sev, const char* filter) {
log_level_ = min_sev;
ApplyLogging();
}
int WebRtcVideoEngine::GetLastEngineError() {
return vie_wrapper_->error();
}
bool WebRtcVideoEngine::SetDefaultEncoderConfig(
const VideoEncoderConfig& config) {
default_encoder_config_ = config;
return true;
}
WebRtcVideoMediaChannel* WebRtcVideoEngine::CreateChannel(
VoiceMediaChannel* voice_channel) {
WebRtcVideoMediaChannel* channel =
new WebRtcVideoMediaChannel(this, voice_channel);
if (channel) {
if (!channel->Init()) {
delete channel;
channel = NULL;
}
}
return channel;
}
// Checks to see whether we comprehend and could receive a particular codec
bool WebRtcVideoEngine::FindCodec(const VideoCodec& in) {
for (int i = 0; i < ARRAY_SIZE(kVideoFormats); ++i) {
const VideoFormat& fmt = kVideoFormats[i];
if ((in.width == 0 && in.height == 0) ||
(fmt.width == in.width && fmt.height == in.height)) {
for (int j = 0; j < ARRAY_SIZE(kVideoCodecPrefs); ++j) {
VideoCodec codec(kVideoCodecPrefs[j].payload_type,
kVideoCodecPrefs[j].name, 0, 0, 0, 0);
if (codec.Matches(in)) {
return true;
}
}
}
}
return false;
}
// SetDefaultCodec may be called while the capturer is running. For example, a
// test call is started in a page with QVGA default codec, and then a real call
// is started in another page with VGA default codec. This is the corner case
// and happens only when a session is started. We ignore this case currently.
bool WebRtcVideoEngine::SetDefaultCodec(const VideoCodec& codec) {
if (!RebuildCodecList(codec)) {
LOG(LS_WARNING) << "Failed to RebuildCodecList";
return false;
}
return true;
}
void WebRtcVideoEngine::ConvertToCricketVideoCodec(
const webrtc::VideoCodec& in_codec, VideoCodec& out_codec) {
out_codec.id = in_codec.plType;
out_codec.name = in_codec.plName;
out_codec.width = in_codec.width;
out_codec.height = in_codec.height;
out_codec.framerate = in_codec.maxFramerate;
}
bool WebRtcVideoEngine::ConvertFromCricketVideoCodec(
const VideoCodec& in_codec, webrtc::VideoCodec& out_codec) {
bool found = false;
int ncodecs = vie_wrapper_->codec()->NumberOfCodecs();
for (int i = 0; i < ncodecs; ++i) {
if ((vie_wrapper_->codec()->GetCodec(i, out_codec) == 0) &&
(strncmp(out_codec.plName,
in_codec.name.c_str(),
webrtc::kPayloadNameSize - 1) == 0)) {
found = true;
break;
}
}
if (!found) {
LOG(LS_ERROR) << "invalid codec type";
return false;
}
if (in_codec.id != 0)
out_codec.plType = in_codec.id;
if (in_codec.width != 0)
out_codec.width = in_codec.width;
if (in_codec.height != 0)
out_codec.height = in_codec.height;
if (in_codec.framerate != 0)
out_codec.maxFramerate = in_codec.framerate;
out_codec.maxBitrate = kMaxVideoBitrate;
out_codec.startBitrate = kStartVideoBitrate;
out_codec.minBitrate = kStartVideoBitrate;
return true;
}
int WebRtcVideoEngine::GetLastVideoEngineError() {
return vie_wrapper_->base()->LastError();
}
void WebRtcVideoEngine::RegisterChannel(WebRtcVideoMediaChannel *channel) {
channels_.push_back(channel);
}
void WebRtcVideoEngine::UnregisterChannel(WebRtcVideoMediaChannel *channel) {
VideoChannels::iterator i = std::find(channels_.begin(),
channels_.end(),
channel);
if (i != channels_.end()) {
channels_.erase(i);
}
}
bool WebRtcVideoEngine::SetVoiceEngine(WebRtcVoiceEngine* voice_engine) {
if (initialized_) {
LOG(LS_WARNING) << "SetVoiceEngine can not be called after Init.";
return false;
}
voice_engine_ = voice_engine;
return true;
}
bool WebRtcVideoEngine::EnableTimedRender() {
if (initialized_) {
LOG(LS_WARNING) << "EnableTimedRender can not be called after Init.";
return false;
}
render_module_.reset(webrtc::VideoRender::CreateVideoRender(0, NULL,
false, webrtc::kRenderExternal));
return true;
}
// WebRtcVideoMediaChannel
WebRtcVideoMediaChannel::WebRtcVideoMediaChannel(
WebRtcVideoEngine* engine, VoiceMediaChannel* channel)
: engine_(engine),
voice_channel_(channel),
vie_channel_(-1),
sending_(false),
connected_(false),
render_started_(false),
send_codec_(NULL) {
engine->RegisterChannel(this);
}
bool WebRtcVideoMediaChannel::Init() {
bool ret = true;
if (engine_->video_engine()->base()->CreateChannel(vie_channel_) != 0) {
LOG_RTCERR1(CreateChannel, vie_channel_);
return false;
}
LOG(LS_INFO) << "WebRtcVideoMediaChannel::Init "
<< "video_channel " << vie_channel_ << " created";
// connect audio channel
if (voice_channel_) {
WebRtcVoiceMediaChannel* channel =
static_cast<WebRtcVoiceMediaChannel*> (voice_channel_);
if (engine_->video_engine()->base()->ConnectAudioChannel(
vie_channel_, channel->voe_channel()) != 0) {
LOG(LS_WARNING) << "ViE ConnectAudioChannel failed"
<< "A/V not synchronized";
// Don't set ret to false;
}
}
// Register external transport
if (engine_->video_engine()->network()->RegisterSendTransport(
vie_channel_, *this) != 0) {
ret = false;
} else {
EnableRtcp();
EnablePLI();
}
return ret;
}
WebRtcVideoMediaChannel::~WebRtcVideoMediaChannel() {
// Stop and remote renderer
SetRender(false);
if (engine()->video_engine()->render()->RemoveRenderer(vie_channel_)
== -1) {
LOG_RTCERR1(RemoveRenderer, vie_channel_);
}
// DeRegister external transport
if (engine()->video_engine()->network()->DeregisterSendTransport(
vie_channel_) == -1) {
LOG_RTCERR1(DeregisterSendTransport, vie_channel_);
}
// Unregister RtcChannel with the engine.
engine()->UnregisterChannel(this);
// Delete VideoChannel
if (engine()->video_engine()->base()->DeleteChannel(vie_channel_) == -1) {
LOG_RTCERR1(DeleteChannel, vie_channel_);
}
}
bool WebRtcVideoMediaChannel::SetRecvCodecs(
const std::vector<VideoCodec>& codecs) {
bool ret = true;
for (std::vector<VideoCodec>::const_iterator iter = codecs.begin();
iter != codecs.end(); ++iter) {
if (engine()->FindCodec(*iter)) {
webrtc::VideoCodec wcodec;
if (engine()->ConvertFromCricketVideoCodec(*iter, wcodec)) {
if (engine()->video_engine()->codec()->SetReceiveCodec(
vie_channel_, wcodec) != 0) {
LOG_RTCERR2(SetReceiveCodec, vie_channel_, wcodec.plName);
ret = false;
}
}
} else {
LOG(LS_INFO) << "Unknown codec" << iter->name;
ret = false;
}
}
// make channel ready to receive packets
if (ret) {
if (engine()->video_engine()->base()->StartReceive(vie_channel_) != 0) {
LOG_RTCERR1(StartReceive, vie_channel_);
ret = false;
}
}
if (ret && network_interface_) {
network_interface_->SetOption(NetworkInterface::ST_RTP,
talk_base::Socket::OPT_RCVBUF,
kMaxVideoRtpBufferSize);
network_interface_->SetOption(NetworkInterface::ST_RTP,
talk_base::Socket::OPT_SNDBUF,
kMaxVideoRtpBufferSize);
}
return ret;
}
bool WebRtcVideoMediaChannel::SetSendCodecs(
const std::vector<VideoCodec>& codecs) {
if (sending_) {
LOG(LS_ERROR) << "channel is alredy sending";
return false;
}
// match with local video codec list
std::vector<webrtc::VideoCodec> send_codecs;
for (std::vector<VideoCodec>::const_iterator iter = codecs.begin();
iter != codecs.end(); ++iter) {
if (engine()->FindCodec(*iter)) {
webrtc::VideoCodec wcodec;
if (engine()->ConvertFromCricketVideoCodec(*iter, wcodec))
send_codecs.push_back(wcodec);
}
}
// if none matches, return with set
if (send_codecs.empty()) {
LOG(LS_ERROR) << "No matching codecs avilable";
return false;
}
// select the first matched codec
const webrtc::VideoCodec& codec(send_codecs[0]);
send_codec_.reset(new webrtc::VideoCodec(codec));
if (engine()->video_engine()->codec()->SetSendCodec(
vie_channel_, codec) != 0) {
LOG_RTCERR2(SetSendCodec, vie_channel_, codec.plName);
return false;
}
return true;
}
bool WebRtcVideoMediaChannel::SetRender(bool render) {
if (render != render_started_) {
int ret;
if (render) {
ret = engine()->video_engine()->render()->StartRender(vie_channel_);
} else {
ret = engine()->video_engine()->render()->StopRender(vie_channel_);
}
if (ret != 0) {
return false;
}
render_started_ = render;
}
return true;
}
bool WebRtcVideoMediaChannel::SetSend(bool send) {
if (send == sending()) {
return true; // no action required
}
bool ret = true;
if (send) { // enable
if (engine()->video_engine()->base()->StartSend(vie_channel_) != 0) {
LOG_RTCERR1(StartSend, vie_channel_);
ret = false;
}
// If the channel has not been connected to the capturer yet,
// connect it now.
if (!connected()) {
if (engine()->video_engine()->capture()->ConnectCaptureDevice(
engine()->capture_id(), vie_channel_) != 0) {
LOG_RTCERR2(ConnectCaptureDevice, engine()->capture_id(), vie_channel_);
ret = false;
} else {
set_connected(true);
}
}
} else { // disable
if (engine()->video_engine()->base()->StopSend(vie_channel_) != 0) {
LOG_RTCERR1(StopSend, vie_channel_);
ret = false;
}
}
if (ret) {
sending_ = send;
}
return ret;
}
bool WebRtcVideoMediaChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) {
return false;
}
bool WebRtcVideoMediaChannel::RemoveStream(uint32 ssrc) {
return false;
}
bool WebRtcVideoMediaChannel::SetRenderer(
uint32 ssrc, VideoRenderer* renderer) {
ASSERT(vie_channel_ != -1);
if (ssrc != 0)
return false;
if (remote_renderer_.get()) {
// If the renderer already set, stop and remove it first
if (engine_->video_engine()->render()->StopRender(vie_channel_) != 0) {
LOG_RTCERR1(StopRender, vie_channel_);
}
if (engine_->video_engine()->render()->RemoveRenderer(vie_channel_) != 0) {
LOG_RTCERR1(RemoveRenderer, vie_channel_);
}
}
remote_renderer_.reset(new WebRtcRenderAdapter(renderer));
if (engine_->video_engine()->render()->AddRenderer(vie_channel_,
webrtc::kVideoI420, remote_renderer_.get()) != 0) {
LOG_RTCERR3(AddRenderer, vie_channel_, webrtc::kVideoI420,
remote_renderer_.get());
remote_renderer_.reset();
return false;
}
if (engine_->video_engine()->render()->StartRender(vie_channel_) != 0) {
LOG_RTCERR1(StartRender, vie_channel_);
return false;
}
return true;
}
bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) {
VideoSenderInfo sinfo;
memset(&sinfo, 0, sizeof(sinfo));
unsigned int ssrc;
if (engine_->video_engine()->rtp()->GetLocalSSRC(vie_channel_,
ssrc) != 0) {
LOG_RTCERR2(GetLocalSSRC, vie_channel_, ssrc);
return false;
}
sinfo.ssrc = ssrc;
unsigned int cumulative_lost, extended_max, jitter;
int rtt_ms;
uint16 fraction_lost;
if (engine_->video_engine()->rtp()->GetReceivedRTCPStatistics(vie_channel_,
fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) {
LOG_RTCERR6(GetReceivedRTCPStatistics, vie_channel_,
fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms);
return false;
}
sinfo.fraction_lost = fraction_lost;
sinfo.packets_lost = cumulative_lost;
sinfo.rtt_ms = rtt_ms;
unsigned int bytes_sent, packets_sent, bytes_recv, packets_recv;
if (engine_->video_engine()->rtp()->GetRTPStatistics(vie_channel_,
bytes_sent, packets_sent, bytes_recv, packets_recv) != 0) {
LOG_RTCERR5(GetRTPStatistics, vie_channel_,
bytes_sent, packets_sent, bytes_recv, packets_recv);
return false;
}
sinfo.packets_sent = packets_sent;
sinfo.bytes_sent = bytes_sent;
sinfo.packets_lost = -1;
sinfo.packets_cached = -1;
info->senders.push_back(sinfo);
// build receiver info.
// reusing the above local variables
VideoReceiverInfo rinfo;
memset(&rinfo, 0, sizeof(rinfo));
if (engine_->video_engine()->rtp()->GetSentRTCPStatistics(vie_channel_,
fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) {
LOG_RTCERR6(GetSentRTCPStatistics, vie_channel_,
fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms);
return false;
}
rinfo.bytes_rcvd = bytes_recv;
rinfo.packets_rcvd = packets_recv;
rinfo.fraction_lost = fraction_lost;
rinfo.packets_lost = cumulative_lost;
if (engine_->video_engine()->rtp()->GetRemoteSSRC(vie_channel_,
ssrc) != 0) {
return false;
}
rinfo.ssrc = ssrc;
// Get codec for wxh
info->receivers.push_back(rinfo);
return true;
}
bool WebRtcVideoMediaChannel::SendIntraFrame() {
bool ret = true;
if (engine()->video_engine()->codec()->SendKeyFrame(vie_channel_) != 0) {
LOG_RTCERR1(SendKeyFrame, vie_channel_);
ret = false;
}
return ret;
}
bool WebRtcVideoMediaChannel::RequestIntraFrame() {
// There is no API exposed to application to request a key frame
// ViE does this internally when there are errors from decoder
return false;
}
void WebRtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
engine()->video_engine()->network()->ReceivedRTPPacket(vie_channel_,
packet->data(),
packet->length());
}
void WebRtcVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) {
engine_->video_engine()->network()->ReceivedRTCPPacket(vie_channel_,
packet->data(),
packet->length());
}
void WebRtcVideoMediaChannel::SetSendSsrc(uint32 id) {
if (!sending_) {
if (engine()->video_engine()->rtp()->SetLocalSSRC(vie_channel_,
id) != 0) {
LOG_RTCERR1(SetLocalSSRC, vie_channel_);
}
} else {
LOG(LS_ERROR) << "Channel already in send state";
}
}
bool WebRtcVideoMediaChannel::SetRtcpCName(const std::string& cname) {
if (engine()->video_engine()->rtp()->SetRTCPCName(vie_channel_,
cname.c_str()) != 0) {
LOG_RTCERR2(SetRTCPCName, vie_channel_, cname.c_str());
return false;
}
return true;
}
bool WebRtcVideoMediaChannel::Mute(bool on) {
// stop send??
return false;
}
bool WebRtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) {
LOG(LS_INFO) << "RtcVideoMediaChanne::SetSendBandwidth";
if (!send_codec_.get()) {
LOG(LS_INFO) << "The send codec has not been set up yet.";
return true;
}
if (!autobw) {
send_codec_->startBitrate = bps;
send_codec_->minBitrate = bps;
}
send_codec_->maxBitrate = bps;
if (engine()->video_engine()->codec()->SetSendCodec(vie_channel_,
*send_codec_.get()) != 0) {
LOG_RTCERR2(SetSendCodec, vie_channel_, send_codec_->plName);
return false;
}
return true;
}
bool WebRtcVideoMediaChannel::SetOptions(int options) {
return true;
}
void WebRtcVideoMediaChannel::EnableRtcp() {
engine()->video_engine()->rtp()->SetRTCPStatus(
vie_channel_, webrtc::kRtcpCompound_RFC4585);
}
void WebRtcVideoMediaChannel::EnablePLI() {
engine_->video_engine()->rtp()->SetKeyFrameRequestMethod(
vie_channel_, webrtc::kViEKeyFrameRequestPliRtcp);
}
void WebRtcVideoMediaChannel::EnableTMMBR() {
engine_->video_engine()->rtp()->SetTMMBRStatus(vie_channel_, true);
}
int WebRtcVideoMediaChannel::SendPacket(int channel, const void* data,
int len) {
if (!network_interface_) {
return -1;
}
talk_base::Buffer packet(data, len, kMaxRtpPacketLen);
return network_interface_->SendPacket(&packet) ? len : -1;
}
int WebRtcVideoMediaChannel::SendRTCPPacket(int channel,
const void* data,
int len) {
if (!network_interface_) {
return -1;
}
talk_base::Buffer packet(data, len, kMaxRtpPacketLen);
return network_interface_->SendRtcp(&packet) ? len : -1;
}
} // namespace cricket
#endif // HAVE_WEBRTC_VIDEO