blob: 41eddea17ee4b8127fcc2e3ad608d92acd2aa707 [file] [log] [blame]
Benjamin Wright00765292018-11-30 16:18:26 -08001/*
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
15#include "rtc_base/logging.h"
16#include "rtc_base/system/fallthrough.h"
Benjamin Wright168456c2018-12-07 11:31:25 -080017#include "system_wrappers/include/field_trial.h"
Benjamin Wright00765292018-11-30 16:18:26 -080018
19namespace webrtc {
20
21BufferedFrameDecryptor::BufferedFrameDecryptor(
22 OnDecryptedFrameCallback* decrypted_frame_callback,
Benjamin Wrighta5564482019-04-03 10:44:18 -070023 OnDecryptionStatusChangeCallback* decryption_status_change_callback)
Benjamin Wright168456c2018-12-07 11:31:25 -080024 : generic_descriptor_auth_experiment_(
25 field_trial::IsEnabled("WebRTC-GenericDescriptorAuth")),
Benjamin Wright52426ed2019-03-01 11:01:59 -080026 decrypted_frame_callback_(decrypted_frame_callback),
27 decryption_status_change_callback_(decryption_status_change_callback) {}
Benjamin Wright00765292018-11-30 16:18:26 -080028
29BufferedFrameDecryptor::~BufferedFrameDecryptor() {}
30
Benjamin Wrighta5564482019-04-03 10:44:18 -070031void BufferedFrameDecryptor::SetFrameDecryptor(
32 rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) {
33 frame_decryptor_ = std::move(frame_decryptor);
34}
35
Benjamin Wright00765292018-11-30 16:18:26 -080036void BufferedFrameDecryptor::ManageEncryptedFrame(
37 std::unique_ptr<video_coding::RtpFrameObject> encrypted_frame) {
38 switch (DecryptFrame(encrypted_frame.get())) {
39 case FrameDecision::kStash:
40 if (stashed_frames_.size() >= kMaxStashedFrames) {
Benjamin Wrighta5564482019-04-03 10:44:18 -070041 RTC_LOG(LS_WARNING) << "Encrypted frame stash full poping oldest item.";
Benjamin Wright00765292018-11-30 16:18:26 -080042 stashed_frames_.pop_front();
43 }
44 stashed_frames_.push_back(std::move(encrypted_frame));
45 break;
46 case FrameDecision::kDecrypted:
47 RetryStashedFrames();
48 decrypted_frame_callback_->OnDecryptedFrame(std::move(encrypted_frame));
49 break;
50 case FrameDecision::kDrop:
51 break;
52 }
53}
54
55BufferedFrameDecryptor::FrameDecision BufferedFrameDecryptor::DecryptFrame(
56 video_coding::RtpFrameObject* frame) {
57 // Optionally attempt to decrypt the raw video frame if it was provided.
58 if (frame_decryptor_ == nullptr) {
Benjamin Wrighta5564482019-04-03 10:44:18 -070059 RTC_LOG(LS_INFO) << "Frame decryption required but not attached to this "
60 "stream. Stashing frame.";
61 return FrameDecision::kStash;
Benjamin Wright00765292018-11-30 16:18:26 -080062 }
63 // When using encryption we expect the frame to have the generic descriptor.
64 absl::optional<RtpGenericFrameDescriptor> descriptor =
65 frame->GetGenericFrameDescriptor();
66 if (!descriptor) {
67 RTC_LOG(LS_ERROR) << "No generic frame descriptor found dropping frame.";
68 return FrameDecision::kDrop;
69 }
Benjamin Wright00765292018-11-30 16:18:26 -080070 // 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.
Niels Möller9c843902019-01-11 10:21:35 +010076 rtc::ArrayView<uint8_t> inline_decrypted_bitstream(frame->data(),
Benjamin Wright00765292018-11-30 16:18:26 -080077 max_plaintext_byte_size);
Benjamin Wright168456c2018-12-07 11:31:25 -080078
79 // Only enable authenticating the header if the field trial is enabled.
80 rtc::ArrayView<const uint8_t> additional_data;
81 if (generic_descriptor_auth_experiment_) {
82 additional_data = descriptor->GetByteRepresentation();
83 }
84
Benjamin Wright00765292018-11-30 16:18:26 -080085 // Attempt to decrypt the video frame.
Benjamin Wright2af5dcb2019-04-09 20:08:41 +000086 const FrameDecryptorInterface::Result decrypt_result =
87 frame_decryptor_->Decrypt(cricket::MEDIA_TYPE_VIDEO, /*csrcs=*/{},
88 additional_data, *frame,
89 inline_decrypted_bitstream);
Benjamin Wright52426ed2019-03-01 11:01:59 -080090 // Optionally call the callback if there was a change in status
Benjamin Wright2af5dcb2019-04-09 20:08:41 +000091 if (decrypt_result.status != last_status_) {
92 last_status_ = decrypt_result.status;
93 decryption_status_change_callback_->OnDecryptionStatusChange(
94 decrypt_result.status);
Benjamin Wright52426ed2019-03-01 11:01:59 -080095 }
96
Benjamin Wright2af5dcb2019-04-09 20:08:41 +000097 if (!decrypt_result.IsOk()) {
Benjamin Wright00765292018-11-30 16:18:26 -080098 // Only stash frames if we have never decrypted a frame before.
99 return first_frame_decrypted_ ? FrameDecision::kDrop
100 : FrameDecision::kStash;
101 }
Benjamin Wright2af5dcb2019-04-09 20:08:41 +0000102 RTC_CHECK_LE(decrypt_result.bytes_written, max_plaintext_byte_size);
Benjamin Wright00765292018-11-30 16:18:26 -0800103 // Update the frame to contain just the written bytes.
Benjamin Wright2af5dcb2019-04-09 20:08:41 +0000104 frame->set_size(decrypt_result.bytes_written);
Benjamin Wright00765292018-11-30 16:18:26 -0800105
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
114void BufferedFrameDecryptor::RetryStashedFrames() {
Benjamin Wright2108cc62019-04-08 16:50:04 -0700115 if (!stashed_frames_.empty()) {
116 RTC_LOG(LS_INFO) << "Retrying stashed encrypted frames. Count: "
117 << stashed_frames_.size();
118 }
Benjamin Wright00765292018-11-30 16:18:26 -0800119 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