Sframe API support for sender and receiver This CL introduces the public API surface for Sframe (Secure Frame) encryption and decryption on RTP senders and receivers. The actual pipeline wiring is deferred to a follow-up CL; all factory methods currently return nullptr. New API headers (api/sframe/): - sframe_types.h: shared enums SframeMode (per-frame / per-packet) and SframeCipherSuite (AES-CTR-HMAC variants, AES-GCM-128/256). - sframe_encrypter_interface.h: SframeEncrypterInit config struct and SframeEncrypterInterface for sender-side key management (SetEncryptionKey). - sframe_decrypter_interface.h: SframeDecrypterInit config struct and SframeDecrypterInterface for receiver-side key management (AddDecryptionKey, RemoveDecryptionKey). Interface changes: - RtpSenderInterface gains CreateSframeEncrypter(SframeEncrypterInit) - RtpReceiverInterface gains CreateSframeDecrypter(SframeDecrypterInit) - Both proxy files updated with corresponding PROXY_METHOD entries. New RtpReceiverBase class introduced inheriting RtpReceiverInternal, providing CreateSframeDecrypter. This mirrors the existing RtpSenderBase pattern. AudioRtpReceiver and VideoRtpReceiver now inherit RtpReceiverBase instead of RtpReceiverInternal, eliminating future code duplication. Bug: webrtc:479862368 Change-Id: I2d62b61f2d10325d8dca9f15f08a0eb0509eed01 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/452260 Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#47224}
diff --git a/api/BUILD.gn b/api/BUILD.gn index d7587e6..d7f763f 100644 --- a/api/BUILD.gn +++ b/api/BUILD.gn
@@ -261,6 +261,31 @@ deps = [ ":ref_count" ] } +rtc_library("sframe_types") { + visibility = [ "*" ] + sources = [ "sframe/sframe_types.h" ] +} + +rtc_library("sframe_encrypter_interface") { + visibility = [ "*" ] + sources = [ "sframe/sframe_encrypter_interface.h" ] + deps = [ + ":ref_count", + ":rtc_error", + ":sframe_types", + ] +} + +rtc_library("sframe_decrypter_interface") { + visibility = [ "*" ] + sources = [ "sframe/sframe_decrypter_interface.h" ] + deps = [ + ":ref_count", + ":rtc_error", + ":sframe_types", + ] +} + rtc_library("rtp_sender_interface") { visibility = [ "*" ] @@ -277,6 +302,7 @@ ":rtc_error", ":rtp_parameters", ":scoped_refptr", + ":sframe_encrypter_interface", "../rtc_base:checks", "../rtc_base/system:rtc_export", "crypto:frame_encryptor_interface", @@ -477,8 +503,12 @@ ":frame_transformer_interface", ":media_stream_interface", ":ref_count", + ":rtc_error", ":rtp_parameters", ":scoped_refptr", + ":sframe_decrypter_interface", + ":sframe_types", + "../rtc_base:checks", "../rtc_base/system:rtc_export", "crypto:frame_decryptor_interface", "transport/rtp:rtp_source", @@ -599,6 +629,9 @@ ":sctp_transport_interface", ":sequence_checker", ":set_local_description_observer_interface", + ":sframe_decrypter_interface", + ":sframe_encrypter_interface", + ":sframe_types", ":turn_customizer", "../call:rtp_interfaces", "../media:media_engine",
diff --git a/api/rtp_receiver_interface.h b/api/rtp_receiver_interface.h index 9ac347b..7dae849 100644 --- a/api/rtp_receiver_interface.h +++ b/api/rtp_receiver_interface.h
@@ -25,9 +25,13 @@ #include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/ref_count.h" +#include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "api/transport/rtp/rtp_source.h" +#include "rtc_base/checks.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -131,6 +135,17 @@ void SetFrameTransformer( scoped_refptr<FrameTransformerInterface> frame_transformer) override; + // Creates an internal Sframe decrypter and returns a handle for key + // management. + // Default implementation of CreateSframeDecrypterOrError. + // TODO: issues.webrtc.org/479862368 - make pure virtual when all + // implementations are updated + virtual RTCErrorOr<scoped_refptr<SframeDecrypterInterface>> + CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) { + RTC_DCHECK_NOTREACHED(); + return RTCError(); + } + protected: ~RtpReceiverInterface() override = default; };
diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h index be83928..5ea6efe 100644 --- a/api/rtp_sender_interface.h +++ b/api/rtp_sender_interface.h
@@ -31,7 +31,9 @@ #include "api/rtc_error.h" #include "api/rtp_parameters.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/video_codecs/video_encoder_factory.h" +#include "rtc_base/checks.h" #include "rtc_base/system/rtc_export.h" namespace webrtc { @@ -135,6 +137,17 @@ void SetFrameTransformer(scoped_refptr<FrameTransformerInterface> /* frame_transformer */) override {} + // Creates an internal Sframe encrypter and returns a handle for key + // management. + // Default implementation of CreateSframeEncrypterOrError. + // TODO: bugs.webrtc.org/479862368 - remove when all implementations are + // updated + virtual RTCErrorOr<scoped_refptr<SframeEncrypterInterface>> + CreateSframeEncrypterOrError(const SframeEncrypterInit& options) { + RTC_DCHECK_NOTREACHED(); + return RTCError(); + } + // TODO(crbug.com/1354101): make pure virtual again after Chrome roll. virtual RTCError GenerateKeyFrame(const std::vector<std::string>& rids) { return RTCError::OK();
diff --git a/api/sframe/sframe_decrypter_interface.h b/api/sframe/sframe_decrypter_interface.h new file mode 100644 index 0000000..7ad85bd --- /dev/null +++ b/api/sframe/sframe_decrypter_interface.h
@@ -0,0 +1,36 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_SFRAME_SFRAME_DECRYPTER_INTERFACE_H_ +#define API_SFRAME_SFRAME_DECRYPTER_INTERFACE_H_ + +#include <cstdint> +#include <span> + +#include "api/ref_count.h" +#include "api/rtc_error.h" + +namespace webrtc { + +// Key management handle for Sframe receiver decryption. +class SframeDecrypterInterface : public RefCountInterface { + public: + virtual RTCError AddDecryptionKey(uint64_t key_id, + std::span<const uint8_t> key_material) = 0; + + virtual RTCError RemoveDecryptionKey(uint64_t key_id) = 0; + + protected: + ~SframeDecrypterInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_SFRAME_SFRAME_DECRYPTER_INTERFACE_H_
diff --git a/api/sframe/sframe_encrypter_interface.h b/api/sframe/sframe_encrypter_interface.h new file mode 100644 index 0000000..60babc3 --- /dev/null +++ b/api/sframe/sframe_encrypter_interface.h
@@ -0,0 +1,40 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_SFRAME_SFRAME_ENCRYPTER_INTERFACE_H_ +#define API_SFRAME_SFRAME_ENCRYPTER_INTERFACE_H_ + +#include <cstdint> +#include <span> + +#include "api/ref_count.h" +#include "api/rtc_error.h" +#include "api/sframe/sframe_types.h" + +namespace webrtc { + +struct SframeEncrypterInit { + SframeMode mode; + SframeCipherSuite cipher_suite; +}; + +// Key management handle for Sframe sender encryption. +class SframeEncrypterInterface : public RefCountInterface { + public: + virtual RTCError SetEncryptionKey(uint64_t key_id, + std::span<const uint8_t> key_material) = 0; + + protected: + ~SframeEncrypterInterface() override = default; +}; + +} // namespace webrtc + +#endif // API_SFRAME_SFRAME_ENCRYPTER_INTERFACE_H_
diff --git a/api/sframe/sframe_types.h b/api/sframe/sframe_types.h new file mode 100644 index 0000000..2414bf3 --- /dev/null +++ b/api/sframe/sframe_types.h
@@ -0,0 +1,31 @@ +/* + * Copyright 2026 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef API_SFRAME_SFRAME_TYPES_H_ +#define API_SFRAME_SFRAME_TYPES_H_ + +namespace webrtc { + +enum class SframeMode { + kPerFrame, + kPerPacket, +}; + +enum class SframeCipherSuite { + kAes128CtrHmacSha256_80, + kAes128CtrHmacSha256_64, + kAes128CtrHmacSha256_32, + kAes128GcmSha256_128, + kAes256GcmSha512_128, +}; + +} // namespace webrtc + +#endif // API_SFRAME_SFRAME_TYPES_H_
diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 88287d5..8a57696 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn
@@ -547,9 +547,12 @@ "../api:dtls_transport_interface", "../api:frame_transformer_interface", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtp_parameters", "../api:rtp_receiver_interface", "../api:scoped_refptr", + "../api:sframe_decrypter_interface", + "../api:sframe_types", "../api/crypto:frame_decryptor_interface", "../api/transport/rtp:rtp_source", ] @@ -568,6 +571,7 @@ "../api:rtp_parameters", "../api:rtp_sender_interface", "../api:scoped_refptr", + "../api:sframe_encrypter_interface", "../api/crypto:frame_encryptor_interface", "../api/video_codecs:video_codecs_api", ] @@ -1806,10 +1810,15 @@ ":media_stream_proxy", "../api:dtls_transport_interface", "../api:media_stream_interface", + "../api:rtc_error", "../api:rtp_receiver_interface", "../api:scoped_refptr", + "../api:sequence_checker", + "../api:sframe_decrypter_interface", + "../api:sframe_types", "../media:media_channel", "../rtc_base:threading", + "../rtc_base/system:no_unique_address", "//third_party/abseil-cpp/absl/functional:any_invocable", ] } @@ -1841,7 +1850,6 @@ "../rtc_base:checks", "../rtc_base:macromagic", "../rtc_base:threading", - "../rtc_base/system:no_unique_address", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -1876,7 +1884,6 @@ "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:threading", - "../rtc_base/system:no_unique_address", "//third_party/abseil-cpp/absl/functional:any_invocable", "//third_party/abseil-cpp/absl/strings:string_view", ] @@ -2006,6 +2013,7 @@ "../api:rtp_sender_interface", "../api:scoped_refptr", "../api:sequence_checker", + "../api:sframe_encrypter_interface", "../api/crypto:frame_encryptor_interface", "../api/environment", "../api/task_queue",
diff --git a/pc/audio_rtp_receiver.cc b/pc/audio_rtp_receiver.cc index b90e70a..ab3fbad 100644 --- a/pc/audio_rtp_receiver.cc +++ b/pc/audio_rtp_receiver.cc
@@ -34,6 +34,7 @@ #include "pc/audio_track.h" #include "pc/media_stream_track_proxy.h" #include "pc/remote_audio_source.h" +#include "pc/rtp_receiver.h" #include "rtc_base/checks.h" #include "rtc_base/thread.h" @@ -81,7 +82,7 @@ const std::vector<scoped_refptr<MediaStreamInterface>>& streams, VoiceMediaReceiveChannelInterface* voice_channel, RemoteAudioSource::OnAudioChannelGoneAction source_gone_action) - : worker_thread_(worker_thread), + : RtpReceiverBase(worker_thread), 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 a8b07a0..f89ab65 100644 --- a/pc/audio_rtp_receiver.h +++ b/pc/audio_rtp_receiver.h
@@ -36,7 +36,6 @@ #include "pc/media_stream_track_proxy.h" #include "pc/remote_audio_source.h" #include "pc/rtp_receiver.h" -#include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" @@ -44,7 +43,7 @@ class AudioRtpReceiver : public ObserverInterface, public AudioSourceInterface::AudioObserver, - public RtpReceiverInternal { + public RtpReceiverBase { public: // The constructor supports optionally passing the voice channel to the // instance at construction time without having to call `SetMediaChannel()` @@ -147,8 +146,6 @@ void Reconfigure(bool track_enabled) RTC_RUN_ON(worker_thread_); void SetOutputVolume_w(double volume) RTC_RUN_ON(worker_thread_); - RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; - Thread* const worker_thread_; const std::string id_; const scoped_refptr<RemoteAudioSource> source_; const scoped_refptr<AudioTrackProxyWithInternal<AudioTrack>> track_;
diff --git a/pc/rtp_receiver.cc b/pc/rtp_receiver.cc index 31a20df..33dddb8 100644 --- a/pc/rtp_receiver.cc +++ b/pc/rtp_receiver.cc
@@ -17,7 +17,11 @@ #include <vector> #include "api/media_stream_interface.h" +#include "api/rtc_error.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "pc/media_stream.h" #include "pc/media_stream_proxy.h" #include "rtc_base/thread.h" @@ -43,4 +47,16 @@ return streams; } +RtpReceiverBase::RtpReceiverBase(Thread* worker_thread) + : worker_thread_(worker_thread) {} + +RTCErrorOr<scoped_refptr<SframeDecrypterInterface>> +RtpReceiverBase::CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) { + RTC_DCHECK_RUN_ON(&signaling_thread_checker_); + // TODO(bugs.webrtc.org/479862368): Create the internal Sframe decryption + // pipeline and return a key management handle. + return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Sframe decrypter not yet implemented"); +} + } // namespace webrtc
diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h index 45399b6..6a2b21d 100644 --- a/pc/rtp_receiver.h +++ b/pc/rtp_receiver.h
@@ -24,9 +24,15 @@ #include "absl/functional/any_invocable.h" #include "api/dtls_transport_interface.h" #include "api/media_stream_interface.h" +#include "api/rtc_error.h" #include "api/rtp_receiver_interface.h" #include "api/scoped_refptr.h" +#include "api/sequence_checker.h" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "media/base/media_channel.h" +#include "rtc_base/system/no_unique_address.h" +#include "rtc_base/thread.h" namespace webrtc { @@ -97,6 +103,18 @@ std::vector<std::string> stream_ids); }; +class RtpReceiverBase : public RtpReceiverInternal { + public: + RTCErrorOr<scoped_refptr<SframeDecrypterInterface>> + CreateSframeDecrypterOrError(SframeCipherSuite cipher_suite) override; + + protected: + explicit RtpReceiverBase(Thread* worker_thread); + + RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; + Thread* const worker_thread_; +}; + } // namespace webrtc #endif // PC_RTP_RECEIVER_H_
diff --git a/pc/rtp_receiver_proxy.h b/pc/rtp_receiver_proxy.h index 223b307..2022cbf 100644 --- a/pc/rtp_receiver_proxy.h +++ b/pc/rtp_receiver_proxy.h
@@ -20,9 +20,12 @@ #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" +#include "api/sframe/sframe_decrypter_interface.h" +#include "api/sframe/sframe_types.h" #include "api/transport/rtp/rtp_source.h" #include "pc/proxy.h" @@ -55,6 +58,9 @@ PROXY_SECONDARY_METHOD1(void, SetFrameTransformer, scoped_refptr<FrameTransformerInterface>) +PROXY_METHOD1(RTCErrorOr<scoped_refptr<SframeDecrypterInterface>>, + CreateSframeDecrypterOrError, + SframeCipherSuite) END_PROXY_MAP(RtpReceiver) } // namespace webrtc
diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc index 3a4e6b7..7af007c 100644 --- a/pc/rtp_sender.cc +++ b/pc/rtp_sender.cc
@@ -38,6 +38,7 @@ #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/video_codecs/video_encoder_factory.h" @@ -916,6 +917,15 @@ } } +RTCErrorOr<scoped_refptr<SframeEncrypterInterface>> +RtpSenderBase::CreateSframeEncrypterOrError( + const SframeEncrypterInit& options) { + RTC_DCHECK_RUN_ON(signaling_thread_); + // TODO(bugs.webrtc.org/479862368): Implement Sframe encrypter creation. + return RTCError(RTCErrorType::UNSUPPORTED_OPERATION, + "Sframe encrypter not yet implemented"); +} + LocalAudioSinkAdapter::LocalAudioSinkAdapter() : sink_(nullptr) {} LocalAudioSinkAdapter::~LocalAudioSinkAdapter() {
diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index 848db55..eeec9ae 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h
@@ -37,6 +37,7 @@ #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" #include "api/sequence_checker.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/task_queue/pending_task_safety_flag.h" #include "api/task_queue/task_queue_base.h" #include "api/video_codecs/video_encoder_factory.h" @@ -224,6 +225,9 @@ void SetFrameTransformer( scoped_refptr<FrameTransformerInterface> frame_transformer) override; + RTCErrorOr<scoped_refptr<SframeEncrypterInterface>> + CreateSframeEncrypterOrError(const SframeEncrypterInit& options) override; + void SetEncoderSelector( std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface> encoder_selector) override;
diff --git a/pc/rtp_sender_proxy.h b/pc/rtp_sender_proxy.h index 8e609c5..74d28b5 100644 --- a/pc/rtp_sender_proxy.h +++ b/pc/rtp_sender_proxy.h
@@ -26,6 +26,7 @@ #include "api/rtp_parameters.h" #include "api/rtp_sender_interface.h" #include "api/scoped_refptr.h" +#include "api/sframe/sframe_encrypter_interface.h" #include "api/video_codecs/video_encoder_factory.h" #include "pc/proxy.h" @@ -58,6 +59,9 @@ PROXY_METHOD1(void, SetFrameTransformer, scoped_refptr<FrameTransformerInterface>) +PROXY_METHOD1(RTCErrorOr<scoped_refptr<SframeEncrypterInterface>>, + CreateSframeEncrypterOrError, + const SframeEncrypterInit&) PROXY_METHOD1(void, SetEncoderSelector, std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>)
diff --git a/pc/video_rtp_receiver.cc b/pc/video_rtp_receiver.cc index b296c81..250caa9 100644 --- a/pc/video_rtp_receiver.cc +++ b/pc/video_rtp_receiver.cc
@@ -34,6 +34,7 @@ #include "api/video/video_sink_interface.h" #include "media/base/media_channel.h" #include "pc/media_stream_track_proxy.h" +#include "pc/rtp_receiver.h" #include "pc/video_rtp_track_source.h" #include "pc/video_track.h" #include "rtc_base/checks.h" @@ -57,7 +58,7 @@ absl::string_view receiver_id, const std::vector<scoped_refptr<MediaStreamInterface>>& streams, VideoMediaReceiveChannelInterface* media_channel) - : worker_thread_(worker_thread), + : RtpReceiverBase(worker_thread), 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 ec0d8bb..552539d 100644 --- a/pc/video_rtp_receiver.h +++ b/pc/video_rtp_receiver.h
@@ -37,13 +37,12 @@ #include "pc/rtp_receiver.h" #include "pc/video_rtp_track_source.h" #include "pc/video_track.h" -#include "rtc_base/system/no_unique_address.h" #include "rtc_base/thread.h" #include "rtc_base/thread_annotations.h" namespace webrtc { -class VideoRtpReceiver : public RtpReceiverInternal { +class VideoRtpReceiver : public RtpReceiverBase { public: // An SSRC of 0 will create a receiver that will match the first SSRC it // sees. Must be called on signaling thread. @@ -146,9 +145,6 @@ VideoRtpReceiver* const receiver_; } source_callback_{this}; - RTC_NO_UNIQUE_ADDRESS SequenceChecker signaling_thread_checker_; - Thread* const worker_thread_; - const std::string id_; VideoMediaReceiveChannelInterface* media_channel_ RTC_GUARDED_BY(worker_thread_) = nullptr;