* 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
973 lines
28 KiB
C++
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
|