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