1 /*
2 * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "video/buffered_frame_decryptor.h"
12
13 #include <utility>
14 #include <vector>
15
16 #include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
17 #include "modules/video_coding/frame_object.h"
18 #include "rtc_base/logging.h"
19 #include "system_wrappers/include/field_trial.h"
20
21 namespace webrtc {
22
BufferedFrameDecryptor(OnDecryptedFrameCallback * decrypted_frame_callback,OnDecryptionStatusChangeCallback * decryption_status_change_callback)23 BufferedFrameDecryptor::BufferedFrameDecryptor(
24 OnDecryptedFrameCallback* decrypted_frame_callback,
25 OnDecryptionStatusChangeCallback* decryption_status_change_callback)
26 : generic_descriptor_auth_experiment_(
27 !field_trial::IsDisabled("WebRTC-GenericDescriptorAuth")),
28 decrypted_frame_callback_(decrypted_frame_callback),
29 decryption_status_change_callback_(decryption_status_change_callback) {}
30
~BufferedFrameDecryptor()31 BufferedFrameDecryptor::~BufferedFrameDecryptor() {}
32
SetFrameDecryptor(rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor)33 void BufferedFrameDecryptor::SetFrameDecryptor(
34 rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
35 frame_decryptor_ = std::move(frame_decryptor);
36 }
37
ManageEncryptedFrame(std::unique_ptr<video_coding::RtpFrameObject> encrypted_frame)38 void BufferedFrameDecryptor::ManageEncryptedFrame(
39 std::unique_ptr<video_coding::RtpFrameObject> encrypted_frame) {
40 switch (DecryptFrame(encrypted_frame.get())) {
41 case FrameDecision::kStash:
42 if (stashed_frames_.size() >= kMaxStashedFrames) {
43 RTC_LOG(LS_WARNING) << "Encrypted frame stash full poping oldest item.";
44 stashed_frames_.pop_front();
45 }
46 stashed_frames_.push_back(std::move(encrypted_frame));
47 break;
48 case FrameDecision::kDecrypted:
49 RetryStashedFrames();
50 decrypted_frame_callback_->OnDecryptedFrame(std::move(encrypted_frame));
51 break;
52 case FrameDecision::kDrop:
53 break;
54 }
55 }
56
DecryptFrame(video_coding::RtpFrameObject * frame)57 BufferedFrameDecryptor::FrameDecision BufferedFrameDecryptor::DecryptFrame(
58 video_coding::RtpFrameObject* frame) {
59 // Optionally attempt to decrypt the raw video frame if it was provided.
60 if (frame_decryptor_ == nullptr) {
61 RTC_LOG(LS_INFO) << "Frame decryption required but not attached to this "
62 "stream. Stashing frame.";
63 return FrameDecision::kStash;
64 }
65 // When using encryption we expect the frame to have the generic descriptor.
66 if (frame->GetRtpVideoHeader().generic == absl::nullopt) {
67 RTC_LOG(LS_ERROR) << "No generic frame descriptor found dropping frame.";
68 return FrameDecision::kDrop;
69 }
70 // Retrieve the maximum possible size of the decrypted payload.
71 const size_t max_plaintext_byte_size =
72 frame_decryptor_->GetMaxPlaintextByteSize(cricket::MEDIA_TYPE_VIDEO,
73 frame->size());
74 RTC_CHECK_LE(max_plaintext_byte_size, frame->size());
75 // Place the decrypted frame inline into the existing frame.
76 rtc::ArrayView<uint8_t> inline_decrypted_bitstream(frame->data(),
77 max_plaintext_byte_size);
78
79 // Enable authenticating the header if the field trial isn't disabled.
80 std::vector<uint8_t> additional_data;
81 if (generic_descriptor_auth_experiment_) {
82 additional_data = RtpDescriptorAuthentication(frame->GetRtpVideoHeader());
83 }
84
85 // Attempt to decrypt the video frame.
86 const FrameDecryptorInterface::Result decrypt_result =
87 frame_decryptor_->Decrypt(cricket::MEDIA_TYPE_VIDEO, /*csrcs=*/{},
88 additional_data, *frame,
89 inline_decrypted_bitstream);
90 // Optionally call the callback if there was a change in status
91 if (decrypt_result.status != last_status_) {
92 last_status_ = decrypt_result.status;
93 decryption_status_change_callback_->OnDecryptionStatusChange(
94 decrypt_result.status);
95 }
96
97 if (!decrypt_result.IsOk()) {
98 // Only stash frames if we have never decrypted a frame before.
99 return first_frame_decrypted_ ? FrameDecision::kDrop
100 : FrameDecision::kStash;
101 }
102 RTC_CHECK_LE(decrypt_result.bytes_written, max_plaintext_byte_size);
103 // Update the frame to contain just the written bytes.
104 frame->set_size(decrypt_result.bytes_written);
105
106 // Indicate that all future fail to decrypt frames should be dropped.
107 if (!first_frame_decrypted_) {
108 first_frame_decrypted_ = true;
109 }
110
111 return FrameDecision::kDecrypted;
112 }
113
RetryStashedFrames()114 void BufferedFrameDecryptor::RetryStashedFrames() {
115 if (!stashed_frames_.empty()) {
116 RTC_LOG(LS_INFO) << "Retrying stashed encrypted frames. Count: "
117 << stashed_frames_.size();
118 }
119 for (auto& frame : stashed_frames_) {
120 if (DecryptFrame(frame.get()) == FrameDecision::kDecrypted) {
121 decrypted_frame_callback_->OnDecryptedFrame(std::move(frame));
122 }
123 }
124 stashed_frames_.clear();
125 }
126
127 } // namespace webrtc
128