Implement SimulcastEncoderAdapter support.

R=stefan@webrtc.org
BUG=1788

Review URL: https://webrtc-codereview.appspot.com/37589004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8061 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
pbos@webrtc.org 2015-01-14 16:26:23 +00:00
parent 8315d7de85
commit f18fba2f7b
10 changed files with 113 additions and 42 deletions

View File

@ -153,9 +153,16 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
virtual int32 InitEncode(const webrtc::VideoCodec* codecSettings, virtual int32 InitEncode(const webrtc::VideoCodec* codecSettings,
int32 numberOfCores, int32 numberOfCores,
size_t maxPayloadSize) { size_t maxPayloadSize) {
rtc::CritScope lock(&crit_);
codec_settings_ = *codecSettings;
return WEBRTC_VIDEO_CODEC_OK; return WEBRTC_VIDEO_CODEC_OK;
} }
webrtc::VideoCodec GetCodecSettings() {
rtc::CritScope lock(&crit_);
return codec_settings_;
}
virtual int32 Encode( virtual int32 Encode(
const webrtc::I420VideoFrame& inputImage, const webrtc::I420VideoFrame& inputImage,
const webrtc::CodecSpecificInfo* codecSpecificInfo, const webrtc::CodecSpecificInfo* codecSpecificInfo,
@ -192,6 +199,7 @@ class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
private: private:
rtc::CriticalSection crit_; rtc::CriticalSection crit_;
int num_frames_encoded_ GUARDED_BY(crit_); int num_frames_encoded_ GUARDED_BY(crit_);
webrtc::VideoCodec codec_settings_ GUARDED_BY(crit_);
}; };
// Fake class for mocking out WebRtcVideoEncoderFactory. // Fake class for mocking out WebRtcVideoEncoderFactory.

View File

