| Index: webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
|
| diff --git a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
|
| index cbd4ec16fda932f84363541babf160af08accd94..f1a629784193aab4ca0c7f2221b7f9e6469b4c4b 100644
|
| --- a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
|
| +++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc
|
| @@ -26,31 +26,8 @@ const unsigned int kDefaultMaxQp = 56;
|
| // Max qp for lowest spatial resolution when doing simulcast.
|
| const unsigned int kLowestResMaxQp = 45;
|
|
|
| -uint32_t SumStreamTargetBitrate(int streams, const webrtc::VideoCodec& codec) {
|
| - uint32_t bitrate_sum = 0;
|
| - for (int i = 0; i < streams; ++i) {
|
| - bitrate_sum += codec.simulcastStream[i].targetBitrate;
|
| - }
|
| - return bitrate_sum;
|
| -}
|
| -
|
| -uint32_t SumStreamMaxBitrate(int streams, const webrtc::VideoCodec& codec) {
|
| - uint32_t bitrate_sum = 0;
|
| - for (int i = 0; i < streams; ++i) {
|
| - bitrate_sum += codec.simulcastStream[i].maxBitrate;
|
| - }
|
| - return bitrate_sum;
|
| -}
|
| -
|
| -int NumberOfStreams(const webrtc::VideoCodec& codec) {
|
| - int streams =
|
| - codec.numberOfSimulcastStreams < 1 ? 1 : codec.numberOfSimulcastStreams;
|
| - uint32_t simulcast_max_bitrate = SumStreamMaxBitrate(streams, codec);
|
| - if (simulcast_max_bitrate == 0) {
|
| - streams = 1;
|
| - }
|
| - return streams;
|
| -}
|
| +static const char* kDefaultImplementationName =
|
| + "SimulcastEncoderAdapter (uninitialized)";
|
|
|
| bool ValidSimulcastResolutions(const webrtc::VideoCodec& codec,
|
| int num_streams) {
|
| @@ -92,13 +69,8 @@ int VerifyCodec(const webrtc::VideoCodec* inst) {
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| -// TL1 FrameDropper's max time to drop frames.
|
| -const float kTl1MaxTimeToDropFrames = 20.0f;
|
| -
|
| struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayersFactory {
|
| - ScreenshareTemporalLayersFactory()
|
| - : tl1_frame_dropper_(kTl1MaxTimeToDropFrames) {}
|
| -
|
| + ScreenshareTemporalLayersFactory() {}
|
| virtual ~ScreenshareTemporalLayersFactory() {}
|
|
|
| virtual webrtc::TemporalLayers* Create(int num_temporal_layers,
|
| @@ -106,9 +78,6 @@ struct ScreenshareTemporalLayersFactory : webrtc::TemporalLayersFactory {
|
| return new webrtc::ScreenshareLayers(num_temporal_layers, rand(),
|
| webrtc::Clock::GetRealTimeClock());
|
| }
|
| -
|
| - mutable webrtc::FrameDropper tl0_frame_dropper_;
|
| - mutable webrtc::FrameDropper tl1_frame_dropper_;
|
| };
|
|
|
| // An EncodedImageCallback implementation that forwards on calls to a
|
| @@ -133,16 +102,22 @@ class AdapterEncodedImageCallback : public webrtc::EncodedImageCallback {
|
| const size_t stream_idx_;
|
| };
|
|
|
| +webrtc::VideoCodec DefaultCodec() {
|
| + webrtc::VideoCodec codec;
|
| + memset(&codec, 0, sizeof(webrtc::VideoCodec));
|
| + return codec;
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace webrtc {
|
|
|
| SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory)
|
| : factory_(factory),
|
| - encoded_complete_callback_(NULL),
|
| - implementation_name_("SimulcastEncoderAdapter") {
|
| - memset(&codec_, 0, sizeof(webrtc::VideoCodec));
|
| -}
|
| + codec_(DefaultCodec()),
|
| + stream_states_(codec_),
|
| + encoded_complete_callback_(nullptr),
|
| + implementation_name_(kDefaultImplementationName) {}
|
|
|
| SimulcastEncoderAdapter::~SimulcastEncoderAdapter() {
|
| Release();
|
| @@ -154,13 +129,11 @@ int SimulcastEncoderAdapter::Release() {
|
| // resolutions doesn't require reallocation of the first encoder, but only
|
| // reinitialization, which makes sense. Then Destroy this instance instead in
|
| // ~SimulcastEncoderAdapter().
|
| - while (!streaminfos_.empty()) {
|
| - VideoEncoder* encoder = streaminfos_.back().encoder;
|
| - EncodedImageCallback* callback = streaminfos_.back().callback;
|
| - factory_->Destroy(encoder);
|
| - delete callback;
|
| - streaminfos_.pop_back();
|
| - }
|
| + stream_states_ = SimulcastState(codec_);
|
| + for (auto& it : encoders_)
|
| + factory_->Destroy(it.second.first);
|
| + encoders_.clear();
|
| +
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| @@ -181,7 +154,9 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
|
| return ret;
|
| }
|
|
|
| - int number_of_streams = NumberOfStreams(*inst);
|
| + std::unique_ptr<SimulcastState> simulcast(new SimulcastState(*inst));
|
| +
|
| + int number_of_streams = simulcast->NumStreams();
|
| const bool doing_simulcast = (number_of_streams > 1);
|
|
|
| if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) {
|
| @@ -198,17 +173,27 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
|
|
|
| std::string implementation_name;
|
| // Create |number_of_streams| of encoder instances and init them.
|
| - for (int i = 0; i < number_of_streams; ++i) {
|
| + stream_states_ = *simulcast;
|
| + for (const SimulcastState::Stream& stream : stream_states_.Streams()) {
|
| VideoCodec stream_codec;
|
| - bool send_stream = true;
|
| if (!doing_simulcast) {
|
| stream_codec = codec_;
|
| stream_codec.numberOfSimulcastStreams = 1;
|
| } else {
|
| - bool highest_resolution_stream = (i == (number_of_streams - 1));
|
| - PopulateStreamCodec(&codec_, i, number_of_streams,
|
| - highest_resolution_stream, &stream_codec,
|
| - &send_stream);
|
| + stream_codec = stream.AsCodec();
|
| + if (stream.idx == 0) {
|
| + // Settings for lowest spatial resolutions.
|
| + stream_codec.qpMax = kLowestResMaxQp;
|
| + }
|
| + if (stream.idx < codec_.numberOfSimulcastStreams - 1) {
|
| + // For resolutions below CIF, set the codec |complexity| parameter to
|
| + // kComplexityHigher, which maps to cpu_used = -4.
|
| + if (stream_codec.width * stream_codec.height < 352 * 288) {
|
| + stream_codec.codecSpecific.VP8.complexity = webrtc::kComplexityHigher;
|
| + }
|
| + // Turn off denoising for all streams but the highest resolution.
|
| + stream_codec.codecSpecific.VP8.denoisingOn = false;
|
| + }
|
| }
|
|
|
| // TODO(ronghuawu): Remove once this is handled in VP8EncoderImpl.
|
| @@ -222,14 +207,16 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst,
|
| Release();
|
| return ret;
|
| }
|
| - EncodedImageCallback* callback = new AdapterEncodedImageCallback(this, i);
|
| + EncodedImageCallback* callback =
|
| + new AdapterEncodedImageCallback(this, stream.idx);
|
| encoder->RegisterEncodeCompleteCallback(callback);
|
| - streaminfos_.push_back(StreamInfo(encoder, callback, stream_codec.width,
|
| - stream_codec.height, send_stream));
|
| - if (i != 0)
|
| + encoders_[stream.idx] = EncoderContext(
|
| + encoder, std::unique_ptr<EncodedImageCallback>(callback));
|
| + if (stream.idx != 0)
|
| implementation_name += ", ";
|
| - implementation_name += streaminfos_[i].encoder->ImplementationName();
|
| + implementation_name += encoder->ImplementationName();
|
| }
|
| +
|
| if (doing_simulcast) {
|
| implementation_name_ =
|
| "SimulcastEncoderAdapter (" + implementation_name + ")";
|
| @@ -250,51 +237,39 @@ int SimulcastEncoderAdapter::Encode(
|
| return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
| }
|
|
|
| - // All active streams should generate a key frame if
|
| - // a key frame is requested by any stream.
|
| - bool send_key_frame = false;
|
| - if (frame_types) {
|
| - for (size_t i = 0; i < frame_types->size(); ++i) {
|
| - if (frame_types->at(i) == kVideoFrameKey) {
|
| - send_key_frame = true;
|
| - break;
|
| - }
|
| - }
|
| - }
|
| - for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
|
| - if (streaminfos_[stream_idx].key_frame_request &&
|
| - streaminfos_[stream_idx].send_stream) {
|
| - send_key_frame = true;
|
| - break;
|
| - }
|
| - }
|
| + // TODO(sprang): Do we really need to force key frames on all streams?
|
| + // All active streams should generate a key frame if a key frame is requested
|
| + // by any stream.
|
| + bool force_key_frame = frame_types &&
|
| + std::find(frame_types->begin(), frame_types->end(),
|
| + kVideoFrameKey) != frame_types->end();
|
| + int active_streams = stream_states_.NumSendingStreams();
|
| + for (int i = 0; i < active_streams; ++i)
|
| + force_key_frame |= stream_states_.GetAndResetKeyFrameRequest(i);
|
|
|
| int src_width = input_image.width();
|
| int src_height = input_image.height();
|
| - for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
|
| - // Don't encode frames in resolutions that we don't intend to send.
|
| - if (!streaminfos_[stream_idx].send_stream)
|
| + for (const SimulcastState::Stream& stream : stream_states_.Streams()) {
|
| + if (!stream.sending)
|
| continue;
|
|
|
| std::vector<FrameType> stream_frame_types;
|
| - if (send_key_frame) {
|
| + if (force_key_frame) {
|
| stream_frame_types.push_back(kVideoFrameKey);
|
| - streaminfos_[stream_idx].key_frame_request = false;
|
| } else {
|
| stream_frame_types.push_back(kVideoFrameDelta);
|
| }
|
|
|
| - int dst_width = streaminfos_[stream_idx].width;
|
| - int dst_height = streaminfos_[stream_idx].height;
|
| - // If scaling isn't required, because the input resolution
|
| - // matches the destination or the input image is empty (e.g.
|
| - // a keyframe request for encoders with internal camera
|
| - // sources), pass the image on directly. Otherwise, we'll
|
| - // scale it to match what the encoder expects (below).
|
| + int dst_width = stream.config.width;
|
| + int dst_height = stream.config.height;
|
| + // If scaling isn't required, because the input resolution matches the
|
| + // destination or the input image is empty (e.g. a keyframe request for
|
| + // encoders with internal camera sources), pass the image on directly.
|
| + // Otherwise, we'll scale it to match what the encoder expects (below).
|
| if ((dst_width == src_width && dst_height == src_height) ||
|
| input_image.IsZeroSize()) {
|
| - streaminfos_[stream_idx].encoder->Encode(input_image, codec_specific_info,
|
| - &stream_frame_types);
|
| + encoders_[stream.idx].first->Encode(input_image, codec_specific_info,
|
| + &stream_frame_types);
|
| } else {
|
| VideoFrame dst_frame;
|
| // Making sure that destination frame is of sufficient size.
|
| @@ -311,8 +286,8 @@ int SimulcastEncoderAdapter::Encode(
|
| dst_height, libyuv::kFilterBilinear);
|
| dst_frame.set_timestamp(input_image.timestamp());
|
| dst_frame.set_render_time_ms(input_image.render_time_ms());
|
| - streaminfos_[stream_idx].encoder->Encode(dst_frame, codec_specific_info,
|
| - &stream_frame_types);
|
| + encoders_[stream.idx].first->Encode(dst_frame, codec_specific_info,
|
| + &stream_frame_types);
|
| }
|
| }
|
|
|
| @@ -327,59 +302,35 @@ int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback(
|
|
|
| int SimulcastEncoderAdapter::SetChannelParameters(uint32_t packet_loss,
|
| int64_t rtt) {
|
| - for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
|
| - streaminfos_[stream_idx].encoder->SetChannelParameters(packet_loss, rtt);
|
| - }
|
| + for (const auto& it : encoders_)
|
| + it.second.first->SetChannelParameters(packet_loss, rtt);
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| }
|
|
|
| int SimulcastEncoderAdapter::SetRates(uint32_t new_bitrate_kbit,
|
| uint32_t new_framerate) {
|
| - if (!Initialized()) {
|
| + if (!Initialized())
|
| return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
| - }
|
| - if (new_framerate < 1) {
|
| +
|
| + if (new_framerate < 1)
|
| return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
| - }
|
| - if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate) {
|
| +
|
| + if (codec_.maxBitrate > 0 && new_bitrate_kbit > codec_.maxBitrate)
|
| new_bitrate_kbit = codec_.maxBitrate;
|
| - }
|
| - if (new_bitrate_kbit < codec_.minBitrate) {
|
| +
|
| + if (new_bitrate_kbit < codec_.minBitrate)
|
| new_bitrate_kbit = codec_.minBitrate;
|
| - }
|
| +
|
| if (codec_.numberOfSimulcastStreams > 0 &&
|
| new_bitrate_kbit < codec_.simulcastStream[0].minBitrate) {
|
| new_bitrate_kbit = codec_.simulcastStream[0].minBitrate;
|
| }
|
| - codec_.maxFramerate = new_framerate;
|
| -
|
| - bool send_stream = true;
|
| - uint32_t stream_bitrate = 0;
|
| - for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) {
|
| - stream_bitrate = GetStreamBitrate(stream_idx, streaminfos_.size(),
|
| - new_bitrate_kbit, &send_stream);
|
| - // Need a key frame if we have not sent this stream before.
|
| - if (send_stream && !streaminfos_[stream_idx].send_stream) {
|
| - streaminfos_[stream_idx].key_frame_request = true;
|
| - }
|
| - streaminfos_[stream_idx].send_stream = send_stream;
|
| -
|
| - // TODO(holmer): This is a temporary hack for screensharing, where we
|
| - // interpret the startBitrate as the encoder target bitrate. This is
|
| - // to allow for a different max bitrate, so if the codec can't meet
|
| - // the target we still allow it to overshoot up to the max before dropping
|
| - // frames. This hack should be improved.
|
| - if (codec_.targetBitrate > 0 &&
|
| - (codec_.codecSpecific.VP8.numberOfTemporalLayers == 2 ||
|
| - codec_.simulcastStream[0].numberOfTemporalLayers == 2)) {
|
| - stream_bitrate = std::min(codec_.maxBitrate, stream_bitrate);
|
| - // TODO(ronghuawu): Can't change max bitrate via the VideoEncoder
|
| - // interface. And VP8EncoderImpl doesn't take negative framerate.
|
| - // max_bitrate = std::min(codec_.maxBitrate, stream_bitrate);
|
| - // new_framerate = -1;
|
| - }
|
|
|
| - streaminfos_[stream_idx].encoder->SetRates(stream_bitrate, new_framerate);
|
| + codec_.maxFramerate = new_framerate;
|
| + stream_states_.AllocateBitrate(new_bitrate_kbit * 1000);
|
| + for (const SimulcastState::Stream& stream : stream_states_.Streams()) {
|
| + encoders_[stream.idx].first->SetRates(stream.allocated_rate_bps / 1000,
|
| + new_framerate);
|
| }
|
|
|
| return WEBRTC_VIDEO_CODEC_OK;
|
| @@ -398,109 +349,22 @@ int32_t SimulcastEncoderAdapter::Encoded(
|
| encodedImage, &stream_codec_specific, fragmentation);
|
| }
|
|
|
| -uint32_t SimulcastEncoderAdapter::GetStreamBitrate(
|
| - int stream_idx,
|
| - size_t total_number_of_streams,
|
| - uint32_t new_bitrate_kbit,
|
| - bool* send_stream) const {
|
| - if (total_number_of_streams == 1) {
|
| - *send_stream = true;
|
| - return new_bitrate_kbit;
|
| - }
|
| -
|
| - // The bitrate needed to start sending this stream is given by the
|
| - // minimum bitrate allowed for encoding this stream, plus the sum target
|
| - // rates of all lower streams.
|
| - uint32_t sum_target_lower_streams =
|
| - SumStreamTargetBitrate(stream_idx, codec_);
|
| - uint32_t bitrate_to_send_this_layer =
|
| - codec_.simulcastStream[stream_idx].minBitrate + sum_target_lower_streams;
|
| - if (new_bitrate_kbit >= bitrate_to_send_this_layer) {
|
| - // We have enough bandwidth to send this stream.
|
| - *send_stream = true;
|
| - // Bitrate for this stream is the new bitrate (|new_bitrate_kbit|) minus the
|
| - // sum target rates of the lower streams, and capped to a maximum bitrate.
|
| - // The maximum cap depends on whether we send the next higher stream.
|
| - // If we will be sending the next higher stream, |max_rate| is given by
|
| - // current stream's |targetBitrate|, otherwise it's capped by |maxBitrate|.
|
| - if (stream_idx < codec_.numberOfSimulcastStreams - 1) {
|
| - unsigned int max_rate = codec_.simulcastStream[stream_idx].maxBitrate;
|
| - if (new_bitrate_kbit >=
|
| - SumStreamTargetBitrate(stream_idx + 1, codec_) +
|
| - codec_.simulcastStream[stream_idx + 1].minBitrate) {
|
| - max_rate = codec_.simulcastStream[stream_idx].targetBitrate;
|
| - }
|
| - return std::min(new_bitrate_kbit - sum_target_lower_streams, max_rate);
|
| - } else {
|
| - // For the highest stream (highest resolution), the |targetBitRate| and
|
| - // |maxBitrate| are not used. Any excess bitrate (above the targets of
|
| - // all lower streams) is given to this (highest resolution) stream.
|
| - return new_bitrate_kbit - sum_target_lower_streams;
|
| - }
|
| - } else {
|
| - // Not enough bitrate for this stream.
|
| - // Return our max bitrate of |stream_idx| - 1, but we don't send it. We need
|
| - // to keep this resolution coding in order for the multi-encoder to work.
|
| - *send_stream = false;
|
| - return codec_.simulcastStream[stream_idx - 1].maxBitrate;
|
| - }
|
| -}
|
| -
|
| -void SimulcastEncoderAdapter::PopulateStreamCodec(
|
| - const webrtc::VideoCodec* inst,
|
| - int stream_index,
|
| - size_t total_number_of_streams,
|
| - bool highest_resolution_stream,
|
| - webrtc::VideoCodec* stream_codec,
|
| - bool* send_stream) {
|
| - *stream_codec = *inst;
|
| -
|
| - // Stream specific settings.
|
| - stream_codec->codecSpecific.VP8.numberOfTemporalLayers =
|
| - inst->simulcastStream[stream_index].numberOfTemporalLayers;
|
| - stream_codec->numberOfSimulcastStreams = 0;
|
| - stream_codec->width = inst->simulcastStream[stream_index].width;
|
| - stream_codec->height = inst->simulcastStream[stream_index].height;
|
| - stream_codec->maxBitrate = inst->simulcastStream[stream_index].maxBitrate;
|
| - stream_codec->minBitrate = inst->simulcastStream[stream_index].minBitrate;
|
| - stream_codec->qpMax = inst->simulcastStream[stream_index].qpMax;
|
| - // Settings that are based on stream/resolution.
|
| - if (stream_index == 0) {
|
| - // Settings for lowest spatial resolutions.
|
| - stream_codec->qpMax = kLowestResMaxQp;
|
| - }
|
| - if (!highest_resolution_stream) {
|
| - // For resolutions below CIF, set the codec |complexity| parameter to
|
| - // kComplexityHigher, which maps to cpu_used = -4.
|
| - int pixels_per_frame = stream_codec->width * stream_codec->height;
|
| - if (pixels_per_frame < 352 * 288) {
|
| - stream_codec->codecSpecific.VP8.complexity = webrtc::kComplexityHigher;
|
| - }
|
| - // Turn off denoising for all streams but the highest resolution.
|
| - stream_codec->codecSpecific.VP8.denoisingOn = false;
|
| - }
|
| - // TODO(ronghuawu): what to do with targetBitrate.
|
| -
|
| - int stream_bitrate = GetStreamBitrate(stream_index, total_number_of_streams,
|
| - inst->startBitrate, send_stream);
|
| - stream_codec->startBitrate = stream_bitrate;
|
| -}
|
| -
|
| bool SimulcastEncoderAdapter::Initialized() const {
|
| - return !streaminfos_.empty();
|
| + return !encoders_.empty();
|
| }
|
|
|
| void SimulcastEncoderAdapter::OnDroppedFrame() {
|
| - streaminfos_[0].encoder->OnDroppedFrame();
|
| + for (const auto& it : encoders_)
|
| + it.second.first->OnDroppedFrame();
|
| }
|
|
|
| bool SimulcastEncoderAdapter::SupportsNativeHandle() const {
|
| // We should not be calling this method before streaminfos_ are configured.
|
| - RTC_DCHECK(!streaminfos_.empty());
|
| + RTC_DCHECK(Initialized());
|
| // TODO(pbos): Support textures when using more than one encoder.
|
| - if (streaminfos_.size() != 1)
|
| + if (encoders_.size() != 1)
|
| return false;
|
| - return streaminfos_[0].encoder->SupportsNativeHandle();
|
| + return encoders_.begin()->second.first->SupportsNativeHandle();
|
| }
|
|
|
| const char* SimulcastEncoderAdapter::ImplementationName() const {
|
|
|