Wire up external encoders.
R=pthatcher@webrtc.org
BUG=1788
Review URL: https://webrtc-codereview.appspot.com/30649005
git-svn-id: http://webrtc.googlecode.com/svn/trunk@7440 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/media/webrtc/constants.h b/talk/media/webrtc/constants.h
index 68f664b..5390c0d 100755
--- a/talk/media/webrtc/constants.h
+++ b/talk/media/webrtc/constants.h
@@ -33,6 +33,7 @@
extern const int kVideoRtpBufferSize;
extern const char kVp8CodecName[];
+extern const char kH264CodecName[];
extern const int kDefaultFramerate;
extern const int kMinVideoBitrate;
diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h
index e6de35b..729179b 100644
--- a/talk/media/webrtc/fakewebrtcvideoengine.h
+++ b/talk/media/webrtc/fakewebrtcvideoengine.h
@@ -38,8 +38,10 @@
#include "talk/media/webrtc/webrtcvideoencoderfactory.h"
#include "talk/media/webrtc/webrtcvie.h"
#include "webrtc/base/basictypes.h"
+#include "webrtc/base/criticalsection.h"
#include "webrtc/base/gunit.h"
#include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread_annotations.h"
namespace cricket {
@@ -146,7 +148,7 @@
// Fake class for mocking out webrtc::VideoEnoder
class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
public:
- FakeWebRtcVideoEncoder() {}
+ FakeWebRtcVideoEncoder() : num_frames_encoded_(0) {}
virtual int32 InitEncode(const webrtc::VideoCodec* codecSettings,
int32 numberOfCores,
@@ -158,6 +160,8 @@
const webrtc::I420VideoFrame& inputImage,
const webrtc::CodecSpecificInfo* codecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* frame_types) {
+ rtc::CritScope lock(&crit_);
+ ++num_frames_encoded_;
return WEBRTC_VIDEO_CODEC_OK;
}
@@ -179,6 +183,15 @@
uint32 frameRate) {
return WEBRTC_VIDEO_CODEC_OK;
}
+
+ int GetNumEncodedFrames() {
+ rtc::CritScope lock(&crit_);
+ return num_frames_encoded_;
+ }
+
+ private:
+ rtc::CriticalSection crit_;
+ int num_frames_encoded_ GUARDED_BY(crit_);
};
// Fake class for mocking out WebRtcVideoEncoderFactory.
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index d8b5616..628ed50 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -49,6 +49,65 @@
ASSERT(false)
namespace cricket {
+namespace {
+
+static bool CodecNameMatches(const std::string& name1,
+ const std::string& name2) {
+ return _stricmp(name1.c_str(), name2.c_str()) == 0;
+}
+
+// True if codec is supported by a software implementation that's always
+// available.
+static bool CodecIsInternallySupported(const std::string& codec_name) {
+ return CodecNameMatches(codec_name, kVp8CodecName);
+}
+
+static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) {
+ std::stringstream out;
+ out << '{';
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ out << codecs[i].ToString();
+ if (i != codecs.size() - 1) {
+ out << ", ";
+ }
+ }
+ out << '}';
+ return out.str();
+}
+
+static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) {
+ bool has_video = false;
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ if (!codecs[i].ValidateCodecFormat()) {
+ return false;
+ }
+ if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) {
+ has_video = true;
+ }
+ }
+ if (!has_video) {
+ LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: "
+ << CodecVectorToString(codecs);
+ return false;
+ }
+ return true;
+}
+
+static std::string RtpExtensionsToString(
+ const std::vector<RtpHeaderExtension>& extensions) {
+ std::stringstream out;
+ out << '{';
+ for (size_t i = 0; i < extensions.size(); ++i) {
+ out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
+ if (i != extensions.size() - 1) {
+ out << ", ";
+ }
+ }
+ out << '}';
+ return out.str();
+}
+
+} // namespace
// This constant is really an on/off, lower-level configurable NACK history
// duration hasn't been implemented.
@@ -58,6 +117,13 @@
static const int kDefaultRtcpReceiverReportSsrc = 1;
+// External video encoders are given payloads 120-127. This also means that we
+// only support up to 8 external payload types.
+static const int kExternalVideoPayloadTypeBase = 120;
+#ifndef NDEBUG
+static const size_t kMaxExternalVideoCodecs = 8;
+#endif
+
struct VideoCodecPref {
int payload_type;
int width;
@@ -66,6 +132,8 @@
int rtx_payload_type;
} kDefaultVideoCodecPref = {100, 640, 400, kVp8CodecName, 96};
+const char kH264CodecName[] = "H264";
+
VideoCodecPref kRedPref = {116, -1, -1, kRedCodecName, -1};
VideoCodecPref kUlpfecPref = {117, -1, -1, kUlpfecCodecName, -1};
@@ -169,7 +237,6 @@
const VideoCodec& codec,
const VideoOptions& options,
size_t num_streams) {
- assert(SupportsCodec(codec));
if (num_streams != 1) {
LOG(LS_ERROR) << "Unsupported number of streams: " << num_streams;
return std::vector<webrtc::VideoStream>();
@@ -196,24 +263,10 @@
return streams;
}
-webrtc::VideoEncoder* WebRtcVideoEncoderFactory2::CreateVideoEncoder(
- const VideoCodec& codec,
- const VideoOptions& options) {
- assert(SupportsCodec(codec));
- if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
- return webrtc::VideoEncoder::Create(webrtc::VideoEncoder::kVp8);
- }
- // This shouldn't happen, we should be able to create encoders for all codecs
- // we support.
- assert(false);
- return NULL;
-}
-
void* WebRtcVideoEncoderFactory2::CreateVideoEncoderSettings(
const VideoCodec& codec,
const VideoOptions& options) {
- assert(SupportsCodec(codec));
- if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
+ if (CodecNameMatches(codec.name, kVp8CodecName)) {
webrtc::VideoCodecVP8* settings = new webrtc::VideoCodecVP8(
webrtc::VideoEncoder::GetDefaultVp8Settings());
options.video_noise_reduction.Get(&settings->denoisingOn);
@@ -225,19 +278,14 @@
void WebRtcVideoEncoderFactory2::DestroyVideoEncoderSettings(
const VideoCodec& codec,
void* encoder_settings) {
- assert(SupportsCodec(codec));
if (encoder_settings == NULL) {
return;
}
- if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
+ if (CodecNameMatches(codec.name, kVp8CodecName)) {
delete reinterpret_cast<webrtc::VideoCodecVP8*>(encoder_settings);
}
}
-bool WebRtcVideoEncoderFactory2::SupportsCodec(const VideoCodec& codec) {
- return _stricmp(codec.name.c_str(), kVp8CodecName) == 0;
-}
-
DefaultUnsignalledSsrcHandler::DefaultUnsignalledSsrcHandler()
: default_recv_ssrc_(0), default_renderer_(NULL) {}
@@ -284,7 +332,6 @@
WebRtcVideoEngine2::WebRtcVideoEngine2()
: worker_thread_(NULL),
voice_engine_(NULL),
- video_codecs_(DefaultVideoCodecs()),
default_codec_format_(kDefaultVideoCodecPref.width,
kDefaultVideoCodecPref.height,
FPS_TO_INTERVAL(kDefaultFramerate),
@@ -295,6 +342,7 @@
external_decoder_factory_(NULL),
external_encoder_factory_(NULL) {
LOG(LS_INFO) << "WebRtcVideoEngine2::WebRtcVideoEngine2()";
+ video_codecs_ = GetSupportedCodecs();
rtp_header_extensions_.push_back(
RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension,
kRtpTimestampOffsetHeaderExtensionDefaultId));
@@ -312,6 +360,7 @@
}
void WebRtcVideoEngine2::SetCallFactory(WebRtcCallFactory* call_factory) {
+ assert(!initialized_);
call_factory_ = call_factory;
}
@@ -344,7 +393,7 @@
const VideoEncoderConfig& config) {
const VideoCodec& codec = config.max_codec;
// TODO(pbos): Make use of external encoder factory.
- if (!GetVideoEncoderFactory()->SupportsCodec(codec)) {
+ if (!CodecIsInternallySupported(codec.name)) {
LOG(LS_ERROR) << "SetDefaultEncoderConfig, codec not supported:"
<< codec.ToString();
return false;
@@ -366,11 +415,16 @@
WebRtcVideoChannel2* WebRtcVideoEngine2::CreateChannel(
VoiceMediaChannel* voice_channel) {
+ assert(initialized_);
LOG(LS_INFO) << "CreateChannel: "
<< (voice_channel != NULL ? "With" : "Without")
<< " voice channel.";
- WebRtcVideoChannel2* channel = new WebRtcVideoChannel2(
- call_factory_, voice_channel, GetVideoEncoderFactory());
+ WebRtcVideoChannel2* channel =
+ new WebRtcVideoChannel2(call_factory_,
+ voice_channel,
+ external_encoder_factory_,
+ external_decoder_factory_,
+ GetVideoEncoderFactory());
if (!channel->Init()) {
delete channel;
return NULL;
@@ -400,12 +454,16 @@
void WebRtcVideoEngine2::SetExternalDecoderFactory(
WebRtcVideoDecoderFactory* decoder_factory) {
+ assert(!initialized_);
external_decoder_factory_ = decoder_factory;
}
void WebRtcVideoEngine2::SetExternalEncoderFactory(
WebRtcVideoEncoderFactory* encoder_factory) {
+ assert(!initialized_);
external_encoder_factory_ = encoder_factory;
+
+ video_codecs_ = GetSupportedCodecs();
}
bool WebRtcVideoEngine2::EnableTimedRender() {
@@ -495,6 +553,35 @@
return &default_video_encoder_factory_;
}
+std::vector<VideoCodec> WebRtcVideoEngine2::GetSupportedCodecs() const {
+ std::vector<VideoCodec> supported_codecs = DefaultVideoCodecs();
+
+ if (external_encoder_factory_ == NULL) {
+ return supported_codecs;
+ }
+
+ assert(external_encoder_factory_->codecs().size() <= kMaxExternalVideoCodecs);
+ const std::vector<WebRtcVideoEncoderFactory::VideoCodec>& codecs =
+ external_encoder_factory_->codecs();
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ // Don't add internally-supported codecs twice.
+ if (CodecIsInternallySupported(codecs[i].name)) {
+ continue;
+ }
+
+ VideoCodec codec(kExternalVideoPayloadTypeBase + static_cast<int>(i),
+ codecs[i].name,
+ codecs[i].max_width,
+ codecs[i].max_height,
+ codecs[i].max_fps,
+ 0);
+
+ AddDefaultFeedbackParams(&codec);
+ supported_codecs.push_back(codec);
+ }
+ return supported_codecs;
+}
+
// Thin map between VideoFrame and an existing webrtc::I420VideoFrame
// to avoid having to copy the rendered VideoFrame prematurely.
// This implementation is only safe to use in a const context and should never
@@ -656,8 +743,12 @@
WebRtcVideoChannel2::WebRtcVideoChannel2(
WebRtcCallFactory* call_factory,
VoiceMediaChannel* voice_channel,
+ WebRtcVideoEncoderFactory* external_encoder_factory,
+ WebRtcVideoDecoderFactory* external_decoder_factory,
WebRtcVideoEncoderFactory2* encoder_factory)
: unsignalled_ssrc_handler_(&default_unsignalled_ssrc_handler_),
+ external_encoder_factory_(external_encoder_factory),
+ external_decoder_factory_(external_decoder_factory),
encoder_factory_(encoder_factory) {
// TODO(pbos): Connect the video and audio with |voice_channel|.
webrtc::Call::Config config(this);
@@ -696,55 +787,6 @@
bool WebRtcVideoChannel2::Init() { return true; }
-namespace {
-
-static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) {
- std::stringstream out;
- out << '{';
- for (size_t i = 0; i < codecs.size(); ++i) {
- out << codecs[i].ToString();
- if (i != codecs.size() - 1) {
- out << ", ";
- }
- }
- out << '}';
- return out.str();
-}
-
-static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) {
- bool has_video = false;
- for (size_t i = 0; i < codecs.size(); ++i) {
- if (!codecs[i].ValidateCodecFormat()) {
- return false;
- }
- if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) {
- has_video = true;
- }
- }
- if (!has_video) {
- LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: "
- << CodecVectorToString(codecs);
- return false;
- }
- return true;
-}
-
-static std::string RtpExtensionsToString(
- const std::vector<RtpHeaderExtension>& extensions) {
- std::stringstream out;
- out << '{';
- for (size_t i = 0; i < extensions.size(); ++i) {
- out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
- if (i != extensions.size() - 1) {
- out << ", ";
- }
- }
- out << '}';
- return out.str();
-}
-
-} // namespace
-
bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
LOG(LS_INFO) << "SetRecvCodecs: " << CodecVectorToString(codecs);
if (!ValidateCodecFormats(codecs)) {
@@ -760,7 +802,7 @@
// TODO(pbos): Add a decoder factory which controls supported codecs.
// Blocked on webrtc:2854.
for (size_t i = 0; i < mapped_codecs.size(); ++i) {
- if (_stricmp(mapped_codecs[i].codec.name.c_str(), kVp8CodecName) != 0) {
+ if (!CodecNameMatches(mapped_codecs[i].codec.name, kVp8CodecName)) {
LOG(LS_ERROR) << "SetRecvCodecs called with unsupported codec: '"
<< mapped_codecs[i].codec.name << "'";
return false;
@@ -881,6 +923,7 @@
WebRtcVideoSendStream* stream =
new WebRtcVideoSendStream(call_.get(),
+ external_encoder_factory_,
encoder_factory_,
options_,
send_codec_,
@@ -1307,15 +1350,18 @@
WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream(
webrtc::Call* call,
+ WebRtcVideoEncoderFactory* external_encoder_factory,
WebRtcVideoEncoderFactory2* encoder_factory,
const VideoOptions& options,
const Settable<VideoCodecSettings>& codec_settings,
const StreamParams& sp,
const std::vector<webrtc::RtpExtension>& rtp_extensions)
: call_(call),
+ external_encoder_factory_(external_encoder_factory),
encoder_factory_(encoder_factory),
stream_(NULL),
parameters_(webrtc::VideoSendStream::Config(), options, codec_settings),
+ allocated_encoder_(NULL, webrtc::kVideoCodecUnknown, false),
capturer_(NULL),
sending_(false),
muted_(false) {
@@ -1338,7 +1384,7 @@
if (stream_ != NULL) {
call_->DestroyVideoSendStream(stream_);
}
- delete parameters_.config.encoder_settings.encoder;
+ DestroyVideoEncoder(&allocated_encoder_);
}
static void SetWebRtcFrameToBlack(webrtc::I420VideoFrame* video_frame) {
@@ -1505,11 +1551,60 @@
parameters_.options = options;
}
}
+
void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodec(
const VideoCodecSettings& codec_settings) {
rtc::CritScope cs(&lock_);
SetCodecAndOptions(codec_settings, parameters_.options);
}
+
+webrtc::VideoCodecType CodecTypeFromName(const std::string& name) {
+ if (CodecNameMatches(name, kVp8CodecName)) {
+ return webrtc::kVideoCodecVP8;
+ } else if (CodecNameMatches(name, kH264CodecName)) {
+ return webrtc::kVideoCodecH264;
+ }
+ return webrtc::kVideoCodecUnknown;
+}
+
+WebRtcVideoChannel2::WebRtcVideoSendStream::AllocatedEncoder
+WebRtcVideoChannel2::WebRtcVideoSendStream::CreateVideoEncoder(
+ const VideoCodec& codec) {
+ webrtc::VideoCodecType type = CodecTypeFromName(codec.name);
+
+ // Do not re-create encoders of the same type.
+ if (type == allocated_encoder_.type && allocated_encoder_.encoder != NULL) {
+ return allocated_encoder_;
+ }
+
+ if (external_encoder_factory_ != NULL) {
+ webrtc::VideoEncoder* encoder =
+ external_encoder_factory_->CreateVideoEncoder(type);
+ if (encoder != NULL) {
+ return AllocatedEncoder(encoder, type, true);
+ }
+ }
+
+ if (type == webrtc::kVideoCodecVP8) {
+ return AllocatedEncoder(
+ webrtc::VideoEncoder::Create(webrtc::VideoEncoder::kVp8), type, false);
+ }
+
+ // This shouldn't happen, we should not be trying to create something we don't
+ // support.
+ assert(false);
+ return AllocatedEncoder(NULL, webrtc::kVideoCodecUnknown, false);
+}
+
+void WebRtcVideoChannel2::WebRtcVideoSendStream::DestroyVideoEncoder(
+ AllocatedEncoder* encoder) {
+ if (encoder->external) {
+ external_encoder_factory_->DestroyVideoEncoder(encoder->encoder);
+ } else {
+ delete encoder->encoder;
+ }
+}
+
void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodecAndOptions(
const VideoCodecSettings& codec_settings,
const VideoOptions& options) {
@@ -1525,10 +1620,8 @@
VideoFormat::FpsToInterval(30),
FOURCC_I420);
- webrtc::VideoEncoder* old_encoder =
- parameters_.config.encoder_settings.encoder;
- parameters_.config.encoder_settings.encoder =
- encoder_factory_->CreateVideoEncoder(codec_settings.codec, options);
+ AllocatedEncoder new_encoder = CreateVideoEncoder(codec_settings.codec);
+ parameters_.config.encoder_settings.encoder = new_encoder.encoder;
parameters_.config.encoder_settings.payload_name = codec_settings.codec.name;
parameters_.config.encoder_settings.payload_type = codec_settings.codec.id;
parameters_.config.rtp.fec = codec_settings.fec;
@@ -1552,7 +1645,10 @@
parameters_.options = options;
RecreateWebRtcStream();
- delete old_encoder;
+ if (allocated_encoder_.encoder != new_encoder.encoder) {
+ DestroyVideoEncoder(&allocated_encoder_);
+ allocated_encoder_ = new_encoder;
+ }
}
void WebRtcVideoChannel2::WebRtcVideoSendStream::SetRtpExtensions(
@@ -1957,8 +2053,21 @@
const std::vector<WebRtcVideoChannel2::VideoCodecSettings>& mapped_codecs) {
std::vector<VideoCodecSettings> supported_codecs;
for (size_t i = 0; i < mapped_codecs.size(); ++i) {
- if (encoder_factory_->SupportsCodec(mapped_codecs[i].codec)) {
- supported_codecs.push_back(mapped_codecs[i]);
+ const VideoCodecSettings& codec = mapped_codecs[i];
+ if (CodecIsInternallySupported(codec.codec.name)) {
+ supported_codecs.push_back(codec);
+ }
+
+ if (external_encoder_factory_ == NULL) {
+ continue;
+ }
+ const std::vector<WebRtcVideoEncoderFactory::VideoCodec> external_codecs =
+ external_encoder_factory_->codecs();
+ for (size_t c = 0; c < external_codecs.size(); ++c) {
+ if (CodecNameMatches(codec.codec.name, external_codecs[c].name)) {
+ supported_codecs.push_back(codec);
+ break;
+ }
}
}
return supported_codecs;
diff --git a/talk/media/webrtc/webrtcvideoengine2.h b/talk/media/webrtc/webrtcvideoengine2.h
index de591f0..6b9eaa7 100644
--- a/talk/media/webrtc/webrtcvideoengine2.h
+++ b/talk/media/webrtc/webrtcvideoengine2.h
@@ -72,7 +72,6 @@
class WebRtcEncoderObserver;
class WebRtcLocalStreamInfo;
class WebRtcRenderAdapter;
-class WebRtcVideoChannel2;
class WebRtcVideoChannelRecvInfo;
class WebRtcVideoChannelSendInfo;
class WebRtcVoiceEngine;
@@ -80,7 +79,6 @@
struct CapturedFrame;
struct Device;
-class WebRtcVideoChannel2;
class WebRtcVideoRenderer;
class UnsignalledSsrcHandler {
@@ -116,17 +114,11 @@
const VideoOptions& options,
size_t num_streams);
- virtual webrtc::VideoEncoder* CreateVideoEncoder(
- const VideoCodec& codec,
- const VideoOptions& options);
-
virtual void* CreateVideoEncoderSettings(const VideoCodec& codec,
const VideoOptions& options);
virtual void DestroyVideoEncoderSettings(const VideoCodec& codec,
void* encoder_settings);
-
- virtual bool SupportsCodec(const cricket::VideoCodec& codec);
};
// CallFactory, overridden for testing to verify that webrtc::Call is configured
@@ -192,6 +184,8 @@
virtual WebRtcVideoEncoderFactory2* GetVideoEncoderFactory();
private:
+ std::vector<VideoCodec> GetSupportedCodecs() const;
+
rtc::Thread* worker_thread_;
WebRtcVoiceEngine* voice_engine_;
std::vector<VideoCodec> video_codecs_;
@@ -217,6 +211,8 @@
public:
WebRtcVideoChannel2(WebRtcCallFactory* call_factory,
VoiceMediaChannel* voice_channel,
+ WebRtcVideoEncoderFactory* external_encoder_factory,
+ WebRtcVideoDecoderFactory* external_decoder_factory,
WebRtcVideoEncoderFactory2* encoder_factory);
~WebRtcVideoChannel2();
bool Init();
@@ -292,6 +288,7 @@
public:
WebRtcVideoSendStream(
webrtc::Call* call,
+ WebRtcVideoEncoderFactory* external_encoder_factory,
WebRtcVideoEncoderFactory2* encoder_factory,
const VideoOptions& options,
const Settable<VideoCodecSettings>& codec_settings,
@@ -337,6 +334,19 @@
webrtc::VideoEncoderConfig encoder_config;
};
+ struct AllocatedEncoder {
+ AllocatedEncoder(webrtc::VideoEncoder* encoder,
+ webrtc::VideoCodecType type,
+ bool external)
+ : encoder(encoder), type(type), external(external) {}
+ webrtc::VideoEncoder* encoder;
+ webrtc::VideoCodecType type;
+ bool external;
+ };
+
+ AllocatedEncoder CreateVideoEncoder(const VideoCodec& codec)
+ EXCLUSIVE_LOCKS_REQUIRED(lock_);
+ void DestroyVideoEncoder(AllocatedEncoder* encoder);
void SetCodecAndOptions(const VideoCodecSettings& codec,
const VideoOptions& options)
EXCLUSIVE_LOCKS_REQUIRED(lock_);
@@ -346,11 +356,13 @@
EXCLUSIVE_LOCKS_REQUIRED(lock_);
webrtc::Call* const call_;
+ WebRtcVideoEncoderFactory* const external_encoder_factory_;
WebRtcVideoEncoderFactory2* const encoder_factory_;
rtc::CriticalSection lock_;
webrtc::VideoSendStream* stream_ GUARDED_BY(lock_);
VideoSendStreamParameters parameters_ GUARDED_BY(lock_);
+ AllocatedEncoder allocated_encoder_ GUARDED_BY(lock_);
VideoCapturer* capturer_ GUARDED_BY(lock_);
bool sending_ GUARDED_BY(lock_);
@@ -436,6 +448,8 @@
Settable<VideoCodecSettings> send_codec_;
std::vector<webrtc::RtpExtension> send_rtp_extensions_;
+ WebRtcVideoEncoderFactory* const external_encoder_factory_;
+ WebRtcVideoDecoderFactory* const external_decoder_factory_;
WebRtcVideoEncoderFactory2* const encoder_factory_;
std::vector<VideoCodecSettings> recv_codecs_;
std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index b718992..0cac1d5 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -30,6 +30,7 @@
#include "talk/media/base/testutils.h"
#include "talk/media/base/videoengine_unittest.h"
+#include "talk/media/webrtc/fakewebrtcvideoengine.h"
#include "talk/media/webrtc/webrtcvideochannelfactory.h"
#include "talk/media/webrtc/webrtcvideoengine2.h"
#include "talk/media/webrtc/webrtcvideoengine2_unittest.h"
@@ -40,11 +41,11 @@
namespace {
static const cricket::VideoCodec kVp8Codec720p(100, "VP8", 1280, 720, 30, 0);
static const cricket::VideoCodec kVp8Codec360p(100, "VP8", 640, 360, 30, 0);
-static const cricket::VideoCodec kVp8Codec270p(100, "VP8", 480, 270, 30, 0);
-static const cricket::VideoCodec kVp8Codec180p(100, "VP8", 320, 180, 30, 0);
static const cricket::VideoCodec kVp8Codec(100, "VP8", 640, 400, 30, 0);
static const cricket::VideoCodec kVp9Codec(101, "VP9", 640, 400, 30, 0);
+static const cricket::VideoCodec kH264Codec(102, "H264", 640, 400, 30, 0);
+
static const cricket::VideoCodec kRedCodec(116, "red", 0, 0, 0, 0);
static const cricket::VideoCodec kUlpfecCodec(117, "ulpfec", 0, 0, 0, 0);
@@ -303,7 +304,7 @@
network_state_ = state;
}
-class WebRtcVideoEngine2Test : public testing::Test {
+class WebRtcVideoEngine2Test : public ::testing::Test {
public:
WebRtcVideoEngine2Test() {
std::vector<VideoCodec> engine_codecs = engine_.codecs();
@@ -326,6 +327,9 @@
}
protected:
+ VideoMediaChannel* SetUpForExternalEncoderFactory(
+ cricket::WebRtcVideoEncoderFactory* encoder_factory,
+ const std::vector<VideoCodec>& codecs);
WebRtcVideoEngine2 engine_;
VideoCodec default_codec_;
VideoCodec default_red_codec_;
@@ -422,6 +426,7 @@
}
TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) {
+ engine_.Init(rtc::Thread::Current());
rtc::scoped_ptr<VideoMediaChannel> channel(engine_.CreateChannel(NULL));
EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
@@ -433,12 +438,110 @@
}
TEST_F(WebRtcVideoEngine2Test, GetStatsWithoutSendCodecsSetDoesNotCrash) {
+ engine_.Init(rtc::Thread::Current());
rtc::scoped_ptr<VideoMediaChannel> channel(engine_.CreateChannel(NULL));
EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
VideoMediaInfo info;
channel->GetStats(&info);
}
+TEST_F(WebRtcVideoEngine2Test, UseExternalFactoryForVp8WhenSupported) {
+ cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+ encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecVP8, "VP8");
+ std::vector<cricket::VideoCodec> codecs;
+ codecs.push_back(kVp8Codec);
+
+ rtc::scoped_ptr<VideoMediaChannel> channel(
+ SetUpForExternalEncoderFactory(&encoder_factory, codecs));
+
+ EXPECT_TRUE(
+ channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+ ASSERT_EQ(1u, encoder_factory.encoders().size());
+ EXPECT_TRUE(channel->SetSend(true));
+
+ cricket::FakeVideoCapturer capturer;
+ EXPECT_TRUE(channel->SetCapturer(kSsrc, &capturer));
+ EXPECT_EQ(cricket::CS_RUNNING,
+ capturer.Start(capturer.GetSupportedFormats()->front()));
+ EXPECT_TRUE(capturer.CaptureFrame());
+
+ EXPECT_TRUE_WAIT(encoder_factory.encoders()[0]->GetNumEncodedFrames() > 0,
+ kTimeout);
+
+ // Setting codecs of the same type should not reallocate the encoder.
+ EXPECT_TRUE(channel->SetSendCodecs(codecs));
+ EXPECT_EQ(1, encoder_factory.GetNumCreatedEncoders());
+
+ // Remove stream previously added to free the external encoder instance.
+ EXPECT_TRUE(channel->RemoveSendStream(kSsrc));
+ EXPECT_EQ(0u, encoder_factory.encoders().size());
+}
+
+VideoMediaChannel* WebRtcVideoEngine2Test::SetUpForExternalEncoderFactory(
+ cricket::WebRtcVideoEncoderFactory* encoder_factory,
+ const std::vector<VideoCodec>& codecs) {
+ engine_.SetExternalEncoderFactory(encoder_factory);
+ engine_.Init(rtc::Thread::Current());
+
+ VideoMediaChannel* channel = engine_.CreateChannel(NULL);
+ EXPECT_TRUE(channel->SetSendCodecs(codecs));
+
+ return channel;
+}
+
+TEST_F(WebRtcVideoEngine2Test, ChannelWithExternalH264CanChangeToInternalVp8) {
+ cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+ encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
+ std::vector<cricket::VideoCodec> codecs;
+ codecs.push_back(kH264Codec);
+
+ rtc::scoped_ptr<VideoMediaChannel> channel(
+ SetUpForExternalEncoderFactory(&encoder_factory, codecs));
+
+ EXPECT_TRUE(
+ channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+ ASSERT_EQ(1u, encoder_factory.encoders().size());
+
+ codecs.clear();
+ codecs.push_back(kVp8Codec);
+ EXPECT_TRUE(channel->SetSendCodecs(codecs));
+
+ ASSERT_EQ(0u, encoder_factory.encoders().size());
+}
+
+TEST_F(WebRtcVideoEngine2Test,
+ DontUseExternalEncoderFactoryForUnsupportedCodecs) {
+ cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+ encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
+ std::vector<cricket::VideoCodec> codecs;
+ codecs.push_back(kVp8Codec);
+
+ rtc::scoped_ptr<VideoMediaChannel> channel(
+ SetUpForExternalEncoderFactory(&encoder_factory, codecs));
+
+ EXPECT_TRUE(
+ channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+ ASSERT_EQ(0u, encoder_factory.encoders().size());
+}
+
+// Test external codec with be added to the end of the supported codec list.
+TEST_F(WebRtcVideoEngine2Test, ReportSupportedExternalCodecs) {
+ cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+ encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
+ engine_.SetExternalEncoderFactory(&encoder_factory);
+
+ engine_.Init(rtc::Thread::Current());
+
+ std::vector<cricket::VideoCodec> codecs(engine_.codecs());
+ ASSERT_GE(codecs.size(), 2u);
+ cricket::VideoCodec internal_codec = codecs.front();
+ cricket::VideoCodec external_codec = codecs.back();
+
+ // The external codec will appear at last.
+ EXPECT_EQ("VP8", internal_codec.name);
+ EXPECT_EQ("H264", external_codec.name);
+}
+
class WebRtcVideoEngine2BaseTest
: public VideoEngineTest<cricket::WebRtcVideoEngine2> {
protected:
@@ -581,6 +684,7 @@
WebRtcVideoChannel2Test() : fake_call_(NULL) {}
virtual void SetUp() OVERRIDE {
engine_.SetCallFactory(this);
+ engine_.Init(rtc::Thread::Current());
channel_.reset(engine_.CreateChannel(NULL));
ASSERT_TRUE(fake_call_ != NULL) << "Call not created through factory.";
last_ssrc_ = 123;
@@ -1604,28 +1708,6 @@
FAIL() << "Not implemented."; // TODO(pbos): Implement.
}
-TEST_F(WebRtcVideoChannel2Test,
- DISABLED_DontRegisterEncoderIfFactoryIsNotGiven) {
- FAIL() << "Not implemented."; // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test, DISABLED_RegisterEncoderIfFactoryIsGiven) {
- FAIL() << "Not implemented."; // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderMultipleTimes) {
- FAIL() << "Not implemented."; // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test,
- DISABLED_RegisterEncoderWithMultipleSendStreams) {
- FAIL() << "Not implemented."; // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderForNonVP8) {
- FAIL() << "Not implemented."; // TODO(pbos): Implement.
-}
-
TEST_F(WebRtcVideoChannel2Test, DISABLED_FeedbackParamsForNonVP8) {
FAIL() << "Not implemented."; // TODO(pbos): Implement.
}