Enable End-to-End Encrypted Video Frames.

This change integrates the FrameDecryptorInterface and the FrameEncryptorInterface into
the video send and receive path. If a FrameEncryptorInterface is set on an outgoing video RTPSender
then each outgoing video frame will first pass through the provided FrameEncryptor which
will have a chance to modify the payload contents for the purposes of encryption. In addition to
this the new GenericFrameDescriptor will be added as additional data.

If a FrameDecryptorInterface is set on an incoming video RtpReceiver then each incoming
video payload will first pass through the provided FrameDecryptor which have a chance to
modify the payload contents for the purpose of decryption.

Bug: webrtc:9795
Change-Id: I9f743ce0cb63df0cf070f6144be7ada078b4e5d2
Reviewed-on: https://webrtc-review.googlesource.com/c/103920
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Åsa Persson <asapersson@webrtc.org>
Commit-Queue: Benjamin Wright <benwright@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25258}
This commit is contained in:
Benjamin Wright
2018-10-17 17:27:25 -07:00
committed by Commit Bot
parent 6714bf9f18
commit 192eeec14d
26 changed files with 343 additions and 87 deletions

View File

@ -89,14 +89,16 @@ RtpVideoSenderInterface* RtpTransportControllerSend::CreateRtpVideoSender(
Transport* send_transport,
const RtpSenderObservers& observers,
RtcEventLog* event_log,
std::unique_ptr<FecController> fec_controller) {
std::unique_ptr<FecController> fec_controller,
const RtpSenderFrameEncryptionConfig& frame_encryption_config) {
video_rtp_senders_.push_back(absl::make_unique<RtpVideoSender>(
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();
}

View File

@ -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<FecController> fec_controller) override;
std::unique_ptr<FecController> fec_controller,
const RtpSenderFrameEncryptionConfig& frame_encryption_config) override;
void DestroyRtpVideoSender(
RtpVideoSenderInterface* rtp_video_sender) override;

View File

@ -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<FecController> fec_controller) = 0;
std::unique_ptr<FecController> fec_controller,
const RtpSenderFrameEncryptionConfig& frame_encryption_config) = 0;
virtual void DestroyRtpVideoSender(
RtpVideoSenderInterface* rtp_video_sender) = 0;

View File

@ -56,8 +56,11 @@ std::vector<std::unique_ptr<RtpRtcp>> 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<std::unique_ptr<RtpRtcp>> 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<std::unique_ptr<RtpRtcp>> modules;
const std::vector<uint32_t>& 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<FecController> fec_controller)
std::unique_ptr<FecController> 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),

View File

@ -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<FecController> fec_controller);
std::unique_ptr<FecController> fec_controller,
FrameEncryptorInterface* frame_encryptor,
const CryptoOptions& crypto_options); // move inside RtpTransport
~RtpVideoSender() override;
// RegisterProcessThread register |module_process_thread| with those objects

View File

@ -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<FecControllerDefault>(&clock_));
absl::make_unique<FecControllerDefault>(&clock_), nullptr,
CryptoOptions{});
}
RtpVideoSender* router() { return router_.get(); }

View File

@ -17,6 +17,8 @@
#include <vector>
#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<uint32_t>&,
std::map<uint32_t, RtpState>,
@ -40,7 +42,8 @@ class MockRtpTransportControllerSend
Transport*,
const RtpSenderObservers&,
RtcEventLog*,
std::unique_ptr<FecController>));
std::unique_ptr<FecController>,
const RtpSenderFrameEncryptionConfig&));
MOCK_METHOD1(DestroyRtpVideoSender, void(RtpVideoSenderInterface*));
MOCK_METHOD0(GetWorkerQueue, rtc::TaskQueue*());
MOCK_METHOD0(packet_router, PacketRouter*());

View File

@ -17,6 +17,7 @@
#include <vector>
#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<webrtc::FrameDecryptorInterface> frame_decryptor;
// Per PeerConnection cryptography options.
CryptoOptions crypto_options;
};
// Starts stream activity.

View File

