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 8cef40d42e807afec74299b1f7af84b87a1276e3..89aae1b66dc9346e887bd9bb4d31f0a1636f6eb8 100644 |
--- a/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc |
+++ b/webrtc/modules/video_coding/codecs/vp8/simulcast_encoder_adapter.cc |
@@ -62,7 +62,7 @@ bool ValidSimulcastResolutions(const webrtc::VideoCodec& codec, |
} |
int VerifyCodec(const webrtc::VideoCodec* inst) { |
- if (inst == NULL) { |
+ if (inst == nullptr) { |
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
} |
if (inst->maxFramerate < 1) { |
@@ -127,36 +127,49 @@ class TemporalLayersFactoryAdapter : public webrtc::TemporalLayersFactory { |
namespace webrtc { |
SimulcastEncoderAdapter::SimulcastEncoderAdapter(VideoEncoderFactory* factory) |
- : factory_(factory), |
+ : inited_(0), |
+ factory_(factory), |
encoded_complete_callback_(nullptr), |
implementation_name_("SimulcastEncoderAdapter") { |
+ // The adapter is typically created on the worker thread, but operated on |
+ // the encoder task queue. |
+ encoder_queue_.Detach(); |
+ |
memset(&codec_, 0, sizeof(webrtc::VideoCodec)); |
} |
SimulcastEncoderAdapter::~SimulcastEncoderAdapter() { |
- Release(); |
+ RTC_DCHECK(!Initialized()); |
+ DestroyStoredEncoders(); |
} |
int SimulcastEncoderAdapter::Release() { |
- // TODO(pbos): Keep the last encoder instance but call ::Release() on it, then |
- // re-use this instance in ::InitEncode(). This means that changing |
- // resolutions doesn't require reallocation of the first encoder, but only |
- // reinitialization, which makes sense. Then Destroy this instance instead in |
- // ~SimulcastEncoderAdapter(). |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
+ |
while (!streaminfos_.empty()) { |
VideoEncoder* encoder = streaminfos_.back().encoder; |
- EncodedImageCallback* callback = streaminfos_.back().callback; |
encoder->Release(); |
- factory_->Destroy(encoder); |
- delete callback; |
- streaminfos_.pop_back(); |
+ // Even though it seems very unlikely, there are no guarantees that the |
+ // encoder will not call back after being Release()'d. Therefore, we disable |
+ // the callbacks here. |
+ encoder->RegisterEncodeCompleteCallback(nullptr); |
+ streaminfos_.pop_back(); // Deletes callback adapter. |
+ stored_encoders_.push(encoder); |
} |
+ |
+ // It's legal to move the encoder to another queue now. |
+ encoder_queue_.Detach(); |
+ |
+ rtc::AtomicOps::ReleaseStore(&inited_, 0); |
+ |
return WEBRTC_VIDEO_CODEC_OK; |
} |
int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst, |
int number_of_cores, |
size_t max_payload_size) { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
+ |
if (number_of_cores < 1) { |
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
} |
@@ -172,6 +185,7 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst, |
} |
int number_of_streams = NumberOfStreams(*inst); |
+ RTC_DCHECK_LE(number_of_streams, kMaxSimulcastStreams); |
const bool doing_simulcast = (number_of_streams > 1); |
if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) { |
@@ -202,7 +216,7 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst, |
start_bitrate_kbps = |
std::max(codec_.simulcastStream[i].minBitrate, start_bitrate_kbps); |
bool highest_resolution_stream = (i == (number_of_streams - 1)); |
- PopulateStreamCodec(&codec_, i, start_bitrate_kbps, |
+ PopulateStreamCodec(codec_, i, start_bitrate_kbps, |
highest_resolution_stream, &stream_codec); |
} |
TemporalLayersFactoryAdapter tl_factory_adapter(i, |
@@ -214,7 +228,17 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst, |
stream_codec.qpMax = kDefaultMaxQp; |
} |
- VideoEncoder* encoder = factory_->Create(); |
+ // If an existing encoder instance exists, reuse it. |
+ // TODO(brandtr): Set initial RTP state (e.g., picture_id/tl0_pic_idx) here, |
+ // when we start storing that state outside the encoder wrappers. |
+ VideoEncoder* encoder; |
+ if (!stored_encoders_.empty()) { |
+ encoder = stored_encoders_.top(); |
+ stored_encoders_.pop(); |
+ } else { |
+ encoder = factory_->Create(); |
+ } |
+ |
ret = encoder->InitEncode(&stream_codec, number_of_cores, max_payload_size); |
if (ret < 0) { |
// Explicitly destroy the current encoder; because we haven't registered a |
@@ -223,21 +247,30 @@ int SimulcastEncoderAdapter::InitEncode(const VideoCodec* inst, |
Release(); |
return ret; |
} |
- EncodedImageCallback* callback = new AdapterEncodedImageCallback(this, i); |
- encoder->RegisterEncodeCompleteCallback(callback); |
- streaminfos_.push_back(StreamInfo(encoder, callback, stream_codec.width, |
- stream_codec.height, |
- start_bitrate_kbps > 0)); |
- if (i != 0) |
+ std::unique_ptr<EncodedImageCallback> callback( |
+ new AdapterEncodedImageCallback(this, i)); |
+ encoder->RegisterEncodeCompleteCallback(callback.get()); |
+ streaminfos_.emplace_back(encoder, std::move(callback), stream_codec.width, |
+ stream_codec.height, start_bitrate_kbps > 0); |
+ |
+ if (i != 0) { |
implementation_name += ", "; |
+ } |
implementation_name += streaminfos_[i].encoder->ImplementationName(); |
} |
+ |
if (doing_simulcast) { |
implementation_name_ = |
"SimulcastEncoderAdapter (" + implementation_name + ")"; |
} else { |
implementation_name_ = implementation_name; |
} |
+ |
+ // To save memory, don't store encoders that we don't use. |
+ DestroyStoredEncoders(); |
+ |
+ rtc::AtomicOps::ReleaseStore(&inited_, 1); |
+ |
return WEBRTC_VIDEO_CODEC_OK; |
} |
@@ -245,10 +278,12 @@ int SimulcastEncoderAdapter::Encode( |
const VideoFrame& input_image, |
const CodecSpecificInfo* codec_specific_info, |
const std::vector<FrameType>* frame_types) { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
+ |
if (!Initialized()) { |
return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
} |
- if (encoded_complete_callback_ == NULL) { |
+ if (encoded_complete_callback_ == nullptr) { |
return WEBRTC_VIDEO_CODEC_UNINITIALIZED; |
} |
@@ -275,8 +310,9 @@ int SimulcastEncoderAdapter::Encode( |
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) |
+ if (!streaminfos_[stream_idx].send_stream) { |
continue; |
+ } |
std::vector<FrameType> stream_frame_types; |
if (send_key_frame) { |
@@ -338,12 +374,14 @@ int SimulcastEncoderAdapter::Encode( |
int SimulcastEncoderAdapter::RegisterEncodeCompleteCallback( |
EncodedImageCallback* callback) { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
encoded_complete_callback_ = callback; |
return WEBRTC_VIDEO_CODEC_OK; |
} |
int SimulcastEncoderAdapter::SetChannelParameters(uint32_t packet_loss, |
int64_t rtt) { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
for (size_t stream_idx = 0; stream_idx < streaminfos_.size(); ++stream_idx) { |
streaminfos_[stream_idx].encoder->SetChannelParameters(packet_loss, rtt); |
} |
@@ -352,20 +390,26 @@ int SimulcastEncoderAdapter::SetChannelParameters(uint32_t packet_loss, |
int SimulcastEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate, |
uint32_t new_framerate) { |
- if (!Initialized()) |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
+ |
+ 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 && bitrate.get_sum_kbps() > codec_.maxBitrate) |
+ if (codec_.maxBitrate > 0 && bitrate.get_sum_kbps() > codec_.maxBitrate) { |
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
+ } |
if (bitrate.get_sum_bps() > 0) { |
// Make sure the bitrate fits the configured min bitrates. 0 is a special |
// value that means paused, though, so leave it alone. |
- if (bitrate.get_sum_kbps() < codec_.minBitrate) |
+ if (bitrate.get_sum_kbps() < codec_.minBitrate) { |
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; |
+ } |
if (codec_.numberOfSimulcastStreams > 0 && |
bitrate.get_sum_kbps() < codec_.simulcastStream[0].minBitrate) { |
@@ -388,8 +432,9 @@ int SimulcastEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate, |
// Slice the temporal layers out of the full allocation and pass it on to |
// the encoder handling the current simulcast stream. |
BitrateAllocation stream_allocation; |
- for (int i = 0; i < kMaxTemporalStreams; ++i) |
+ for (int i = 0; i < kMaxTemporalStreams; ++i) { |
stream_allocation.SetBitrate(0, i, bitrate.GetBitrate(stream_idx, i)); |
+ } |
streaminfos_[stream_idx].encoder->SetRateAllocation(stream_allocation, |
new_framerate); |
} |
@@ -397,6 +442,8 @@ int SimulcastEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate, |
return WEBRTC_VIDEO_CODEC_OK; |
} |
+// TODO(brandtr): Add task checker to this member function, when all encoder |
+// callbacks are coming in on the encoder queue. |
EncodedImageCallback::Result SimulcastEncoderAdapter::OnEncodedImage( |
size_t stream_idx, |
const EncodedImage& encodedImage, |
@@ -412,24 +459,25 @@ EncodedImageCallback::Result SimulcastEncoderAdapter::OnEncodedImage( |
} |
void SimulcastEncoderAdapter::PopulateStreamCodec( |
- const webrtc::VideoCodec* inst, |
+ const webrtc::VideoCodec& inst, |
int stream_index, |
uint32_t start_bitrate_kbps, |
bool highest_resolution_stream, |
webrtc::VideoCodec* stream_codec) { |
- *stream_codec = *inst; |
+ *stream_codec = inst; |
// Stream specific settings. |
stream_codec->VP8()->numberOfTemporalLayers = |
- inst->simulcastStream[stream_index].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; |
+ 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) { |
+ const bool lowest_resolution_stream = (stream_index == 0); |
+ if (lowest_resolution_stream) { |
// Settings for lowest spatial resolutions. |
stream_codec->qpMax = kLowestResMaxQp; |
} |
@@ -449,28 +497,42 @@ void SimulcastEncoderAdapter::PopulateStreamCodec( |
} |
bool SimulcastEncoderAdapter::Initialized() const { |
- return !streaminfos_.empty(); |
+ return rtc::AtomicOps::AcquireLoad(&inited_) == 1; |
+} |
+ |
+void SimulcastEncoderAdapter::DestroyStoredEncoders() { |
+ while (!stored_encoders_.empty()) { |
+ VideoEncoder* encoder = stored_encoders_.top(); |
+ factory_->Destroy(encoder); |
+ stored_encoders_.pop(); |
+ } |
} |
bool SimulcastEncoderAdapter::SupportsNativeHandle() const { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
// We should not be calling this method before streaminfos_ are configured. |
RTC_DCHECK(!streaminfos_.empty()); |
for (const auto& streaminfo : streaminfos_) { |
- if (!streaminfo.encoder->SupportsNativeHandle()) |
+ if (!streaminfo.encoder->SupportsNativeHandle()) { |
return false; |
+ } |
} |
return true; |
} |
VideoEncoder::ScalingSettings SimulcastEncoderAdapter::GetScalingSettings() |
const { |
+ // TODO(brandtr): Investigate why the sequence checker below fails on mac. |
+ // RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
// Turn off quality scaling for simulcast. |
- if (!Initialized() || NumberOfStreams(codec_) != 1) |
+ if (!Initialized() || NumberOfStreams(codec_) != 1) { |
return VideoEncoder::ScalingSettings(false); |
+ } |
return streaminfos_[0].encoder->GetScalingSettings(); |
} |
const char* SimulcastEncoderAdapter::ImplementationName() const { |
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&encoder_queue_); |
return implementation_name_.c_str(); |
} |