chromium/chromecast/starboard/media/media/drm_util.cc

// Copyright 2024 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/starboard/media/media/drm_util.h"

#include <utility>

#include "base/check.h"
#include "base/logging.h"

namespace chromecast {
namespace media {

// Returns the length of an array.
template <typename T, size_t n>
static constexpr size_t ArrayLength(const T (&)[n]) {
  return n;
}

// Rather than hard-coding values here, we simply read the length of the
// relevant arrays in StarboardDrmSampleInfo.
constexpr int kMaxIvLength = ArrayLength(
    static_cast<StarboardDrmSampleInfo*>(nullptr)->initialization_vector);
constexpr int kMaxIdLength =
    ArrayLength(static_cast<StarboardDrmSampleInfo*>(nullptr)->identifier);

DrmInfoWrapper::DrmInfoWrapper() = default;

DrmInfoWrapper::DrmInfoWrapper(
    StarboardDrmSampleInfo drm_sample_info,
    std::vector<StarboardDrmSubSampleMapping> mappings) {
  drm_sample_info_ = std::move(drm_sample_info);
  subsample_mappings_ = std::move(mappings);

  UpdateSubsampleInfo();
}

DrmInfoWrapper::DrmInfoWrapper(DrmInfoWrapper&& other) {
  drm_sample_info_ = std::move(other.drm_sample_info_);
  subsample_mappings_ = std::move(other.subsample_mappings_);

  UpdateSubsampleInfo();
  other.drm_sample_info_ = std::nullopt;
}

DrmInfoWrapper& DrmInfoWrapper::operator=(DrmInfoWrapper&& other) {
  drm_sample_info_ = std::move(other.drm_sample_info_);
  subsample_mappings_ = std::move(other.subsample_mappings_);

  UpdateSubsampleInfo();
  other.drm_sample_info_ = std::nullopt;
  return *this;
}

DrmInfoWrapper::~DrmInfoWrapper() = default;

StarboardDrmSampleInfo* DrmInfoWrapper::GetDrmSampleInfo() {
  return drm_sample_info_ ? &*drm_sample_info_ : nullptr;
}

void DrmInfoWrapper::UpdateSubsampleInfo() {
  if (!drm_sample_info_) {
    return;
  }

  drm_sample_info_->subsample_count = subsample_mappings_.size();
  drm_sample_info_->subsample_mapping =
      subsample_mappings_.empty() ? nullptr : subsample_mappings_.data();
}

DrmInfoWrapper GetDrmInfo(const CastDecoderBuffer& buffer) {
  const CastDecryptConfig* decrypt_config = buffer.decrypt_config();
  if (!decrypt_config) {
    return DrmInfoWrapper();
  }

  // Populate drm_sample_info.
  StarboardDrmSampleInfo drm_info;

  switch (decrypt_config->encryption_scheme()) {
    case EncryptionScheme::kUnencrypted:
      return DrmInfoWrapper();
    case EncryptionScheme::kAesCtr:
      drm_info.encryption_scheme = kStarboardDrmEncryptionSchemeAesCtr;
      break;
    case EncryptionScheme::kAesCbc:
      drm_info.encryption_scheme = kStarboardDrmEncryptionSchemeAesCbc;
      break;
  }

  drm_info.encryption_pattern.crypt_byte_block =
      decrypt_config->pattern().encrypt_blocks;
  drm_info.encryption_pattern.skip_byte_block =
      decrypt_config->pattern().skip_blocks;

  int iv_size = decrypt_config->iv().size();
  if (iv_size > kMaxIvLength) {
    LOG(ERROR)
        << "Encrypted buffer contained too many initialization vector values "
           "(max supported by Starboard is "
        << kMaxIvLength << "): " << iv_size;
    iv_size = kMaxIvLength;
  }
  for (int i = 0; i < iv_size; ++i) {
    drm_info.initialization_vector[i] =
        static_cast<uint8_t>(decrypt_config->iv().at(i));
  }
  drm_info.initialization_vector_size = iv_size;

  int id_size = decrypt_config->key_id().size();
  if (id_size > kMaxIdLength) {
    LOG(ERROR) << "Encrypted buffer contained too many key ID vector values "
                  "(max supported by Starboard is "
               << kMaxIdLength << "): " << id_size;
    id_size = kMaxIdLength;
  }
  for (int i = 0; i < id_size; ++i) {
    drm_info.identifier[i] =
        static_cast<uint8_t>(decrypt_config->key_id().at(i));
  }
  drm_info.identifier_size = id_size;

  // Populate subsample_mappings.
  std::vector<StarboardDrmSubSampleMapping> subsample_mappings;
  subsample_mappings.reserve(decrypt_config->subsamples().size());
  for (const SubsampleEntry& subsample : decrypt_config->subsamples()) {
    StarboardDrmSubSampleMapping mapping;
    mapping.clear_byte_count = subsample.clear_bytes;
    mapping.encrypted_byte_count = subsample.cypher_bytes;
    subsample_mappings.push_back(std::move(mapping));
  }

  return DrmInfoWrapper(std::move(drm_info), std::move(subsample_mappings));
}

}  // namespace media
}  // namespace chromecast