@ -9,6 +9,7 @@
*/
#include "call/video_send_stream.h"
#include "api/crypto/frameencryptorinterface.h"
#include "rtc_base/strings/string_builder.h"
namespace webrtc {

View File

@ -17,6 +17,7 @@
#include <vector>
#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<webrtc::FrameEncryptorInterface> 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.

View File

@ -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<webrtc::FrameDecryptorInterface> 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<webrtc::FrameEncryptorInterface> 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<uint32_t> WebRtcVideoChannel::GetDefaultReceiveStreamSsrc() {
rtc::CritScope stream_lock(&stream_crit_);
absl::optional<uint32_t> ssrc;
@ -1821,6 +1846,15 @@ WebRtcVideoChannel::WebRtcVideoSendStream::GetRtpParameters() const {
return rtp_parameters_;
}
void WebRtcVideoChannel::WebRtcVideoSendStream::SetFrameEncryptor(
rtc::scoped_refptr<webrtc::FrameEncryptorInterface> 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<webrtc::FrameDecryptorInterface> frame_decryptor) {
config_.frame_decryptor = frame_decryptor;
if (stream_) {
RecreateWebRtcVideoStream();
}
}
void WebRtcVideoChannel::WebRtcVideoReceiveStream::SetSink(
rtc::VideoSinkInterface<webrtc::VideoFrame>* sink) {
rtc::CritScope crit(&sink_lock_);

View File

@ -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<webrtc::FrameDecryptorInterface>
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<webrtc::FrameEncryptorInterface>
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<webrtc::FrameEncryptorInterface> frame_encryptor);
// Implements rtc::VideoSourceInterface<webrtc::VideoFrame>.
// 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<webrtc::FrameDecryptorInterface> frame_decryptor);
void SetSink(rtc::VideoSinkInterface<webrtc::VideoFrame>* 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

View File

@ -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);
};

View File

@ -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());

View File

@ -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),

View File

@ -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();

View File

@ -184,7 +184,8 @@ class RtpSenderTest : public ::testing::TestWithParam<bool> {
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<MockSendSideDelayObserver> 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,
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));
&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);

View File

@ -19,6 +19,7 @@
#include <vector>
#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,10 +438,43 @@ bool RTPSenderVideo::SendVideo(enum VideoCodecType video_type,
RTPVideoHeader minimized_video_header;
const RTPVideoHeader* packetize_video_header = video_header;
if (first_packet->HasExtension<RtpGenericFrameDescriptorExtension>() &&
MinimizeDescriptor(*video_header, &minimized_video_header)) {
rtc::ArrayView<const uint8_t> generic_descriptor_raw =
first_packet->GetRawExtension<RtpGenericFrameDescriptorExtension>();
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<RtpPacketizer> packetizer = RtpPacketizer::Create(
video_type, rtc::MakeArrayView(payload_data, payload_size), limits,

View File

@ -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

View File

@ -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)
*/

View File

@ -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<FrameDecryptorInterface> 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<video_coding::RtpFrameObject> 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<RtpGenericFrameDescriptor> 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<const uint8_t> 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<uint8_t> 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)

View File

@ -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<FrameDecryptorInterface> 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<int64_t> last_received_rtp_system_time_ms_
RTC_GUARDED_BY(rtp_sources_lock_);
// E2EE Video Frame Decryptor (Optional)
rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor_;
};
} // namespace webrtc

View File

@ -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() {

View File

@ -141,7 +141,8 @@ VideoReceiveStream::VideoReceiveStream(
process_thread_,
this, // NackSender
this, // KeyFrameRequestSender
this), // OnCompleteFrameCallback
this, // OnCompleteFrameCallback
config_.frame_decryptor),
rtp_stream_sync_(this) {
RTC_LOG(LS_INFO) << "VideoReceiveStream: " << config_.ToString();

View File

@ -111,6 +111,14 @@ int CalculateMaxPadBitrateBps(const std::vector<VideoStream>& 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,8 +246,8 @@ VideoSendStreamImpl::VideoSendStreamImpl(
config_->rtp.ssrcs,
video_stream_encoder),
bandwidth_observer_(transport->GetBandwidthObserver()),
rtp_video_sender_(
transport_->CreateRtpVideoSender(config_->rtp.ssrcs,
rtp_video_sender_(transport_->CreateRtpVideoSender(
config_->rtp.ssrcs,
suspended_ssrcs,
suspended_payload_states,
config_->rtp,
@ -250,7 +258,8 @@ VideoSendStreamImpl::VideoSendStreamImpl(
stats_proxy_,
send_delay_stats),
event_log,
std::move(fec_controller))),
std::move(fec_controller),
CreateFrameEncryptionConfig(config_))),
weak_ptr_factory_(this) {
RTC_DCHECK_RUN_ON(worker_queue_);
RTC_LOG(LS_INFO) << "VideoSendStreamInternal: " << config_->ToString();

View File

@ -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(