diff --git a/api/rtp_sender_interface.cc b/api/rtp_sender_interface.cc index d23fd1844c..57a5a10fb5 100644 --- a/api/rtp_sender_interface.cc +++ b/api/rtp_sender_interface.cc @@ -30,4 +30,7 @@ rtc::scoped_refptr RtpSenderInterface::dtls_transport() return nullptr; } +void RtpSenderInterface::SetEncoderToPacketizerFrameTransformer( + rtc::scoped_refptr frame_transformer) {} + } // namespace webrtc diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h index 5d6271f5d3..bdbd6dc645 100644 --- a/api/rtp_sender_interface.h +++ b/api/rtp_sender_interface.h @@ -20,6 +20,7 @@ #include "api/crypto/frame_encryptor_interface.h" #include "api/dtls_transport_interface.h" #include "api/dtmf_sender_interface.h" +#include "api/frame_transformer_interface.h" #include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/proxy.h" @@ -93,6 +94,9 @@ class RTC_EXPORT RtpSenderInterface : public rtc::RefCountInterface { // user. This can be used to update the state of the object. virtual rtc::scoped_refptr GetFrameEncryptor() const; + virtual void SetEncoderToPacketizerFrameTransformer( + rtc::scoped_refptr frame_transformer); + protected: ~RtpSenderInterface() override = default; }; @@ -119,6 +123,9 @@ PROXY_METHOD1(void, PROXY_CONSTMETHOD0(rtc::scoped_refptr, GetFrameEncryptor) PROXY_METHOD1(void, SetStreams, const std::vector&) +PROXY_METHOD1(void, + SetEncoderToPacketizerFrameTransformer, + rtc::scoped_refptr) END_PROXY_MAP() } // namespace webrtc diff --git a/call/BUILD.gn b/call/BUILD.gn index b6f8bac268..a2e8c05891 100644 --- a/call/BUILD.gn +++ b/call/BUILD.gn @@ -89,6 +89,7 @@ rtc_library("rtp_interfaces") { deps = [ "../api:array_view", "../api:fec_controller_api", + "../api:frame_transformer_interface", "../api:rtp_headers", "../api:rtp_parameters", "../api/crypto:options", @@ -293,6 +294,7 @@ rtc_library("video_stream_api") { ] deps = [ ":rtp_interfaces", + "../api:frame_transformer_interface", "../api:rtp_headers", "../api:rtp_parameters", "../api:transport_api", @@ -501,6 +503,7 @@ if (rtc_include_tests) { ] deps = [ ":rtp_interfaces", + "../api:frame_transformer_interface", "../api:libjingle_peerconnection_api", "../api/crypto:frame_encryptor_interface", "../api/crypto:options", diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc index 50c34f9762..fb684ca014 100644 --- a/call/rtp_transport_controller_send.cc +++ b/call/rtp_transport_controller_send.cc @@ -139,7 +139,8 @@ RtpVideoSenderInterface* RtpTransportControllerSend::CreateRtpVideoSender( const RtpSenderObservers& observers, RtcEventLog* event_log, std::unique_ptr fec_controller, - const RtpSenderFrameEncryptionConfig& frame_encryption_config) { + const RtpSenderFrameEncryptionConfig& frame_encryption_config, + rtc::scoped_refptr frame_transformer) { video_rtp_senders_.push_back(std::make_unique( clock_, suspended_ssrcs, states, rtp_config, rtcp_report_interval_ms, send_transport, observers, @@ -147,7 +148,7 @@ RtpVideoSenderInterface* RtpTransportControllerSend::CreateRtpVideoSender( // the parts of RtpTransportControllerSendInterface that are really used. this, event_log, &retransmission_rate_limiter_, std::move(fec_controller), frame_encryption_config.frame_encryptor, - frame_encryption_config.crypto_options)); + frame_encryption_config.crypto_options, std::move(frame_transformer))); return video_rtp_senders_.back().get(); } diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h index b07bea73d8..0e71cb652f 100644 --- a/call/rtp_transport_controller_send.h +++ b/call/rtp_transport_controller_send.h @@ -71,7 +71,8 @@ class RtpTransportControllerSend final const RtpSenderObservers& observers, RtcEventLog* event_log, std::unique_ptr fec_controller, - const RtpSenderFrameEncryptionConfig& frame_encryption_config) override; + const RtpSenderFrameEncryptionConfig& frame_encryption_config, + rtc::scoped_refptr frame_transformer) override; void DestroyRtpVideoSender( RtpVideoSenderInterface* rtp_video_sender) override; diff --git a/call/rtp_transport_controller_send_interface.h b/call/rtp_transport_controller_send_interface.h index b40aabdc2c..f073424968 100644 --- a/call/rtp_transport_controller_send_interface.h +++ b/call/rtp_transport_controller_send_interface.h @@ -21,6 +21,7 @@ #include "absl/types/optional.h" #include "api/crypto/crypto_options.h" #include "api/fec_controller.h" +#include "api/frame_transformer_interface.h" #include "api/rtc_event_log/rtc_event_log.h" #include "api/transport/bitrate_settings.h" #include "api/units/timestamp.h" @@ -110,7 +111,8 @@ class RtpTransportControllerSendInterface { const RtpSenderObservers& observers, RtcEventLog* event_log, std::unique_ptr fec_controller, - const RtpSenderFrameEncryptionConfig& frame_encryption_config) = 0; + const RtpSenderFrameEncryptionConfig& frame_encryption_config, + rtc::scoped_refptr frame_transformer) = 0; virtual void DestroyRtpVideoSender( RtpVideoSenderInterface* rtp_video_sender) = 0; diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index 8ec534e0c9..8938030e2f 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -126,7 +126,8 @@ std::vector CreateRtpStreamSenders( RateLimiter* retransmission_rate_limiter, OverheadObserver* overhead_observer, FrameEncryptorInterface* frame_encryptor, - const CryptoOptions& crypto_options) { + const CryptoOptions& crypto_options, + rtc::scoped_refptr frame_transformer) { RTC_DCHECK_GT(rtp_config.ssrcs.size(), 0); RtpRtcp::Configuration configuration; @@ -206,6 +207,7 @@ std::vector CreateRtpStreamSenders( !should_disable_red_and_ulpfec) { video_config.ulpfec_payload_type = rtp_config.ulpfec.ulpfec_payload_type; } + video_config.frame_transformer = std::move(frame_transformer); auto sender_video = std::make_unique(video_config); rtp_streams.emplace_back(std::move(rtp_rtcp), std::move(sender_video)); } @@ -291,7 +293,8 @@ RtpVideoSender::RtpVideoSender( RateLimiter* retransmission_limiter, std::unique_ptr fec_controller, FrameEncryptorInterface* frame_encryptor, - const CryptoOptions& crypto_options) + const CryptoOptions& crypto_options, + rtc::scoped_refptr frame_transformer) : send_side_bwe_with_overhead_( webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")), account_for_packetization_overhead_(!webrtc::field_trial::IsDisabled( @@ -318,7 +321,8 @@ RtpVideoSender::RtpVideoSender( retransmission_limiter, this, frame_encryptor, - crypto_options)), + crypto_options, + std::move(frame_transformer))), rtp_config_(rtp_config), codec_type_(GetVideoCodecType(rtp_config)), transport_(transport), diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index 620c975810..6c941f8acc 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -85,7 +85,8 @@ class RtpVideoSender : public RtpVideoSenderInterface, RateLimiter* retransmission_limiter, // move inside RtpTransport std::unique_ptr fec_controller, FrameEncryptorInterface* frame_encryptor, - const CryptoOptions& crypto_options); // move inside RtpTransport + const CryptoOptions& crypto_options, // move inside RtpTransport + rtc::scoped_refptr frame_transformer); ~RtpVideoSender() override; // RegisterProcessThread register |module_process_thread| with those objects diff --git a/call/rtp_video_sender_unittest.cc b/call/rtp_video_sender_unittest.cc index a7336dab6c..71bec5e7bb 100644 --- a/call/rtp_video_sender_unittest.cc +++ b/call/rtp_video_sender_unittest.cc @@ -151,7 +151,7 @@ class RtpVideoSenderTestFixture { &send_delay_stats_), &transport_controller_, &event_log_, &retransmission_rate_limiter_, std::make_unique(time_controller_.GetClock()), - nullptr, CryptoOptions{}); + nullptr, CryptoOptions{}, nullptr); } RtpVideoSenderTestFixture( const std::vector& ssrcs, diff --git a/call/test/mock_rtp_transport_controller_send.h b/call/test/mock_rtp_transport_controller_send.h index fad27b018f..afc8400f73 100644 --- a/call/test/mock_rtp_transport_controller_send.h +++ b/call/test/mock_rtp_transport_controller_send.h @@ -18,6 +18,7 @@ #include "api/crypto/crypto_options.h" #include "api/crypto/frame_encryptor_interface.h" +#include "api/frame_transformer_interface.h" #include "api/transport/bitrate_settings.h" #include "call/rtp_transport_controller_send_interface.h" #include "modules/pacing/packet_router.h" @@ -31,7 +32,7 @@ namespace webrtc { class MockRtpTransportControllerSend : public RtpTransportControllerSendInterface { public: - MOCK_METHOD9( + MOCK_METHOD10( CreateRtpVideoSender, RtpVideoSenderInterface*(std::map, const std::map&, @@ -41,7 +42,8 @@ class MockRtpTransportControllerSend const RtpSenderObservers&, RtcEventLog*, std::unique_ptr, - const RtpSenderFrameEncryptionConfig&)); + const RtpSenderFrameEncryptionConfig&, + rtc::scoped_refptr)); MOCK_METHOD1(DestroyRtpVideoSender, void(RtpVideoSenderInterface*)); MOCK_METHOD0(GetWorkerQueue, rtc::TaskQueue*()); MOCK_METHOD0(packet_router, PacketRouter*()); diff --git a/call/video_send_stream.h b/call/video_send_stream.h index 85d8019747..962578d60b 100644 --- a/call/video_send_stream.h +++ b/call/video_send_stream.h @@ -20,6 +20,7 @@ #include "absl/types/optional.h" #include "api/call/transport.h" #include "api/crypto/crypto_options.h" +#include "api/frame_transformer_interface.h" #include "api/rtp_parameters.h" #include "api/video/video_content_type.h" #include "api/video/video_frame.h" @@ -163,6 +164,8 @@ class VideoSendStream { // Per PeerConnection cryptography options. CryptoOptions crypto_options; + rtc::scoped_refptr frame_transformer; + private: // Access to the copy constructor is private to force use of the Copy() // method for those exceptional cases where we do use it. diff --git a/media/BUILD.gn b/media/BUILD.gn index 5c1ffe870c..f68f9911f3 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -86,6 +86,7 @@ rtc_library("rtc_media_base") { "..:webrtc_common", "../api:array_view", "../api:audio_options_api", + "../api:frame_transformer_interface", "../api:media_stream_interface", "../api:rtc_error", "../api:rtp_parameters", diff --git a/media/base/media_channel.cc b/media/base/media_channel.cc index 579cbc6b6f..29cf550655 100644 --- a/media/base/media_channel.cc +++ b/media/base/media_channel.cc @@ -49,6 +49,10 @@ void MediaChannel::SetFrameDecryptor( void MediaChannel::SetVideoCodecSwitchingEnabled(bool enabled) {} +void MediaChannel::SetEncoderToPacketizerFrameTransformer( + uint32_t ssrc, + rtc::scoped_refptr frame_transformer) {} + MediaSenderInfo::MediaSenderInfo() = default; MediaSenderInfo::~MediaSenderInfo() = default; diff --git a/media/base/media_channel.h b/media/base/media_channel.h index 90c33bd7a6..9631722ff6 100644 --- a/media/base/media_channel.h +++ b/media/base/media_channel.h @@ -22,6 +22,7 @@ #include "api/audio_options.h" #include "api/crypto/frame_decryptor_interface.h" #include "api/crypto/frame_encryptor_interface.h" +#include "api/frame_transformer_interface.h" #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/transport/media/media_transport_config.h" @@ -287,6 +288,10 @@ class MediaChannel : public sigslot::has_slots<> { uint32_t ssrc, const webrtc::RtpParameters& parameters) = 0; + virtual void SetEncoderToPacketizerFrameTransformer( + uint32_t ssrc, + rtc::scoped_refptr frame_transformer); + protected: bool DscpEnabled() const { return enable_dscp_; } diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc index 1b8e1374aa..3f43f7906b 100644 --- a/media/engine/webrtc_video_engine.cc +++ b/media/engine/webrtc_video_engine.cc @@ -2464,6 +2464,14 @@ void WebRtcVideoChannel::WebRtcVideoSendStream::FillBitrateInfo( bwe_info->actual_enc_bitrate += stats.media_bitrate_bps; } +void WebRtcVideoChannel::WebRtcVideoSendStream:: + SetEncoderToPacketizerFrameTransformer( + rtc::scoped_refptr + frame_transformer) { + RTC_DCHECK_RUN_ON(&thread_checker_); + parameters_.config.frame_transformer = std::move(frame_transformer); +} + void WebRtcVideoChannel::WebRtcVideoSendStream::RecreateWebRtcStream() { RTC_DCHECK_RUN_ON(&thread_checker_); if (stream_ != NULL) { @@ -3133,6 +3141,17 @@ void WebRtcVideoChannel::GenerateKeyFrame(uint32_t ssrc) { } } +void WebRtcVideoChannel::SetEncoderToPacketizerFrameTransformer( + uint32_t ssrc, + rtc::scoped_refptr frame_transformer) { + RTC_DCHECK_RUN_ON(&thread_checker_); + auto matching_stream = send_streams_.find(ssrc); + if (matching_stream != send_streams_.end()) { + matching_stream->second->SetEncoderToPacketizerFrameTransformer( + std::move(frame_transformer)); + } +} + // TODO(bugs.webrtc.org/8785): Consider removing max_qp as member of // EncoderStreamFactory and instead set this value individually for each stream // in the VideoEncoderConfig.simulcast_layers. diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h index e4506ad096..b1cbd1bae8 100644 --- a/media/engine/webrtc_video_engine.h +++ b/media/engine/webrtc_video_engine.h @@ -224,6 +224,11 @@ class WebRtcVideoChannel : public VideoMediaChannel, void ClearRecordableEncodedFrameCallback(uint32_t ssrc) override; void GenerateKeyFrame(uint32_t ssrc) override; + void SetEncoderToPacketizerFrameTransformer( + uint32_t ssrc, + rtc::scoped_refptr frame_transformer) + override; + private: class WebRtcVideoReceiveStream; @@ -336,6 +341,10 @@ class WebRtcVideoChannel : public VideoMediaChannel, VideoSenderInfo GetVideoSenderInfo(bool log_stats); void FillBitrateInfo(BandwidthEstimationInfo* bwe_info); + void SetEncoderToPacketizerFrameTransformer( + rtc::scoped_refptr + frame_transformer); + private: // Parameters needed to reconstruct the underlying stream. // webrtc::VideoSendStream doesn't support setting a lot of options on the diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index 067c83cab3..83e8ecbcd4 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -239,6 +239,7 @@ rtc_library("rtp_rtcp") { "..:module_fec_api", "../..:webrtc_common", "../../api:array_view", + "../../api:frame_transformer_interface", "../../api:function_view", "../../api:libjingle_peerconnection_api", "../../api:rtp_headers", diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h index b256f381d8..bb99b1ac27 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp.h +++ b/modules/rtp_rtcp/include/rtp_rtcp.h @@ -19,6 +19,8 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" +#include "api/frame_transformer_interface.h" +#include "api/scoped_refptr.h" #include "api/transport/webrtc_key_value_config.h" #include "api/video/video_bitrate_allocation.h" #include "modules/include/module.h" @@ -109,6 +111,8 @@ class RtpRtcp : public Module, public RtcpFeedbackSenderInterface { // Update network2 instead of pacer_exit field of video timing extension. bool populate_network2_timestamp = false; + rtc::scoped_refptr frame_transformer; + // E2EE Custom Video Frame Encryption FrameEncryptorInterface* frame_encryptor = nullptr; // Require all outgoing frames to be encrypted with a FrameEncryptor. diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index efc674c695..45298b81c2 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -52,9 +52,9 @@ constexpr int64_t kMaxUnretransmittableFrameIntervalMs = 33 * 4; // result recovered packets will be corrupt unless we also remove transport // sequence number during FEC calculation. // -// TODO(sukhanov): We need to find find better way to implement FEC with -// datagram transport, probably moving FEC to datagram integration layter. We -// should also remove special field trial once we switch datagram path from +// TODO(sukhanov): We need to find a better way to implement FEC with datagram +// transport, probably moving FEC to datagram integration layter. We should +// also remove special field trial once we switch datagram path from // RTCConfiguration flags to field trial and use the same field trial for FEC // workaround. const char kExcludeTransportSequenceNumberFromFecFieldTrial[] = @@ -275,7 +275,8 @@ RTPSenderVideo::RTPSenderVideo(const Config& config) config.field_trials ->Lookup(kExcludeTransportSequenceNumberFromFecFieldTrial) .find("Enabled") == 0), - absolute_capture_time_sender_(config.clock) {} + absolute_capture_time_sender_(config.clock), + frame_transformer_(config.frame_transformer) {} RTPSenderVideo::~RTPSenderVideo() {} diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index 5c9657e56f..58398803fa 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -18,6 +18,8 @@ #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include "api/array_view.h" +#include "api/frame_transformer_interface.h" +#include "api/scoped_refptr.h" #include "api/transport/rtp/dependency_descriptor.h" #include "api/video/video_codec_type.h" #include "api/video/video_frame_type.h" @@ -74,6 +76,7 @@ class RTPSenderVideo { absl::optional red_payload_type; absl::optional ulpfec_payload_type; const WebRtcKeyValueConfig* field_trials = nullptr; + rtc::scoped_refptr frame_transformer; }; explicit RTPSenderVideo(const Config& config); @@ -222,6 +225,8 @@ class RTPSenderVideo { const bool exclude_transport_sequence_number_from_fec_experiment_; AbsoluteCaptureTimeSender absolute_capture_time_sender_; + + const rtc::scoped_refptr frame_transformer_; }; } // namespace webrtc diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 8f6ef59900..c41f21a5a7 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -231,6 +231,7 @@ rtc_library("peerconnection") { "../api:audio_options_api", "../api:call_api", "../api:fec_controller_api", + "../api:frame_transformer_interface", "../api:ice_transport_factory", "../api:libjingle_peerconnection_api", "../api:media_stream_interface", @@ -624,7 +625,8 @@ if (rtc_include_tests) { "../api/video_codecs:video_codecs_api", "../call:call_interfaces", "../media:rtc_audio_video", - "../media:rtc_data", # TODO(phoglund): AFAIK only used for one sctp constant. + "../media:rtc_data", # TODO(phoglund): AFAIK only used for one sctp + # constant. "../media:rtc_media_base", "../media:rtc_media_tests_utils", "../modules/audio_processing", diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index 73cfcd045a..8a1fa79dd6 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc @@ -297,6 +297,9 @@ void RtpSenderBase::SetSsrc(uint32_t ssrc) { if (frame_encryptor_) { SetFrameEncryptor(frame_encryptor_); } + if (frame_transformer_) { + SetEncoderToPacketizerFrameTransformer(frame_transformer_); + } } void RtpSenderBase::Stop() { @@ -364,6 +367,17 @@ RTCError RtpSenderBase::DisableEncodingLayers( return result; } +void RtpSenderBase::SetEncoderToPacketizerFrameTransformer( + rtc::scoped_refptr frame_transformer) { + frame_transformer_ = std::move(frame_transformer); + if (media_channel_ && ssrc_ && !stopped_) { + worker_thread_->Invoke(RTC_FROM_HERE, [&] { + media_channel_->SetEncoderToPacketizerFrameTransformer( + ssrc_, frame_transformer_); + }); + } +} + LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(nullptr) {} LocalAudioSinkAdapter::~LocalAudioSinkAdapter() { diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index fcf8448e4c..1e0de22c5c 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -149,6 +149,9 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { // If the specified list is empty, this is a no-op. RTCError DisableEncodingLayers(const std::vector& rid) override; + void SetEncoderToPacketizerFrameTransformer( + rtc::scoped_refptr frame_transformer) override; + protected: // If |set_streams_observer| is not null, it is invoked when SetStreams() // is called. |set_streams_observer| is not owned by this object. If not @@ -197,6 +200,8 @@ class RtpSenderBase : public RtpSenderInternal, public ObserverInterface { std::vector disabled_rids_; SetStreamsObserver* set_streams_observer_ = nullptr; + + rtc::scoped_refptr frame_transformer_; }; // LocalAudioSinkAdapter receives data callback as a sink to the local diff --git a/video/video_send_stream_impl.cc b/video/video_send_stream_impl.cc index d52f3ba260..405de7cd3b 100644 --- a/video/video_send_stream_impl.cc +++ b/video/video_send_stream_impl.cc @@ -202,19 +202,20 @@ VideoSendStreamImpl::VideoSendStreamImpl( video_stream_encoder_(video_stream_encoder), encoder_feedback_(clock, config_->rtp.ssrcs, video_stream_encoder), bandwidth_observer_(transport->GetBandwidthObserver()), - rtp_video_sender_(transport_->CreateRtpVideoSender( - suspended_ssrcs, - suspended_payload_states, - config_->rtp, - config_->rtcp_report_interval_ms, - config_->send_transport, - CreateObservers(call_stats, - &encoder_feedback_, - stats_proxy_, - send_delay_stats), - event_log, - std::move(fec_controller), - CreateFrameEncryptionConfig(config_))), + rtp_video_sender_( + transport_->CreateRtpVideoSender(suspended_ssrcs, + suspended_payload_states, + config_->rtp, + config_->rtcp_report_interval_ms, + config_->send_transport, + CreateObservers(call_stats, + &encoder_feedback_, + stats_proxy_, + send_delay_stats), + event_log, + std::move(fec_controller), + CreateFrameEncryptionConfig(config_), + config->frame_transformer)), weak_ptr_factory_(this) { video_stream_encoder->SetFecControllerOverride(rtp_video_sender_); RTC_DCHECK_RUN_ON(worker_queue_);