diff --git a/talk/media/webrtc/constants.h b/talk/media/webrtc/constants.h index 68f664bd9..5390c0d51 100755 --- a/talk/media/webrtc/constants.h +++ b/talk/media/webrtc/constants.h @@ -33,6 +33,7 @@ extern const int kVideoMtu; 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 e6de35bc6..729179b68 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 @@ class FakeWebRtcVideoDecoderFactory : public WebRtcVideoDecoderFactory { // 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 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder { const webrtc::I420VideoFrame& inputImage, const webrtc::CodecSpecificInfo* codecSpecificInfo, const std::vector* frame_types) { + rtc::CritScope lock(&crit_); + ++num_frames_encoded_; return WEBRTC_VIDEO_CODEC_OK; } @@ -179,6 +183,15 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder { 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 d8b561664..628ed509e 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& 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& 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& 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 kDefaultQpMax = 56; 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 @@ struct VideoCodecPref { 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 @@ std::vector WebRtcVideoEncoderFactory2::CreateVideoStreams( 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(); @@ -196,24 +263,10 @@ std::vector WebRtcVideoEncoderFactory2::CreateVideoStreams( 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::CreateVideoEncoderSettings( 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(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 @@ void DefaultUnsignalledSsrcHandler::SetDefaultRenderer( 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 @@ WebRtcVideoEngine2::WebRtcVideoEngine2() 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 @@ WebRtcVideoEngine2::~WebRtcVideoEngine2() { } void WebRtcVideoEngine2::SetCallFactory(WebRtcCallFactory* call_factory) { + assert(!initialized_); call_factory_ = call_factory; } @@ -344,7 +393,7 @@ bool WebRtcVideoEngine2::SetDefaultEncoderConfig( 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 @@ VideoEncoderConfig WebRtcVideoEngine2::GetDefaultEncoderConfig() const { 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::SetLogging(int min_sev, const char* filter) { 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 @@ WebRtcVideoEncoderFactory2* WebRtcVideoEngine2::GetVideoEncoderFactory() { return &default_video_encoder_factory_; } +std::vector WebRtcVideoEngine2::GetSupportedCodecs() const { + std::vector supported_codecs = DefaultVideoCodecs(); + + if (external_encoder_factory_ == NULL) { + return supported_codecs; + } + + assert(external_encoder_factory_->codecs().size() <= kMaxExternalVideoCodecs); + const std::vector& 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(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 @@ class WebRtcVideoRenderFrame : public VideoFrame { 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 @@ WebRtcVideoChannel2::~WebRtcVideoChannel2() { bool WebRtcVideoChannel2::Init() { return true; } -namespace { - -static std::string CodecVectorToString(const std::vector& 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& 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& 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& codecs) { LOG(LS_INFO) << "SetRecvCodecs: " << CodecVectorToString(codecs); if (!ValidateCodecFormats(codecs)) { @@ -760,7 +802,7 @@ bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector& codecs) { // 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 @@ bool WebRtcVideoChannel2::AddSendStream(const StreamParams& sp) { WebRtcVideoSendStream* stream = new WebRtcVideoSendStream(call_.get(), + external_encoder_factory_, encoder_factory_, options_, send_codec_, @@ -1307,15 +1350,18 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::VideoSendStreamParameters:: WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream( webrtc::Call* call, + WebRtcVideoEncoderFactory* external_encoder_factory, WebRtcVideoEncoderFactory2* encoder_factory, const VideoOptions& options, const Settable& codec_settings, const StreamParams& sp, const std::vector& 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 @@ WebRtcVideoChannel2::WebRtcVideoSendStream::~WebRtcVideoSendStream() { 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 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetOptions( 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 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodecAndOptions( 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 @@ void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodecAndOptions( 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 @@ WebRtcVideoChannel2::FilterSupportedCodecs( const std::vector& mapped_codecs) { std::vector 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 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 de591f056..6b9eaa76f 100644 --- a/talk/media/webrtc/webrtcvideoengine2.h +++ b/talk/media/webrtc/webrtcvideoengine2.h @@ -72,7 +72,6 @@ class WebRtcDecoderObserver; class WebRtcEncoderObserver; class WebRtcLocalStreamInfo; class WebRtcRenderAdapter; -class WebRtcVideoChannel2; class WebRtcVideoChannelRecvInfo; class WebRtcVideoChannelSendInfo; class WebRtcVoiceEngine; @@ -80,7 +79,6 @@ class WebRtcVoiceEngine; struct CapturedFrame; struct Device; -class WebRtcVideoChannel2; class WebRtcVideoRenderer; class UnsignalledSsrcHandler { @@ -116,17 +114,11 @@ class WebRtcVideoEncoderFactory2 { 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 @@ class WebRtcVideoEngine2 : public sigslot::has_slots<> { virtual WebRtcVideoEncoderFactory2* GetVideoEncoderFactory(); private: + std::vector GetSupportedCodecs() const; + rtc::Thread* worker_thread_; WebRtcVoiceEngine* voice_engine_; std::vector video_codecs_; @@ -217,6 +211,8 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, 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 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, public: WebRtcVideoSendStream( webrtc::Call* call, + WebRtcVideoEncoderFactory* external_encoder_factory, WebRtcVideoEncoderFactory2* encoder_factory, const VideoOptions& options, const Settable& codec_settings, @@ -337,6 +334,19 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, 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 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, 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 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, Settable send_codec_; std::vector send_rtp_extensions_; + WebRtcVideoEncoderFactory* const external_encoder_factory_; + WebRtcVideoDecoderFactory* const external_decoder_factory_; WebRtcVideoEncoderFactory2* const encoder_factory_; std::vector recv_codecs_; std::vector recv_rtp_extensions_; diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc index b718992f1..0cac1d525 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 @@ void FakeCall::SignalNetworkState(webrtc::Call::NetworkState state) { network_state_ = state; } -class WebRtcVideoEngine2Test : public testing::Test { +class WebRtcVideoEngine2Test : public ::testing::Test { public: WebRtcVideoEngine2Test() { std::vector engine_codecs = engine_.codecs(); @@ -326,6 +327,9 @@ class WebRtcVideoEngine2Test : public testing::Test { } protected: + VideoMediaChannel* SetUpForExternalEncoderFactory( + cricket::WebRtcVideoEncoderFactory* encoder_factory, + const std::vector& codecs); WebRtcVideoEngine2 engine_; VideoCodec default_codec_; VideoCodec default_red_codec_; @@ -422,6 +426,7 @@ TEST_F(WebRtcVideoEngine2Test, SupportsAbsoluteSenderTimeHeaderExtension) { } TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) { + engine_.Init(rtc::Thread::Current()); rtc::scoped_ptr channel(engine_.CreateChannel(NULL)); EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123))); @@ -433,12 +438,110 @@ TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) { } TEST_F(WebRtcVideoEngine2Test, GetStatsWithoutSendCodecsSetDoesNotCrash) { + engine_.Init(rtc::Thread::Current()); rtc::scoped_ptr 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 codecs; + codecs.push_back(kVp8Codec); + + rtc::scoped_ptr 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& 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 codecs; + codecs.push_back(kH264Codec); + + rtc::scoped_ptr 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 codecs; + codecs.push_back(kVp8Codec); + + rtc::scoped_ptr 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 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 { protected: @@ -581,6 +684,7 @@ class WebRtcVideoChannel2Test : public WebRtcVideoEngine2Test, 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 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterDecoderForNonVP8) { 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. }