xref: /aosp_15_r20/external/webrtc/video/buffered_frame_decryptor.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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,const FieldTrialsView & field_trials)23 BufferedFrameDecryptor::BufferedFrameDecryptor(
24     OnDecryptedFrameCallback* decrypted_frame_callback,
25     OnDecryptionStatusChangeCallback* decryption_status_change_callback,
26     const FieldTrialsView& field_trials)
27     : generic_descriptor_auth_experiment_(
28           !field_trials.IsDisabled("WebRTC-GenericDescriptorAuth")),
29       decrypted_frame_callback_(decrypted_frame_callback),
30       decryption_status_change_callback_(decryption_status_change_callback) {}
31 
~BufferedFrameDecryptor()32 BufferedFrameDecryptor::~BufferedFrameDecryptor() {}
33 
SetFrameDecryptor(rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor)34 void BufferedFrameDecryptor::SetFrameDecryptor(
35     rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
36   frame_decryptor_ = std::move(frame_decryptor);
37 }
38 
ManageEncryptedFrame(std::unique_ptr<RtpFrameObject> encrypted_frame)39 void BufferedFrameDecryptor::ManageEncryptedFrame(
40     std::unique_ptr<RtpFrameObject> encrypted_frame) {
41   switch (DecryptFrame(encrypted_frame.get())) {
42     case FrameDecision::kStash:
43       if (stashed_frames_.size() >= kMaxStashedFrames) {
44         RTC_LOG(LS_WARNING) << "Encrypted frame stash full poping oldest item.";
45         stashed_frames_.pop_front();
46       }
47       stashed_frames_.push_back(std::move(encrypted_frame));
48       break;
49     case FrameDecision::kDecrypted:
50       RetryStashedFrames();
51       decrypted_frame_callback_->OnDecryptedFrame(std::move(encrypted_frame));
52       break;
53     case FrameDecision::kDrop:
54       break;
55   }
56 }
57 
DecryptFrame(RtpFrameObject * frame)58 BufferedFrameDecryptor::FrameDecision BufferedFrameDecryptor::DecryptFrame(
59     RtpFrameObject* frame) {
60   // Optionally attempt to decrypt the raw video frame if it was provided.
61   if (frame_decryptor_ == nullptr) {
62     RTC_LOG(LS_INFO) << "Frame decryption required but not attached to this "
63                         "stream. Stashing frame.";
64     return FrameDecision::kStash;
65   }
66   // Retrieve the maximum possible size of the decrypted payload.
67   const size_t max_plaintext_byte_size =
68       frame_decryptor_->GetMaxPlaintextByteSize(cricket::MEDIA_TYPE_VIDEO,
69                                                 frame->size());
70   RTC_CHECK_LE(max_plaintext_byte_size, frame->size());
71   // Place the decrypted frame inline into the existing frame.
72   rtc::ArrayView<uint8_t> inline_decrypted_bitstream(frame->mutable_data(),
73                                                      max_plaintext_byte_size);
74 
75   // Enable authenticating the header if the field trial isn't disabled.
76   std::vector<uint8_t> additional_data;
77   if (generic_descriptor_auth_experiment_) {
78     additional_data = RtpDescriptorAuthentication(frame->GetRtpVideoHeader());
79   }
80 
81   // Attempt to decrypt the video frame.
82   const FrameDecryptorInterface::Result decrypt_result =
83       frame_decryptor_->Decrypt(cricket::MEDIA_TYPE_VIDEO, /*csrcs=*/{},
84                                 additional_data, *frame,
85                                 inline_decrypted_bitstream);
86   // Optionally call the callback if there was a change in status
87   if (decrypt_result.status != last_status_) {
88     last_status_ = decrypt_result.status;
89     decryption_status_change_callback_->OnDecryptionStatusChange(
90         decrypt_result.status);
91   }
92 
93   if (!decrypt_result.IsOk()) {
94     // Only stash frames if we have never decrypted a frame before.
95     return first_frame_decrypted_ ? FrameDecision::kDrop
96                                   : FrameDecision::kStash;
97   }
98   RTC_CHECK_LE(decrypt_result.bytes_written, max_plaintext_byte_size);
99   // Update the frame to contain just the written bytes.
100   frame->set_size(decrypt_result.bytes_written);
101 
102   // Indicate that all future fail to decrypt frames should be dropped.
103   if (!first_frame_decrypted_) {
104     first_frame_decrypted_ = true;
105   }
106 
107   return FrameDecision::kDecrypted;
108 }
109 
RetryStashedFrames()110 void BufferedFrameDecryptor::RetryStashedFrames() {
111   if (!stashed_frames_.empty()) {
112     RTC_LOG(LS_INFO) << "Retrying stashed encrypted frames. Count: "
113                      << stashed_frames_.size();
114   }
115   for (auto& frame : stashed_frames_) {
116     if (DecryptFrame(frame.get()) == FrameDecision::kDecrypted) {
117       decrypted_frame_callback_->OnDecryptedFrame(std::move(frame));
118     }
119   }
120   stashed_frames_.clear();
121 }
122 
123 }  // namespace webrtc
124