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