Chromium Code Reviews| 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 55a4402cbe4e0d7b1f4cee5b12b1bfbdfeaf4654..3cbe3020d4090a5f61060d517b573c7a2a3fa54c 100644 |
| --- a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc |
| +++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc |
| @@ -17,6 +17,7 @@ |
| #include "webrtc/base/checks.h" |
| #include "webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h" |
| +#include "webrtc/modules/video_coding/utility/simulcast_state.h" |
| #include "webrtc/system_wrappers/include/clock.h" |
| namespace { |
| @@ -26,32 +27,6 @@ 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; |
| -} |
| - |
| bool ValidSimulcastResolutions(const webrtc::VideoCodec& codec, |
| int num_streams) { |
| if (codec.width != codec.simulcastStream[num_streams - 1].width || |
| @@ -92,13 +67,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 +76,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 |
| @@ -154,13 +121,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_.reset(); |
| + for (auto& it : encoders_) |
| + factory_->Destroy(it.second.first); |
| + encoders_.clear(); |
| + |
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| @@ -181,7 +146,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 +165,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_ = std::move(simulcast); |
| + for (SimulcastState::Stream stream : *stream_states_) { |
| 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 +199,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 +229,37 @@ 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; |
| - } |
| - } |
| + // All active streams should generate a key frame if a key frame is requested |
| + // by any stream. |
| + bool send_key_frame = stream_states_->AnyKeyFrameRequested() || |
| + (frame_types && |
| + std::find(frame_types->begin(), frame_types->end(), |
| + kVideoFrameKey) != frame_types->end()); |
| 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_) { |
| + if (!stream.sending) |
| continue; |
| std::vector<FrameType> stream_frame_types; |
| if (send_key_frame) { |
| stream_frame_types.push_back(kVideoFrameKey); |
| - streaminfos_[stream_idx].key_frame_request = false; |
| + stream_states_->GetAndResetKeyFrameRequest(stream.idx); |
| } 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 +276,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,60 +292,32 @@ 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); |
|
pbos-webrtc
2016/04/29 21:23:27
Shouldn't we also set the rates? I'm concerned wit
|
| return WEBRTC_VIDEO_CODEC_OK; |
| } |
| @@ -398,113 +335,25 @@ 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 stream_states_.get() != nullptr; |
| } |
| void SimulcastEncoderAdapter::OnDroppedFrame() { |
| - streaminfos_[0].encoder->OnDroppedFrame(); |
| + encoders_[0].first->OnDroppedFrame(); |
|
pbos-webrtc
2016/04/29 21:23:27
I think this should report to all encoders.
|
| } |
| int SimulcastEncoderAdapter::GetTargetFramerate() { |
| - return streaminfos_[0].encoder->GetTargetFramerate(); |
| + return encoders_[0].first->GetTargetFramerate(); |
| } |
| 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 { |