diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc index a0a5547287..f225bed59c 100644 --- a/call/rtp_transport_controller_send.cc +++ b/call/rtp_transport_controller_send.cc @@ -89,14 +89,16 @@ RtpVideoSenderInterface* RtpTransportControllerSend::CreateRtpVideoSender( Transport* send_transport, const RtpSenderObservers& observers, RtcEventLog* event_log, - std::unique_ptr fec_controller) { + std::unique_ptr fec_controller, + const RtpSenderFrameEncryptionConfig& frame_encryption_config) { video_rtp_senders_.push_back(absl::make_unique( ssrcs, suspended_ssrcs, states, rtp_config, rtcp_config, send_transport, observers, // TODO(holmer): Remove this circular dependency by injecting // the parts of RtpTransportControllerSendInterface that are really used. - this, event_log, &retransmission_rate_limiter_, - std::move(fec_controller))); + this, event_log, &retransmission_rate_limiter_, std::move(fec_controller), + frame_encryption_config.frame_encryptor, + frame_encryption_config.crypto_options)); return video_rtp_senders_.back().get(); } diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h index 6aaf29d6bc..c6b23629a7 100644 --- a/call/rtp_transport_controller_send.h +++ b/call/rtp_transport_controller_send.h @@ -29,7 +29,9 @@ #include "rtc_base/task_queue.h" namespace webrtc { + class Clock; +class FrameEncryptorInterface; class RtcEventLog; // TODO(nisse): When we get the underlying transports here, we should @@ -56,7 +58,8 @@ class RtpTransportControllerSend final Transport* send_transport, const RtpSenderObservers& observers, RtcEventLog* event_log, - std::unique_ptr fec_controller) override; + std::unique_ptr fec_controller, + const RtpSenderFrameEncryptionConfig& frame_encryption_config) 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 5c51c54fe1..e93e6caf32 100644 --- a/call/rtp_transport_controller_send_interface.h +++ b/call/rtp_transport_controller_send_interface.h @@ -20,6 +20,7 @@ #include "absl/types/optional.h" #include "api/bitrate_constraints.h" +#include "api/crypto/cryptooptions.h" #include "api/fec_controller.h" #include "api/transport/bitrate_settings.h" #include "call/rtp_config.h" @@ -35,6 +36,7 @@ namespace webrtc { class CallStats; class CallStatsObserver; +class FrameEncryptorInterface; class TargetTransferRateObserver; class Transport; class Module; @@ -62,6 +64,11 @@ struct RtpSenderObservers { SendPacketObserver* send_packet_observer; }; +struct RtpSenderFrameEncryptionConfig { + FrameEncryptorInterface* frame_encryptor = nullptr; + CryptoOptions crypto_options; +}; + // An RtpTransportController should own everything related to the RTP // transport to/from a remote endpoint. We should have separate // interfaces for send and receive side, even if they are implemented @@ -101,7 +108,8 @@ class RtpTransportControllerSendInterface { Transport* send_transport, const RtpSenderObservers& observers, RtcEventLog* event_log, - std::unique_ptr fec_controller) = 0; + std::unique_ptr fec_controller, + const RtpSenderFrameEncryptionConfig& frame_encryption_config) = 0; virtual void DestroyRtpVideoSender( RtpVideoSenderInterface* rtp_video_sender) = 0; diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc index 10d7e7daed..9bc6017440 100644 --- a/call/rtp_video_sender.cc +++ b/call/rtp_video_sender.cc @@ -56,8 +56,11 @@ std::vector> CreateRtpRtcpModules( RtcEventLog* event_log, RateLimiter* retransmission_rate_limiter, OverheadObserver* overhead_observer, - RtpKeepAliveConfig keepalive_config) { + RtpKeepAliveConfig keepalive_config, + FrameEncryptorInterface* frame_encryptor, + const CryptoOptions& crypto_options) { RTC_DCHECK_GT(ssrcs.size(), 0); + RtpRtcp::Configuration configuration; configuration.audio = false; configuration.receiver_only = false; @@ -83,6 +86,10 @@ std::vector> CreateRtpRtcpModules( rtcp_config.video_report_interval_ms; configuration.rtcp_interval_config.audio_interval_ms = rtcp_config.audio_report_interval_ms; + configuration.frame_encryptor = frame_encryptor; + configuration.require_frame_encryption = + crypto_options.sframe.require_frame_encryption; + std::vector> modules; const std::vector& flexfec_protected_ssrcs = protected_media_ssrcs; for (uint32_t ssrc : ssrcs) { @@ -183,7 +190,9 @@ RtpVideoSender::RtpVideoSender( RtpTransportControllerSendInterface* transport, RtcEventLog* event_log, RateLimiter* retransmission_limiter, - std::unique_ptr fec_controller) + std::unique_ptr fec_controller, + FrameEncryptorInterface* frame_encryptor, + const CryptoOptions& crypto_options) : send_side_bwe_with_overhead_( webrtc::field_trial::IsEnabled("WebRTC-SendSideBwe-WithOverhead")), active_(false), @@ -209,7 +218,9 @@ RtpVideoSender::RtpVideoSender( event_log, retransmission_limiter, this, - transport->keepalive_config())), + transport->keepalive_config(), + frame_encryptor, + crypto_options)), rtp_config_(rtp_config), transport_(transport), transport_overhead_bytes_per_packet_(0), diff --git a/call/rtp_video_sender.h b/call/rtp_video_sender.h index db329be00b..91e5fd67cb 100644 --- a/call/rtp_video_sender.h +++ b/call/rtp_video_sender.h @@ -36,6 +36,7 @@ namespace webrtc { +class FrameEncryptorInterface; class RTPFragmentationHeader; class RtpRtcp; class RtpTransportControllerSendInterface; @@ -59,7 +60,9 @@ class RtpVideoSender : public RtpVideoSenderInterface, RtpTransportControllerSendInterface* transport, RtcEventLog* event_log, RateLimiter* retransmission_limiter, // move inside RtpTransport - std::unique_ptr fec_controller); + std::unique_ptr fec_controller, + FrameEncryptorInterface* frame_encryptor, + const CryptoOptions& crypto_options); // move inside RtpTransport ~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 e91bfc4aeb..00b61fe31c 100644 --- a/call/rtp_video_sender_unittest.cc +++ b/call/rtp_video_sender_unittest.cc @@ -107,7 +107,8 @@ class RtpVideoSenderTestFixture { &stats_proxy_, &stats_proxy_, &stats_proxy_, &stats_proxy_, &stats_proxy_, &send_delay_stats_), &transport_controller_, &event_log_, &retransmission_rate_limiter_, - absl::make_unique(&clock_)); + absl::make_unique(&clock_), nullptr, + CryptoOptions{}); } RtpVideoSender* router() { return router_.get(); } diff --git a/call/test/mock_rtp_transport_controller_send.h b/call/test/mock_rtp_transport_controller_send.h index 88d0469cbe..621447dae5 100644 --- a/call/test/mock_rtp_transport_controller_send.h +++ b/call/test/mock_rtp_transport_controller_send.h @@ -17,6 +17,8 @@ #include #include "api/bitrate_constraints.h" +#include "api/crypto/cryptooptions.h" +#include "api/crypto/frameencryptorinterface.h" #include "call/rtp_transport_controller_send_interface.h" #include "modules/congestion_controller/include/network_changed_observer.h" #include "modules/pacing/packet_router.h" @@ -30,7 +32,7 @@ namespace webrtc { class MockRtpTransportControllerSend : public RtpTransportControllerSendInterface { public: - MOCK_METHOD9( + MOCK_METHOD10( CreateRtpVideoSender, RtpVideoSenderInterface*(const std::vector&, std::map, @@ -40,7 +42,8 @@ class MockRtpTransportControllerSend Transport*, const RtpSenderObservers&, RtcEventLog*, - std::unique_ptr)); + std::unique_ptr, + const RtpSenderFrameEncryptionConfig&)); MOCK_METHOD1(DestroyRtpVideoSender, void(RtpVideoSenderInterface*)); MOCK_METHOD0(GetWorkerQueue, rtc::TaskQueue*()); MOCK_METHOD0(packet_router, PacketRouter*()); diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h index 071e1f266e..34d217fc64 100644 --- a/call/video_receive_stream.h +++ b/call/video_receive_stream.h @@ -17,6 +17,7 @@ #include #include "api/call/transport.h" +#include "api/crypto/cryptooptions.h" #include "api/rtp_headers.h" #include "api/rtpparameters.h" #include "api/rtpreceiverinterface.h" @@ -30,6 +31,7 @@ namespace webrtc { +class FrameDecryptorInterface; class RtpPacketSinkInterface; class VideoDecoderFactory; @@ -214,6 +216,14 @@ class VideoReceiveStream { // TODO(nisse): Used with VideoDecoderFactory::LegacyCreateVideoDecoder. // Delete when that method is retired. std::string stream_id; + + // An optional custom frame decryptor that allows the entire frame to be + // decrypted in whatever way the caller choses. This is not required by + // default. + rtc::scoped_refptr frame_decryptor; + + // Per PeerConnection cryptography options. + CryptoOptions crypto_options; }; // Starts stream activity. diff --git a/call/video_send_stream.cc b/call/video_send_stream.cc index a1161553cb..eba951806f 100644 --- a/call/video_send_stream.cc +++ b/call/video_send_stream.cc @@ -9,6 +9,7 @@ */ #include "call/video_send_stream.h" +#include "api/crypto/frameencryptorinterface.h" #include "rtc_base/strings/string_builder.h" namespace webrtc { diff --git a/call/video_send_stream.h b/call/video_send_stream.h index 918ce93bb5..fa7ffcd2bd 100644 --- a/call/video_send_stream.h +++ b/call/video_send_stream.h @@ -17,6 +17,7 @@ #include #include "api/call/transport.h" +#include "api/crypto/cryptooptions.h" #include "api/video/video_frame.h" #include "api/video/video_sink_interface.h" #include "api/video/video_source_interface.h" @@ -29,6 +30,8 @@ namespace webrtc { +class FrameEncryptorInterface; + class VideoSendStream { public: struct StreamStats { @@ -137,6 +140,14 @@ class VideoSendStream { // Track ID as specified during track creation. std::string track_id; + // An optional custom frame encryptor that allows the entire frame to be + // encrypted in whatever way the caller chooses. This is not required by + // default. + rtc::scoped_refptr frame_encryptor; + + // Per PeerConnection cryptography options. + CryptoOptions crypto_options; + 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/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc index 94380ddf26..37a7f34bbe 100644 --- a/media/engine/webrtcvideoengine.cc +++ b/media/engine/webrtcvideoengine.cc @@ -525,7 +525,8 @@ WebRtcVideoChannel::WebRtcVideoChannel( default_send_options_(options), last_stats_log_ms_(-1), discard_unknown_ssrc_packets_(webrtc::field_trial::IsEnabled( - "WebRTC-Video-DiscardPacketsWithUnknownSsrc")) { + "WebRTC-Video-DiscardPacketsWithUnknownSsrc")), + crypto_options_(crypto_options) { RTC_DCHECK(thread_checker_.CalledOnValidThread()); rtcp_receiver_report_ssrc_ = kDefaultRtcpReceiverReportSsrc; @@ -1037,6 +1038,7 @@ bool WebRtcVideoChannel::AddSendStream(const StreamParams& sp) { config.encoder_settings.experiment_cpu_load_estimator = video_config_.experiment_cpu_load_estimator; config.encoder_settings.encoder_factory = encoder_factory_; + config.crypto_options = crypto_options_; WebRtcVideoSendStream* stream = new WebRtcVideoSendStream( call_, sp, std::move(config), default_send_options_, @@ -1153,6 +1155,7 @@ bool WebRtcVideoChannel::AddRecvStream(const StreamParams& sp, webrtc::FlexfecReceiveStream::Config flexfec_config(this); ConfigureReceiverRtp(&config, &flexfec_config, sp); + config.crypto_options = crypto_options_; // TODO(nisse): Rename config variable to avoid negation. config.disable_prerenderer_smoothing = !video_config_.enable_prerenderer_smoothing; @@ -1457,6 +1460,28 @@ void WebRtcVideoChannel::SetInterface( kVideoRtpBufferSize); } +void WebRtcVideoChannel::SetFrameDecryptor( + uint32_t ssrc, + rtc::scoped_refptr frame_decryptor) { + rtc::CritScope stream_lock(&stream_crit_); + auto matching_stream = receive_streams_.find(ssrc); + if (matching_stream != receive_streams_.end()) { + matching_stream->second->SetFrameDecryptor(frame_decryptor); + } +} + +void WebRtcVideoChannel::SetFrameEncryptor( + uint32_t ssrc, + rtc::scoped_refptr frame_encryptor) { + rtc::CritScope stream_lock(&stream_crit_); + auto matching_stream = send_streams_.find(ssrc); + if (matching_stream != send_streams_.end()) { + matching_stream->second->SetFrameEncryptor(frame_encryptor); + } else { + RTC_LOG(LS_ERROR) << "No stream found to attach frame encryptor"; + } +} + absl::optional WebRtcVideoChannel::GetDefaultReceiveStreamSsrc() { rtc::CritScope stream_lock(&stream_crit_); absl::optional ssrc; @@ -1821,6 +1846,15 @@ WebRtcVideoChannel::WebRtcVideoSendStream::GetRtpParameters() const { return rtp_parameters_; } +void WebRtcVideoChannel::WebRtcVideoSendStream::SetFrameEncryptor( + rtc::scoped_refptr frame_encryptor) { + RTC_DCHECK_RUN_ON(&thread_checker_); + parameters_.config.frame_encryptor = frame_encryptor; + if (stream_) { + RecreateWebRtcStream(); + } +} + void WebRtcVideoChannel::WebRtcVideoSendStream::UpdateSendState() { RTC_DCHECK_RUN_ON(&thread_checker_); if (sending_) { @@ -2394,6 +2428,14 @@ bool WebRtcVideoChannel::WebRtcVideoReceiveStream::IsDefaultStream() const { return default_stream_; } +void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetFrameDecryptor( + rtc::scoped_refptr frame_decryptor) { + config_.frame_decryptor = frame_decryptor; + if (stream_) { + RecreateWebRtcVideoStream(); + } +} + void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetSink( rtc::VideoSinkInterface* sink) { rtc::CritScope crit(&sink_lock_); diff --git a/media/engine/webrtcvideoengine.h b/media/engine/webrtcvideoengine.h index 7de68a930c..fe5f93b38e 100644 --- a/media/engine/webrtcvideoengine.h +++ b/media/engine/webrtcvideoengine.h @@ -159,6 +159,20 @@ class WebRtcVideoChannel : public VideoMediaChannel, public webrtc::Transport { void SetInterface(NetworkInterface* iface, webrtc::MediaTransportInterface* media_transport) override; + // E2E Encrypted Video Frame API + // Set a frame decryptor to a particular ssrc that will intercept all + // incoming video frames and attempt to decrypt them before forwarding the + // result. + void SetFrameDecryptor(uint32_t ssrc, + rtc::scoped_refptr + frame_decryptor) override; + // Set a frame encryptor to a particular ssrc that will intercept all + // outgoing video frames and attempt to encrypt them and forward the result + // to the packetizer. + void SetFrameEncryptor(uint32_t ssrc, + rtc::scoped_refptr + frame_encryptor) override; + // Implemented for VideoMediaChannelTest. bool sending() const { return sending_; } @@ -261,6 +275,9 @@ class WebRtcVideoChannel : public VideoMediaChannel, public webrtc::Transport { webrtc::RTCError SetRtpParameters(const webrtc::RtpParameters& parameters); webrtc::RtpParameters GetRtpParameters() const; + void SetFrameEncryptor( + rtc::scoped_refptr frame_encryptor); + // Implements rtc::VideoSourceInterface. // WebRtcVideoSendStream acts as a source to the webrtc::VideoSendStream // in |stream_|. This is done to proxy VideoSinkWants from the encoder to @@ -375,6 +392,9 @@ class WebRtcVideoChannel : public VideoMediaChannel, public webrtc::Transport { void OnFrame(const webrtc::VideoFrame& frame) override; bool IsDefaultStream() const; + void SetFrameDecryptor( + rtc::scoped_refptr frame_decryptor); + void SetSink(rtc::VideoSinkInterface* sink); VideoReceiverInfo GetVideoReceiverInfo(bool log_stats); @@ -488,6 +508,9 @@ class WebRtcVideoChannel : public VideoMediaChannel, public webrtc::Transport { // before the unsignaled receive stream is created when the first packet is // received. StreamParams unsignaled_stream_params_; + // Per peer connection crypto options that last for the lifetime of the peer + // connection. + const webrtc::CryptoOptions crypto_options_; }; class EncoderStreamFactory diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h index a99544cfa6..8fa0faab7a 100644 --- a/modules/rtp_rtcp/include/rtp_rtcp.h +++ b/modules/rtp_rtcp/include/rtp_rtcp.h @@ -28,6 +28,7 @@ namespace webrtc { // Forward declarations. +class FrameEncryptorInterface; class OverheadObserver; class RateLimiter; class ReceiveStatisticsProvider; @@ -97,6 +98,11 @@ class RtpRtcp : public Module, public RtcpFeedbackSenderInterface { // Update network2 instead of pacer_exit field of video timing extension. bool populate_network2_timestamp = false; + // E2EE Custom Video Frame Encryption + FrameEncryptorInterface* frame_encryptor = nullptr; + // Require all outgoing frames to be encrypted with a FrameEncryptor. + bool require_frame_encryption = false; + private: RTC_DISALLOW_COPY_AND_ASSIGN(Configuration); }; diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 392b91ddcf..ee3c4801f7 100644 --- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -99,7 +99,8 @@ ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const Configuration& configuration) configuration.send_packet_observer, configuration.retransmission_rate_limiter, configuration.overhead_observer, - configuration.populate_network2_timestamp)); + configuration.populate_network2_timestamp, + configuration.frame_encryptor, configuration.require_frame_encryption)); // Make sure rtcp sender use same timestamp offset as rtp sender. rtcp_sender_.SetTimestampOffset(rtp_sender_->TimestampOffset()); diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc index 89300780d7..d180013188 100644 --- a/modules/rtp_rtcp/source/rtp_sender.cc +++ b/modules/rtp_rtcp/source/rtp_sender.cc @@ -119,14 +119,21 @@ RTPSender::RTPSender( SendPacketObserver* send_packet_observer, RateLimiter* retransmission_rate_limiter, OverheadObserver* overhead_observer, - bool populate_network2_timestamp) + bool populate_network2_timestamp, + FrameEncryptorInterface* frame_encryptor, + bool require_frame_encryption) : clock_(clock), // TODO(holmer): Remove this conversion? clock_delta_ms_(clock_->TimeInMilliseconds() - rtc::TimeMillis()), random_(clock_->TimeInMicroseconds()), audio_configured_(audio), audio_(audio ? new RTPSenderAudio(clock, this) : nullptr), - video_(audio ? nullptr : new RTPSenderVideo(clock, this, flexfec_sender)), + video_(audio ? nullptr + : new RTPSenderVideo(clock, + this, + flexfec_sender, + frame_encryptor, + require_frame_encryption)), paced_sender_(paced_sender), transport_sequence_number_allocator_(sequence_number_allocator), transport_feedback_observer_(transport_feedback_observer), diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h index e9095d175f..dd70bb3803 100644 --- a/modules/rtp_rtcp/source/rtp_sender.h +++ b/modules/rtp_rtcp/source/rtp_sender.h @@ -37,6 +37,7 @@ namespace webrtc { +class FrameEncryptorInterface; class OverheadObserver; class RateLimiter; class RtcEventLog; @@ -62,7 +63,9 @@ class RTPSender { SendPacketObserver* send_packet_observer, RateLimiter* nack_rate_limiter, OverheadObserver* overhead_observer, - bool populate_network2_timestamp); + bool populate_network2_timestamp, + FrameEncryptorInterface* frame_encryptor, + bool require_frame_encryption); ~RTPSender(); diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc index 1b7e99298b..ace34ff07a 100644 --- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -184,7 +184,8 @@ class RtpSenderTest : public ::testing::TestWithParam { false, &fake_clock_, &transport_, pacer ? &mock_paced_sender_ : nullptr, nullptr, &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, &send_packet_observer_, - &retransmission_rate_limiter_, nullptr, populate_network2)); + &retransmission_rate_limiter_, nullptr, populate_network2, nullptr, + false)); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetTimestampOffset(0); rtp_sender_->SetSSRC(kSsrc); @@ -276,7 +277,7 @@ class TestRtpSenderVideo : public RTPSenderVideo { TestRtpSenderVideo(Clock* clock, RTPSender* rtp_sender, FlexfecSender* flexfec_sender) - : RTPSenderVideo(clock, rtp_sender, flexfec_sender) {} + : RTPSenderVideo(clock, rtp_sender, flexfec_sender, nullptr, false) {} ~TestRtpSenderVideo() override {} StorageType GetStorageType(const RTPVideoHeader& header, @@ -382,7 +383,7 @@ TEST_P(RtpSenderTest, AssignSequenceNumberAllowsPaddingOnAudio) { rtp_sender_.reset(new RTPSender( kEnableAudio, &fake_clock_, &transport, &mock_paced_sender_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, - nullptr, &retransmission_rate_limiter_, nullptr, false)); + nullptr, &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetTimestampOffset(0); rtp_sender_->SetSSRC(kSsrc); @@ -428,7 +429,8 @@ TEST_P(RtpSenderTestWithoutPacer, rtp_sender_.reset(new RTPSender( false, &fake_clock_, &transport_, nullptr, nullptr, &seq_num_allocator_, &feedback_observer_, nullptr, nullptr, nullptr, &mock_rtc_event_log_, - nullptr, &retransmission_rate_limiter_, &mock_overhead_observer, false)); + nullptr, &retransmission_rate_limiter_, &mock_overhead_observer, false, + nullptr, false)); rtp_sender_->SetSSRC(kSsrc); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionTransportSequenceNumber, @@ -455,7 +457,8 @@ TEST_P(RtpSenderTestWithoutPacer, SendsPacketsWithTransportSequenceNumber) { rtp_sender_.reset(new RTPSender( false, &fake_clock_, &transport_, nullptr, nullptr, &seq_num_allocator_, &feedback_observer_, nullptr, nullptr, nullptr, &mock_rtc_event_log_, - &send_packet_observer_, &retransmission_rate_limiter_, nullptr, false)); + &send_packet_observer_, &retransmission_rate_limiter_, nullptr, false, + nullptr, false)); rtp_sender_->SetSSRC(kSsrc); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionTransportSequenceNumber, @@ -486,7 +489,8 @@ TEST_P(RtpSenderTestWithoutPacer, PacketOptionsNoRetransmission) { rtp_sender_.reset(new RTPSender( false, &fake_clock_, &transport_, nullptr, nullptr, &seq_num_allocator_, &feedback_observer_, nullptr, nullptr, nullptr, &mock_rtc_event_log_, - &send_packet_observer_, &retransmission_rate_limiter_, nullptr, false)); + &send_packet_observer_, &retransmission_rate_limiter_, nullptr, false, + nullptr, false)); rtp_sender_->SetSSRC(kSsrc); SendGenericPayload(); @@ -537,10 +541,10 @@ TEST_P(RtpSenderTestWithoutPacer, DoesnSetIncludedInAllocationByDefault) { TEST_P(RtpSenderTestWithoutPacer, OnSendSideDelayUpdated) { testing::StrictMock send_side_delay_observer_; - rtp_sender_.reset( - new RTPSender(false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, &send_side_delay_observer_, - &mock_rtc_event_log_, nullptr, nullptr, nullptr, false)); + rtp_sender_.reset(new RTPSender( + false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, &send_side_delay_observer_, &mock_rtc_event_log_, + nullptr, nullptr, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kSsrc); const uint8_t kPayloadType = 127; @@ -618,7 +622,7 @@ TEST_P(RtpSenderTest, SendsPacketsWithTransportSequenceNumber) { false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr, &seq_num_allocator_, &feedback_observer_, nullptr, nullptr, nullptr, &mock_rtc_event_log_, &send_packet_observer_, - &retransmission_rate_limiter_, nullptr, false)); + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetSSRC(kSsrc); rtp_sender_->SetStorePacketsStatus(true, 10); @@ -978,7 +982,7 @@ TEST_P(RtpSenderTest, OnSendPacketNotUpdatedWithoutSeqNumAllocator) { false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr, nullptr /* TransportSequenceNumberAllocator */, nullptr, nullptr, nullptr, nullptr, nullptr, &send_packet_observer_, &retransmission_rate_limiter_, - nullptr, false)); + nullptr, false, nullptr, false)); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetSSRC(kSsrc); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( @@ -1004,7 +1008,7 @@ TEST_P(RtpSenderTest, SendRedundantPayloads) { rtp_sender_.reset(new RTPSender( false, &fake_clock_, &transport, &mock_paced_sender_, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, nullptr, - &retransmission_rate_limiter_, nullptr, false)); + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetSSRC(kSsrc); rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload); @@ -1128,7 +1132,7 @@ TEST_P(RtpSenderTest, SendFlexfecPackets) { false, &fake_clock_, &transport_, &mock_paced_sender_, &flexfec_sender, &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, &send_packet_observer_, - &retransmission_rate_limiter_, nullptr, false)); + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kMediaSsrc); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetStorePacketsStatus(true, 10); @@ -1188,7 +1192,7 @@ TEST_P(RtpSenderTest, NoFlexfecForTimingFrames) { false, &fake_clock_, &transport_, &mock_paced_sender_, &flexfec_sender, &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, &send_packet_observer_, - &retransmission_rate_limiter_, nullptr, false)); + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kMediaSsrc); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetStorePacketsStatus(true, 10); @@ -1283,11 +1287,11 @@ TEST_P(RtpSenderTestWithoutPacer, SendFlexfecPackets) { nullptr /* rtp_state */, &fake_clock_); // Reset |rtp_sender_| to use FlexFEC. - rtp_sender_.reset( - new RTPSender(false, &fake_clock_, &transport_, nullptr, &flexfec_sender, - &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr, - &mock_rtc_event_log_, &send_packet_observer_, - &retransmission_rate_limiter_, nullptr, false)); + rtp_sender_.reset(new RTPSender( + false, &fake_clock_, &transport_, nullptr, &flexfec_sender, + &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr, + &mock_rtc_event_log_, &send_packet_observer_, + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kMediaSsrc); rtp_sender_->SetSequenceNumber(kSeqNum); @@ -1351,7 +1355,7 @@ TEST_P(RtpSenderTest, FecOverheadRate) { false, &fake_clock_, &transport_, &mock_paced_sender_, &flexfec_sender, &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, &send_packet_observer_, - &retransmission_rate_limiter_, nullptr, false)); + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kMediaSsrc); rtp_sender_->SetSequenceNumber(kSeqNum); @@ -1403,7 +1407,7 @@ TEST_P(RtpSenderTest, FrameCountCallbacks) { rtp_sender_.reset(new RTPSender( false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr, nullptr, nullptr, nullptr, &callback, nullptr, nullptr, nullptr, - &retransmission_rate_limiter_, nullptr, false)); + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kSsrc); char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC"; const uint8_t payload_type = 127; @@ -1463,10 +1467,10 @@ TEST_P(RtpSenderTest, BitrateCallbacks) { uint32_t total_bitrate_; uint32_t retransmit_bitrate_; } callback; - rtp_sender_.reset( - new RTPSender(false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, - nullptr, &callback, nullptr, nullptr, nullptr, nullptr, - &retransmission_rate_limiter_, nullptr, false)); + rtp_sender_.reset(new RTPSender( + false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr, + &callback, nullptr, nullptr, nullptr, nullptr, + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kSsrc); // Simulate kNumPackets sent with kPacketInterval ms intervals, with the @@ -1523,10 +1527,10 @@ class RtpSenderAudioTest : public RtpSenderTest { void SetUp() override { payload_ = kAudioPayload; - rtp_sender_.reset( - new RTPSender(true, &fake_clock_, &transport_, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, - nullptr, &retransmission_rate_limiter_, nullptr, false)); + rtp_sender_.reset(new RTPSender( + true, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSSRC(kSsrc); rtp_sender_->SetSequenceNumber(kSeqNum); } @@ -2189,10 +2193,11 @@ TEST_P(RtpSenderVideoTest, TEST_P(RtpSenderTest, OnOverheadChanged) { MockOverheadObserver mock_overhead_observer; - rtp_sender_.reset(new RTPSender( - false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, - &retransmission_rate_limiter_, &mock_overhead_observer, false)); + rtp_sender_.reset( + new RTPSender(false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + &retransmission_rate_limiter_, &mock_overhead_observer, + false, nullptr, false)); rtp_sender_->SetSSRC(kSsrc); // RTP overhead is 12B. @@ -2210,10 +2215,11 @@ TEST_P(RtpSenderTest, OnOverheadChanged) { TEST_P(RtpSenderTest, DoesNotUpdateOverheadOnEqualSize) { MockOverheadObserver mock_overhead_observer; - rtp_sender_.reset(new RTPSender( - false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, nullptr, - &retransmission_rate_limiter_, &mock_overhead_observer, false)); + rtp_sender_.reset( + new RTPSender(false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + &retransmission_rate_limiter_, &mock_overhead_observer, + false, nullptr, false)); rtp_sender_->SetSSRC(kSsrc); EXPECT_CALL(mock_overhead_observer, OnOverheadChanged(_)).Times(1); @@ -2223,10 +2229,10 @@ TEST_P(RtpSenderTest, DoesNotUpdateOverheadOnEqualSize) { TEST_P(RtpSenderTest, SendsKeepAlive) { MockTransport transport; - rtp_sender_.reset( - new RTPSender(false, &fake_clock_, &transport, nullptr, nullptr, nullptr, - nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, - nullptr, &retransmission_rate_limiter_, nullptr, false)); + rtp_sender_.reset(new RTPSender( + false, &fake_clock_, &transport, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, &mock_rtc_event_log_, nullptr, + &retransmission_rate_limiter_, nullptr, false, nullptr, false)); rtp_sender_->SetSequenceNumber(kSeqNum); rtp_sender_->SetTimestampOffset(0); rtp_sender_->SetSSRC(kSsrc); diff --git a/modules/rtp_rtcp/source/rtp_sender_video.cc b/modules/rtp_rtcp/source/rtp_sender_video.cc index b395a660db..ff14a2794b 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -19,6 +19,7 @@ #include #include "absl/memory/memory.h" +#include "api/crypto/frameencryptorinterface.h" #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" #include "modules/rtp_rtcp/source/byte_io.h" #include "modules/rtp_rtcp/source/rtp_format_video_generic.h" @@ -120,7 +121,9 @@ bool MinimizeDescriptor(const RTPVideoHeader& full, RTPVideoHeader* minimized) { RTPSenderVideo::RTPSenderVideo(Clock* clock, RTPSender* rtp_sender, - FlexfecSender* flexfec_sender) + FlexfecSender* flexfec_sender, + FrameEncryptorInterface* frame_encryptor, + bool require_frame_encryption) : rtp_sender_(rtp_sender), clock_(clock), video_type_(kVideoCodecGeneric), @@ -133,7 +136,9 @@ RTPSenderVideo::RTPSenderVideo(Clock* clock, delta_fec_params_{0, 1, kFecMaskRandom}, key_fec_params_{0, 1, kFecMaskRandom}, fec_bitrate_(1000, RateStatistics::kBpsScale), - video_bitrate_(1000, RateStatistics::kBpsScale) {} + video_bitrate_(1000, RateStatistics::kBpsScale), + frame_encryptor_(frame_encryptor), + require_frame_encryption_(require_frame_encryption) {} RTPSenderVideo::~RTPSenderVideo() {} @@ -433,9 +438,42 @@ bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type, RTPVideoHeader minimized_video_header; const RTPVideoHeader* packetize_video_header = video_header; - if (first_packet->HasExtension() && - MinimizeDescriptor(*video_header, &minimized_video_header)) { - packetize_video_header = &minimized_video_header; + rtc::ArrayView generic_descriptor_raw = + first_packet->GetRawExtension(); + if (!generic_descriptor_raw.empty()) { + if (MinimizeDescriptor(*video_header, &minimized_video_header)) { + packetize_video_header = &minimized_video_header; + } + } + + // TODO(benwright@webrtc.org) - Allocate enough to always encrypt inline. + rtc::Buffer encrypted_video_payload; + if (frame_encryptor_ != nullptr) { + if (generic_descriptor_raw.empty()) { + return false; + } + + const size_t max_ciphertext_size = + frame_encryptor_->GetMaxCiphertextByteSize(cricket::MEDIA_TYPE_VIDEO, + payload_size); + encrypted_video_payload.SetSize(max_ciphertext_size); + + size_t bytes_written = 0; + if (frame_encryptor_->Encrypt( + cricket::MEDIA_TYPE_VIDEO, first_packet->Ssrc(), + /*additional_data=*/nullptr, + rtc::MakeArrayView(payload_data, payload_size), + encrypted_video_payload, &bytes_written) != 0) { + return false; + } + + encrypted_video_payload.SetSize(bytes_written); + payload_data = encrypted_video_payload.data(); + payload_size = encrypted_video_payload.size(); + } else if (require_frame_encryption_) { + RTC_LOG(LS_WARNING) + << "No FrameEncryptor is attached to this video sending stream but " + << "one is required since require_frame_encryptor is set"; } std::unique_ptr packetizer = RtpPacketizer::Create( diff --git a/modules/rtp_rtcp/source/rtp_sender_video.h b/modules/rtp_rtcp/source/rtp_sender_video.h index ce7be16149..441e7b7a4b 100644 --- a/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/modules/rtp_rtcp/source/rtp_sender_video.h @@ -29,6 +29,8 @@ #include "rtc_base/thread_annotations.h" namespace webrtc { + +class FrameEncryptorInterface; class RtpPacketizer; class RtpPacketToSend; @@ -38,7 +40,9 @@ class RTPSenderVideo { RTPSenderVideo(Clock* clock, RTPSender* rtpSender, - FlexfecSender* flexfec_sender); + FlexfecSender* flexfec_sender, + FrameEncryptorInterface* frame_encryptor, + bool require_frame_encryption); virtual ~RTPSenderVideo(); virtual enum VideoCodecType VideoCodecType() const; @@ -158,6 +162,13 @@ class RTPSenderVideo { RTC_GUARDED_BY(stats_crit_); OneTimeEvent first_frame_sent_; + + // E2EE Custom Video Frame Encryptor (optional) + FrameEncryptorInterface* const frame_encryptor_ = nullptr; + // If set to true will require all outgoing frames to pass through an + // initialized frame_encryptor_ before being sent out of the network. + // Otherwise these payloads will be dropped. + bool require_frame_encryption_; }; } // namespace webrtc diff --git a/modules/video_coding/encoded_frame.h b/modules/video_coding/encoded_frame.h index dae383cc9a..c7efd40072 100644 --- a/modules/video_coding/encoded_frame.h +++ b/modules/video_coding/encoded_frame.h @@ -60,11 +60,21 @@ class VCMEncodedFrame : protected EncodedImage { * Get pointer to frame buffer */ const uint8_t* Buffer() const { return _buffer; } + /** + * Get pointer to frame buffer that can be mutated. + */ + uint8_t* MutableBuffer() { return _buffer; } /** * Get frame length */ size_t Length() const { return _length; } - + /** + * Set frame length + */ + void SetLength(size_t length) { + RTC_DCHECK(length <= _size); + _length = length; + } /** * Frame RTP timestamp (90kHz) */ diff --git a/video/rtp_video_stream_receiver.cc b/video/rtp_video_stream_receiver.cc index 133c09fad5..22f370db62 100644 --- a/video/rtp_video_stream_receiver.cc +++ b/video/rtp_video_stream_receiver.cc @@ -95,7 +95,8 @@ RtpVideoStreamReceiver::RtpVideoStreamReceiver( ProcessThread* process_thread, NackSender* nack_sender, KeyFrameRequestSender* keyframe_request_sender, - video_coding::OnCompleteFrameCallback* complete_frame_callback) + video_coding::OnCompleteFrameCallback* complete_frame_callback, + rtc::scoped_refptr frame_decryptor) : clock_(Clock::GetRealTimeClock()), config_(*config), packet_router_(packet_router), @@ -113,7 +114,8 @@ RtpVideoStreamReceiver::RtpVideoStreamReceiver( packet_router)), complete_frame_callback_(complete_frame_callback), keyframe_request_sender_(keyframe_request_sender), - has_received_frame_(false) { + has_received_frame_(false), + frame_decryptor_(frame_decryptor) { constexpr bool remb_candidate = true; packet_router_->AddReceiveRtpModule(rtp_rtcp_.get(), remb_candidate); rtp_receive_statistics_->RegisterRtpStatisticsCallback(receive_stats_proxy); @@ -363,6 +365,45 @@ int32_t RtpVideoStreamReceiver::ResendPackets(const uint16_t* sequence_numbers, void RtpVideoStreamReceiver::OnReceivedFrame( std::unique_ptr frame) { + // Optionally attempt to decrypt the raw video frame if it was provided. + if (frame_decryptor_ != nullptr) { + // When using encryption we expect the frame to have the generic descriptor. + absl::optional descriptor = + frame->GetGenericFrameDescriptor(); + if (!descriptor) { + RTC_LOG(LS_ERROR) << "No generic frame descriptor found dropping frame."; + return; + } + + // Retrieve the bitstream of the encrypted video frame. + rtc::ArrayView encrypted_frame_bitstream(frame->Buffer(), + frame->size()); + // Retrieve the maximum possible size of the decrypted payload. + const size_t max_plaintext_byte_size = + frame_decryptor_->GetMaxPlaintextByteSize(cricket::MEDIA_TYPE_VIDEO, + frame->size()); + RTC_CHECK(max_plaintext_byte_size <= frame->size()); + // Place the decrypted frame inline into the existing frame. + rtc::ArrayView inline_decrypted_bitstream(frame->MutableBuffer(), + max_plaintext_byte_size); + + // Attempt to decrypt the video frame. + size_t bytes_written = 0; + if (frame_decryptor_->Decrypt( + cricket::MEDIA_TYPE_VIDEO, /*csrcs=*/{}, + /*additional_data=*/nullptr, encrypted_frame_bitstream, + inline_decrypted_bitstream, &bytes_written) != 0) { + return; + } + RTC_CHECK(bytes_written <= max_plaintext_byte_size); + // Update the frame to contain just the written bytes. + frame->SetLength(bytes_written); + } else if (config_.crypto_options.sframe.require_frame_encryption) { + RTC_LOG(LS_WARNING) << "Frame decryption required but not attached to this " + "stream. Dropping frame."; + return; + } + if (!has_received_frame_) { has_received_frame_ = true; if (frame->FrameType() != kVideoFrameKey) diff --git a/video/rtp_video_stream_receiver.h b/video/rtp_video_stream_receiver.h index ca5d7a0abd..9c88173a13 100644 --- a/video/rtp_video_stream_receiver.h +++ b/video/rtp_video_stream_receiver.h @@ -19,6 +19,7 @@ #include "absl/types/optional.h" +#include "api/crypto/framedecryptorinterface.h" #include "api/video_codecs/video_codec.h" #include "call/rtp_packet_sink_interface.h" #include "call/syncable.h" @@ -67,7 +68,8 @@ class RtpVideoStreamReceiver : public RecoveredPacketReceiver, ProcessThread* process_thread, NackSender* nack_sender, KeyFrameRequestSender* keyframe_request_sender, - video_coding::OnCompleteFrameCallback* complete_frame_callback); + video_coding::OnCompleteFrameCallback* complete_frame_callback, + rtc::scoped_refptr frame_decryptor); ~RtpVideoStreamReceiver() override; void AddReceiveCodec(const VideoCodec& video_codec, @@ -203,6 +205,9 @@ class RtpVideoStreamReceiver : public RecoveredPacketReceiver, RTC_GUARDED_BY(rtp_sources_lock_); absl::optional last_received_rtp_system_time_ms_ RTC_GUARDED_BY(rtp_sources_lock_); + + // E2EE Video Frame Decryptor (Optional) + rtc::scoped_refptr frame_decryptor_; }; } // namespace webrtc diff --git a/video/rtp_video_stream_receiver_unittest.cc b/video/rtp_video_stream_receiver_unittest.cc index 590d916f46..81342b826a 100644 --- a/video/rtp_video_stream_receiver_unittest.cc +++ b/video/rtp_video_stream_receiver_unittest.cc @@ -133,7 +133,7 @@ class RtpVideoStreamReceiverTest : public testing::Test { &mock_transport_, nullptr, &packet_router_, &config_, rtp_receive_statistics_.get(), nullptr, process_thread_.get(), &mock_nack_sender_, &mock_key_frame_request_sender_, - &mock_on_complete_frame_callback_); + &mock_on_complete_frame_callback_, nullptr); } WebRtcRTPHeader GetDefaultPacket() { diff --git a/video/video_receive_stream.cc b/video/video_receive_stream.cc index 86b93686ca..9ffd278371 100644 --- a/video/video_receive_stream.cc +++ b/video/video_receive_stream.cc @@ -139,9 +139,10 @@ VideoReceiveStream::VideoReceiveStream( rtp_receive_statistics_.get(), &stats_proxy_, process_thread_, - this, // NackSender - this, // KeyFrameRequestSender - this), // OnCompleteFrameCallback + this, // NackSender + this, // KeyFrameRequestSender + this, // OnCompleteFrameCallback + config_.frame_decryptor), rtp_stream_sync_(this) { RTC_LOG(LS_INFO) << "VideoReceiveStream: " << config_.ToString(); diff --git a/video/video_send_stream_impl.cc b/video/video_send_stream_impl.cc index b725e63d68..84545978b0 100644 --- a/video/video_send_stream_impl.cc +++ b/video/video_send_stream_impl.cc @@ -111,6 +111,14 @@ int CalculateMaxPadBitrateBps(const std::vector& streams, return pad_up_to_bitrate_bps; } +RtpSenderFrameEncryptionConfig CreateFrameEncryptionConfig( + const VideoSendStream::Config* config) { + RtpSenderFrameEncryptionConfig frame_encryption_config; + frame_encryption_config.frame_encryptor = config->frame_encryptor; + frame_encryption_config.crypto_options = config->crypto_options; + return frame_encryption_config; +} + RtpSenderObservers CreateObservers(CallStats* call_stats, EncoderRtcpFeedback* encoder_feedback, SendStatisticsProxy* stats_proxy, @@ -238,19 +246,20 @@ VideoSendStreamImpl::VideoSendStreamImpl( config_->rtp.ssrcs, video_stream_encoder), bandwidth_observer_(transport->GetBandwidthObserver()), - rtp_video_sender_( - transport_->CreateRtpVideoSender(config_->rtp.ssrcs, - suspended_ssrcs, - suspended_payload_states, - config_->rtp, - config_->rtcp, - config_->send_transport, - CreateObservers(call_stats, - &encoder_feedback_, - stats_proxy_, - send_delay_stats), - event_log, - std::move(fec_controller))), + rtp_video_sender_(transport_->CreateRtpVideoSender( + config_->rtp.ssrcs, + suspended_ssrcs, + suspended_payload_states, + config_->rtp, + config_->rtcp, + config_->send_transport, + CreateObservers(call_stats, + &encoder_feedback_, + stats_proxy_, + send_delay_stats), + event_log, + std::move(fec_controller), + CreateFrameEncryptionConfig(config_))), weak_ptr_factory_(this) { RTC_DCHECK_RUN_ON(worker_queue_); RTC_LOG(LS_INFO) << "VideoSendStreamInternal: " << config_->ToString(); diff --git a/video/video_send_stream_impl_unittest.cc b/video/video_send_stream_impl_unittest.cc index dd036909a6..7d94c40383 100644 --- a/video/video_send_stream_impl_unittest.cc +++ b/video/video_send_stream_impl_unittest.cc @@ -91,7 +91,7 @@ class VideoSendStreamImplTest : public ::testing::Test { EXPECT_CALL(transport_controller_, packet_router()) .WillRepeatedly(Return(&packet_router_)); EXPECT_CALL(transport_controller_, - CreateRtpVideoSender(_, _, _, _, _, _, _, _, _)) + CreateRtpVideoSender(_, _, _, _, _, _, _, _, _, _)) .WillRepeatedly(Return(&rtp_video_sender_)); EXPECT_CALL(rtp_video_sender_, SetActive(_)) .WillRepeatedly(testing::Invoke(