/* * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "video/buffered_frame_decryptor.h" #include #include #include "modules/rtp_rtcp/source/frame_object.h" #include "modules/rtp_rtcp/source/rtp_descriptor_authentication.h" #include "rtc_base/logging.h" #include "system_wrappers/include/field_trial.h" namespace webrtc { BufferedFrameDecryptor::BufferedFrameDecryptor( OnDecryptedFrameCallback* decrypted_frame_callback, OnDecryptionStatusChangeCallback* decryption_status_change_callback, const FieldTrialsView& field_trials) : generic_descriptor_auth_experiment_( !field_trials.IsDisabled("WebRTC-GenericDescriptorAuth")), decrypted_frame_callback_(decrypted_frame_callback), decryption_status_change_callback_(decryption_status_change_callback) {} BufferedFrameDecryptor::~BufferedFrameDecryptor() {} void BufferedFrameDecryptor::SetFrameDecryptor( rtc::scoped_refptr frame_decryptor) { frame_decryptor_ = std::move(frame_decryptor); } void BufferedFrameDecryptor::ManageEncryptedFrame( std::unique_ptr encrypted_frame) { switch (DecryptFrame(encrypted_frame.get())) { case FrameDecision::kStash: if (stashed_frames_.size() >= kMaxStashedFrames) { RTC_LOG(LS_WARNING) << "Encrypted frame stash full poping oldest item."; stashed_frames_.pop_front(); } stashed_frames_.push_back(std::move(encrypted_frame)); break; case FrameDecision::kDecrypted: RetryStashedFrames(); decrypted_frame_callback_->OnDecryptedFrame(std::move(encrypted_frame)); break; case FrameDecision::kDrop: break; } } BufferedFrameDecryptor::FrameDecision BufferedFrameDecryptor::DecryptFrame( RtpFrameObject* frame) { // Optionally attempt to decrypt the raw video frame if it was provided. if (frame_decryptor_ == nullptr) { RTC_LOG(LS_INFO) << "Frame decryption required but not attached to this " "stream. Stashing frame."; return FrameDecision::kStash; } // Retrieve the maximum possible size of the decrypted payload. const size_t max_plaintext_byte_size = frame_decryptor_->GetMaxPlaintextByteSize(cricket::MEDIA_TYPE_VIDEO, frame->size()); RTC_CHECK_LE(max_plaintext_byte_size, frame->size()); // Place the decrypted frame inline into the existing frame. rtc::ArrayView inline_decrypted_bitstream(frame->mutable_data(), max_plaintext_byte_size); // Enable authenticating the header if the field trial isn't disabled. std::vector additional_data; if (generic_descriptor_auth_experiment_) { additional_data = RtpDescriptorAuthentication(frame->GetRtpVideoHeader()); } // Attempt to decrypt the video frame. const FrameDecryptorInterface::Result decrypt_result = frame_decryptor_->Decrypt(cricket::MEDIA_TYPE_VIDEO, /*csrcs=*/{}, additional_data, *frame, inline_decrypted_bitstream); // Optionally call the callback if there was a change in status if (decrypt_result.status != last_status_) { last_status_ = decrypt_result.status; decryption_status_change_callback_->OnDecryptionStatusChange( decrypt_result.status); } if (!decrypt_result.IsOk()) { // Only stash frames if we have never decrypted a frame before. return first_frame_decrypted_ ? FrameDecision::kDrop : FrameDecision::kStash; } RTC_CHECK_LE(decrypt_result.bytes_written, max_plaintext_byte_size); // Update the frame to contain just the written bytes. frame->set_size(decrypt_result.bytes_written); // Indicate that all future fail to decrypt frames should be dropped. if (!first_frame_decrypted_) { first_frame_decrypted_ = true; } return FrameDecision::kDecrypted; } void BufferedFrameDecryptor::RetryStashedFrames() { if (!stashed_frames_.empty()) { RTC_LOG(LS_INFO) << "Retrying stashed encrypted frames. Count: " << stashed_frames_.size(); } for (auto& frame : stashed_frames_) { if (DecryptFrame(frame.get()) == FrameDecision::kDecrypted) { decrypted_frame_callback_->OnDecryptedFrame(std::move(frame)); } } stashed_frames_.clear(); } } // namespace webrtc