diff options
author | pbos@webrtc.org <pbos@webrtc.org> | 2014-11-03 14:46:44 +0000 |
---|---|---|
committer | pbos@webrtc.org <pbos@webrtc.org> | 2014-11-03 14:46:44 +0000 |
commit | 377ebd591332f6b6a38ad87f3d8ac207c3b9c64a (patch) | |
tree | 80c938b317543160d15063711cabc34d9714fd87 | |
parent | 9d70622f8a422f18146622ac58930db172f6fd7e (diff) | |
download | talk-377ebd591332f6b6a38ad87f3d8ac207c3b9c64a.tar.gz |
Implement external decoder support in WebRtcVideoEngine2.
R=stefan@webrtc.org
BUG=1788
Review URL: https://webrtc-codereview.appspot.com/30839004
git-svn-id: http://webrtc.googlecode.com/svn/trunk/talk@7594 4adac7df-926f-26a2-2b94-8c16560cd09d
-rw-r--r-- | media/webrtc/webrtcvideoengine2.cc | 164 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine2.h | 15 | ||||
-rw-r--r-- | media/webrtc/webrtcvideoengine2_unittest.cc | 81 |
3 files changed, 177 insertions, 83 deletions
diff --git a/media/webrtc/webrtcvideoengine2.cc b/media/webrtc/webrtcvideoengine2.cc index d902718..a877921 100644 --- a/media/webrtc/webrtcvideoengine2.cc +++ b/media/webrtc/webrtcvideoengine2.cc @@ -57,10 +57,18 @@ static bool CodecNameMatches(const std::string& name1, return _stricmp(name1.c_str(), name2.c_str()) == 0; } +const char* kInternallySupportedCodecs[] = { + kVp8CodecName, +}; + // 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); + for (size_t i = 0; i < ARRAY_SIZE(kInternallySupportedCodecs); ++i) { + if (CodecNameMatches(codec_name, kInternallySupportedCodecs[i])) + return true; + } + return false; } static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) { @@ -807,6 +815,37 @@ WebRtcVideoChannel2::~WebRtcVideoChannel2() { bool WebRtcVideoChannel2::Init() { return true; } +bool WebRtcVideoChannel2::CodecIsExternallySupported( + const std::string& name) const { + if (external_encoder_factory_ == NULL) { + return false; + } + + const std::vector<WebRtcVideoEncoderFactory::VideoCodec> external_codecs = + external_encoder_factory_->codecs(); + for (size_t c = 0; c < external_codecs.size(); ++c) { + if (CodecNameMatches(name, external_codecs[c].name)) { + return true; + } + } + return false; +} + +std::vector<WebRtcVideoChannel2::VideoCodecSettings> +WebRtcVideoChannel2::FilterSupportedCodecs( + const std::vector<WebRtcVideoChannel2::VideoCodecSettings>& mapped_codecs) + const { + std::vector<VideoCodecSettings> supported_codecs; + for (size_t i = 0; i < mapped_codecs.size(); ++i) { + const VideoCodecSettings& codec = mapped_codecs[i]; + if (CodecIsInternallySupported(codec.codec.name) || + CodecIsExternallySupported(codec.codec.name)) { + supported_codecs.push_back(codec); + } + } + return supported_codecs; +} + bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) { LOG(LS_INFO) << "SetRecvCodecs: " << CodecVectorToString(codecs); if (!ValidateCodecFormats(codecs)) { @@ -815,21 +854,19 @@ bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) { const std::vector<VideoCodecSettings> mapped_codecs = MapCodecs(codecs); if (mapped_codecs.empty()) { - LOG(LS_ERROR) << "SetRecvCodecs called without video codec payloads."; + LOG(LS_ERROR) << "SetRecvCodecs called without any video codecs."; return false; } - // 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 (!CodecNameMatches(mapped_codecs[i].codec.name, kVp8CodecName)) { - LOG(LS_ERROR) << "SetRecvCodecs called with unsupported codec: '" - << mapped_codecs[i].codec.name << "'"; - return false; - } + const std::vector<VideoCodecSettings> supported_codecs = + FilterSupportedCodecs(mapped_codecs); + + if (mapped_codecs.size() != supported_codecs.size()) { + LOG(LS_ERROR) << "SetRecvCodecs called with unsupported video codecs."; + return false; } - recv_codecs_ = mapped_codecs; + recv_codecs_ = supported_codecs; rtc::CritScope stream_lock(&stream_crit_); for (std::map<uint32, WebRtcVideoReceiveStream*>::iterator it = @@ -1899,34 +1936,67 @@ WebRtcVideoChannel2::WebRtcVideoReceiveStream::WebRtcVideoReceiveStream( WebRtcVideoChannel2::WebRtcVideoReceiveStream::~WebRtcVideoReceiveStream() { call_->DestroyVideoReceiveStream(stream_); - ClearDecoders(); + ClearDecoders(&allocated_decoders_); } -void WebRtcVideoChannel2::WebRtcVideoReceiveStream::SetRecvCodecs( - const std::vector<VideoCodecSettings>& recv_codecs) { - // TODO(pbos): Reconfigure RTX based on incoming recv_codecs. - // TODO(pbos): Base receive codecs off recv_codecs_ and set up using a - // DecoderFactory similar to send side. Pending webrtc:2854. - // Also set up default codecs if there's nothing in recv_codecs_. - ClearDecoders(); +WebRtcVideoChannel2::WebRtcVideoReceiveStream::AllocatedDecoder +WebRtcVideoChannel2::WebRtcVideoReceiveStream::CreateOrReuseVideoDecoder( + std::vector<AllocatedDecoder>* old_decoders, + const VideoCodec& codec) { + webrtc::VideoCodecType type = CodecTypeFromName(codec.name); - AllocatedDecoder allocated_decoder( - webrtc::VideoDecoder::Create(webrtc::VideoDecoder::kVp8), false); - allocated_decoders_.push_back(allocated_decoder); + for (size_t i = 0; i < old_decoders->size(); ++i) { + if ((*old_decoders)[i].type == type) { + AllocatedDecoder decoder = (*old_decoders)[i]; + (*old_decoders)[i] = old_decoders->back(); + old_decoders->pop_back(); + return decoder; + } + } - webrtc::VideoReceiveStream::Decoder decoder; - decoder.decoder = allocated_decoder.decoder; - decoder.payload_type = kDefaultVideoCodecPref.payload_type; - decoder.payload_name = "VP8"; + if (external_decoder_factory_ != NULL) { + webrtc::VideoDecoder* decoder = + external_decoder_factory_->CreateVideoDecoder(type); + if (decoder != NULL) { + return AllocatedDecoder(decoder, type, true); + } + } - config_.decoders.push_back(decoder); + if (type == webrtc::kVideoCodecVP8) { + return AllocatedDecoder( + webrtc::VideoDecoder::Create(webrtc::VideoDecoder::kVp8), type, false); + } - config_.rtp.fec = recv_codecs.front().fec; + // This shouldn't happen, we should not be trying to create something we don't + // support. + assert(false); + return AllocatedDecoder(NULL, webrtc::kVideoCodecUnknown, false); +} + +void WebRtcVideoChannel2::WebRtcVideoReceiveStream::SetRecvCodecs( + const std::vector<VideoCodecSettings>& recv_codecs) { + std::vector<AllocatedDecoder> old_decoders = allocated_decoders_; + allocated_decoders_.clear(); + config_.decoders.clear(); + for (size_t i = 0; i < recv_codecs.size(); ++i) { + AllocatedDecoder allocated_decoder = + CreateOrReuseVideoDecoder(&old_decoders, recv_codecs[i].codec); + allocated_decoders_.push_back(allocated_decoder); + webrtc::VideoReceiveStream::Decoder decoder; + decoder.decoder = allocated_decoder.decoder; + decoder.payload_type = recv_codecs[i].codec.id; + decoder.payload_name = recv_codecs[i].codec.name; + config_.decoders.push_back(decoder); + } + + // TODO(pbos): Reconfigure RTX based on incoming recv_codecs. + config_.rtp.fec = recv_codecs.front().fec; config_.rtp.nack.rtp_history_ms = IsNackEnabled(recv_codecs.begin()->codec) ? kNackHistoryMs : 0; config_.rtp.remb = IsRembEnabled(recv_codecs.begin()->codec); + ClearDecoders(&old_decoders); RecreateWebRtcStream(); } @@ -1944,16 +2014,17 @@ void WebRtcVideoChannel2::WebRtcVideoReceiveStream::RecreateWebRtcStream() { stream_->Start(); } -void WebRtcVideoChannel2::WebRtcVideoReceiveStream::ClearDecoders() { - for (size_t i = 0; i < allocated_decoders_.size(); ++i) { - if (allocated_decoders_[i].external) { +void WebRtcVideoChannel2::WebRtcVideoReceiveStream::ClearDecoders( + std::vector<AllocatedDecoder>* allocated_decoders) { + for (size_t i = 0; i < allocated_decoders->size(); ++i) { + if ((*allocated_decoders)[i].external) { external_decoder_factory_->DestroyVideoDecoder( - allocated_decoders_[i].decoder); + (*allocated_decoders)[i].decoder); } else { - delete allocated_decoders_[i].decoder; + delete (*allocated_decoders)[i].decoder; } } - allocated_decoders_.clear(); + allocated_decoders->clear(); } void WebRtcVideoChannel2::WebRtcVideoReceiveStream::RenderFrame( @@ -2115,31 +2186,6 @@ WebRtcVideoChannel2::MapCodecs(const std::vector<VideoCodec>& codecs) { return video_codecs; } -std::vector<WebRtcVideoChannel2::VideoCodecSettings> -WebRtcVideoChannel2::FilterSupportedCodecs( - const std::vector<WebRtcVideoChannel2::VideoCodecSettings>& mapped_codecs) { - std::vector<VideoCodecSettings> supported_codecs; - for (size_t i = 0; i < mapped_codecs.size(); ++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; -} - } // namespace cricket #endif // HAVE_WEBRTC_VIDEO diff --git a/media/webrtc/webrtcvideoengine2.h b/media/webrtc/webrtcvideoengine2.h index b9a8682..0b812ef 100644 --- a/media/webrtc/webrtcvideoengine2.h +++ b/media/webrtc/webrtcvideoengine2.h @@ -276,6 +276,7 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, private: void ConfigureReceiverRtp(webrtc::VideoReceiveStream::Config* config, const StreamParams& sp) const; + bool CodecIsExternallySupported(const std::string& name) const; struct VideoCodecSettings { VideoCodecSettings(); @@ -408,16 +409,22 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, private: struct AllocatedDecoder { - AllocatedDecoder(webrtc::VideoDecoder* decoder, bool external) - : decoder(decoder), external(external) {} + AllocatedDecoder(webrtc::VideoDecoder* decoder, + webrtc::VideoCodecType type, + bool external) + : decoder(decoder), type(type), external(external) {} webrtc::VideoDecoder* decoder; + webrtc::VideoCodecType type; bool external; }; void SetSize(int width, int height); void RecreateWebRtcStream(); - void ClearDecoders(); + AllocatedDecoder CreateOrReuseVideoDecoder( + std::vector<AllocatedDecoder>* old_decoder, + const VideoCodec& codec); + void ClearDecoders(std::vector<AllocatedDecoder>* allocated_decoders); webrtc::Call* const call_; @@ -445,7 +452,7 @@ class WebRtcVideoChannel2 : public rtc::MessageHandler, static std::vector<VideoCodecSettings> MapCodecs( const std::vector<VideoCodec>& codecs); std::vector<VideoCodecSettings> FilterSupportedCodecs( - const std::vector<VideoCodecSettings>& mapped_codecs); + const std::vector<VideoCodecSettings>& mapped_codecs) const; void FillSenderStats(VideoMediaInfo* info); void FillReceiverStats(VideoMediaInfo* info); diff --git a/media/webrtc/webrtcvideoengine2_unittest.cc b/media/webrtc/webrtcvideoengine2_unittest.cc index 7c8da13..e45ad6d 100644 --- a/media/webrtc/webrtcvideoengine2_unittest.cc +++ b/media/webrtc/webrtcvideoengine2_unittest.cc @@ -349,6 +349,10 @@ class WebRtcVideoEngine2Test : public ::testing::Test { cricket::WebRtcVideoEncoderFactory* encoder_factory, const std::vector<VideoCodec>& codecs); + VideoMediaChannel* SetUpForExternalDecoderFactory( + cricket::WebRtcVideoDecoderFactory* decoder_factory, + const std::vector<VideoCodec>& codecs); + void TestStartBitrate(bool override_start_bitrate, int start_bitrate_bps); WebRtcVideoEngine2 engine_; @@ -579,6 +583,19 @@ VideoMediaChannel* WebRtcVideoEngine2Test::SetUpForExternalEncoderFactory( return channel; } +VideoMediaChannel* WebRtcVideoEngine2Test::SetUpForExternalDecoderFactory( + cricket::WebRtcVideoDecoderFactory* decoder_factory, + const std::vector<VideoCodec>& codecs) { + engine_.SetExternalDecoderFactory(decoder_factory); + engine_.Init(rtc::Thread::Current()); + + VideoMediaChannel* channel = + engine_.CreateChannel(cricket::VideoOptions(), NULL); + EXPECT_TRUE(channel->SetRecvCodecs(codecs)); + + return channel; +} + TEST_F(WebRtcVideoEngine2Test, ChannelWithExternalH264CanChangeToInternalVp8) { cricket::FakeWebRtcVideoEncoderFactory encoder_factory; encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264"); @@ -632,6 +649,50 @@ TEST_F(WebRtcVideoEngine2Test, ReportSupportedExternalCodecs) { EXPECT_EQ("H264", external_codec.name); } +TEST_F(WebRtcVideoEngine2Test, RegisterExternalDecodersIfSupported) { + cricket::FakeWebRtcVideoDecoderFactory decoder_factory; + decoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecVP8); + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kVp8Codec); + + rtc::scoped_ptr<VideoMediaChannel> channel( + SetUpForExternalDecoderFactory(&decoder_factory, codecs)); + + EXPECT_TRUE( + channel->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc))); + ASSERT_EQ(1u, decoder_factory.decoders().size()); + + // Setting codecs of the same type should not reallocate the decoder. + EXPECT_TRUE(channel->SetRecvCodecs(codecs)); + EXPECT_EQ(1, decoder_factory.GetNumCreatedDecoders()); + + // Remove stream previously added to free the external decoder instance. + EXPECT_TRUE(channel->RemoveRecvStream(kSsrc)); + EXPECT_EQ(0u, decoder_factory.decoders().size()); +} + +// Verifies that we can set up decoders that are not internally supported. +TEST_F(WebRtcVideoEngine2Test, RegisterExternalH264DecoderIfSupported) { + // TODO(pbos): Do not assume that encoder/decoder support is symmetric. We + // can't even query the WebRtcVideoDecoderFactory for supported codecs. + // For now we add a FakeWebRtcVideoEncoderFactory to add H264 to supported + // codecs. + cricket::FakeWebRtcVideoEncoderFactory encoder_factory; + encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264"); + engine_.SetExternalEncoderFactory(&encoder_factory); + cricket::FakeWebRtcVideoDecoderFactory decoder_factory; + decoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264); + std::vector<cricket::VideoCodec> codecs; + codecs.push_back(kH264Codec); + + rtc::scoped_ptr<VideoMediaChannel> channel( + SetUpForExternalDecoderFactory(&decoder_factory, codecs)); + + EXPECT_TRUE( + channel->AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrc))); + ASSERT_EQ(1u, decoder_factory.decoders().size()); +} + class WebRtcVideoEngine2BaseTest : public VideoEngineTest<cricket::WebRtcVideoEngine2> { protected: @@ -1800,26 +1861,6 @@ TEST_F(WebRtcVideoChannel2Test, DISABLED_SetOptionsWithMaxBitrate) { FAIL() << "Not implemented."; // TODO(pbos): Implement. } -TEST_F(WebRtcVideoChannel2Test, DISABLED_ResetCodecOnScreencast) { - FAIL() << "Not implemented."; // TODO(pbos): Implement. -} - -TEST_F(WebRtcVideoChannel2Test, DISABLED_RegisterDecoderIfFactoryIsGiven) { - FAIL() << "Not implemented."; // TODO(pbos): Implement. -} - -TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterDecoderMultipleTimes) { - FAIL() << "Not implemented."; // TODO(pbos): Implement. -} - -TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterDecoderForNonVP8) { - FAIL() << "Not implemented."; // TODO(pbos): Implement. -} - -TEST_F(WebRtcVideoChannel2Test, DISABLED_ExternalCodecIgnored) { - FAIL() << "Not implemented."; // TODO(pbos): Implement. -} - TEST_F(WebRtcVideoChannel2Test, OnReadyToSendSignalsNetworkState) { EXPECT_EQ(webrtc::Call::kNetworkUp, fake_call_->GetNetworkState()); |