chromium/chromecast/media/cma/pipeline/backend_decryptor.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromecast/media/cma/pipeline/backend_decryptor.h"

#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "chromecast/base/task_runner_impl.h"
#include "chromecast/media/cma/pipeline/decrypt_util.h"

namespace chromecast {
namespace media {

BackendDecryptor::BackendDecryptor(EncryptionScheme scheme)
    : decrypt_success_(true),
      wait_eos_(false),
      task_runner_(new TaskRunnerImpl) {
  DCHECK(MediaPipelineBackend::CreateAudioDecryptor);

  task_runner_ = std::make_unique<TaskRunnerImpl>();
  decryptor_ = base::WrapUnique(
      MediaPipelineBackend::CreateAudioDecryptor(scheme, task_runner_.get()));

  DCHECK(decryptor_);
  decryptor_->SetDelegate(this);
}

BackendDecryptor::~BackendDecryptor() = default;

void BackendDecryptor::Init(const DecryptCB& decrypt_cb) {
  DCHECK(!decrypt_cb_);
  decrypt_cb_ = decrypt_cb;
}

void BackendDecryptor::Decrypt(scoped_refptr<DecoderBufferBase> buffer) {
  DCHECK(!wait_eos_);

  // Push both clear and encrypted buffers to backend, so that |decryptor_|
  // won't be blocked if there are not enough encrypted buffers. EOS buffer is
  // also needed so that the last buffer can be flushed.
  pending_buffers_.push(buffer);

  if (buffer->end_of_stream())
    wait_eos_ = true;

  MediaPipelineBackend::BufferStatus status = decryptor_->PushBufferForDecrypt(
      buffer.get(),
      buffer->end_of_stream() ? nullptr : buffer->writable_data());

  if (status != MediaPipelineBackend::kBufferPending)
    OnPushBufferForDecryptComplete(status);
}

void BackendDecryptor::OnPushBufferForDecryptComplete(
    MediaPipelineBackend::BufferStatus status) {
  // If the pushed buffer is EOS, the callback should be called when all the
  // buffers are decrypted.
  if (wait_eos_)
    return;

  DCHECK(decrypt_cb_);
  decrypt_cb_.Run(
      decrypt_success_ && status == MediaPipelineBackend::kBufferSuccess,
      std::move(ready_buffers_));
}

void BackendDecryptor::OnDecryptComplete(bool success) {
  DCHECK(!pending_buffers_.empty());

  // Cache the success value and return it in OnPushBufferForDecryptComplete.
  decrypt_success_ &= success;

  scoped_refptr<DecoderBufferBase> buffer = std::move(pending_buffers_.front());
  pending_buffers_.pop();

  ready_buffers_.push(buffer->end_of_stream() || !buffer->decrypt_config()
                          ? buffer
                          : base::MakeRefCounted<DecoderBufferClear>(buffer));

  if (wait_eos_ && buffer->end_of_stream()) {
    // Last frame, all the buffers should be decrypted.
    DCHECK(pending_buffers_.empty());
    DCHECK(decrypt_cb_);
    LOG(INFO) << "Return all the ready buffers, size = "
              << ready_buffers_.size();
    decrypt_cb_.Run(decrypt_success_, std::move(ready_buffers_));
  }
}

}  // namespace media
}  // namespace chromecast