ronghuawu@google.com e6988b9de5 * Update the session layer to p4 37930
* Update the peerconnection_client in sync with updates on the libjingle side.
Review URL: http://webrtc-codereview.appspot.com/29008

git-svn-id: http://webrtc.googlecode.com/svn/trunk@34 4adac7df-926f-26a2-2b94-8c16560cd09d
2011-06-01 18:50:40 +00:00

973 lines
28 KiB
C++

#include "talk/app/videomediaengine.h"
#include <iostream>
#ifdef PLATFORM_CHROMIUM
#include "content/renderer/video_capture_chrome.h"
#endif
#include "talk/base/buffer.h"
#include "talk/base/byteorder.h"
#include "talk/base/logging.h"
#include "talk/base/stringutils.h"
#include "talk/app/voicemediaengine.h"
#include "modules/video_capture/main/interface/video_capture.h"
#include "vplib.h"
#ifndef ARRAYSIZE
#define ARRAYSIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif
namespace webrtc {
static const int kDefaultLogSeverity = 3;
static const int kStartVideoBitrate = 300;
static const int kMaxVideoBitrate = 1000;
CricketWebRTCVideoFrame::CricketWebRTCVideoFrame() {
}
CricketWebRTCVideoFrame::~CricketWebRTCVideoFrame() {
// TODO(ronghuawu): should the CricketWebRTCVideoFrame owns the buffer?
WebRtc_UWord8* newMemory = NULL;
WebRtc_UWord32 newLength = 0;
WebRtc_UWord32 newSize = 0;
video_frame_.Swap(newMemory, newLength, newSize);
}
void CricketWebRTCVideoFrame::Attach(unsigned char* buffer, int bufferSize,
int w, int h) {
WebRtc_UWord8* newMemory = buffer;
WebRtc_UWord32 newLength = bufferSize;
WebRtc_UWord32 newSize = bufferSize;
video_frame_.Swap(newMemory, newLength, newSize);
video_frame_.SetWidth(w);
video_frame_.SetHeight(h);
}
size_t CricketWebRTCVideoFrame::GetWidth() const {
return video_frame_.Width();
}
size_t CricketWebRTCVideoFrame::GetHeight() const {
return video_frame_.Height();
}
const uint8* CricketWebRTCVideoFrame::GetYPlane() const {
WebRtc_UWord8* buffer = video_frame_.Buffer();
return buffer;
}
const uint8* CricketWebRTCVideoFrame::GetUPlane() const {
WebRtc_UWord8* buffer = video_frame_.Buffer();
if (buffer)
buffer += (video_frame_.Width() * video_frame_.Height());
return buffer;
}
const uint8* CricketWebRTCVideoFrame::GetVPlane() const {
WebRtc_UWord8* buffer = video_frame_.Buffer();
if (buffer)
buffer += (video_frame_.Width() * video_frame_.Height() * 5 / 4);
return buffer;
}
uint8* CricketWebRTCVideoFrame::GetYPlane() {
WebRtc_UWord8* buffer = video_frame_.Buffer();
return buffer;
}
uint8* CricketWebRTCVideoFrame::GetUPlane() {
WebRtc_UWord8* buffer = video_frame_.Buffer();
if (buffer)
buffer += (video_frame_.Width() * video_frame_.Height());
return buffer;
}
uint8* CricketWebRTCVideoFrame::GetVPlane() {
WebRtc_UWord8* buffer = video_frame_.Buffer();
if (buffer)
buffer += (video_frame_.Width() * video_frame_.Height() * 3 / 2);
return buffer;
}
cricket::VideoFrame* CricketWebRTCVideoFrame::Copy() const {
WebRtc_UWord8* buffer = video_frame_.Buffer();
if (buffer) {
int new_buffer_size = video_frame_.Length();
unsigned char* new_buffer = new unsigned char[new_buffer_size];
memcpy(new_buffer, buffer, new_buffer_size);
CricketWebRTCVideoFrame* copy = new CricketWebRTCVideoFrame();
copy->Attach(new_buffer, new_buffer_size,
video_frame_.Width(), video_frame_.Height());
copy->SetTimeStamp(video_frame_.TimeStamp());
copy->SetElapsedTime(elapsed_time_);
return copy;
}
return NULL;
}
size_t CricketWebRTCVideoFrame::CopyToBuffer(
uint8* buffer, size_t size) const {
if (!video_frame_.Buffer()) {
return 0;
}
size_t needed = video_frame_.Length();
if (needed <= size) {
memcpy(buffer, video_frame_.Buffer(), needed);
}
return needed;
}
size_t CricketWebRTCVideoFrame::ConvertToRgbBuffer(uint32 to_fourcc,
uint8* buffer,
size_t size,
size_t pitch_rgb) const {
if (!video_frame_.Buffer()) {
return 0;
}
size_t width = video_frame_.Width();
size_t height = video_frame_.Height();
// See http://www.virtualdub.org/blog/pivot/entry.php?id=190 for a good
// explanation of pitch and why this is the amount of space we need.
size_t needed = pitch_rgb * (height - 1) + 4 * width;
if (needed > size) {
LOG(LS_WARNING) << "RGB buffer is not large enough";
return needed;
}
VideoType outgoingVideoType = kUnknown;
switch (to_fourcc) {
case cricket::FOURCC_ARGB:
outgoingVideoType = kARGB;
break;
default:
LOG(LS_WARNING) << "RGB type not supported: " << to_fourcc;
break;
}
if (outgoingVideoType != kUnknown)
ConvertFromI420(outgoingVideoType, video_frame_.Buffer(),
width, height, buffer);
return needed;
}
// TODO(ronghuawu): Implement StretchToPlanes
void CricketWebRTCVideoFrame::StretchToPlanes(
uint8* y, uint8* u, uint8* v,
int32 dst_pitch_y, int32 dst_pitch_u, int32 dst_pitch_v,
size_t width, size_t height, bool interpolate, bool crop) const {
}
size_t CricketWebRTCVideoFrame::StretchToBuffer(size_t w, size_t h,
uint8* buffer, size_t size,
bool interpolate,
bool crop) const {
if (!video_frame_.Buffer()) {
return 0;
}
size_t needed = video_frame_.Length();
if (needed <= size) {
uint8* bufy = buffer;
uint8* bufu = bufy + w * h;
uint8* bufv = bufu + ((w + 1) >> 1) * ((h + 1) >> 1);
StretchToPlanes(bufy, bufu, bufv, w, (w + 1) >> 1, (w + 1) >> 1, w, h,
interpolate, crop);
}
return needed;
}
void CricketWebRTCVideoFrame::StretchToFrame(cricket::VideoFrame *target,
bool interpolate, bool crop) const {
if (!target) return;
StretchToPlanes(target->GetYPlane(),
target->GetUPlane(),
target->GetVPlane(),
target->GetYPitch(),
target->GetUPitch(),
target->GetVPitch(),
target->GetWidth(),
target->GetHeight(),
interpolate, crop);
target->SetElapsedTime(GetElapsedTime());
target->SetTimeStamp(GetTimeStamp());
}
cricket::VideoFrame* CricketWebRTCVideoFrame::Stretch(size_t w, size_t h,
bool interpolate, bool crop) const {
// TODO(ronghuawu): implement
CricketWebRTCVideoFrame* frame = new CricketWebRTCVideoFrame();
return frame;
}
CricketWebRTCVideoRenderer::CricketWebRTCVideoRenderer
(cricket::VideoRenderer* renderer)
:renderer_(renderer) {
}
CricketWebRTCVideoRenderer::~CricketWebRTCVideoRenderer() {
}
int CricketWebRTCVideoRenderer::FrameSizeChange(unsigned int width,
unsigned int height,
unsigned int numberOfStreams) {
ASSERT(renderer_ != NULL);
width_ = width;
height_ = height;
number_of_streams_ = numberOfStreams;
return renderer_->SetSize(width_, height_, 0) ? 0 : -1;
}
int CricketWebRTCVideoRenderer::DeliverFrame(unsigned char* buffer,
int bufferSize) {
ASSERT(renderer_ != NULL);
video_frame_.Attach(buffer, bufferSize, width_, height_);
return renderer_->RenderFrame(&video_frame_) ? 0 : -1;
}
const RtcVideoEngine::VideoCodecPref RtcVideoEngine::kVideoCodecPrefs[] = {
{"VP8", 104, 0},
{"H264", 105, 1}
};
RtcVideoEngine::RtcVideoEngine()
: video_engine_(new VideoEngineWrapper()),
capture_(NULL),
capture_id_(-1),
voice_engine_(NULL),
initialized_(false),
log_level_(kDefaultLogSeverity),
capture_started_(false){
}
RtcVideoEngine::RtcVideoEngine(RtcVoiceEngine* voice_engine)
: video_engine_(new VideoEngineWrapper()),
capture_(NULL),
capture_id_(-1),
voice_engine_(voice_engine),
initialized_(false),
log_level_(kDefaultLogSeverity),
capture_started_(false){
}
RtcVideoEngine::~RtcVideoEngine() {
LOG(LS_VERBOSE) << " RtcVideoEngine::~RtcVideoEngine";
video_engine_->engine()->SetTraceCallback(NULL);
Terminate();
}
bool RtcVideoEngine::Init() {
LOG(LS_VERBOSE) << "RtcVideoEngine::Init";
ApplyLogging();
if (video_engine_->engine()->SetTraceCallback(this) != 0) {
LOG(LS_ERROR) << "SetTraceCallback error";
}
bool result = InitVideoEngine(voice_engine_);
if (result) {
LOG(LS_INFO) << "VideoEngine Init done";
} else {
LOG(LS_ERROR) << "VideoEngine Init failed, releasing";
Terminate();
}
return result;
}
bool RtcVideoEngine::InitVideoEngine(RtcVoiceEngine* voice_engine) {
LOG(LS_VERBOSE) << "RtcVideoEngine::InitVideoEngine";
bool ret = true;
if (video_engine_->base()->Init() != 0) {
LOG(LS_ERROR) << "VideoEngine Init method failed";
ret = false;
}
if (!voice_engine) {
LOG(LS_WARNING) << "NULL voice engine";
} else if ((video_engine_->base()->SetVoiceEngine(
voice_engine->webrtc()->engine())) != 0) {
LOG(LS_WARNING) << "Failed to SetVoiceEngine";
}
if ((video_engine_->base()->RegisterObserver(*this)) != 0) {
LOG(LS_WARNING) << "Failed to register observer";
}
int ncodecs = video_engine_->codec()->NumberOfCodecs();
for (int i = 0; i < ncodecs - 2; ++i) {
VideoCodec wcodec;
if ((video_engine_->codec()->GetCodec(i, wcodec) == 0) &&
(strncmp(wcodec.plName, "I420", 4) != 0)) { //ignore I420
cricket::VideoCodec codec(wcodec.plType, wcodec.plName, wcodec.width,
wcodec.height, wcodec.maxFramerate, i);
LOG(LS_INFO) << codec.ToString();
video_codecs_.push_back(codec);
}
}
std::sort(video_codecs_.begin(), video_codecs_.end(),
&cricket::VideoCodec::Preferable);
return ret;
}
void RtcVideoEngine::PerformanceAlarm(const unsigned int cpuLoad) {
return;
}
void RtcVideoEngine::Print(const TraceLevel level, const char *traceString,
const int length) {
return;
}
int RtcVideoEngine::GetCodecPreference(const char* name) {
for (size_t i = 0; i < ARRAY_SIZE(kVideoCodecPrefs); ++i) {
if (strcmp(kVideoCodecPrefs[i].payload_name, name) == 0) {
return kVideoCodecPrefs[i].pref;
}
}
return -1;
}
void RtcVideoEngine::ApplyLogging() {
int filter = 0;
switch(log_level_) {
case talk_base::LS_VERBOSE: filter |= kTraceAll;
case talk_base::LS_INFO: filter |= kTraceStateInfo;
case talk_base::LS_WARNING: filter |= kTraceWarning;
case talk_base::LS_ERROR: filter |= kTraceError | kTraceCritical;
}
}
void RtcVideoEngine::Terminate() {
LOG(LS_INFO) << "RtcVideoEngine::Terminate";
ReleaseCaptureDevice();
}
int RtcVideoEngine::GetCapabilities() {
return cricket::MediaEngine::VIDEO_RECV | cricket::MediaEngine::VIDEO_SEND;
}
bool RtcVideoEngine::SetOptions(int options) {
return true;
}
bool RtcVideoEngine::ReleaseCaptureDevice() {
if (capture_) {
// Stop capture
SetCapture(false);
// DisconnectCaptureDevice
RtcVideoMediaChannel* channel;
for (VideoChannels::const_iterator it = channels_.begin();
it != channels_.end(); ++it) {
ASSERT(*it != NULL);
channel = *it;
video_engine_->capture()->DisconnectCaptureDevice(channel->video_channel());
}
// ReleaseCaptureDevice
video_engine_->capture()->ReleaseCaptureDevice(capture_id_);
capture_id_ = -1;
#ifdef PLATFORM_CHROMIUM
VideoCaptureChrome::DestroyVideoCapture(
static_cast<VideoCaptureChrome*>(capture_));
#else
webrtc::VideoCaptureModule::Destroy(capture_);
#endif
capture_ = NULL;
}
return true;
}
bool RtcVideoEngine::SetCaptureDevice(const cricket::Device* cam) {
ASSERT(video_engine_.get());
ASSERT(cam != NULL);
ReleaseCaptureDevice();
#ifdef PLATFORM_CHROMIUM
int cam_id = atol(cam->id.c_str());
if (cam_id == -1)
return false;
unsigned char uniqueId[16];
capture_ = VideoCaptureChrome::CreateVideoCapture(cam_id, uniqueId);
#else
WebRtc_UWord8 device_name[128];
WebRtc_UWord8 device_id[260];
VideoCaptureModule::DeviceInfo* device_info =
VideoCaptureModule::CreateDeviceInfo(0);
for (WebRtc_UWord32 i = 0; i < device_info->NumberOfDevices(); ++i) {
if (device_info->GetDeviceName(i, device_name, ARRAYSIZE(device_name),
device_id, ARRAYSIZE(device_id)) == 0) {
if ((cam->name.compare("") == 0) ||
(cam->id.compare((char*) device_id) == 0)) {
capture_ = VideoCaptureModule::Create(1234, device_id);
if (capture_) {
LOG(INFO) << "Found video capture device: " << device_name;
break;
}
}
}
}
VideoCaptureModule::DestroyDeviceInfo(device_info);
#endif
if (!capture_)
return false;
ViECapture* vie_capture = video_engine_->capture();
if (vie_capture->AllocateCaptureDevice(*capture_, capture_id_) == 0) {
// Connect to all the channels
RtcVideoMediaChannel* channel;
for (VideoChannels::const_iterator it = channels_.begin();
it != channels_.end(); ++it) {
ASSERT(*it != NULL);
channel = *it;
vie_capture->ConnectCaptureDevice(capture_id_, channel->video_channel());
}
SetCapture(true);
} else {
ASSERT(capture_id_ == -1);
}
return (capture_id_ != -1);
}
bool RtcVideoEngine::SetLocalRenderer(cricket::VideoRenderer* renderer) {
if (!local_renderer_.get()) {
local_renderer_.reset(new CricketWebRTCVideoRenderer(renderer));
} else {
// Renderer already set
return true;
}
int ret;
ret = video_engine_->render()->AddRenderer(capture_id_,
kVideoI420,
local_renderer_.get());
if (ret != 0)
return false;
ret = video_engine_->render()->StartRender(capture_id_);
return (ret == 0);
}
cricket::CaptureResult RtcVideoEngine::SetCapture(bool capture) {
if (capture_started_ == capture)
return cricket::CR_SUCCESS;
if (capture_id_ != -1) {
int ret;
if (capture)
ret = video_engine_->capture()->StartCapture(capture_id_);
else
ret = video_engine_->capture()->StopCapture(capture_id_);
if (ret == 0) {
capture_started_ = capture;
return cricket::CR_SUCCESS;
}
}
return cricket::CR_NO_DEVICE;
}
const std::vector<cricket::VideoCodec>& RtcVideoEngine::codecs() const {
return video_codecs_;
}
void RtcVideoEngine::SetLogging(int min_sev, const char* filter) {
log_level_ = min_sev;
ApplyLogging();
}
bool RtcVideoEngine::SetDefaultEncoderConfig(
const cricket::VideoEncoderConfig& config) {
bool ret = SetDefaultCodec(config.max_codec);
if (ret) {
default_encoder_config_ = config;
}
return ret;
}
bool RtcVideoEngine::SetDefaultCodec(const cricket::VideoCodec& codec) {
default_codec_ = codec;
return true;
}
RtcVideoMediaChannel* RtcVideoEngine::CreateChannel(
cricket::VoiceMediaChannel* voice_channel) {
RtcVideoMediaChannel* channel =
new RtcVideoMediaChannel(this, voice_channel);
if (channel) {
if (!channel->Init()) {
delete channel;
channel = NULL;
}
}
return channel;
}
bool RtcVideoEngine::FindCodec(const cricket::VideoCodec& codec) {
for (size_t i = 0; i < video_codecs_.size(); ++i) {
if (video_codecs_[i].Matches(codec)) {
return true;
}
}
return false;
}
void RtcVideoEngine::ConvertToCricketVideoCodec(
const VideoCodec& in_codec, cricket::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;
}
void RtcVideoEngine::ConvertFromCricketVideoCodec(
const cricket::VideoCodec& in_codec, VideoCodec& out_codec) {
out_codec.plType = in_codec.id;
strcpy(out_codec.plName, in_codec.name.c_str());
out_codec.width = 352; //in_codec.width;
out_codec.height = 288; //in_codec.height;
out_codec.maxFramerate = 30; //in_codec.framerate;
if (strncmp(out_codec.plName, "VP8", 3) == 0) {
out_codec.codecType = kVideoCodecVP8;
} else if (strncmp(out_codec.plName, "H263", 4) == 0) {
out_codec.codecType = kVideoCodecH263;
} else if (strncmp(out_codec.plName, "H264", 4) == 0) {
out_codec.codecType = kVideoCodecH264;
} else if (strncmp(out_codec.plName, "I420", 4) == 0) {
out_codec.codecType = kVideoCodecI420;
} else {
LOG(LS_INFO) << "invalid codec type";
}
out_codec.maxBitrate = kMaxVideoBitrate;
out_codec.startBitrate = kStartVideoBitrate;
out_codec.minBitrate = kStartVideoBitrate;
}
int RtcVideoEngine::GetLastVideoEngineError() {
return video_engine_->base()->LastError();
}
void RtcVideoEngine::RegisterChannel(RtcVideoMediaChannel *channel) {
talk_base::CritScope lock(&channels_cs_);
channels_.push_back(channel);
}
void RtcVideoEngine::UnregisterChannel(RtcVideoMediaChannel *channel) {
talk_base::CritScope lock(&channels_cs_);
VideoChannels::iterator i = std::find(channels_.begin(),
channels_.end(),
channel);
if (i != channels_.end()) {
channels_.erase(i);
}
}
// RtcVideoMediaChannel
RtcVideoMediaChannel::RtcVideoMediaChannel(
RtcVideoEngine* engine, cricket::VoiceMediaChannel* channel)
: engine_(engine),
voice_channel_(channel),
video_channel_(-1),
sending_(false),
render_started_(false) {
engine->RegisterChannel(this);
}
bool RtcVideoMediaChannel::Init() {
bool ret = true;
if (engine_->video_engine()->base()->CreateChannel(video_channel_) != 0) {
LOG(LS_ERROR) << "ViE CreateChannel Failed!!";
ret = false;
}
LOG(LS_INFO) << "RtcVideoMediaChannel::Init "
<< "video_channel " << video_channel_ << " created";
//connect audio channel
if (voice_channel_) {
RtcVoiceMediaChannel* channel =
static_cast<RtcVoiceMediaChannel*> (voice_channel_);
if (engine_->video_engine()->base()->ConnectAudioChannel(
video_channel_, channel->audio_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(
video_channel_, *this) != 0) {
ret = false;
} else {
EnableRtcp();
EnablePLI();
}
return ret;
}
RtcVideoMediaChannel::~RtcVideoMediaChannel() {
// Stop and remote renderer
SetRender(false);
if (engine()->video_engine()->render()->RemoveRenderer(video_channel_) == -1) {
LOG(LS_ERROR) << "Video RemoveRenderer failed for channel "
<< video_channel_;
}
// DeRegister external transport
if (engine()->video_engine()->network()->DeregisterSendTransport(
video_channel_) == -1) {
LOG(LS_ERROR) << "DeRegisterSendTransport failed for channel id "
<< video_channel_;
}
// Unregister RtcChannel with the engine.
engine()->UnregisterChannel(this);
// Delete VideoChannel
if (engine()->video_engine()->base()->DeleteChannel(video_channel_) == -1) {
LOG(LS_ERROR) << "Video DeleteChannel failed for channel "
<< video_channel_;
}
}
bool RtcVideoMediaChannel::SetRecvCodecs(
const std::vector<cricket::VideoCodec>& codecs) {
bool ret = true;
for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
iter != codecs.end(); ++iter) {
if (engine()->FindCodec(*iter)) {
VideoCodec wcodec;
engine()->ConvertFromCricketVideoCodec(*iter, wcodec);
if (engine()->video_engine()->codec()->SetReceiveCodec(
video_channel_, wcodec) != 0) {
LOG(LS_ERROR) << "ViE SetReceiveCodec failed"
<< " VideoChannel : " << video_channel_ << " Error: "
<< engine()->video_engine()->base()->LastError()
<< "wcodec " << 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(video_channel_) != 0) {
LOG(LS_ERROR) << "ViE StartReceive failure";
ret = false;
}
}
return ret;
}
bool RtcVideoMediaChannel::SetSendCodecs(
const std::vector<cricket::VideoCodec>& codecs) {
if (sending_) {
LOG(LS_ERROR) << "channel is alredy sending";
return false;
}
//match with local video codec list
std::vector<VideoCodec> send_codecs;
for (std::vector<cricket::VideoCodec>::const_iterator iter = codecs.begin();
iter != codecs.end(); ++iter) {
if (engine()->FindCodec(*iter)) {
VideoCodec wcodec;
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 VideoCodec& codec(send_codecs[0]);
send_codec_ = codec;
if (engine()->video_engine()->codec()->SetSendCodec(
video_channel_, codec) != 0) {
LOG(LS_ERROR) << "ViE SetSendCodec failed";
return false;
}
return true;
}
bool RtcVideoMediaChannel::SetRender(bool render) {
if (video_channel_ != -1) {
int ret = -1;
if (render == render_started_)
return true;
if (render) {
ret = engine()->video_engine()->render()->StartRender(video_channel_);
} else {
ret = engine()->video_engine()->render()->StopRender(video_channel_);
}
if (ret == 0) {
render_started_ = render;
return true;
}
}
return false;
}
bool RtcVideoMediaChannel::SetSend(bool send) {
if (send == sending()) {
return true; // no action required
}
bool ret = true;
if (send) { //enable
if (engine()->video_engine()->base()->StartSend(video_channel_) != 0) {
LOG(LS_ERROR) << "ViE StartSend failed";
ret = false;
}
} else { // disable
if (engine()->video_engine()->base()->StopSend(video_channel_) != 0) {
LOG(LS_ERROR) << "ViE StopSend failed";
ret = false;
}
}
if (ret)
sending_ = send;
return ret;
}
bool RtcVideoMediaChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) {
return false;
}
bool RtcVideoMediaChannel::RemoveStream(uint32 ssrc) {
return false;
}
bool RtcVideoMediaChannel::SetRenderer(
uint32 ssrc, cricket::VideoRenderer* renderer) {
if (!remote_renderer_.get()) {
remote_renderer_.reset(new CricketWebRTCVideoRenderer(renderer));
} else {
// Renderer already set
return true;
}
int ret;
ret = engine_->video_engine()->render()->AddRenderer(video_channel_,
kVideoI420,
remote_renderer_.get());
if (ret != 0)
return false;
ret = engine_->video_engine()->render()->StartRender(video_channel_);
return (ret == 0);
}
bool RtcVideoMediaChannel::SetExternalRenderer(uint32 ssrc, void* renderer)
{
int ret;
ret = engine_->video_engine()->render()->AddRenderer(
video_channel_,
kVideoI420,
static_cast<ExternalRenderer*>(renderer));
if (ret != 0)
return false;
ret = engine_->video_engine()->render()->StartRender(video_channel_);
return (ret == 0);
}
bool RtcVideoMediaChannel::GetStats(cricket::VideoMediaInfo* info) {
cricket::VideoSenderInfo sinfo;
memset(&sinfo, 0, sizeof(sinfo));
unsigned int ssrc;
if (engine_->video_engine()->rtp()->GetLocalSSRC(video_channel_,
ssrc) != 0) {
LOG(LS_ERROR) << "ViE GetLocalSSRC failed";
return false;
}
sinfo.ssrc = ssrc;
unsigned int cumulative_lost, extended_max, jitter;
int rtt_ms;
unsigned short fraction_lost;
if (engine_->video_engine()->rtp()->GetSentRTCPStatistics(video_channel_,
fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) {
LOG(LS_ERROR) << "ViE GetLocalSSRC failed";
return false;
}
sinfo.fraction_lost = fraction_lost;
sinfo.rtt_ms = rtt_ms;
unsigned int bytes_sent, packets_sent, bytes_recv, packets_recv;
if (engine_->video_engine()->rtp()->GetRTPStatistics(video_channel_,
bytes_sent, packets_sent, bytes_recv, packets_recv) != 0) {
LOG(LS_ERROR) << "ViE GetRTPStatistics";
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
cricket::VideoReceiverInfo rinfo;
memset(&rinfo, 0, sizeof(rinfo));
if (engine_->video_engine()->rtp()->GetReceivedRTCPStatistics(video_channel_,
fraction_lost, cumulative_lost, extended_max, jitter, rtt_ms) != 0) {
LOG(LS_ERROR) << "ViE GetReceivedRTPStatistics Failed";
return false;
}
rinfo.bytes_rcvd = bytes_recv;
rinfo.packets_rcvd = packets_recv;
rinfo.fraction_lost = fraction_lost;
if (engine_->video_engine()->rtp()->GetRemoteSSRC(video_channel_,
ssrc) != 0) {
return false;
}
rinfo.ssrc = ssrc;
//Get codec for wxh
info->receivers.push_back(rinfo);
return true;
}
bool RtcVideoMediaChannel::SendIntraFrame() {
bool ret = true;
if (engine()->video_engine()->codec()->SendKeyFrame(video_channel_) != 0) {
LOG(LS_ERROR) << "ViE SendKeyFrame failed";
ret = false;
}
return ret;
}
bool RtcVideoMediaChannel::RequestIntraFrame() {
//There is no API exposed to application to request a key frame
// ViE does this internally when there are errors from decoder
return true;
}
void RtcVideoMediaChannel::OnPacketReceived(talk_base::Buffer* packet) {
engine()->video_engine()->network()->ReceivedRTPPacket(video_channel_,
packet->data(),
packet->length());
}
void RtcVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) {
engine_->video_engine()->network()->ReceivedRTCPPacket(video_channel_,
packet->data(),
packet->length());
}
void RtcVideoMediaChannel::SetSendSsrc(uint32 id) {
if (!sending_){
if (engine()->video_engine()->rtp()->SetLocalSSRC(video_channel_, id) != 0) {
LOG(LS_ERROR) << "ViE SetLocalSSRC failed";
}
} else {
LOG(LS_ERROR) << "Channel already in send state";
}
}
bool RtcVideoMediaChannel::SetRtcpCName(const std::string& cname) {
if (engine()->video_engine()->rtp()->SetRTCPCName(video_channel_,
cname.c_str()) != 0) {
LOG(LS_ERROR) << "ViE SetRTCPCName failed";
return false;
}
return true;
}
bool RtcVideoMediaChannel::Mute(bool on) {
// stop send??
return false;
}
bool RtcVideoMediaChannel::SetSendBandwidth(bool autobw, int bps) {
LOG(LS_VERBOSE) << "RtcVideoMediaChanne::SetSendBandwidth";
VideoCodec current = send_codec_;
send_codec_.startBitrate = bps;
if (engine()->video_engine()->codec()->SetSendCodec(video_channel_,
send_codec_) != 0) {
LOG(LS_ERROR) << "ViE SetSendCodec failed";
if (engine()->video_engine()->codec()->SetSendCodec(video_channel_,
current) != 0) {
// should call be ended in this case?
}
return false;
}
return true;
}
bool RtcVideoMediaChannel::SetOptions(int options) {
return true;
}
void RtcVideoMediaChannel::EnableRtcp() {
engine()->video_engine()->rtp()->SetRTCPStatus(
video_channel_, kRtcpCompound_RFC4585);
}
void RtcVideoMediaChannel::EnablePLI() {
engine_->video_engine()->rtp()->SetKeyFrameRequestMethod(
video_channel_, kViEKeyFrameRequestPliRtcp);
}
void RtcVideoMediaChannel::EnableTMMBR() {
engine_->video_engine()->rtp()->SetTMMBRStatus(video_channel_, true);
}
int RtcVideoMediaChannel::SendPacket(int channel, const void* data, int len) {
if (!network_interface_) {
return -1;
}
talk_base::Buffer packet(data, len, cricket::kMaxRtpPacketLen);
return network_interface_->SendPacket(&packet) ? len : -1;
}
int RtcVideoMediaChannel::SendRTCPPacket(int channel,
const void* data,
int len) {
if (!network_interface_) {
return -1;
}
talk_base::Buffer packet(data, len, cricket::kMaxRtpPacketLen);
return network_interface_->SendRtcp(&packet) ? len : -1;
}
} // namespace webrtc