@ -138,36 +138,6 @@ class EncoderFactoryAdapter : public webrtc::VideoEncoderFactory {
cricket::WebRtcVideoEncoderFactory* factory_; cricket::WebRtcVideoEncoderFactory* factory_;
}; };
// Wrap encoder factory to a simulcast encoder factory.
class SimulcastEncoderFactory : public cricket::WebRtcVideoEncoderFactory {
public:
// SimulcastEncoderFactory doesn't take ownership of |factory|, which is owned
// by e.g. PeerConnectionFactory.
explicit SimulcastEncoderFactory(cricket::WebRtcVideoEncoderFactory* factory)
: factory_(factory) {}
virtual ~SimulcastEncoderFactory() {}
virtual webrtc::VideoEncoder* CreateVideoEncoder(
webrtc::VideoCodecType type) OVERRIDE {
ASSERT(type == webrtc::kVideoCodecVP8);
ASSERT(factory_ != NULL);
return new webrtc::SimulcastEncoderAdapter(
webrtc::scoped_ptr<webrtc::VideoEncoderFactory>(
new EncoderFactoryAdapter(factory_)).Pass());
}
virtual const std::vector<VideoCodec>& codecs() const OVERRIDE {
return factory_->codecs();
}
virtual void DestroyVideoEncoder(webrtc::VideoEncoder* encoder) OVERRIDE {
delete encoder;
}
private:
cricket::WebRtcVideoEncoderFactory* factory_;
};
} // namespace } // namespace
namespace cricket { namespace cricket {
@ -316,6 +286,37 @@ std::vector<VideoCodec> DefaultVideoCodecList() {
return codecs; return codecs;
} }
WebRtcSimulcastEncoderFactory::WebRtcSimulcastEncoderFactory(
cricket::WebRtcVideoEncoderFactory* factory)
: factory_(factory) {
}
WebRtcSimulcastEncoderFactory::~WebRtcSimulcastEncoderFactory() {
}
bool WebRtcSimulcastEncoderFactory::UseSimulcastEncoderFactory(
const std::vector<WebRtcVideoEncoderFactory::VideoCodec>& codecs) {
return codecs.size() == 1 && codecs[0].type == webrtc::kVideoCodecVP8;
}
webrtc::VideoEncoder* WebRtcSimulcastEncoderFactory::CreateVideoEncoder(
webrtc::VideoCodecType type) {
ASSERT(type == webrtc::kVideoCodecVP8);
ASSERT(factory_ != NULL);
return new webrtc::SimulcastEncoderAdapter(
new EncoderFactoryAdapter(factory_));
}
const std::vector<WebRtcVideoEncoderFactory::VideoCodec>&
WebRtcSimulcastEncoderFactory::codecs() const {
return factory_->codecs();
}
void WebRtcSimulcastEncoderFactory::DestroyVideoEncoder(
webrtc::VideoEncoder* encoder) {
delete encoder;
}
struct FlushBlackFrameData : public rtc::MessageData { struct FlushBlackFrameData : public rtc::MessageData {
FlushBlackFrameData(uint32 s, int64 t, int i) FlushBlackFrameData(uint32 s, int64 t, int i)
: ssrc(s), timestamp(t), interval(i) { : ssrc(s), timestamp(t), interval(i) {
@ -1690,15 +1691,15 @@ void WebRtcVideoEngine::SetExternalEncoderFactory(
return; return;
// No matter what happens we shouldn't hold on to a stale // No matter what happens we shouldn't hold on to a stale
// SimulcastEncoderFactory. // WebRtcSimulcastEncoderFactory.
simulcast_encoder_factory_.reset(); simulcast_encoder_factory_.reset();
if (encoder_factory) { if (encoder_factory) {
const std::vector<WebRtcVideoEncoderFactory::VideoCodec>& codecs = const std::vector<WebRtcVideoEncoderFactory::VideoCodec>& codecs =
encoder_factory->codecs(); encoder_factory->codecs();
if (codecs.size() == 1 && codecs[0].type == webrtc::kVideoCodecVP8) { if (WebRtcSimulcastEncoderFactory::UseSimulcastEncoderFactory(codecs)) {
simulcast_encoder_factory_.reset( simulcast_encoder_factory_.reset(
new SimulcastEncoderFactory(encoder_factory)); new WebRtcSimulcastEncoderFactory(encoder_factory));
encoder_factory = simulcast_encoder_factory_.get(); encoder_factory = simulcast_encoder_factory_.get();
} }
} }

View File

@ -523,6 +523,28 @@ class WebRtcVideoMediaChannel : public rtc::MessageHandler,
int ratio_h_; int ratio_h_;
}; };
// Wrap encoder factory to a simulcast encoder factory. Exposed here for code to
// be shared with WebRtcVideoEngine2, not to be used externally.
class WebRtcSimulcastEncoderFactory
: public cricket::WebRtcVideoEncoderFactory {
public:
// WebRtcSimulcastEncoderFactory doesn't take ownership of |factory|, which is
// owned by e.g. PeerConnectionFactory.
explicit WebRtcSimulcastEncoderFactory(
cricket::WebRtcVideoEncoderFactory* factory);
virtual ~WebRtcSimulcastEncoderFactory();
static bool UseSimulcastEncoderFactory(const std::vector<VideoCodec>& codecs);
virtual webrtc::VideoEncoder* CreateVideoEncoder(
webrtc::VideoCodecType type) OVERRIDE;
virtual const std::vector<VideoCodec>& codecs() const OVERRIDE;
virtual void DestroyVideoEncoder(webrtc::VideoEncoder* encoder) OVERRIDE;
private:
cricket::WebRtcVideoEncoderFactory* factory_;
};
} // namespace cricket } // namespace cricket
#endif // TALK_MEDIA_WEBRTCVIDEOENGINE_H_ #endif // TALK_MEDIA_WEBRTCVIDEOENGINE_H_

View File

@ -477,6 +477,20 @@ void WebRtcVideoEngine2::SetExternalDecoderFactory(
void WebRtcVideoEngine2::SetExternalEncoderFactory( void WebRtcVideoEngine2::SetExternalEncoderFactory(
WebRtcVideoEncoderFactory* encoder_factory) { WebRtcVideoEncoderFactory* encoder_factory) {
assert(!initialized_); assert(!initialized_);
if (external_encoder_factory_ == encoder_factory)
return;
// No matter what happens we shouldn't hold on to a stale
// WebRtcSimulcastEncoderFactory.
simulcast_encoder_factory_.reset();
if (encoder_factory &&
WebRtcSimulcastEncoderFactory::UseSimulcastEncoderFactory(
encoder_factory->codecs())) {
simulcast_encoder_factory_.reset(
new WebRtcSimulcastEncoderFactory(encoder_factory));
encoder_factory = simulcast_encoder_factory_.get();
}
external_encoder_factory_ = encoder_factory; external_encoder_factory_ = encoder_factory;
video_codecs_ = GetSupportedCodecs(); video_codecs_ = GetSupportedCodecs();

View File

@ -208,6 +208,7 @@ class WebRtcVideoEngine2 : public sigslot::has_slots<> {
WebRtcVideoDecoderFactory* external_decoder_factory_; WebRtcVideoDecoderFactory* external_decoder_factory_;
WebRtcVideoEncoderFactory* external_encoder_factory_; WebRtcVideoEncoderFactory* external_encoder_factory_;
rtc::scoped_ptr<WebRtcVideoEncoderFactory> simulcast_encoder_factory_;
}; };
class WebRtcVideoChannel2 : public rtc::MessageHandler, class WebRtcVideoChannel2 : public rtc::MessageHandler,

View File

@ -592,6 +592,35 @@ VideoMediaChannel* WebRtcVideoEngine2Test::SetUpForExternalDecoderFactory(
return channel; return channel;
} }
TEST_F(WebRtcVideoEngine2Test, UsesSimulcastAdapterForVp8Factories) {
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));
std::vector<uint32> ssrcs = MAKE_VECTOR(kSsrcs3);
EXPECT_TRUE(
channel->AddSendStream(CreateSimStreamParams("cname", ssrcs)));
EXPECT_TRUE(channel->SetSend(true));
EXPECT_GT(encoder_factory.encoders().size(), 1u);
// Verify that encoders are configured for simulcast through adapter
// (increasing resolution and only configured to send one stream each).
int prev_width = -1;
for (size_t i = 0; i < encoder_factory.encoders().size(); ++i) {
webrtc::VideoCodec codec_settings =
encoder_factory.encoders()[i]->GetCodecSettings();
EXPECT_EQ(0, codec_settings.numberOfSimulcastStreams);
EXPECT_GT(codec_settings.width, prev_width);
prev_width = codec_settings.width;
}
}
TEST_F(WebRtcVideoEngine2Test, ChannelWithExternalH264CanChangeToInternalVp8) { TEST_F(WebRtcVideoEngine2Test, ChannelWithExternalH264CanChangeToInternalVp8) {
cricket::FakeWebRtcVideoEncoderFactory encoder_factory; cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264"); encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");

View File

@ -116,9 +116,8 @@ struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayers::Factory {
namespace webrtc { namespace webrtc {
SimulcastEncoderAdapter::SimulcastEncoderAdapter( SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory)
scoped_ptr<VideoEncoderFactory> factory) : factory_(factory), encoded_complete_callback_(NULL) {
: factory_(factory.Pass()), encoded_complete_callback_(NULL) {
memset(&codec_, 0, sizeof(webrtc::VideoCodec)); memset(&codec_, 0, sizeof(webrtc::VideoCodec));
} }

View File

@ -33,8 +33,7 @@ class VideoEncoderFactory {
class SimulcastEncoderAdapter : public VP8Encoder, class SimulcastEncoderAdapter : public VP8Encoder,
public EncodedImageCallback { public EncodedImageCallback {
public: public:
explicit SimulcastEncoderAdapter(scoped_ptr<VideoEncoderFactory> factory); explicit SimulcastEncoderAdapter(VideoEncoderFactory* factory);
virtual ~SimulcastEncoderAdapter(); virtual ~SimulcastEncoderAdapter();
// Implements VideoEncoder // Implements VideoEncoder

View File

@ -171,8 +171,7 @@ class TestSimulcastEncoderAdapterFakeHelper {
// Can only be called once as the SimulcastEncoderAdapter will take the // Can only be called once as the SimulcastEncoderAdapter will take the
// ownership of |factory_|. // ownership of |factory_|.
VP8Encoder* CreateMockEncoderAdapter() { VP8Encoder* CreateMockEncoderAdapter() {
scoped_ptr<VideoEncoderFactory> scoped_factory(factory_); return new SimulcastEncoderAdapter(factory_);
return new SimulcastEncoderAdapter(scoped_factory.Pass());
} }
void ExpectCallSetChannelParameters(uint32_t packetLoss, int64_t rtt) { void ExpectCallSetChannelParameters(uint32_t packetLoss, int64_t rtt) {

View File

@ -32,8 +32,7 @@ class VP8EncoderImplFactory : public VideoEncoderFactory {
VP8Encoder* VP8Encoder::Create() { VP8Encoder* VP8Encoder::Create() {
if (VP8EncoderFactoryConfig::use_simulcast_adapter()) { if (VP8EncoderFactoryConfig::use_simulcast_adapter()) {
scoped_ptr<VideoEncoderFactory> factory(new VP8EncoderImplFactory()); return new SimulcastEncoderAdapter(new VP8EncoderImplFactory());
return new SimulcastEncoderAdapter(factory.Pass());
} else { } else {
return new VP8EncoderImpl(); return new VP8EncoderImpl();
} }