blob: 61e88122fd33208a379248655db68dfcf8492a00 [file] [log] [blame]
Benjamin Wright00765292018-12-01 00:18:261/*
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>
Danil Chapovalove209fe62020-02-12 18:25:5714#include <vector>
Benjamin Wright00765292018-12-01 00:18:2615
Tony Herrebe9b5762023-02-03 11:29:0416#include "modules/rtp_rtcp/source/frame_object.h"
Danil Chapovalove209fe62020-02-12 18:25:5717#include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h"
Benjamin Wright00765292018-12-01 00:18:2618#include "rtc_base/logging.h"
Benjamin Wright168456c2018-12-07 19:31:2519#include "system_wrappers/include/field_trial.h"
Benjamin Wright00765292018-12-01 00:18:2620
21namespace webrtc {
22
23BufferedFrameDecryptor::BufferedFrameDecryptor(
24 OnDecryptedFrameCallback* decrypted_frame_callback,
Jonas Oreland8ca06132022-03-14 11:52:4825 OnDecryptionStatusChangeCallback* decryption_status_change_callback,
Jonas Orelande62c2f22022-03-29 09:04:4826 const FieldTrialsView& field_trials)
Benjamin Wright168456c2018-12-07 19:31:2527 : generic_descriptor_auth_experiment_(
Jonas Oreland8ca06132022-03-14 11:52:4828 !field_trials.IsDisabled("WebRTC-GenericDescriptorAuth")),
Benjamin Wright52426ed2019-03-01 19:01:5929 decrypted_frame_callback_(decrypted_frame_callback),
30 decryption_status_change_callback_(decryption_status_change_callback) {}
Benjamin Wright00765292018-12-01 00:18:2631
32BufferedFrameDecryptor::~BufferedFrameDecryptor() {}
33
Benjamin Wrighta5564482019-04-03 17:44:1834void BufferedFrameDecryptor::SetFrameDecryptor(
35 rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
36 frame_decryptor_ = std::move(frame_decryptor);
37}
38
Benjamin Wright00765292018-12-01 00:18:2639void BufferedFrameDecryptor::ManageEncryptedFrame(
philipelca188092021-03-23 11:00:4940 std::unique_ptr<RtpFrameObject> encrypted_frame) {
Benjamin Wright00765292018-12-01 00:18:2641 switch (DecryptFrame(encrypted_frame.get())) {
42 case FrameDecision::kStash:
43 if (stashed_frames_.size() >= kMaxStashedFrames) {
Benjamin Wrighta5564482019-04-03 17:44:1844 RTC_LOG(LS_WARNING) << "Encrypted frame stash full poping oldest item.";
Benjamin Wright00765292018-12-01 00:18:2645 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
58BufferedFrameDecryptor::FrameDecision BufferedFrameDecryptor::DecryptFrame(
philipelca188092021-03-23 11:00:4959 RtpFrameObject* frame) {
Benjamin Wright00765292018-12-01 00:18:2660 // Optionally attempt to decrypt the raw video frame if it was provided.
61 if (frame_decryptor_ == nullptr) {
Benjamin Wrighta5564482019-04-03 17:44:1862 RTC_LOG(LS_INFO) << "Frame decryption required but not attached to this "
63 "stream. Stashing frame.";
64 return FrameDecision::kStash;
Benjamin Wright00765292018-12-01 00:18:2665 }
Benjamin Wright00765292018-12-01 00:18:2666 // 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.
Niels Möller08ae7ce2020-09-23 13:58:1272 rtc::ArrayView<uint8_t> inline_decrypted_bitstream(frame->mutable_data(),
Benjamin Wright00765292018-12-01 00:18:2673 max_plaintext_byte_size);
Benjamin Wright168456c2018-12-07 19:31:2574
Markus Handellc1cbf6b2020-02-17 19:03:5775 // Enable authenticating the header if the field trial isn't disabled.
Danil Chapovalove209fe62020-02-12 18:25:5776 std::vector<uint8_t> additional_data;
Benjamin Wright168456c2018-12-07 19:31:2577 if (generic_descriptor_auth_experiment_) {
Danil Chapovalove209fe62020-02-12 18:25:5778 additional_data = RtpDescriptorAuthentication(frame->GetRtpVideoHeader());
Benjamin Wright168456c2018-12-07 19:31:2579 }
80
Benjamin Wright00765292018-12-01 00:18:2681 // Attempt to decrypt the video frame.
Benjamin Wright2af5dcb2019-04-09 20:08:4182 const FrameDecryptorInterface::Result decrypt_result =
83 frame_decryptor_->Decrypt(cricket::MEDIA_TYPE_VIDEO, /*csrcs=*/{},
84 additional_data, *frame,
85 inline_decrypted_bitstream);
Benjamin Wright52426ed2019-03-01 19:01:5986 // Optionally call the callback if there was a change in status
Benjamin Wright2af5dcb2019-04-09 20:08:4187 if (decrypt_result.status != last_status_) {
88 last_status_ = decrypt_result.status;
89 decryption_status_change_callback_->OnDecryptionStatusChange(
90 decrypt_result.status);
Benjamin Wright52426ed2019-03-01 19:01:5991 }
92
Benjamin Wright2af5dcb2019-04-09 20:08:4193 if (!decrypt_result.IsOk()) {
Benjamin Wright00765292018-12-01 00:18:2694 // Only stash frames if we have never decrypted a frame before.
95 return first_frame_decrypted_ ? FrameDecision::kDrop
96 : FrameDecision::kStash;
97 }
Benjamin Wright2af5dcb2019-04-09 20:08:4198 RTC_CHECK_LE(decrypt_result.bytes_written, max_plaintext_byte_size);
Benjamin Wright00765292018-12-01 00:18:2699 // Update the frame to contain just the written bytes.
Benjamin Wright2af5dcb2019-04-09 20:08:41100 frame->set_size(decrypt_result.bytes_written);
Benjamin Wright00765292018-12-01 00:18:26101
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
110void BufferedFrameDecryptor::RetryStashedFrames() {
Benjamin Wright2108cc62019-04-08 23:50:04111 if (!stashed_frames_.empty()) {
112 RTC_LOG(LS_INFO) << "Retrying stashed encrypted frames. Count: "
113 << stashed_frames_.size();
114 }
Benjamin Wright00765292018-12-01 00:18:26115 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