Wire Sframe enable callback through sender and receiver constructors. This CL implements the C++ plumbing for the SFrame enable signal described in the WebRTC Encoded Transform spec (https://w3c.github.io/webrtc-encoded-transform/#attribute, Section 2.2). Per the spec, when an SFrame transform is set on an RTCRtpSender or RTCRtpReceiver, the setter must propagate [[useSFrame]] = true to the owning RTCRtpTransceiver and trigger SDP renegotiation. If the transceiver's [[useSFrame]] has already been negotiated to false, it must throw InvalidModificationError. This is modeled by wiring an on_sframe_enable_requested callback from each sender/receiver to RtpTransceiver::TryToEnableSframe(). When the application calls CreateSframeEncrypterOrError() (sender) or CreateSframeDecrypterOrError() (receiver), the callback fires, which sets sframe_enabled_ = true on the transceiver and triggers on_negotiation_needed_(). After negotiation completes, ApplySframeEnabled() locks the state — matching the spec's behavior where [[useSFrame]] becomes immutable once associated with a media description. Bug: webrtc:479862368 Change-Id: Ie5edd365aa4896bfda44cf34a2ab52dfe32bb441 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/459460 Reviewed-by: Philip Eliasson <philipel@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47536}
diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 1b7768f..6514d2f 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn
@@ -1749,6 +1749,7 @@ "//third_party/abseil-cpp/absl/algorithm:container", "//third_party/abseil-cpp/absl/base:nullability", "//third_party/abseil-cpp/absl/functional:any_invocable", + "//third_party/abseil-cpp/absl/functional:bind_front", "//third_party/abseil-cpp/absl/strings:string_view", ] } @@ -1845,6 +1846,8 @@ "../api:sframe_decrypter_interface", "../api:sframe_types", "../media:media_channel", + "../rtc_base:logging", + "../rtc_base:macromagic", "../rtc_base:threading", "../rtc_base/system:no_unique_address", "//third_party/abseil-cpp/absl/functional:any_invocable", @@ -1867,6 +1870,7 @@ "../api:frame_transformer_interface", "../api:make_ref_counted", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtp_parameters", "../api:rtp_receiver_interface", "../api:scoped_refptr", @@ -1899,6 +1903,7 @@ "../api:frame_transformer_interface", "../api:make_ref_counted", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtp_parameters", "../api:rtp_receiver_interface", "../api:scoped_refptr", @@ -3182,6 +3187,8 @@ "../api:rtp_transceiver_interface", "../api:scoped_refptr", "../api:set_remote_description_observer_interface", + "../api:sframe_encrypter_interface", + "../api:sframe_types", "../api/audio_codecs:builtin_audio_decoder_factory", "../api/audio_codecs:builtin_audio_encoder_factory", "../api/units:data_rate", @@ -3433,6 +3440,8 @@ "../api:rtp_parameters", "../api:rtp_receiver_interface", "../api:scoped_refptr", + "../api:sframe_encrypter_interface", + "../api:sframe_types", "../api/crypto:frame_decryptor_interface", "../api/crypto:frame_encryptor_interface", "../api/crypto:options",
diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc index ab3fbad..585719c6 100644 --- a/pc/audio_rtp_receiver.cc +++ b/pc/audio_rtp_receiver.cc
@@ -24,6 +24,7 @@ #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" @@ -44,10 +45,12 @@ Thread* worker_thread, absl::string_view receiver_id, std::vector<std::string> stream_ids, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VoiceMediaReceiveChannelInterface* voice_channel) : AudioRtpReceiver(worker_thread, receiver_id, CreateStreamsFromIds(std::move(stream_ids)), + std::move(enable_sframe_at_owner), voice_channel, RemoteAudioSource::OnAudioChannelGoneAction::kSurvive) {} @@ -60,6 +63,7 @@ : AudioRtpReceiver(worker_thread, receiver_id, streams, + nullptr, media_channel, RemoteAudioSource::OnAudioChannelGoneAction::kEnd) { RTC_DCHECK(!is_unified_plan); @@ -73,6 +77,7 @@ : AudioRtpReceiver(worker_thread, receiver_id, streams, + nullptr, media_channel, RemoteAudioSource::OnAudioChannelGoneAction::kSurvive) {} @@ -80,9 +85,10 @@ Thread* worker_thread, absl::string_view receiver_id, const std::vector<scoped_refptr<MediaStreamInterface>>& streams, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VoiceMediaReceiveChannelInterface* voice_channel, RemoteAudioSource::OnAudioChannelGoneAction source_gone_action) - : RtpReceiverBase(worker_thread), + : RtpReceiverBase(worker_thread, std::move(enable_sframe_at_owner)), id_(receiver_id), source_(make_ref_counted<RemoteAudioSource>(worker_thread, source_gone_action)),
diff --git a/pc/audio_rtp_receiver.h b/pc/audio_rtp_receiver.h index f89ab65..984a80c 100644 --- a/pc/audio_rtp_receiver.h +++ b/pc/audio_rtp_receiver.h
@@ -24,6 +24,7 @@ #include "api/frame_transformer_interface.h" #include "api/media_stream_interface.h" #include "api/media_types.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" @@ -55,6 +56,7 @@ AudioRtpReceiver(Thread* worker_thread, absl::string_view receiver_id, std::vector<std::string> stream_ids, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VoiceMediaReceiveChannelInterface* voice_channel = nullptr); // Note: This is a PlanB-only constructor. // TODO(https://crbug.com/webrtc/9480): Remove this when streams() is removed. @@ -134,6 +136,7 @@ Thread* worker_thread, absl::string_view receiver_id, const std::vector<scoped_refptr<MediaStreamInterface>>& streams, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VoiceMediaReceiveChannelInterface* media_channel, RemoteAudioSource::OnAudioChannelGoneAction source_gone_action);
diff --git a/pc/audio_rtp_receiver_unittest.cc b/pc/audio_rtp_receiver_unittest.cc index 6a69cd6..b296dd5 100644 --- a/pc/audio_rtp_receiver_unittest.cc +++ b/pc/audio_rtp_receiver_unittest.cc
@@ -44,10 +44,11 @@ protected: AudioRtpReceiverTest() : worker_thread_(Thread::Create()), - receiver_( - make_ref_counted<AudioRtpReceiver>(worker_thread_.get(), - std::string(), - std::vector<std::string>())) { + receiver_(make_ref_counted<AudioRtpReceiver>( + worker_thread_.get(), + std::string(), + std::vector<std::string>(), + /*enable_sframe_at_owner=*/nullptr)) { worker_thread_->Start(); EXPECT_CALL(receive_channel_, SetRawAudioSink(kSsrc, _)); EXPECT_CALL(receive_channel_, SetBaseMinimumPlayoutDelayMs(kSsrc, _)); @@ -124,7 +125,7 @@ MockVoiceMediaReceiveChannelInterface receive_channel; auto receiver = make_ref_counted<AudioRtpReceiver>( worker_thread.get(), std::string(), std::vector<std::string>(), - &receive_channel); + /*enable_sframe_at_owner=*/nullptr, &receive_channel); EXPECT_CALL(receive_channel, SetDefaultRawAudioSink(_)).Times(1); EXPECT_CALL(receive_channel, SetDefaultOutputVolume(kDefaultVolume)).Times(1);
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 9a3e98a..1b4e0c2 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc
@@ -1287,6 +1287,7 @@ auto audio_sender = AudioRtpSender::Create(env_, signaling_thread(), worker_thread(), CreateRandomUuid(), legacy_stats_.get(), nullptr, + /*enable_sframe_at_owner=*/nullptr, rtp_manager()->voice_media_send_channel()); new_sender = RtpSenderProxyWithInternal<RtpSenderInternal>::Create( signaling_thread(), audio_sender); @@ -1295,6 +1296,7 @@ } else if (kind == MediaStreamTrackInterface::kVideoKind) { auto video_sender = VideoRtpSender::Create( env_, signaling_thread(), worker_thread(), CreateRandomUuid(), nullptr, + /*enable_sframe_at_owner=*/nullptr, rtp_manager()->video_media_send_channel(), /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{});
diff --git a/pc/rtc_stats_collector_unittest.cc b/pc/rtc_stats_collector_unittest.cc index ef92482..57f7e84 100644 --- a/pc/rtc_stats_collector_unittest.cc +++ b/pc/rtc_stats_collector_unittest.cc
@@ -3974,6 +3974,7 @@ scoped_refptr<AudioRtpSender> sender = AudioRtpSender::Create( env_, pc_->signaling_thread(), pc_->worker_thread(), "sender_id", /*stats=*/nullptr, /*set_streams_observer=*/nullptr, + /*enable_sframe_at_owner=*/nullptr, /*media_channel=*/nullptr); {
diff --git a/pc/rtp_receiver.cc b/pc/rtp_receiver.cc index 33dddb8..0151622 100644 --- a/pc/rtp_receiver.cc +++ b/pc/rtp_receiver.cc
@@ -16,6 +16,7 @@ #include <utility> #include <vector> +#include "absl/functional/any_invocable.h" #include "api/media_stream_interface.h" #include "api/rtc_error.h" #include "api/scoped_refptr.h" @@ -47,12 +48,26 @@ return streams; } -RtpReceiverBase::RtpReceiverBase(Thread* worker_thread) - : worker_thread_(worker_thread) {} +RtpReceiverBase::RtpReceiverBase( + Thread* worker_thread, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner) + : worker_thread_(worker_thread), + enable_sframe_at_owner_(std::move(enable_sframe_at_owner)) {} RTCErrorOr<scoped_refptr<SframeDecrypterInterface>> RtpReceiverBase::CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) { RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + + if (!enable_sframe_at_owner_) { + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Receiver is not associated with a transceiver"); + } + + RTCError error = enable_sframe_at_owner_(); + if (!error.ok()) { + return error; + } + // TODO(bugs.webrtc.org/479862368): Create the internal Sframe decryption // pipeline and return a key management handle. return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h index 6a2b21d..8f8616c 100644 --- a/pc/rtp_receiver.h +++ b/pc/rtp_receiver.h
@@ -33,6 +33,7 @@ #include "media/base/media_channel.h" #include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread.h" +#include "rtc_base/thread_annotations.h" namespace webrtc { @@ -109,10 +110,16 @@ CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) override; protected: - explicit RtpReceiverBase(Thread* worker_thread); + explicit RtpReceiverBase( + Thread* worker_thread, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner); RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; Thread* const worker_thread_; + + private: + absl::AnyInvocable<RTCError()> enable_sframe_at_owner_ + RTC_GUARDED_BY(signaling_thread_checker_); }; } // namespace webrtc
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index 5fbb37f..49cdc86 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc
@@ -221,13 +221,15 @@ return false; } -RtpSenderBase::RtpSenderBase(const Environment& env, - Thread* signaling_thread, - Thread* worker_thread, - absl::string_view id, - MediaType media_type, - SetStreamsObserver* set_streams_observer, - MediaSendChannelInterface* media_channel) +RtpSenderBase::RtpSenderBase( + const Environment& env, + Thread* signaling_thread, + Thread* worker_thread, + absl::string_view id, + MediaType media_type, + SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, + MediaSendChannelInterface* media_channel) : env_(env), signaling_thread_(signaling_thread), worker_thread_(worker_thread), @@ -240,7 +242,8 @@ worker_thread_)), signaling_safety_( PendingTaskSafetyFlag::CreateAttachedToTaskQueue(/*alive=*/true, - signaling_thread_)) { + signaling_thread_)), + enable_sframe_at_owner_(std::move(enable_sframe_at_owner)) { RTC_DCHECK(worker_thread_); init_parameters_.encodings.emplace_back(); if (media_channel) { @@ -1071,6 +1074,17 @@ RtpSenderBase::CreateSframeEncrypterOrError( const SframeEncrypterInit& options) { RTC_DCHECK_RUN_ON(signaling_thread_); + + if (!enable_sframe_at_owner_) { + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Sender is not associated with a transceiver"); + } + + RTCError error = enable_sframe_at_owner_(); + if (!error.ok()) { + return error; + } + // TODO(bugs.webrtc.org/479862368): Implement Sframe encrypter creation. return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, "Sframe encrypter not yet implemented"); @@ -1114,25 +1128,29 @@ absl::string_view id, LegacyStatsCollectorInterface* stats, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel) { - return make_ref_counted<AudioRtpSender>(env, signaling_thread, worker_thread, - id, stats, set_streams_observer, - media_channel); + return make_ref_counted<AudioRtpSender>( + env, signaling_thread, worker_thread, id, stats, set_streams_observer, + std::move(enable_sframe_at_owner), media_channel); } -AudioRtpSender::AudioRtpSender(const Environment& env, - Thread* signaling_thread, - Thread* worker_thread, - absl::string_view id, - LegacyStatsCollectorInterface* stats, - SetStreamsObserver* set_streams_observer, - MediaSendChannelInterface* media_channel) +AudioRtpSender::AudioRtpSender( + const Environment& env, + Thread* signaling_thread, + Thread* worker_thread, + absl::string_view id, + LegacyStatsCollectorInterface* stats, + SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, + MediaSendChannelInterface* media_channel) : RtpSenderBase(env, signaling_thread, worker_thread, id, MediaType::AUDIO, set_streams_observer, + std::move(enable_sframe_at_owner), media_channel), legacy_stats_(stats), dtmf_sender_(DtmfSender::Create(signaling_thread, this)), @@ -1286,14 +1304,15 @@ Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel, const std::vector<RtpEncodingParameters>& init_send_encodings, bool simulcast_rejected, const std::vector<SimulcastLayer>& initial_simulcast_layers) { return make_ref_counted<VideoRtpSender>( env, signaling_thread, worker_thread, id, set_streams_observer, - media_channel, init_send_encodings, simulcast_rejected, - initial_simulcast_layers); + std::move(enable_sframe_at_owner), media_channel, init_send_encodings, + simulcast_rejected, initial_simulcast_layers); } VideoRtpSender::VideoRtpSender( @@ -1302,6 +1321,7 @@ Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel, const std::vector<RtpEncodingParameters>& init_send_encodings, bool simulcast_rejected, @@ -1312,6 +1332,7 @@ id, MediaType::VIDEO, set_streams_observer, + std::move(enable_sframe_at_owner), media_channel) { set_init_send_encodings( CalculateInitialEncodings(init_parameters_.encodings, init_send_encodings,
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index d0b6f80..f39ac69 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h
@@ -273,6 +273,7 @@ absl::string_view id, MediaType media_type, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel); // TODO(bugs.webrtc.org/8694): Since SSRC == 0 is technically valid, figure @@ -352,6 +353,9 @@ scoped_refptr<PendingTaskSafetyFlag> worker_safety_; ScopedTaskSafety signaling_safety_; + + absl::AnyInvocable<RTCError()> enable_sframe_at_owner_ + RTC_GUARDED_BY(signaling_thread_); }; // LocalAudioSinkAdapter receives data callback as a sink to the local @@ -410,6 +414,7 @@ absl::string_view id, LegacyStatsCollectorInterface* stats, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel); ~AudioRtpSender() override; @@ -434,6 +439,7 @@ absl::string_view id, LegacyStatsCollectorInterface* legacy_stats, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel); void SetSend() override; @@ -483,6 +489,7 @@ Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel, const std::vector<RtpEncodingParameters>& init_send_encodings, bool simulcast_rejected, @@ -505,6 +512,7 @@ Thread* worker_thread, absl::string_view id, SetStreamsObserver* set_streams_observer, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_channel, const std::vector<RtpEncodingParameters>& init_send_encodings, bool simulcast_rejected,
diff --git a/pc/rtp_sender_receiver_unittest.cc b/pc/rtp_sender_receiver_unittest.cc index ee13647..95f419a 100644 --- a/pc/rtp_sender_receiver_unittest.cc +++ b/pc/rtp_sender_receiver_unittest.cc
@@ -32,6 +32,8 @@ #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_encrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "api/task_queue/task_queue_base.h" #include "api/test/fake_frame_decryptor.h" #include "api/test/fake_frame_encryptor.h" @@ -212,7 +214,7 @@ audio_rtp_sender_ = AudioRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), audio_track_->id(), nullptr, set_streams_observer.get(), - voice_media_send_channel_.get()); + /*enable_sframe_at_owner=*/nullptr, voice_media_send_channel_.get()); }); ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); @@ -225,7 +227,8 @@ worker_thread_->BlockingCall([&]() { audio_rtp_sender_ = AudioRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - /*id=*/"", nullptr, nullptr, voice_media_send_channel_.get()); + /*id=*/"", nullptr, nullptr, + /*enable_sframe_at_owner=*/nullptr, voice_media_send_channel_.get()); }); } @@ -278,7 +281,7 @@ video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), video_track_->id(), set_streams_observer.get(), - video_media_send_channel(), + /*enable_sframe_at_owner=*/nullptr, video_media_send_channel(), /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); }); @@ -292,7 +295,8 @@ worker_thread_->BlockingCall([&]() { video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - /*id=*/"", nullptr, video_media_send_channel(), + /*id=*/"", nullptr, + /*enable_sframe_at_owner=*/nullptr, video_media_send_channel(), /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); }); @@ -331,7 +335,8 @@ void CreateVideoRtpReceiver( std::vector<scoped_refptr<MediaStreamInterface>> streams = {}) { video_rtp_receiver_ = make_ref_counted<VideoRtpReceiver>( - worker_thread_.get(), kVideoTrackId, streams); + worker_thread_.get(), kVideoTrackId, streams, + /*enable_sframe_at_owner=*/nullptr); worker_thread_->BlockingCall([this] { video_rtp_receiver_->SetMediaChannel(video_media_receive_channel()); }); @@ -354,7 +359,8 @@ uint32_t primary_ssrc = stream_params.first_ssrc(); video_rtp_receiver_ = make_ref_counted<VideoRtpReceiver>( - worker_thread_.get(), kVideoTrackId, streams); + worker_thread_.get(), kVideoTrackId, streams, + /*enable_sframe_at_owner=*/nullptr); worker_thread_->BlockingCall([this] { video_rtp_receiver_->SetMediaChannel(video_media_receive_channel()); }); @@ -504,7 +510,7 @@ const std::vector<std::string>& disabled_layers) { auto sender = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), "1", - nullptr, nullptr, + nullptr, /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); RtpParameters parameters; @@ -966,7 +972,8 @@ TEST_F(RtpSenderReceiverTest, AudioSenderCanSetParametersBeforeNegotiation) { audio_rtp_sender_ = AudioRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - /*id=*/"", nullptr, nullptr, nullptr); + /*id=*/"", nullptr, nullptr, + /*enable_sframe_at_owner=*/nullptr, nullptr); RtpParameters params = audio_rtp_sender_->GetParameters(); ASSERT_EQ(1u, params.encodings.size()); @@ -984,7 +991,8 @@ AudioSenderCanSetParametersAsyncBeforeNegotiation) { audio_rtp_sender_ = AudioRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - /*id=*/"", nullptr, nullptr, nullptr); + /*id=*/"", nullptr, nullptr, + /*enable_sframe_at_owner=*/nullptr, nullptr); std::optional<RTCError> result; RtpParameters params = audio_rtp_sender_->GetParameters(); @@ -1019,7 +1027,8 @@ std::make_unique<MockSetStreamsObserver>(); audio_rtp_sender_ = AudioRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - audio_track_->id(), nullptr, set_streams_observer.get(), nullptr); + audio_track_->id(), nullptr, set_streams_observer.get(), + /*enable_sframe_at_owner=*/nullptr, nullptr); ASSERT_TRUE(audio_rtp_sender_->SetTrack(audio_track_.get())); EXPECT_CALL(*set_streams_observer, OnSetStreams()); audio_rtp_sender_->SetStreams({local_stream_->id()}); @@ -1053,7 +1062,8 @@ AudioSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) { audio_rtp_sender_ = AudioRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - /*id=*/"", nullptr, nullptr, nullptr); + /*id=*/"", nullptr, nullptr, + /*enable_sframe_at_owner=*/nullptr, nullptr); RtpParameters params; RTCError result = audio_rtp_sender_->SetParameters(params); @@ -1232,27 +1242,7 @@ TEST_F(RtpSenderReceiverTest, VideoSenderCanSetParametersBeforeNegotiation) { video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), /*id=*/"", - nullptr, nullptr, - /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, - /*initial_simulcast_layers=*/{}); - - RtpParameters params = video_rtp_sender_->GetParameters(); - ASSERT_EQ(1u, params.encodings.size()); - params.encodings[0].max_bitrate_bps = 90000; - EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok()); - - params = video_rtp_sender_->GetParameters(); - EXPECT_TRUE(video_rtp_sender_->SetParameters(params).ok()); - EXPECT_EQ(params.encodings[0].max_bitrate_bps, 90000); - - DestroyVideoRtpSender(); -} - -TEST_F(RtpSenderReceiverTest, - VideoSenderCanSetParametersAsyncBeforeNegotiation) { - video_rtp_sender_ = VideoRtpSender::Create( - CreateEnvironment(), signaling_thread_, worker_thread_.get(), /*id=*/"", - nullptr, nullptr, + nullptr, /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); @@ -1286,7 +1276,8 @@ std::make_unique<MockSetStreamsObserver>(); video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - video_track_->id(), set_streams_observer.get(), nullptr, + video_track_->id(), set_streams_observer.get(), + /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); @@ -1332,7 +1323,8 @@ std::make_unique<MockSetStreamsObserver>(); video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - video_track_->id(), set_streams_observer.get(), nullptr, + video_track_->id(), set_streams_observer.get(), + /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); @@ -1397,7 +1389,7 @@ std::make_unique<MockSetStreamsObserver>(); auto video_rtp_sender = VideoRtpSender::Create( env, thread, thread, video_track->id(), set_streams_observer.get(), - nullptr, + /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); @@ -1436,7 +1428,7 @@ VideoSenderMustCallGetParametersBeforeSetParametersBeforeNegotiation) { video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), /*id=*/"", - nullptr, nullptr, + nullptr, /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); @@ -1851,7 +1843,8 @@ video_track_->set_content_hint(VideoTrackInterface::ContentHint::kDetailed); video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - video_track_->id(), set_streams_observer.get(), nullptr, + video_track_->id(), set_streams_observer.get(), + /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); ASSERT_TRUE(video_rtp_sender_->SetTrack(video_track_.get())); @@ -2061,7 +2054,8 @@ AddVideoTrack(); video_rtp_sender_ = VideoRtpSender::Create( CreateEnvironment(), signaling_thread_, worker_thread_.get(), - video_track_->id(), nullptr, nullptr, + video_track_->id(), nullptr, + /*enable_sframe_at_owner=*/nullptr, nullptr, /*init_send_encodings=*/{}, /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); video_rtp_sender_->SetStreams({"1", "2", "1"}); @@ -2130,4 +2124,138 @@ RtpSenderReceiverTest, ::testing::ValuesIn(kDisableSimulcastLayersParameters)); +TEST_F(RtpSenderReceiverTest, AudioSenderCreateSframeEncrypterInvokesCallback) { + bool callback_called = false; + worker_thread_->BlockingCall([&]() { + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, nullptr, + [&callback_called]() -> RTCError { + callback_called = true; + return RTCError::OK(); + }, + nullptr); + }); + + SframeEncrypterInit options{SframeMode::kPerFrame, + SframeCipherSuite::kAes128GcmSha256_128}; + audio_rtp_sender_->CreateSframeEncrypterOrError(options); + EXPECT_TRUE(callback_called); +} + +TEST_F(RtpSenderReceiverTest, VideoSenderCreateSframeEncrypterInvokesCallback) { + bool callback_called = false; + worker_thread_->BlockingCall([&]() { + video_rtp_sender_ = VideoRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, + [&callback_called]() -> RTCError { + callback_called = true; + return RTCError::OK(); + }, + nullptr, /*init_send_encodings=*/{}, + /*simulcast_rejected=*/false, /*initial_simulcast_layers=*/{}); + }); + + SframeEncrypterInit options{SframeMode::kPerFrame, + SframeCipherSuite::kAes128GcmSha256_128}; + video_rtp_sender_->CreateSframeEncrypterOrError(options); + EXPECT_TRUE(callback_called); +} + +TEST_F(RtpSenderReceiverTest, + SenderCreateSframeEncrypterPropagatesCallbackError) { + worker_thread_->BlockingCall([&]() { + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, nullptr, + []() -> RTCError { + return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Rejected for testing"); + }, + nullptr); + }); + + SframeEncrypterInit options{SframeMode::kPerFrame, + SframeCipherSuite::kAes128GcmSha256_128}; + auto result = audio_rtp_sender_->CreateSframeEncrypterOrError(options); + EXPECT_FALSE(result.ok()); + EXPECT_EQ(result.error().type(), RTCErrorType::UNSUPPORTED_OPERATION); + EXPECT_STREQ(result.error().message(), "Rejected for testing"); +} + +TEST_F(RtpSenderReceiverTest, + SenderCreateSframeEncrypterFailsWithNullCallback) { + worker_thread_->BlockingCall([&]() { + audio_rtp_sender_ = AudioRtpSender::Create( + CreateEnvironment(), signaling_thread_, worker_thread_.get(), + /*id=*/"", nullptr, nullptr, + /*enable_sframe_at_owner=*/nullptr, nullptr); + }); + + SframeEncrypterInit options{SframeMode::kPerFrame, + SframeCipherSuite::kAes128GcmSha256_128}; + auto result = audio_rtp_sender_->CreateSframeEncrypterOrError(options); + EXPECT_FALSE(result.ok()); + EXPECT_EQ(result.error().type(), RTCErrorType::INTERNAL_ERROR); +} + +TEST_F(RtpSenderReceiverTest, + AudioReceiverCreateSframeDecrypterInvokesCallback) { + bool callback_called = false; + auto receiver = make_ref_counted<AudioRtpReceiver>( + worker_thread_.get(), kAudioTrackId, std::vector<std::string>(), + [&callback_called]() -> RTCError { + callback_called = true; + return RTCError::OK(); + }); + + receiver->CreateSframeDecrypterOrError( + SframeCipherSuite::kAes128GcmSha256_128); + EXPECT_TRUE(callback_called); +} + +TEST_F(RtpSenderReceiverTest, + VideoReceiverCreateSframeDecrypterInvokesCallback) { + bool callback_called = false; + auto receiver = make_ref_counted<VideoRtpReceiver>( + worker_thread_.get(), kVideoTrackId, std::vector<std::string>(), + [&callback_called]() -> RTCError { + callback_called = true; + return RTCError::OK(); + }); + + receiver->CreateSframeDecrypterOrError( + SframeCipherSuite::kAes128GcmSha256_128); + EXPECT_TRUE(callback_called); +} + +TEST_F(RtpSenderReceiverTest, + ReceiverCreateSframeDecrypterPropagatesCallbackError) { + auto receiver = make_ref_counted<AudioRtpReceiver>( + worker_thread_.get(), kAudioTrackId, std::vector<std::string>(), + []() -> RTCError { + return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Rejected for testing"); + }); + + auto result = receiver->CreateSframeDecrypterOrError( + SframeCipherSuite::kAes128GcmSha256_128); + EXPECT_FALSE(result.ok()); + EXPECT_EQ(result.error().type(), RTCErrorType::UNSUPPORTED_OPERATION); + EXPECT_STREQ(result.error().message(), "Rejected for testing"); +} + +TEST_F(RtpSenderReceiverTest, + ReceiverCreateSframeDecrypterFailsWithNullCallback) { + auto receiver = make_ref_counted<AudioRtpReceiver>( + worker_thread_.get(), kAudioTrackId, std::vector<std::string>(), + /*enable_sframe_at_owner=*/nullptr); + + auto result = receiver->CreateSframeDecrypterOrError( + SframeCipherSuite::kAes128GcmSha256_128); + EXPECT_FALSE(result.ok()); + EXPECT_EQ(result.error().type(), RTCErrorType::INTERNAL_ERROR); +} + } // namespace webrtc
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index 12db4ac..bc2a937 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc
@@ -23,6 +23,7 @@ #include "absl/algorithm/container.h" #include "absl/base/nullability.h" #include "absl/functional/any_invocable.h" +#include "absl/functional/bind_front.h" #include "absl/strings/string_view.h" #include "api/audio_options.h" #include "api/crypto/crypto_options.h" @@ -160,6 +161,7 @@ LegacyStatsCollectorInterface* legacy_stats, RtpSenderBase::SetStreamsObserver* set_streams_observer, absl::string_view sender_id, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, MediaSendChannelInterface* media_send_channel, const std::vector<RtpEncodingParameters>& init_send_encodings, bool simulcast_rejected, @@ -170,6 +172,7 @@ AudioRtpSender::Create( env, context->signaling_thread(), context->worker_thread(), sender_id, legacy_stats, set_streams_observer, + std::move(enable_sframe_at_owner), static_cast<VoiceMediaSendChannelInterface*>(media_send_channel))); } RTC_DCHECK_EQ(media_type, MediaType::VIDEO); @@ -177,7 +180,7 @@ context->signaling_thread(), VideoRtpSender::Create( env, context->signaling_thread(), context->worker_thread(), sender_id, - set_streams_observer, + set_streams_observer, std::move(enable_sframe_at_owner), static_cast<VideoMediaSendChannelInterface*>(media_send_channel), init_send_encodings, simulcast_rejected, initial_simulcast_layers)); } @@ -201,11 +204,13 @@ CreateReceiverOfType(Thread* signaling_thread, Thread* worker_thread, absl::string_view receiver_id, - MediaReceiveChannelInterface* receive_channel) { + MediaReceiveChannelInterface* receive_channel, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner) { return RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create( signaling_thread, worker_thread, make_ref_counted<RtpReceiverT>( worker_thread, receiver_id, std::vector<std::string>(), + std::move(enable_sframe_at_owner), static_cast<ReceiveInterface*>(receive_channel))); } @@ -214,16 +219,19 @@ Thread* signaling_thread, Thread* worker_thread, absl::string_view receiver_id, - MediaReceiveChannelInterface* receive_channel) { + MediaReceiveChannelInterface* receive_channel, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner) { if (media_type == MediaType::AUDIO) { return CreateReceiverOfType<AudioRtpReceiver, VoiceMediaReceiveChannelInterface>( - signaling_thread, worker_thread, receiver_id, receive_channel); + signaling_thread, worker_thread, receiver_id, receive_channel, + std::move(enable_sframe_at_owner)); } RTC_DCHECK_EQ(media_type, MediaType::VIDEO); return CreateReceiverOfType<VideoRtpReceiver, VideoMediaReceiveChannelInterface>( - signaling_thread, worker_thread, receiver_id, receive_channel); + signaling_thread, worker_thread, receiver_id, receive_channel, + std::move(enable_sframe_at_owner)); } std::pair<std::unique_ptr<MediaSendChannelInterface>, @@ -427,8 +435,10 @@ std::move(encoder_switch_callback)); auto sender = CreateSender( media_type_, env_, context_, legacy_stats_, set_streams_observer_, - sender_id, channels.first.get(), init_send_encodings, - simulcast_rejected, initial_simulcast_layers); + sender_id, + absl::bind_front(&RtpTransceiver::TryToEnableSframe, this), + channels.first.get(), init_send_encodings, simulcast_rejected, + initial_simulcast_layers); return ScopedOperationsBatcher::FinalizerTask( [this, channels = std::move(channels), sender = std::move(sender), track, stream_ids, init_send_encodings, receiver_id]() mutable { @@ -444,7 +454,8 @@ media_type_, context_->signaling_thread(), context_->worker_thread(), receiver_id.empty() ? CreateRandomUuid() : receiver_id, - owned_receive_channel_.get())); + owned_receive_channel_.get(), + absl::bind_front(&RtpTransceiver::TryToEnableSframe, this))); }); }); } @@ -795,8 +806,9 @@ RTC_DCHECK_RUN_ON(context()->worker_thread()); senders_.push_back(CreateSender( media_type_, env_, context_, legacy_stats_, set_streams_observer_, - sender_id, channel_ ? channel_->media_send_channel() : nullptr, - send_encodings, false, {})); + sender_id, /*enable_sframe_at_owner=*/nullptr, + channel_ ? channel_->media_send_channel() : nullptr, send_encodings, + false, {})); }); ConfigureSender(senders_.back(), track.get(), stream_ids, send_encodings, codec_vendor());
diff --git a/pc/rtp_transmission_manager.cc b/pc/rtp_transmission_manager.cc index e0ecad9..a40d287 100644 --- a/pc/rtp_transmission_manager.cc +++ b/pc/rtp_transmission_manager.cc
@@ -511,7 +511,8 @@ // TODO(https://crbug.com/webrtc/9480): When we remove remote_streams(), use // the constructor taking stream IDs instead. auto video_receiver = make_ref_counted<VideoRtpReceiver>( - worker_thread(), remote_sender_info.sender_id, streams); + worker_thread(), remote_sender_info.sender_id, streams, + /*enable_sframe_at_owner=*/nullptr); auto task = video_receiver->GetSetupForMediaChannel( remote_sender_info.sender_id == kDefaultVideoSenderId
diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc index 250caa9..5d3bf2b 100644 --- a/pc/video_rtp_receiver.cc +++ b/pc/video_rtp_receiver.cc
@@ -24,6 +24,7 @@ #include "api/frame_transformer_interface.h" #include "api/make_ref_counted.h" #include "api/media_stream_interface.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" @@ -47,18 +48,21 @@ Thread* worker_thread, absl::string_view receiver_id, std::vector<std::string> stream_ids, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VideoMediaReceiveChannelInterface* media_channel) : VideoRtpReceiver(worker_thread, receiver_id, CreateStreamsFromIds(std::move(stream_ids)), + std::move(enable_sframe_at_owner), media_channel) {} VideoRtpReceiver::VideoRtpReceiver( Thread* worker_thread, absl::string_view receiver_id, const std::vector<scoped_refptr<MediaStreamInterface>>& streams, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VideoMediaReceiveChannelInterface* media_channel) - : RtpReceiverBase(worker_thread), + : RtpReceiverBase(worker_thread, std::move(enable_sframe_at_owner)), id_(receiver_id), media_channel_(media_channel), source_(make_ref_counted<VideoRtpTrackSource>(&source_callback_)),
diff --git a/pc/video_rtp_receiver.h b/pc/video_rtp_receiver.h index 552539d..67cafd1 100644 --- a/pc/video_rtp_receiver.h +++ b/pc/video_rtp_receiver.h
@@ -24,6 +24,7 @@ #include "api/frame_transformer_interface.h" #include "api/media_stream_interface.h" #include "api/media_types.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" @@ -49,6 +50,7 @@ VideoRtpReceiver(Thread* worker_thread, absl::string_view receiver_id, std::vector<std::string> streams_ids, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VideoMediaReceiveChannelInterface* media_channel = nullptr); // TODO(hbos): Remove this when streams() is removed. // https://crbug.com/webrtc/9480 @@ -58,6 +60,7 @@ Thread* worker_thread, absl::string_view receiver_id, const std::vector<scoped_refptr<MediaStreamInterface>>& streams, + absl::AnyInvocable<RTCError()> enable_sframe_at_owner, VideoMediaReceiveChannelInterface* media_channel = nullptr); ~VideoRtpReceiver() override;
diff --git a/pc/video_rtp_receiver_unittest.cc b/pc/video_rtp_receiver_unittest.cc index ed73fac..cda5869 100644 --- a/pc/video_rtp_receiver_unittest.cc +++ b/pc/video_rtp_receiver_unittest.cc
@@ -83,7 +83,8 @@ receiver_(make_ref_counted<VideoRtpReceiver>( worker_thread_.get(), "receiver", - std::vector<std::string>({"stream"}))) { + std::vector<std::string>({"stream"}), + /*enable_sframe_at_owner=*/nullptr)) { worker_thread_->Start(); SetMediaChannel(&channel_); }