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