| Index: third_party/libjingle/overrides/talk/session/phone/webrtcvideoengine.cc
|
| ===================================================================
|
| --- third_party/libjingle/overrides/talk/session/phone/webrtcvideoengine.cc (revision 118180)
|
| +++ third_party/libjingle/overrides/talk/session/phone/webrtcvideoengine.cc (working copy)
|
| @@ -1,1619 +0,0 @@
|
| -/*
|
| - * 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_CONFIG_H
|
| -#include <config.h>
|
| -#endif
|
| -
|
| -#ifdef HAVE_WEBRTC_VIDEO
|
| -
|
| -#include "talk/session/phone/webrtcvideoengine.h"
|
| -
|
| -#include "talk/base/basictypes.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/webrtcvideocapturer.h"
|
| -#include "talk/session/phone/webrtcvideoframe.h"
|
| -#include "talk/session/phone/webrtcvie.h"
|
| -#include "talk/session/phone/webrtcvoe.h"
|
| -
|
| -// TODO Change video protection calls when WebRTC API has changed.
|
| -#define WEBRTC_VIDEO_AVPF_NACK_ONLY
|
| -
|
| -namespace cricket {
|
| -
|
| -static const int kDefaultLogSeverity = talk_base::LS_WARNING;
|
| -
|
| -static const int kMinVideoBitrate = 100;
|
| -static const int kStartVideoBitrate = 300;
|
| -static const int kMaxVideoBitrate = 2000;
|
| -
|
| -static const int kVideoMtu = 1200;
|
| -
|
| -static const int kVideoRtpBufferSize = 65536;
|
| -
|
| -static const char kVp8PayloadName[] = "VP8";
|
| -static const char kRedPayloadName[] = "red";
|
| -static const char kFecPayloadName[] = "ulpfec";
|
| -
|
| -static const int kDefaultNumberOfTemporalLayers = 3;
|
| -
|
| -static void LogMultiline(talk_base::LoggingSeverity sev, char* text) {
|
| - const char* delim = "\r\n";
|
| - for (char* tok = strtok(text, delim); tok; tok = strtok(NULL, delim)) {
|
| - LOG_V(sev) << tok;
|
| - }
|
| -}
|
| -
|
| -class WebRtcRenderAdapter : public webrtc::ExternalRenderer {
|
| - public:
|
| - explicit WebRtcRenderAdapter(VideoRenderer* renderer)
|
| - : renderer_(renderer), width_(0), height_(0) {
|
| - }
|
| - virtual ~WebRtcRenderAdapter() {
|
| - }
|
| -
|
| - void SetRenderer(VideoRenderer* renderer) {
|
| - talk_base::CritScope cs(&crit_);
|
| - renderer_ = renderer;
|
| - }
|
| - // Implementation of webrtc::ExternalRenderer.
|
| - virtual int FrameSizeChange(unsigned int width, unsigned int height,
|
| - unsigned int /*number_of_streams*/) {
|
| - talk_base::CritScope cs(&crit_);
|
| - 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) {
|
| - talk_base::CritScope cs(&crit_);
|
| - frame_rate_tracker_.Update(1);
|
| - if (renderer_ == NULL) {
|
| - return 0;
|
| - }
|
| - WebRtcVideoFrame video_frame;
|
| - video_frame.Attach(buffer, buffer_size, width_, height_,
|
| - 1, 1, 0, time_stamp, 0);
|
| -
|
| - 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;
|
| - }
|
| -
|
| - unsigned int width() {
|
| - talk_base::CritScope cs(&crit_);
|
| - return width_;
|
| - }
|
| - unsigned int height() {
|
| - talk_base::CritScope cs(&crit_);
|
| - return height_;
|
| - }
|
| - int framerate() {
|
| - talk_base::CritScope cs(&crit_);
|
| - return frame_rate_tracker_.units_second();
|
| - }
|
| -
|
| - private:
|
| - talk_base::CriticalSection crit_;
|
| - VideoRenderer* renderer_;
|
| - unsigned int width_;
|
| - unsigned int height_;
|
| - talk_base::RateTracker frame_rate_tracker_;
|
| -};
|
| -
|
| -class WebRtcDecoderObserver : public webrtc::ViEDecoderObserver {
|
| - public:
|
| - WebRtcDecoderObserver(int video_channel)
|
| - : video_channel_(video_channel),
|
| - framerate_(0),
|
| - bitrate_(0),
|
| - firs_requested_(0) { }
|
| -
|
| - // virtual functions from VieDecoderObserver.
|
| - virtual void IncomingCodecChanged(const int videoChannel,
|
| - const webrtc::VideoCodec& videoCodec) { }
|
| - virtual void IncomingRate(const int videoChannel,
|
| - const unsigned int framerate,
|
| - const unsigned int bitrate) {
|
| - ASSERT(video_channel_ == videoChannel);
|
| - framerate_ = framerate;
|
| - bitrate_ = bitrate;
|
| - }
|
| - virtual void RequestNewKeyFrame(const int videoChannel) {
|
| - ASSERT(video_channel_ == videoChannel);
|
| - ++firs_requested_;
|
| - }
|
| -
|
| - int framerate() const { return framerate_; }
|
| - int bitrate() const { return bitrate_; }
|
| - int firs_requested() const { return firs_requested_; }
|
| -
|
| - private:
|
| - int video_channel_;
|
| - int framerate_;
|
| - int bitrate_;
|
| - int firs_requested_;
|
| -};
|
| -
|
| -class WebRtcEncoderObserver : public webrtc::ViEEncoderObserver {
|
| - public:
|
| - WebRtcEncoderObserver(int video_channel)
|
| - : video_channel_(video_channel), framerate_(0), bitrate_(0) { }
|
| -
|
| - // virtual functions from VieEncoderObserver.
|
| - virtual void OutgoingRate(const int videoChannel,
|
| - const unsigned int framerate,
|
| - const unsigned int bitrate) {
|
| - ASSERT(video_channel_ == videoChannel);
|
| - framerate_ = framerate;
|
| - bitrate_ = bitrate;
|
| - }
|
| -
|
| - int framerate() const { return framerate_; }
|
| - int bitrate() const { return bitrate_; }
|
| -
|
| - private:
|
| - int video_channel_;
|
| - int framerate_;
|
| - int bitrate_;
|
| -};
|
| -
|
| -class LocalStreamInfo {
|
| - public:
|
| - int width() {
|
| - talk_base::CritScope cs(&crit_);
|
| - return width_;
|
| - }
|
| - int height() {
|
| - talk_base::CritScope cs(&crit_);
|
| - return height_;
|
| - }
|
| - int framerate() {
|
| - talk_base::CritScope cs(&crit_);
|
| - return rate_tracker_.units_second();
|
| - }
|
| -
|
| - void UpdateFrame(int width, int height) {
|
| - talk_base::CritScope cs(&crit_);
|
| - width_ = width;
|
| - height_ = height;
|
| - rate_tracker_.Update(1);
|
| - }
|
| -
|
| - private:
|
| - talk_base::CriticalSection crit_;
|
| - unsigned int width_;
|
| - unsigned int height_;
|
| - talk_base::RateTracker rate_tracker_;
|
| -};
|
| -
|
| -const WebRtcVideoEngine::VideoCodecPref
|
| - WebRtcVideoEngine::kVideoCodecPrefs[] = {
|
| - {kVp8PayloadName, 100, 0},
|
| -#ifndef WEBRTC_VIDEO_AVPF_NACK_ONLY
|
| - {kRedPayloadName, 101, 1},
|
| - {kFecPayloadName, 102, 2},
|
| -#endif
|
| -};
|
| -
|
| -// 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 VideoFormatPod WebRtcVideoEngine::kVideoFormats[] = {
|
| - {1280, 800, 30, FOURCC_ANY},
|
| - {1280, 720, 30, FOURCC_ANY},
|
| - {960, 600, 30, FOURCC_ANY},
|
| - {960, 540, 30, FOURCC_ANY},
|
| - {640, 400, 30, FOURCC_ANY},
|
| - {640, 360, 30, FOURCC_ANY},
|
| - {640, 480, 30, FOURCC_ANY},
|
| - {480, 300, 30, FOURCC_ANY},
|
| - {480, 270, 30, FOURCC_ANY},
|
| - {480, 360, 30, FOURCC_ANY},
|
| - {320, 200, 30, FOURCC_ANY},
|
| - {320, 180, 30, FOURCC_ANY},
|
| - {320, 240, 30, FOURCC_ANY},
|
| - {240, 150, 30, FOURCC_ANY},
|
| - {240, 135, 30, FOURCC_ANY},
|
| - {240, 180, 30, FOURCC_ANY},
|
| - {160, 100, 30, FOURCC_ANY},
|
| - {160, 90, 30, FOURCC_ANY},
|
| - {160, 120, 30, FOURCC_ANY},
|
| -};
|
| -
|
| -const VideoFormatPod WebRtcVideoEngine::kDefaultVideoFormat =
|
| - {640, 400, 30, FOURCC_ANY};
|
| -
|
| -WebRtcVideoEngine::WebRtcVideoEngine() {
|
| - Construct(new ViEWrapper(), new ViETraceWrapper(), NULL);
|
| -}
|
| -
|
| -WebRtcVideoEngine::WebRtcVideoEngine(WebRtcVoiceEngine* voice_engine,
|
| - ViEWrapper* vie_wrapper) {
|
| - Construct(vie_wrapper, new ViETraceWrapper(), voice_engine);
|
| -}
|
| -
|
| -WebRtcVideoEngine::WebRtcVideoEngine(WebRtcVoiceEngine* voice_engine,
|
| - ViEWrapper* vie_wrapper,
|
| - ViETraceWrapper* tracing) {
|
| - Construct(vie_wrapper, tracing, voice_engine);
|
| -}
|
| -
|
| -void WebRtcVideoEngine::Construct(ViEWrapper* vie_wrapper,
|
| - ViETraceWrapper* tracing,
|
| - WebRtcVoiceEngine* voice_engine) {
|
| - LOG(LS_INFO) << "WebRtcVideoEngine::WebRtcVideoEngine";
|
| - vie_wrapper_.reset(vie_wrapper);
|
| - vie_wrapper_base_initialized_ = false;
|
| - tracing_.reset(tracing);
|
| - voice_engine_ = voice_engine;
|
| - initialized_ = false;
|
| - log_level_ = kDefaultLogSeverity;
|
| - render_module_.reset(new WebRtcPassthroughRender());
|
| - local_renderer_w_ = local_renderer_h_ = 0;
|
| - local_renderer_ = NULL;
|
| - owns_capturer_ = false;
|
| - video_capturer_ = NULL;
|
| - capture_started_ = false;
|
| -
|
| - ApplyLogging();
|
| - if (tracing_->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() {
|
| - ClearCapturer();
|
| - LOG(LS_INFO) << "WebRtcVideoEngine::~WebRtcVideoEngine";
|
| - if (initialized_) {
|
| - Terminate();
|
| - }
|
| - tracing_->SetTraceCallback(NULL);
|
| -}
|
| -
|
| -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";
|
| -
|
| - // Init WebRTC VideoEngine.
|
| - if (!vie_wrapper_base_initialized_) {
|
| - if (vie_wrapper_->base()->Init() != 0) {
|
| - LOG_RTCERR0(Init);
|
| - return false;
|
| - }
|
| - vie_wrapper_base_initialized_ = true;
|
| - }
|
| -
|
| - // Log the VoiceEngine version info.
|
| - char buffer[1024] = "";
|
| - if (vie_wrapper_->base()->GetVersion(buffer) != 0) {
|
| - LOG_RTCERR0(GetVersion);
|
| - return false;
|
| - }
|
| -
|
| - LOG(LS_INFO) << "WebRtc VideoEngine Version:";
|
| - LogMultiline(talk_base::LS_INFO, buffer);
|
| -
|
| - // Hook up to VoiceEngine for sync purposes, if supplied.
|
| - 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;
|
| - }
|
| -
|
| - // Register for callbacks from the engine.
|
| - if ((vie_wrapper_->base()->RegisterObserver(*this)) != 0) {
|
| - LOG_RTCERR0(RegisterObserver);
|
| - return false;
|
| - }
|
| -
|
| - // Register our custom render module.
|
| - if (vie_wrapper_->render()->RegisterVideoRenderModule(
|
| - *render_module_.get()) != 0) {
|
| - LOG_RTCERR0(RegisterVideoRenderModule);
|
| - return false;
|
| - }
|
| -
|
| - initialized_ = true;
|
| - return true;
|
| -}
|
| -
|
| -void WebRtcVideoEngine::Terminate() {
|
| - LOG(LS_INFO) << "WebRtcVideoEngine::Terminate";
|
| - initialized_ = false;
|
| - SetCapture(false);
|
| -
|
| - 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);
|
| - }
|
| -}
|
| -
|
| -int WebRtcVideoEngine::GetCapabilities() {
|
| - return VIDEO_RECV | VIDEO_SEND;
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::SetOptions(int options) {
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::SetDefaultEncoderConfig(
|
| - const VideoEncoderConfig& config) {
|
| - return SetDefaultCodec(config.max_codec);
|
| -}
|
| -
|
| -// 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;
|
| - }
|
| -
|
| - default_codec_format_ = VideoFormat(
|
| - video_codecs_[0].width,
|
| - video_codecs_[0].height,
|
| - VideoFormat::FpsToInterval(video_codecs_[0].framerate),
|
| - FOURCC_ANY);
|
| - return true;
|
| -}
|
| -
|
| -WebRtcVideoMediaChannel* WebRtcVideoEngine::CreateChannel(
|
| - VoiceMediaChannel* voice_channel) {
|
| - WebRtcVideoMediaChannel* channel =
|
| - new WebRtcVideoMediaChannel(this, voice_channel);
|
| - if (!channel->Init()) {
|
| - delete channel;
|
| - channel = NULL;
|
| - }
|
| - return channel;
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::SetCaptureDevice(const Device* device) {
|
| - if (!device) {
|
| - ClearCapturer();
|
| - LOG(LS_INFO) << "Camera set to NULL";
|
| - return true;
|
| - }
|
| - // No-op if the device hasn't changed.
|
| - if ((video_capturer_ != NULL) && video_capturer_->GetId() == device->id) {
|
| - return true;
|
| - }
|
| - // Create a new capturer for the specified device.
|
| - VideoCapturer* capturer = CreateVideoCapturer(*device);
|
| - if (!capturer) {
|
| - LOG(LS_ERROR) << "Failed to create camera '" << device->name << "', id='"
|
| - << device->id << "'";
|
| - return false;
|
| - }
|
| - const bool owns_capturer = true;
|
| - if (!SetCapturer(capturer, owns_capturer)) {
|
| - return false;
|
| - }
|
| - LOG(LS_INFO) << "Camera set to '" << device->name << "', id='"
|
| - << device->id << "'";
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::SetCaptureModule(webrtc::VideoCaptureModule* vcm) {
|
| - if (!vcm) {
|
| - if ((video_capturer_ != NULL) && video_capturer_->IsRunning()) {
|
| - LOG(LS_WARNING) << "Failed to set camera to NULL when is running.";
|
| - return false;
|
| - } else {
|
| - ClearCapturer();
|
| - LOG(LS_INFO) << "Camera set to NULL";
|
| - return true;
|
| - }
|
| - }
|
| - // Create a new capturer for the specified device.
|
| - WebRtcVideoCapturer* capturer = new WebRtcVideoCapturer;
|
| - if (!capturer->Init(vcm)) {
|
| - LOG(LS_ERROR) << "Failed to create camera from VCM";
|
| - delete capturer;
|
| - return false;
|
| - }
|
| - const bool owns_capturer = true;
|
| - if (!SetCapturer(capturer, owns_capturer)) {
|
| - return false;
|
| - }
|
| - LOG(LS_INFO) << "Camera created with VCM";
|
| - CaptureResult ret = SetCapture(true);
|
| - if (ret != cricket::CR_SUCCESS && ret != cricket::CR_PENDING) {
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::SetVideoCapturer(VideoCapturer* capturer,
|
| - uint32 /*ssrc*/) {
|
| - const bool capture = (capturer != NULL);
|
| - const bool owns_capturer = false;
|
| - CaptureResult res = CR_FAILURE;
|
| - if (capture) {
|
| - // Register the capturer before starting to capture.
|
| - if (!SetCapturer(capturer, owns_capturer)) {
|
| - return false;
|
| - }
|
| - const bool kEnableCapture = true;
|
| - res = SetCapture(kEnableCapture);
|
| - } else {
|
| - // Stop capturing before unregistering the capturer.
|
| - const bool kDisableCapture = false;
|
| - res = SetCapture(kDisableCapture);
|
| - if (!SetCapturer(capturer, owns_capturer)) {
|
| - return false;
|
| - }
|
| - }
|
| - return (res == CR_SUCCESS) || (res == CR_PENDING);
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::SetLocalRenderer(VideoRenderer* renderer) {
|
| - local_renderer_w_ = local_renderer_h_ = 0;
|
| - local_renderer_ = renderer;
|
| - return true;
|
| -}
|
| -
|
| -CaptureResult WebRtcVideoEngine::SetCapture(bool capture) {
|
| - bool old_capture = capture_started_;
|
| - capture_started_ = capture;
|
| - CaptureResult res = UpdateCapturingState();
|
| - if (res != CR_SUCCESS && res != CR_PENDING) {
|
| - capture_started_ = old_capture;
|
| - }
|
| - return res;
|
| -}
|
| -
|
| -VideoCapturer* WebRtcVideoEngine::CreateVideoCapturer(const Device& device) {
|
| - WebRtcVideoCapturer* capturer = new WebRtcVideoCapturer;
|
| - if (!capturer->Init(device)) {
|
| - delete capturer;
|
| - return NULL;
|
| - }
|
| - return capturer;
|
| -}
|
| -
|
| -CaptureResult WebRtcVideoEngine::UpdateCapturingState() {
|
| - CaptureResult result = CR_SUCCESS;
|
| -
|
| - bool capture = capture_started_;
|
| - if (!IsCapturing() && capture) { // Start capturing.
|
| - if (video_capturer_ == NULL) {
|
| - return CR_NO_DEVICE;
|
| - }
|
| -
|
| - VideoFormat capture_format;
|
| - if (!video_capturer_->GetBestCaptureFormat(default_codec_format_,
|
| - &capture_format)) {
|
| - LOG(LS_WARNING) << "Unsupported format:"
|
| - << " width=" << default_codec_format_.width
|
| - << " height=" << default_codec_format_.height
|
| - << ". Supported formats are:";
|
| - const std::vector<VideoFormat>* formats =
|
| - video_capturer_->GetSupportedFormats();
|
| - if (formats) {
|
| - for (std::vector<VideoFormat>::const_iterator i = formats->begin();
|
| - i != formats->end(); ++i) {
|
| - const VideoFormat& format = *i;
|
| - LOG(LS_WARNING) << " " << GetFourccName(format.fourcc) << ":"
|
| - << format.width << "x" << format.height << "x"
|
| - << format.framerate();
|
| - }
|
| - }
|
| - return CR_FAILURE;
|
| - }
|
| -
|
| - // Start the video capturer.
|
| - result = video_capturer_->Start(capture_format);
|
| - if (CR_SUCCESS != result && CR_PENDING != result) {
|
| - LOG(LS_ERROR) << "Failed to start the video capturer";
|
| - return result;
|
| - }
|
| - } else if (IsCapturing() && !capture) { // Stop capturing.
|
| - video_capturer_->Stop();
|
| - }
|
| -
|
| - return result;
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::IsCapturing() const {
|
| - return (video_capturer_ != NULL) && video_capturer_->IsRunning();
|
| -}
|
| -
|
| -void WebRtcVideoEngine::OnFrameCaptured(VideoCapturer* capturer,
|
| - const CapturedFrame* frame) {
|
| - // Force 16:10 for now. We'll be smarter with the capture refactor.
|
| - int cropped_height = frame->width * default_codec_format_.height
|
| - / default_codec_format_.width;
|
| - if (cropped_height > frame->height) {
|
| - // TODO: Once we support horizontal cropping, add cropped_width.
|
| - cropped_height = frame->height;
|
| - }
|
| -
|
| - // This CapturedFrame* will already be in I420. In the future, when
|
| - // WebRtcVideoFrame has support for independent planes, we can just attach
|
| - // to it and update the pointers when cropping.
|
| - WebRtcVideoFrame i420_frame;
|
| - if (!i420_frame.Init(frame, frame->width, cropped_height)) {
|
| - LOG(LS_ERROR) << "Couldn't convert to I420! "
|
| - << frame->width << " x " << cropped_height;
|
| - return;
|
| - }
|
| -
|
| - // Send I420 frame to the local renderer.
|
| - if (local_renderer_) {
|
| - if (local_renderer_w_ != static_cast<int>(i420_frame.GetWidth()) ||
|
| - local_renderer_h_ != static_cast<int>(i420_frame.GetHeight())) {
|
| - local_renderer_->SetSize(local_renderer_w_ = i420_frame.GetWidth(),
|
| - local_renderer_h_ = i420_frame.GetHeight(), 0);
|
| - }
|
| - local_renderer_->RenderFrame(&i420_frame);
|
| - }
|
| -
|
| - // Send I420 frame to the registered senders.
|
| - talk_base::CritScope cs(&channels_crit_);
|
| - for (VideoChannels::iterator it = channels_.begin();
|
| - it != channels_.end(); ++it) {
|
| - if ((*it)->sending()) (*it)->SendFrame(0, &i420_frame);
|
| - }
|
| -}
|
| -
|
| -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();
|
| -}
|
| -
|
| -// 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;
|
| -}
|
| -
|
| -// Given the requested codec, returns true if we can send that codec type and
|
| -// updates out with the best quality we could send for that codec. If current is
|
| -// not empty, we constrain out so that its aspect ratio matches current's.
|
| -bool WebRtcVideoEngine::CanSendCodec(const VideoCodec& requested,
|
| - const VideoCodec& current,
|
| - VideoCodec* out) {
|
| - if (!out) {
|
| - return false;
|
| - }
|
| -
|
| - std::vector<VideoCodec>::const_iterator local_max;
|
| - for (local_max = video_codecs_.begin();
|
| - local_max < video_codecs_.end();
|
| - ++local_max) {
|
| - // First match codecs by payload type
|
| - if (!requested.Matches(local_max->id, local_max->name)) {
|
| - continue;
|
| - }
|
| -
|
| - out->id = requested.id;
|
| - out->name = requested.name;
|
| - out->preference = requested.preference;
|
| - out->framerate = talk_base::_min(requested.framerate, local_max->framerate);
|
| - out->width = 0;
|
| - out->height = 0;
|
| -
|
| - if (0 == requested.width && 0 == requested.height) {
|
| - // Special case with resolution 0. The channel should not send frames.
|
| - return true;
|
| - } else if (0 == requested.width || 0 == requested.height) {
|
| - // 0xn and nx0 are invalid resolutions.
|
| - return false;
|
| - }
|
| -
|
| - // Pick the best quality that is within their and our bounds and has the
|
| - // correct aspect ratio.
|
| - for (int j = 0; j < ARRAY_SIZE(kVideoFormats); ++j) {
|
| - const VideoFormat format(kVideoFormats[j]);
|
| -
|
| - // Skip any format that is larger than the local or remote maximums, or
|
| - // smaller than the current best match
|
| - if (format.width > requested.width || format.height > requested.height ||
|
| - format.width > local_max->width ||
|
| - (format.width < out->width && format.height < out->height)) {
|
| - continue;
|
| - }
|
| -
|
| - bool better = false;
|
| -
|
| - // Check any further constraints on this prospective format
|
| - if (!out->width || !out->height) {
|
| - // If we don't have any matches yet, this is the best so far.
|
| - better = true;
|
| - } else if (current.width && current.height) {
|
| - // current is set so format must match its ratio exactly.
|
| - better =
|
| - (format.width * current.height == format.height * current.width);
|
| - } else {
|
| - // Prefer closer aspect ratios i.e
|
| - // format.aspect - requested.aspect < out.aspect - requested.aspect
|
| - better = abs(format.width * requested.height * out->height -
|
| - requested.width * format.height * out->height) <
|
| - abs(out->width * format.height * requested.height -
|
| - requested.width * format.height * out->height);
|
| - }
|
| -
|
| - if (better) {
|
| - out->width = format.width;
|
| - out->height = format.height;
|
| - }
|
| - }
|
| - if (out->width > 0) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -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 &&
|
| - in_codec.name == out_codec.plName) {
|
| - 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;
|
| -
|
| - // Init the codec with the default bandwidth options.
|
| - out_codec.minBitrate = kMinVideoBitrate;
|
| - out_codec.startBitrate = kStartVideoBitrate;
|
| - out_codec.maxBitrate = kMaxVideoBitrate;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void WebRtcVideoEngine::RegisterChannel(WebRtcVideoMediaChannel *channel) {
|
| - talk_base::CritScope cs(&channels_crit_);
|
| - channels_.push_back(channel);
|
| -}
|
| -
|
| -void WebRtcVideoEngine::UnregisterChannel(WebRtcVideoMediaChannel *channel) {
|
| - talk_base::CritScope cs(&channels_crit_);
|
| - channels_.erase(std::remove(channels_.begin(), channels_.end(), channel),
|
| - channels_.end());
|
| -}
|
| -
|
| -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;
|
| -}
|
| -
|
| -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;
|
| - }
|
| - tracing_->SetTraceFilter(filter);
|
| -}
|
| -
|
| -// 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;
|
| -}
|
| -
|
| -bool WebRtcVideoEngine::SetCapturer(VideoCapturer* capturer,
|
| - bool own_capturer) {
|
| - if (capturer == NULL) {
|
| - ClearCapturer();
|
| - return true;
|
| - }
|
| - // Hook up signals and install the supplied capturer.
|
| - SignalCaptureResult.repeat(capturer->SignalStartResult);
|
| - capturer->SignalFrameCaptured.connect(this,
|
| - &WebRtcVideoEngine::OnFrameCaptured);
|
| - ClearCapturer();
|
| - video_capturer_ = capturer;
|
| - owns_capturer_ = own_capturer;
|
| - // Possibly restart the capturer if it is supposed to be running.
|
| - CaptureResult result = UpdateCapturingState();
|
| - if (result != CR_SUCCESS && result != CR_PENDING) {
|
| - LOG(LS_WARNING) << "Camera failed to restart";
|
| - return false;
|
| - }
|
| - 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.
|
| -bool WebRtcVideoEngine::ShouldIgnoreTrace(const std::string& trace) {
|
| - static const char* const kTracesToIgnore[] = {
|
| - NULL
|
| - };
|
| - for (const char* const* p = kTracesToIgnore; *p; ++p) {
|
| - if (trace.find(*p) == 0) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -int WebRtcVideoEngine::GetNumOfChannels() {
|
| - talk_base::CritScope cs(&channels_crit_);
|
| - return channels_.size();
|
| -}
|
| -
|
| -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) &&
|
| - (!voice_engine_ || !voice_engine_->ShouldIgnoreTrace(msg))) {
|
| - LOG_V(sev) << "WebRtc:" << msg;
|
| - }
|
| - }
|
| - }
|
| -}
|
| -
|
| -// TODO: stubs for now
|
| -bool WebRtcVideoEngine::RegisterProcessor(
|
| - VideoProcessor* video_processor) {
|
| - return true;
|
| -}
|
| -bool WebRtcVideoEngine::UnregisterProcessor(
|
| - VideoProcessor* video_processor) {
|
| - return true;
|
| -}
|
| -
|
| -void WebRtcVideoEngine::ClearCapturer() {
|
| - if (owns_capturer_) {
|
| - delete video_capturer_;
|
| - }
|
| - video_capturer_ = NULL;
|
| -}
|
| -
|
| -// WebRtcVideoMediaChannel
|
| -
|
| -WebRtcVideoMediaChannel::WebRtcVideoMediaChannel(
|
| - WebRtcVideoEngine* engine, VoiceMediaChannel* channel)
|
| - : engine_(engine),
|
| - voice_channel_(channel),
|
| - vie_channel_(-1),
|
| - vie_capture_(-1),
|
| - external_capture_(NULL),
|
| - sending_(false),
|
| - render_started_(false),
|
| - muted_(false),
|
| - send_min_bitrate_(kMinVideoBitrate),
|
| - send_start_bitrate_(kStartVideoBitrate),
|
| - send_max_bitrate_(kMaxVideoBitrate),
|
| - local_stream_info_(new LocalStreamInfo()) {
|
| - engine->RegisterChannel(this);
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::Init() {
|
| - if (engine_->vie()->base()->CreateChannel(vie_channel_) != 0) {
|
| - LOG_RTCERR1(CreateChannel, vie_channel_);
|
| - return false;
|
| - }
|
| -
|
| - LOG(LS_INFO) << "WebRtcVideoMediaChannel::Init "
|
| - << "vie_channel " << vie_channel_ << " created";
|
| -
|
| - // Connect the voice channel, if there is one.
|
| - if (voice_channel_) {
|
| - WebRtcVoiceMediaChannel* channel =
|
| - static_cast<WebRtcVoiceMediaChannel*>(voice_channel_);
|
| - if (engine_->vie()->base()->ConnectAudioChannel(
|
| - vie_channel_, channel->voe_channel()) != 0) {
|
| - LOG_RTCERR2(ConnectAudioChannel, vie_channel_, channel->voe_channel());
|
| - LOG(LS_WARNING) << "A/V not synchronized";
|
| - // Not a fatal error.
|
| - }
|
| - }
|
| -
|
| - // Register external transport.
|
| - if (engine_->vie()->network()->RegisterSendTransport(
|
| - vie_channel_, *this) != 0) {
|
| - LOG_RTCERR1(RegisterSendTransport, vie_channel_);
|
| - return false;
|
| - }
|
| -
|
| - // Set MTU.
|
| - if (engine_->vie()->network()->SetMTU(vie_channel_, kVideoMtu) != 0) {
|
| - LOG_RTCERR2(SetMTU, vie_channel_, kVideoMtu);
|
| - return false;
|
| - }
|
| -
|
| - // Register external capture.
|
| - if (engine()->vie()->capture()->AllocateExternalCaptureDevice(
|
| - vie_capture_, external_capture_) != 0) {
|
| - LOG_RTCERR0(AllocateExternalCaptureDevice);
|
| - return false;
|
| - }
|
| -
|
| - // Connect external capture.
|
| - if (engine()->vie()->capture()->ConnectCaptureDevice(
|
| - vie_capture_, vie_channel_) != 0) {
|
| - LOG_RTCERR2(ConnectCaptureDevice, vie_capture_, vie_channel_);
|
| - return false;
|
| - }
|
| -
|
| - // Install render adapter.
|
| - remote_renderer_.reset(new WebRtcRenderAdapter(NULL));
|
| - if (engine_->vie()->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;
|
| - }
|
| -
|
| - // Register decoder observer for incoming framerate and bitrate.
|
| - decoder_observer_.reset(new WebRtcDecoderObserver(vie_channel_));
|
| - if (engine()->vie()->codec()->RegisterDecoderObserver(
|
| - vie_channel_, *decoder_observer_) != 0) {
|
| - LOG_RTCERR1(RegisterDecoderObserver, decoder_observer_.get());
|
| - return false;
|
| - }
|
| -
|
| - // Register encoder observer for outgoing framerate and bitrate.
|
| - encoder_observer_.reset(new WebRtcEncoderObserver(vie_channel_));
|
| - if (engine()->vie()->codec()->RegisterEncoderObserver(
|
| - vie_channel_, *encoder_observer_) != 0) {
|
| - LOG_RTCERR1(RegisterEncoderObserver, encoder_observer_.get());
|
| - return false;
|
| - }
|
| -
|
| - // Turn on RTCP and loss feedback reporting.
|
| - if (!EnableRtcp() ||
|
| - !EnablePli()) {
|
| - return false;
|
| - }
|
| -
|
| -#ifdef WEBRTC_VIDEO_AVPF_NACK_ONLY
|
| - // Turn on NACK-only loss handling.
|
| - if (!EnableNack())
|
| - return false;
|
| -#endif
|
| -
|
| - // Turn on TMMBR-based BWE reporting.
|
| - if (!EnableTmmbr()) {
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -WebRtcVideoMediaChannel::~WebRtcVideoMediaChannel() {
|
| - if (vie_channel_ != -1) {
|
| - // Stop sending.
|
| - SetSend(false);
|
| - if (engine()->vie()->codec()->DeregisterEncoderObserver(
|
| - vie_channel_) != 0) {
|
| - LOG_RTCERR1(DeregisterEncoderObserver, vie_channel_);
|
| - }
|
| -
|
| - // Stop the renderer.
|
| - SetRender(false);
|
| - if (engine()->vie()->codec()->DeregisterDecoderObserver(
|
| - vie_channel_) != 0) {
|
| - LOG_RTCERR1(DeregisterDecoderObserver, vie_channel_);
|
| - }
|
| - if (remote_renderer_.get() &&
|
| - engine()->vie()->render()->RemoveRenderer(vie_channel_) != 0) {
|
| - LOG_RTCERR1(RemoveRenderer, vie_channel_);
|
| - }
|
| -
|
| - // Destroy the external capture interface.
|
| - if (vie_capture_ != -1) {
|
| - if (engine()->vie()->capture()->DisconnectCaptureDevice(
|
| - vie_channel_) != 0) {
|
| - LOG_RTCERR1(DisconnectCaptureDevice, vie_channel_);
|
| - }
|
| - if (engine()->vie()->capture()->ReleaseCaptureDevice(
|
| - vie_capture_) != 0) {
|
| - LOG_RTCERR1(ReleaseCaptureDevice, vie_capture_);
|
| - }
|
| - }
|
| -
|
| - // Deregister external transport.
|
| - if (engine()->vie()->network()->DeregisterSendTransport(
|
| - vie_channel_) != 0) {
|
| - LOG_RTCERR1(DeregisterSendTransport, vie_channel_);
|
| - }
|
| -
|
| - // Delete the VideoEngine channel.
|
| - if (engine()->vie()->base()->DeleteChannel(vie_channel_) != 0) {
|
| - LOG_RTCERR1(DeleteChannel, vie_channel_);
|
| - }
|
| - }
|
| -
|
| - // Unregister the channel from the engine.
|
| - engine()->UnregisterChannel(this);
|
| -}
|
| -
|
| -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()->vie()->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()->vie()->base()->StartReceive(vie_channel_) != 0) {
|
| - LOG_RTCERR1(StartReceive, vie_channel_);
|
| - ret = false;
|
| - }
|
| - }
|
| - return ret;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SetSendCodecs(
|
| - const std::vector<VideoCodec>& codecs) {
|
| - // Match with local video codec list.
|
| - std::vector<webrtc::VideoCodec> send_codecs;
|
| - int red_type = -1, fec_type = -1;
|
| - VideoCodec checked_codec;
|
| - VideoCodec current; // defaults to 0x0
|
| - if (sending_) {
|
| - engine()->ConvertToCricketVideoCodec(*send_codec_, current);
|
| - }
|
| - for (std::vector<VideoCodec>::const_iterator iter = codecs.begin();
|
| - iter != codecs.end(); ++iter) {
|
| - if (_stricmp(iter->name.c_str(), kRedPayloadName) == 0) {
|
| - red_type = iter->id;
|
| - } else if (_stricmp(iter->name.c_str(), kFecPayloadName) == 0) {
|
| - fec_type = iter->id;
|
| - } else if (engine()->CanSendCodec(*iter, current, &checked_codec)) {
|
| - webrtc::VideoCodec wcodec;
|
| - if (engine()->ConvertFromCricketVideoCodec(checked_codec, wcodec)) {
|
| - send_codecs.push_back(wcodec);
|
| - }
|
| - } else {
|
| - LOG(LS_WARNING) << "Unknown codec " << iter->name;
|
| - }
|
| - }
|
| -
|
| - // Fail if we don't have a match.
|
| - if (send_codecs.empty()) {
|
| - LOG(LS_WARNING) << "No matching codecs avilable";
|
| - return false;
|
| - }
|
| -
|
| -#ifndef WEBRTC_VIDEO_AVPF_NACK_ONLY
|
| - // Configure FEC if enabled.
|
| - if (!SetNackFec(red_type, fec_type)) {
|
| - return false;
|
| - }
|
| -#endif
|
| -
|
| - // Select the first matched codec.
|
| - webrtc::VideoCodec& codec(send_codecs[0]);
|
| -
|
| - // Set the default number of temporal layers for VP8.
|
| - if (webrtc::kVideoCodecVP8 == codec.codecType) {
|
| - codec.codecSpecific.VP8.numberOfTemporalLayers =
|
| - kDefaultNumberOfTemporalLayers;
|
| - }
|
| -
|
| - if (!SetSendCodec(
|
| - codec, send_min_bitrate_, send_start_bitrate_, send_max_bitrate_)) {
|
| - return false;
|
| - }
|
| -
|
| - LOG(LS_INFO) << "Selected video codec " << send_codec_->plName << "/"
|
| - << send_codec_->width << "x" << send_codec_->height << "x"
|
| - << static_cast<int>(send_codec_->maxFramerate);
|
| - if (webrtc::kVideoCodecVP8 == codec.codecType) {
|
| - LOG(LS_INFO) << "VP8 number of layers: "
|
| - << static_cast<int>(
|
| - send_codec_->codecSpecific.VP8.numberOfTemporalLayers);
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SetRender(bool render) {
|
| - if (render == render_started_) {
|
| - return true; // no action required
|
| - }
|
| -
|
| - bool ret = true;
|
| - if (render) {
|
| - if (engine()->vie()->render()->StartRender(vie_channel_) != 0) {
|
| - LOG_RTCERR1(StartRender, vie_channel_);
|
| - ret = false;
|
| - }
|
| - } else {
|
| - if (engine()->vie()->render()->StopRender(vie_channel_) != 0) {
|
| - LOG_RTCERR1(StopRender, vie_channel_);
|
| - ret = false;
|
| - }
|
| - }
|
| - if (ret) {
|
| - render_started_ = render;
|
| - }
|
| -
|
| - return ret;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SetSend(bool send) {
|
| - if (send == sending()) {
|
| - return true; // no action required
|
| - }
|
| -
|
| - if (send) {
|
| - // We've been asked to start sending.
|
| - // SetSendCodecs must have been called already.
|
| - if (!send_codec_.get()) {
|
| - return false;
|
| - }
|
| -
|
| - if (engine()->vie()->base()->StartSend(vie_channel_) != 0) {
|
| - LOG_RTCERR1(StartSend, vie_channel_);
|
| - return false;
|
| - }
|
| - } else {
|
| - // We've been asked to stop sending.
|
| - if (engine()->vie()->base()->StopSend(vie_channel_) != 0) {
|
| - LOG_RTCERR1(StopSend, vie_channel_);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - sending_ = send;
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::AddStream(uint32 ssrc, uint32 voice_ssrc) {
|
| - return false;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::RemoveStream(uint32 ssrc) {
|
| - return false;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SetRenderer(
|
| - uint32 ssrc, VideoRenderer* renderer) {
|
| - if (ssrc != 0)
|
| - return false;
|
| -
|
| - remote_renderer_->SetRenderer(renderer);
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::GetStats(VideoMediaInfo* info) {
|
| - // Get basic statistics.
|
| - unsigned int bytes_sent, packets_sent, bytes_recv, packets_recv;
|
| - unsigned int ssrc;
|
| - if (engine_->vie()->rtp()->GetRTPStatistics(vie_channel_,
|
| - bytes_sent, packets_sent, bytes_recv, packets_recv) != 0) {
|
| - LOG_RTCERR1(GetRTPStatistics, vie_channel_);
|
| - return false;
|
| - }
|
| -
|
| - // Get sender statistics and build VideoSenderInfo.
|
| - if (engine_->vie()->rtp()->GetLocalSSRC(vie_channel_, ssrc) == 0) {
|
| - VideoSenderInfo sinfo;
|
| - sinfo.ssrc = ssrc;
|
| - sinfo.codec_name = send_codec_.get() ? send_codec_->plName : "";
|
| - sinfo.bytes_sent = bytes_sent;
|
| - sinfo.packets_sent = packets_sent;
|
| - sinfo.packets_cached = -1;
|
| - sinfo.packets_lost = -1;
|
| - sinfo.fraction_lost = -1;
|
| - sinfo.firs_rcvd = -1;
|
| - sinfo.nacks_rcvd = -1;
|
| - sinfo.rtt_ms = -1;
|
| - sinfo.frame_width = local_stream_info_->width();
|
| - sinfo.frame_height = local_stream_info_->height();
|
| - sinfo.framerate_input = local_stream_info_->framerate();
|
| - sinfo.framerate_sent = encoder_observer_->framerate();
|
| - sinfo.nominal_bitrate = encoder_observer_->bitrate();
|
| - sinfo.preferred_bitrate = kMaxVideoBitrate;
|
| -
|
| - // Get received RTCP statistics for the sender, if available.
|
| - // It's not a fatal error if we can't, since RTCP may not have arrived yet.
|
| - uint16 r_fraction_lost;
|
| - unsigned int r_cumulative_lost;
|
| - unsigned int r_extended_max;
|
| - unsigned int r_jitter;
|
| - int r_rtt_ms;
|
| - if (engine_->vie()->rtp()->GetReceivedRTCPStatistics(vie_channel_,
|
| - r_fraction_lost, r_cumulative_lost, r_extended_max,
|
| - r_jitter, r_rtt_ms) == 0) {
|
| - // Convert Q8 to float.
|
| - sinfo.packets_lost = r_cumulative_lost;
|
| - sinfo.fraction_lost = static_cast<float>(r_fraction_lost) / (1 << 8);
|
| - sinfo.rtt_ms = r_rtt_ms;
|
| - }
|
| - info->senders.push_back(sinfo);
|
| - } else {
|
| - LOG_RTCERR1(GetLocalSSRC, vie_channel_);
|
| - }
|
| -
|
| - // Get receiver statistics and build VideoReceiverInfo, if we have data.
|
| - if (engine_->vie()->rtp()->GetRemoteSSRC(vie_channel_, ssrc) == 0) {
|
| - VideoReceiverInfo rinfo;
|
| - rinfo.ssrc = ssrc;
|
| - rinfo.bytes_rcvd = bytes_recv;
|
| - rinfo.packets_rcvd = packets_recv;
|
| - rinfo.packets_lost = -1;
|
| - rinfo.packets_concealed = -1;
|
| - rinfo.fraction_lost = -1; // from SentRTCP
|
| - rinfo.firs_sent = decoder_observer_->firs_requested();
|
| - rinfo.nacks_sent = -1;
|
| - rinfo.frame_width = remote_renderer_->width();
|
| - rinfo.frame_height = remote_renderer_->height();
|
| - rinfo.framerate_rcvd = decoder_observer_->framerate();
|
| - int fps = remote_renderer_->framerate();
|
| - rinfo.framerate_decoded = fps;
|
| - rinfo.framerate_output = fps;
|
| -
|
| - // Get sent RTCP statistics.
|
| - uint16 s_fraction_lost;
|
| - unsigned int s_cumulative_lost;
|
| - unsigned int s_extended_max;
|
| - unsigned int s_jitter;
|
| - int s_rtt_ms;
|
| - if (engine_->vie()->rtp()->GetSentRTCPStatistics(vie_channel_,
|
| - s_fraction_lost, s_cumulative_lost, s_extended_max,
|
| - s_jitter, s_rtt_ms) == 0) {
|
| - // Convert Q8 to float.
|
| - rinfo.packets_lost = s_cumulative_lost;
|
| - rinfo.fraction_lost = static_cast<float>(s_fraction_lost) / (1 << 8);
|
| - }
|
| - info->receivers.push_back(rinfo);
|
| - }
|
| -
|
| - // Build BandwidthEstimationInfo.
|
| - // TODO: Fill in more BWE stats once we have them.
|
| - unsigned int total_bitrate_sent;
|
| - unsigned int video_bitrate_sent;
|
| - unsigned int fec_bitrate_sent;
|
| - unsigned int nack_bitrate_sent;
|
| - if (engine_->vie()->rtp()->GetBandwidthUsage(vie_channel_,
|
| - total_bitrate_sent, video_bitrate_sent,
|
| - fec_bitrate_sent, nack_bitrate_sent) == 0) {
|
| - BandwidthEstimationInfo bwe;
|
| - bwe.actual_enc_bitrate = video_bitrate_sent;
|
| - bwe.transmit_bitrate = total_bitrate_sent;
|
| - bwe.retransmit_bitrate = nack_bitrate_sent;
|
| - info->bw_estimations.push_back(bwe);
|
| - } else {
|
| - LOG_RTCERR1(GetBandwidthUsage, vie_channel_);
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SendIntraFrame() {
|
| - bool ret = true;
|
| - if (engine()->vie()->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()->vie()->network()->ReceivedRTPPacket(vie_channel_,
|
| - packet->data(),
|
| - packet->length());
|
| -}
|
| -
|
| -void WebRtcVideoMediaChannel::OnRtcpReceived(talk_base::Buffer* packet) {
|
| - engine_->vie()->network()->ReceivedRTCPPacket(vie_channel_,
|
| - packet->data(),
|
| - packet->length());
|
| -}
|
| -
|
| -void WebRtcVideoMediaChannel::SetSendSsrc(uint32 id) {
|
| - if (!sending_) {
|
| - if (engine()->vie()->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()->vie()->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) {
|
| - muted_ = on;
|
| - return true;
|
| -}
|
| -
|
| -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;
|
| - }
|
| -
|
| - int min_bitrate;
|
| - int start_bitrate;
|
| - int max_bitrate;
|
| - if (autobw) {
|
| - // Use the default values for min bitrate.
|
| - min_bitrate = kMinVideoBitrate;
|
| - // Use the default value or the bps for the max
|
| - max_bitrate = (bps <= 0) ? kMaxVideoBitrate : (bps / 1000);
|
| - // Maximum start bitrate can be kStartVideoBitrate.
|
| - start_bitrate = talk_base::_min(kStartVideoBitrate, max_bitrate);
|
| - } else {
|
| - // Use the default start or the bps as the target bitrate.
|
| - int target_bitrate = (bps <= 0) ? kStartVideoBitrate : (bps / 1000);
|
| - min_bitrate = target_bitrate;
|
| - start_bitrate = target_bitrate;
|
| - max_bitrate = target_bitrate;
|
| - }
|
| -
|
| - if (!SetSendCodec(*send_codec_, min_bitrate, start_bitrate, max_bitrate)) {
|
| - return false;
|
| - }
|
| -
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SetOptions(int options) {
|
| - return true;
|
| -}
|
| -
|
| -void WebRtcVideoMediaChannel::SetInterface(NetworkInterface* iface) {
|
| - MediaChannel::SetInterface(iface);
|
| - // Set the RTP recv/send buffer to a bigger size
|
| - if (network_interface_) {
|
| - network_interface_->SetOption(NetworkInterface::ST_RTP,
|
| - talk_base::Socket::OPT_RCVBUF,
|
| - kVideoRtpBufferSize);
|
| - network_interface_->SetOption(NetworkInterface::ST_RTP,
|
| - talk_base::Socket::OPT_SNDBUF,
|
| - kVideoRtpBufferSize);
|
| - }
|
| -}
|
| -
|
| -// TODO: Add unittests to test this function.
|
| -bool WebRtcVideoMediaChannel::SendFrame(uint32 ssrc, const VideoFrame* frame) {
|
| - if (ssrc != 0 || !sending() || !external_capture_) {
|
| - return false;
|
| - }
|
| -
|
| - // Update local stream statistics.
|
| - local_stream_info_->UpdateFrame(frame->GetWidth(), frame->GetHeight());
|
| -
|
| - // If the captured video format is smaller than what we asked for, reset send
|
| - // codec on video engine.
|
| - if (send_codec_.get() != NULL &&
|
| - frame->GetWidth() < send_codec_->width &&
|
| - frame->GetHeight() < send_codec_->height) {
|
| - LOG(LS_INFO) << "Captured video frame size changed to: "
|
| - << frame->GetWidth() << "x" << frame->GetHeight();
|
| - webrtc::VideoCodec new_codec = *send_codec_;
|
| - new_codec.width = frame->GetWidth();
|
| - new_codec.height = frame->GetHeight();
|
| - if (!SetSendCodec(
|
| - new_codec, send_min_bitrate_, send_start_bitrate_, send_max_bitrate_)) {
|
| - LOG(LS_WARNING) << "Failed to switch to new frame size: "
|
| - << frame->GetWidth() << "x" << frame->GetHeight();
|
| - }
|
| - }
|
| -
|
| - // Blacken the frame if video is muted.
|
| - const VideoFrame* frame_out = frame;
|
| - talk_base::scoped_ptr<VideoFrame> black_frame;
|
| - if (muted_) {
|
| - black_frame.reset(frame->Copy());
|
| - black_frame->SetToBlack();
|
| - frame_out = black_frame.get();
|
| - }
|
| -
|
| - webrtc::ViEVideoFrameI420 frame_i420;
|
| - // TODO: Update the webrtc::ViEVideoFrameI420
|
| - // to use const unsigned char*
|
| - frame_i420.y_plane = const_cast<unsigned char*>(frame_out->GetYPlane());
|
| - frame_i420.u_plane = const_cast<unsigned char*>(frame_out->GetUPlane());
|
| - frame_i420.v_plane = const_cast<unsigned char*>(frame_out->GetVPlane());
|
| - frame_i420.y_pitch = frame_out->GetYPitch();
|
| - frame_i420.u_pitch = frame_out->GetUPitch();
|
| - frame_i420.v_pitch = frame_out->GetVPitch();
|
| - frame_i420.width = frame_out->GetWidth();
|
| - frame_i420.height = frame_out->GetHeight();
|
| -
|
| - // Convert from nanoseconds to milliseconds.
|
| - WebRtc_Word64 clocks = frame_out->GetTimeStamp() /
|
| - talk_base::kNumNanosecsPerMillisec;
|
| -
|
| - return (external_capture_->IncomingFrameI420(frame_i420, clocks) == 0);
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::EnableRtcp() {
|
| - if (engine()->vie()->rtp()->SetRTCPStatus(
|
| - vie_channel_, webrtc::kRtcpCompound_RFC4585) != 0) {
|
| - LOG_RTCERR2(SetRTCPStatus, vie_channel_, webrtc::kRtcpCompound_RFC4585);
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::EnablePli() {
|
| - if (engine_->vie()->rtp()->SetKeyFrameRequestMethod(
|
| - vie_channel_, webrtc::kViEKeyFrameRequestPliRtcp) != 0) {
|
| - LOG_RTCERR2(SetRTCPStatus,
|
| - vie_channel_, webrtc::kViEKeyFrameRequestPliRtcp);
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::EnableTmmbr() {
|
| - if (engine_->vie()->rtp()->SetTMMBRStatus(vie_channel_, true) != 0) {
|
| - LOG_RTCERR1(SetTMMBRStatus, vie_channel_);
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::EnableNack() {
|
| - if (engine_->vie()->rtp()->SetNACKStatus(vie_channel_, true) != 0) {
|
| - LOG_RTCERR1(SetNACKStatus, vie_channel_);
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SetNackFec(int red_payload_type,
|
| - int fec_payload_type) {
|
| - bool enable = (red_payload_type != -1 && fec_payload_type != -1);
|
| - if (engine_->vie()->rtp()->SetHybridNACKFECStatus(
|
| - vie_channel_, enable, red_payload_type, fec_payload_type) != 0) {
|
| - LOG_RTCERR4(SetHybridNACKFECStatus,
|
| - vie_channel_, enable, red_payload_type, fec_payload_type);
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool WebRtcVideoMediaChannel::SetSendCodec(const webrtc::VideoCodec& codec,
|
| - int min_bitrate,
|
| - int start_bitrate,
|
| - int max_bitrate) {
|
| - // Make a copy of the codec
|
| - webrtc::VideoCodec target_codec = codec;
|
| - target_codec.startBitrate = start_bitrate;
|
| - target_codec.minBitrate = min_bitrate;
|
| - target_codec.maxBitrate = max_bitrate;
|
| -
|
| - if (engine()->vie()->codec()->SetSendCodec(vie_channel_, target_codec) != 0) {
|
| - LOG_RTCERR2(SetSendCodec, vie_channel_, send_codec_->plName);
|
| - return false;
|
| - }
|
| -
|
| - // Reset the send_codec_ only if SetSendCodec is success.
|
| - send_codec_.reset(new webrtc::VideoCodec(target_codec));
|
| - send_min_bitrate_ = min_bitrate;
|
| - send_start_bitrate_ = start_bitrate;
|
| - send_max_bitrate_ = max_bitrate;
|
| -
|
| - return 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
|
|
|