// 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 "media/cdm/cdm_type_conversion.h"

#include <stdint.h>

#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/numerics/safe_conversions.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/geometry/size.h"

// Note: Unexpected values must be handled explicitly since some of these
// functions may be used at either side of the CDM interface, and it's possible
// invalid values are passed in. For example, Chromium loading an older CDM, or
// the CDM is loaded by a non-Chromium browser.

namespace media {

namespace {

cdm::ColorRange ToCdmColorRange(gfx::ColorSpace::RangeID range) {
  switch (range) {
    case gfx::ColorSpace::RangeID::INVALID:
      return cdm::ColorRange::kInvalid;
    case gfx::ColorSpace::RangeID::LIMITED:
      return cdm::ColorRange::kLimited;
    case gfx::ColorSpace::RangeID::FULL:
      return cdm::ColorRange::kFull;
    case gfx::ColorSpace::RangeID::DERIVED:
      return cdm::ColorRange::kDerived;
  }

  NOTREACHED() << "Unexpected color range";
}

gfx::ColorSpace::RangeID ToGfxColorRange(cdm::ColorRange range) {
  switch (range) {
    case cdm::ColorRange::kInvalid:
      return gfx::ColorSpace::RangeID::INVALID;
    case cdm::ColorRange::kLimited:
      return gfx::ColorSpace::RangeID::LIMITED;
    case cdm::ColorRange::kFull:
      return gfx::ColorSpace::RangeID::FULL;
    case cdm::ColorRange::kDerived:
      return gfx::ColorSpace::RangeID::DERIVED;
  }

  NOTREACHED() << "Unexpected color range";
}

}  // namespace

// Color Converters

cdm::ColorSpace ToCdmColorSpace(const VideoColorSpace& color_space) {
  // Cast is okay because both VideoColorSpace and cdm::ColorSpace follow the
  // standard ISO 23001-8:2016.
  return {base::checked_cast<uint8_t>(color_space.primaries()),
          base::checked_cast<uint8_t>(color_space.transfer()),
          base::checked_cast<uint8_t>(color_space.matrix()),
          ToCdmColorRange(color_space.range())};
}

VideoColorSpace ToMediaColorSpace(const cdm::ColorSpace& color_space) {
  return VideoColorSpace(color_space.primary_id, color_space.transfer_id,
                         color_space.matrix_id,
                         ToGfxColorRange(color_space.range));
}

// CDM Converters

cdm::HdcpVersion ToCdmHdcpVersion(HdcpVersion hdcp_version) {
  switch (hdcp_version) {
    case HdcpVersion::kHdcpVersionNone:
      return cdm::kHdcpVersionNone;
    case HdcpVersion::kHdcpVersion1_0:
      return cdm::kHdcpVersion1_0;
    case HdcpVersion::kHdcpVersion1_1:
      return cdm::kHdcpVersion1_1;
    case HdcpVersion::kHdcpVersion1_2:
      return cdm::kHdcpVersion1_2;
    case HdcpVersion::kHdcpVersion1_3:
      return cdm::kHdcpVersion1_3;
    case HdcpVersion::kHdcpVersion1_4:
      return cdm::kHdcpVersion1_4;
    case HdcpVersion::kHdcpVersion2_0:
      return cdm::kHdcpVersion2_0;
    case HdcpVersion::kHdcpVersion2_1:
      return cdm::kHdcpVersion2_1;
    case HdcpVersion::kHdcpVersion2_2:
      return cdm::kHdcpVersion2_2;
    case HdcpVersion::kHdcpVersion2_3:
      return cdm::kHdcpVersion2_3;
  }

  NOTREACHED() << "Unexpected HdcpVersion";
}

cdm::SessionType ToCdmSessionType(CdmSessionType session_type) {
  switch (session_type) {
    case CdmSessionType::kTemporary:
      return cdm::kTemporary;
    case CdmSessionType::kPersistentLicense:
      return cdm::kPersistentLicense;
  }

  NOTREACHED() << "Unexpected session type " << static_cast<int>(session_type);
}

CdmSessionType ToMediaSessionType(cdm::SessionType session_type) {
  switch (session_type) {
    case cdm::kTemporary:
      return CdmSessionType::kTemporary;
    case cdm::kPersistentLicense:
      return CdmSessionType::kPersistentLicense;
  }

  NOTREACHED() << "Unexpected cdm::SessionType " << session_type;
}

cdm::InitDataType ToCdmInitDataType(EmeInitDataType init_data_type) {
  switch (init_data_type) {
    case EmeInitDataType::CENC:
      return cdm::kCenc;
    case EmeInitDataType::KEYIDS:
      return cdm::kKeyIds;
    case EmeInitDataType::WEBM:
      return cdm::kWebM;
    case EmeInitDataType::UNKNOWN:
      break;
  }

  NOTREACHED() << "Unexpected EmeInitDataType";
}

EmeInitDataType ToEmeInitDataType(cdm::InitDataType init_data_type) {
  switch (init_data_type) {
    case cdm::kCenc:
      return EmeInitDataType::CENC;
    case cdm::kKeyIds:
      return EmeInitDataType::KEYIDS;
    case cdm::kWebM:
      return EmeInitDataType::WEBM;
  }

  NOTREACHED() << "Unexpected cdm::InitDataType " << init_data_type;
}

CdmKeyInformation::KeyStatus ToMediaKeyStatus(cdm::KeyStatus status) {
  switch (status) {
    case cdm::kUsable:
      return CdmKeyInformation::USABLE;
    case cdm::kInternalError:
      return CdmKeyInformation::INTERNAL_ERROR;
    case cdm::kExpired:
      return CdmKeyInformation::EXPIRED;
    case cdm::kOutputRestricted:
      return CdmKeyInformation::OUTPUT_RESTRICTED;
    case cdm::kOutputDownscaled:
      return CdmKeyInformation::OUTPUT_DOWNSCALED;
    case cdm::kStatusPending:
      return CdmKeyInformation::KEY_STATUS_PENDING;
    case cdm::kReleased:
      return CdmKeyInformation::RELEASED;
  }

  NOTREACHED() << "Unexpected cdm::KeyStatus " << status;
}

cdm::KeyStatus ToCdmKeyStatus(CdmKeyInformation::KeyStatus status) {
  switch (status) {
    case CdmKeyInformation::KeyStatus::USABLE:
      return cdm::kUsable;
    case CdmKeyInformation::KeyStatus::INTERNAL_ERROR:
    // Not compatible with cdm::KeyStatus.
    case CdmKeyInformation::KeyStatus::USABLE_IN_FUTURE:
      return cdm::kInternalError;
    case CdmKeyInformation::KeyStatus::EXPIRED:
      return cdm::kExpired;
    case CdmKeyInformation::KeyStatus::OUTPUT_RESTRICTED:
      return cdm::kOutputRestricted;
    case CdmKeyInformation::KeyStatus::OUTPUT_DOWNSCALED:
      return cdm::kOutputDownscaled;
    case CdmKeyInformation::KeyStatus::KEY_STATUS_PENDING:
      return cdm::kStatusPending;
    case CdmKeyInformation::KeyStatus::RELEASED:
      return cdm::kReleased;
  }

  NOTREACHED() << "Unexpected CdmKeyInformation::KeyStatus " << status;
}

CdmKeyInformation::KeyStatus ToMediaKeyStatus(cdm::KeyStatus_2 status) {
  switch (status) {
    case cdm::KeyStatus_2::kUsable:
      return CdmKeyInformation::USABLE;
    case cdm::KeyStatus_2::kInternalError:
      return CdmKeyInformation::INTERNAL_ERROR;
    case cdm::KeyStatus_2::kExpired:
      return CdmKeyInformation::EXPIRED;
    case cdm::KeyStatus_2::kOutputRestricted:
      return CdmKeyInformation::OUTPUT_RESTRICTED;
    case cdm::KeyStatus_2::kOutputDownscaled:
      return CdmKeyInformation::OUTPUT_DOWNSCALED;
    case cdm::KeyStatus_2::kStatusPending:
      return CdmKeyInformation::KEY_STATUS_PENDING;
    case cdm::KeyStatus_2::kReleased:
      return CdmKeyInformation::RELEASED;
    case cdm::KeyStatus_2::kUsableInFuture:
      return CdmKeyInformation::USABLE_IN_FUTURE;
  }
  DVLOG(1) << "Unexpected cdm::KeyStatus_2 " << static_cast<uint32_t>(status);
  return CdmKeyInformation::INTERNAL_ERROR;  // Default in error case
}

cdm::KeyStatus_2 ToCdmKeyStatus_2(CdmKeyInformation::KeyStatus status) {
  switch (status) {
    case CdmKeyInformation::USABLE:
      return cdm::KeyStatus_2::kUsable;
    case CdmKeyInformation::INTERNAL_ERROR:
      return cdm::KeyStatus_2::kInternalError;
    case CdmKeyInformation::EXPIRED:
      return cdm::KeyStatus_2::kExpired;
    case CdmKeyInformation::OUTPUT_RESTRICTED:
      return cdm::KeyStatus_2::kOutputRestricted;
    case CdmKeyInformation::OUTPUT_DOWNSCALED:
      return cdm::KeyStatus_2::kOutputDownscaled;
    case CdmKeyInformation::KEY_STATUS_PENDING:
      return cdm::KeyStatus_2::kStatusPending;
    case CdmKeyInformation::RELEASED:
      return cdm::KeyStatus_2::kReleased;
    case CdmKeyInformation::USABLE_IN_FUTURE:
      return cdm::KeyStatus_2::kUsableInFuture;
  }
  DVLOG(1) << "Unexpected CdmKeyInformation::KeyStatus "
           << static_cast<int>(status);
  return cdm::KeyStatus_2::kInternalError;  // Default in error case
}

cdm::EncryptionScheme ToCdmEncryptionScheme(EncryptionScheme scheme) {
  switch (scheme) {
    case EncryptionScheme::kUnencrypted:
      return cdm::EncryptionScheme::kUnencrypted;
    case EncryptionScheme::kCenc:
      return cdm::EncryptionScheme::kCenc;
    case EncryptionScheme::kCbcs:
      return cdm::EncryptionScheme::kCbcs;
  }

  NOTREACHED() << "Unexpected EncryptionScheme";
}

CdmPromise::Exception ToMediaCdmPromiseException(cdm::Exception exception) {
  switch (exception) {
    case cdm::kExceptionTypeError:
      return CdmPromise::Exception::TYPE_ERROR;
    case cdm::kExceptionNotSupportedError:
      return CdmPromise::Exception::NOT_SUPPORTED_ERROR;
    case cdm::kExceptionInvalidStateError:
      return CdmPromise::Exception::INVALID_STATE_ERROR;
    case cdm::kExceptionQuotaExceededError:
      return CdmPromise::Exception::QUOTA_EXCEEDED_ERROR;
  }

  NOTREACHED() << "Unexpected cdm::Exception " << exception;
}

cdm::Exception ToCdmException(CdmPromise::Exception exception) {
  switch (exception) {
    case CdmPromise::Exception::NOT_SUPPORTED_ERROR:
      return cdm::kExceptionNotSupportedError;
    case CdmPromise::Exception::INVALID_STATE_ERROR:
      return cdm::kExceptionInvalidStateError;
    case CdmPromise::Exception::TYPE_ERROR:
      return cdm::kExceptionTypeError;
    case CdmPromise::Exception::QUOTA_EXCEEDED_ERROR:
      return cdm::kExceptionQuotaExceededError;
  }

  NOTREACHED() << "Unexpected CdmPromise::Exception";
}

CdmMessageType ToMediaMessageType(cdm::MessageType message_type) {
  switch (message_type) {
    case cdm::kLicenseRequest:
      return CdmMessageType::LICENSE_REQUEST;
    case cdm::kLicenseRenewal:
      return CdmMessageType::LICENSE_RENEWAL;
    case cdm::kLicenseRelease:
      return CdmMessageType::LICENSE_RELEASE;
    case cdm::kIndividualizationRequest:
      return CdmMessageType::INDIVIDUALIZATION_REQUEST;
  }

  NOTREACHED() << "Unexpected cdm::MessageType " << message_type;
}

cdm::MessageType ToCdmMessageType(CdmMessageType message_type) {
  switch (message_type) {
    case CdmMessageType::LICENSE_REQUEST:
      return cdm::kLicenseRequest;
    case CdmMessageType::LICENSE_RENEWAL:
      return cdm::kLicenseRenewal;
    case CdmMessageType::LICENSE_RELEASE:
      return cdm::kLicenseRelease;
    case CdmMessageType::INDIVIDUALIZATION_REQUEST:
      return cdm::kIndividualizationRequest;
  }

  NOTREACHED() << "Unexpected CdmMessageType";
}

cdm::StreamType ToCdmStreamType(Decryptor::StreamType stream_type) {
  switch (stream_type) {
    case Decryptor::kAudio:
      return cdm::kStreamTypeAudio;
    case Decryptor::kVideo:
      return cdm::kStreamTypeVideo;
  }

  NOTREACHED() << "Unexpected Decryptor::StreamType " << stream_type;
}

Decryptor::Status ToMediaDecryptorStatus(cdm::Status status) {
  switch (status) {
    case cdm::kSuccess:
      return Decryptor::kSuccess;
    case cdm::kNoKey:
      return Decryptor::kNoKey;
    case cdm::kNeedMoreData:
      return Decryptor::kNeedMoreData;
    case cdm::kDecryptError:
      return Decryptor::kError;
    case cdm::kDecodeError:
      return Decryptor::kError;
    case cdm::kInitializationError:
    case cdm::kDeferredInitialization:
      break;
  }

  NOTREACHED() << "Unexpected cdm::Status " << status;
}

// Audio Converters

cdm::AudioCodec ToCdmAudioCodec(AudioCodec codec) {
  switch (codec) {
    case AudioCodec::kVorbis:
      return cdm::kCodecVorbis;
    case AudioCodec::kAAC:
      return cdm::kCodecAac;
    default:
      DVLOG(1) << "Unsupported AudioCodec " << codec;
      return cdm::kUnknownAudioCodec;
  }
}

SampleFormat ToMediaSampleFormat(cdm::AudioFormat format) {
  switch (format) {
    case cdm::kAudioFormatU8:
      return kSampleFormatU8;
    case cdm::kAudioFormatS16:
      return kSampleFormatS16;
    case cdm::kAudioFormatS32:
      return kSampleFormatS32;
    case cdm::kAudioFormatF32:
      return kSampleFormatF32;
    case cdm::kAudioFormatPlanarS16:
      return kSampleFormatPlanarS16;
    case cdm::kAudioFormatPlanarF32:
      return kSampleFormatPlanarF32;
    case cdm::kUnknownAudioFormat:
      return kUnknownSampleFormat;
  }

  NOTREACHED() << "Unexpected cdm::AudioFormat " << format;
}

// Video Converters

cdm::VideoCodec ToCdmVideoCodec(VideoCodec codec) {
  switch (codec) {
    case VideoCodec::kVP8:
      return cdm::kCodecVp8;
    case VideoCodec::kH264:
      return cdm::kCodecH264;
    case VideoCodec::kVP9:
      return cdm::kCodecVp9;
    case VideoCodec::kAV1:
      return cdm::kCodecAv1;
    default:
      DVLOG(1) << "Unsupported VideoCodec " << codec;
      return cdm::kUnknownVideoCodec;
  }
}

VideoCodec ToMediaVideoCodec(cdm::VideoCodec codec) {
  switch (codec) {
    case cdm::kUnknownVideoCodec:
      return VideoCodec::kUnknown;
    case cdm::kCodecVp8:
      return VideoCodec::kVP8;
    case cdm::kCodecH264:
      return VideoCodec::kH264;
    case cdm::kCodecVp9:
      return VideoCodec::kVP9;
    case cdm::kCodecAv1:
      return VideoCodec::kAV1;
  }

  NOTREACHED() << "Unexpected cdm::VideoCodec " << codec;
}

cdm::VideoCodecProfile ToCdmVideoCodecProfile(VideoCodecProfile profile) {
  switch (profile) {
    case VP8PROFILE_ANY:
      return cdm::kProfileNotNeeded;
    case VP9PROFILE_PROFILE0:
      return cdm::kVP9Profile0;
    case VP9PROFILE_PROFILE1:
      return cdm::kVP9Profile1;
    case VP9PROFILE_PROFILE2:
      return cdm::kVP9Profile2;
    case VP9PROFILE_PROFILE3:
      return cdm::kVP9Profile3;
    case H264PROFILE_BASELINE:
      return cdm::kH264ProfileBaseline;
    case H264PROFILE_MAIN:
      return cdm::kH264ProfileMain;
    case H264PROFILE_EXTENDED:
      return cdm::kH264ProfileExtended;
    case H264PROFILE_HIGH:
      return cdm::kH264ProfileHigh;
    case H264PROFILE_HIGH10PROFILE:
      return cdm::kH264ProfileHigh10;
    case H264PROFILE_HIGH422PROFILE:
      return cdm::kH264ProfileHigh422;
    case H264PROFILE_HIGH444PREDICTIVEPROFILE:
      return cdm::kH264ProfileHigh444Predictive;
    case AV1PROFILE_PROFILE_MAIN:
      return cdm::kAv1ProfileMain;
    case AV1PROFILE_PROFILE_HIGH:
      return cdm::kAv1ProfileHigh;
    case AV1PROFILE_PROFILE_PRO:
      return cdm::kAv1ProfilePro;
    default:
      DVLOG(1) << "Unsupported VideoCodecProfile " << profile;
      return cdm::kUnknownVideoCodecProfile;
  }
}

VideoCodecProfile ToMediaVideoCodecProfile(cdm::VideoCodecProfile profile) {
  switch (profile) {
    case cdm::kUnknownVideoCodecProfile:
      return VIDEO_CODEC_PROFILE_UNKNOWN;
    case cdm::kProfileNotNeeded:
      // There's no corresponding value for "not needed". Given CdmAdapter only
      // converts VP8PROFILE_ANY to cdm::kProfileNotNeeded, and this code is
      // only used for testing, it's okay to convert it back to VP8PROFILE_ANY.
      return VP8PROFILE_ANY;
    case cdm::kVP9Profile0:
      return VP9PROFILE_PROFILE0;
    case cdm::kVP9Profile1:
      return VP9PROFILE_PROFILE1;
    case cdm::kVP9Profile2:
      return VP9PROFILE_PROFILE2;
    case cdm::kVP9Profile3:
      return VP9PROFILE_PROFILE3;
    case cdm::kH264ProfileBaseline:
      return H264PROFILE_BASELINE;
    case cdm::kH264ProfileMain:
      return H264PROFILE_MAIN;
    case cdm::kH264ProfileExtended:
      return H264PROFILE_EXTENDED;
    case cdm::kH264ProfileHigh:
      return H264PROFILE_HIGH;
    case cdm::kH264ProfileHigh10:
      return H264PROFILE_HIGH10PROFILE;
    case cdm::kH264ProfileHigh422:
      return H264PROFILE_HIGH422PROFILE;
    case cdm::kH264ProfileHigh444Predictive:
      return H264PROFILE_HIGH444PREDICTIVEPROFILE;
    case cdm::kAv1ProfileMain:
      return AV1PROFILE_PROFILE_MAIN;
    case cdm::kAv1ProfileHigh:
      return AV1PROFILE_PROFILE_HIGH;
    case cdm::kAv1ProfilePro:
      return AV1PROFILE_PROFILE_PRO;
  }

  NOTREACHED() << "Unexpected cdm::VideoCodecProfile " << profile;
}

cdm::VideoFormat ToCdmVideoFormat(VideoPixelFormat format) {
  switch (format) {
    case PIXEL_FORMAT_YV12:
      return cdm::kYv12;
    case PIXEL_FORMAT_I420:
      return cdm::kI420;
    case PIXEL_FORMAT_YUV420P10:
      return cdm::kYUV420P10;
    case PIXEL_FORMAT_YUV422P10:
      return cdm::kYUV422P10;
    case PIXEL_FORMAT_YUV444P10:
      return cdm::kYUV444P10;
    case PIXEL_FORMAT_YUV420P12:
      return cdm::kYUV420P12;
    case PIXEL_FORMAT_YUV422P12:
      return cdm::kYUV422P12;
    case PIXEL_FORMAT_YUV444P12:
      return cdm::kYUV444P12;
    default:
      DVLOG(1) << "Unsupported VideoPixelFormat " << format;
      return cdm::kUnknownVideoFormat;
  }
}

VideoPixelFormat ToMediaVideoFormat(cdm::VideoFormat format) {
  switch (format) {
    case cdm::kYv12:
      return PIXEL_FORMAT_YV12;
    case cdm::kI420:
      return PIXEL_FORMAT_I420;
    case cdm::kYUV420P10:
      return PIXEL_FORMAT_YUV420P10;
    case cdm::kYUV422P10:
      return PIXEL_FORMAT_YUV422P10;
    case cdm::kYUV444P10:
      return PIXEL_FORMAT_YUV444P10;
    case cdm::kYUV420P12:
      return PIXEL_FORMAT_YUV420P12;
    case cdm::kYUV422P12:
      return PIXEL_FORMAT_YUV422P12;
    case cdm::kYUV444P12:
      return PIXEL_FORMAT_YUV444P12;
    default:
      DVLOG(1) << "Unsupported cdm::VideoFormat " << format;
      return PIXEL_FORMAT_UNKNOWN;
  }
}

// Aggregate Types

// Warning: The returned config contains raw pointers to the extra data in the
// input |config|. Hence, the caller must make sure the input |config| outlives
// the returned config.
cdm::AudioDecoderConfig_2 ToCdmAudioDecoderConfig(
    const AudioDecoderConfig& config) {
  cdm::AudioDecoderConfig_2 cdm_config = {};
  cdm_config.codec = ToCdmAudioCodec(config.codec());
  cdm_config.channel_count = config.channels();
  cdm_config.bits_per_channel = config.bytes_per_channel() * 8;
  cdm_config.samples_per_second = config.samples_per_second();
  cdm_config.extra_data = const_cast<uint8_t*>(config.extra_data().data());
  cdm_config.extra_data_size = config.extra_data().size();
  cdm_config.encryption_scheme =
      ToCdmEncryptionScheme(config.encryption_scheme());
  return cdm_config;
}

// Warning: The returned config contains raw pointers to the extra data in the
// input |config|. Hence, the caller must make sure the input |config| outlives
// the returned config.
cdm::VideoDecoderConfig_3 ToCdmVideoDecoderConfig(
    const VideoDecoderConfig& config) {
  cdm::VideoDecoderConfig_3 cdm_config = {};
  cdm_config.codec = ToCdmVideoCodec(config.codec());
  cdm_config.profile = ToCdmVideoCodecProfile(config.profile());

  // TODO(dalecurtis): CDM doesn't support alpha, so delete |format|.
  DCHECK_EQ(config.alpha_mode(), VideoDecoderConfig::AlphaMode::kIsOpaque);
  cdm_config.format = cdm::kI420;

  cdm_config.color_space = ToCdmColorSpace(config.color_space_info());
  cdm_config.coded_size.width = config.coded_size().width();
  cdm_config.coded_size.height = config.coded_size().height();
  cdm_config.extra_data = const_cast<uint8_t*>(config.extra_data().data());
  cdm_config.extra_data_size = config.extra_data().size();
  cdm_config.encryption_scheme =
      ToCdmEncryptionScheme(config.encryption_scheme());
  return cdm_config;
}

// Fill |input_buffer| based on the values in |encrypted|. |subsamples|
// is used to hold some of the data. |input_buffer| will contain pointers
// to data contained in |encrypted| and |subsamples|, so the lifetime of
// |input_buffer| must be <= the lifetime of |encrypted| and |subsamples|.
void ToCdmInputBuffer(const DecoderBuffer& encrypted_buffer,
                      std::vector<cdm::SubsampleEntry>* subsamples,
                      cdm::InputBuffer_2* input_buffer) {
  // End of stream buffers are represented as empty resources.
  CHECK(!input_buffer->data);
  if (encrypted_buffer.end_of_stream())
    return;

  auto encrypted_buffer_span = base::span(encrypted_buffer);
  input_buffer->data = encrypted_buffer_span.data();
  input_buffer->data_size = encrypted_buffer_span.size();
  input_buffer->timestamp = encrypted_buffer.timestamp().InMicroseconds();

  const DecryptConfig* decrypt_config = encrypted_buffer.decrypt_config();
  if (!decrypt_config) {
    DVLOG(3) << __func__ << ": Clear buffer.";
    return;
  }

  input_buffer->key_id =
      reinterpret_cast<const uint8_t*>(decrypt_config->key_id().data());
  input_buffer->key_id_size = decrypt_config->key_id().size();
  input_buffer->iv =
      reinterpret_cast<const uint8_t*>(decrypt_config->iv().data());
  input_buffer->iv_size = decrypt_config->iv().size();

  CHECK(subsamples->empty());
  size_t num_subsamples = decrypt_config->subsamples().size();
  if (num_subsamples > 0) {
    subsamples->reserve(num_subsamples);
    for (const auto& sample : decrypt_config->subsamples()) {
      subsamples->push_back({sample.clear_bytes, sample.cypher_bytes});
    }
  }

  input_buffer->subsamples = subsamples->data();
  input_buffer->num_subsamples = num_subsamples;

  input_buffer->encryption_scheme =
      ToCdmEncryptionScheme(decrypt_config->encryption_scheme());
  if (decrypt_config->HasPattern()) {
    input_buffer->pattern = {
        decrypt_config->encryption_pattern()->crypt_byte_block(),
        decrypt_config->encryption_pattern()->skip_byte_block()};
  }
}

base::span<uint8_t> AsSpan(cdm::Buffer* buffer) {
  CHECK(buffer);
  // SAFETY: |buffer->Data()| must return a buffer of |buffer->Size()| bytes.
  return UNSAFE_BUFFERS(base::span(buffer->Data(), buffer->Size()));
}

base::span<const uint8_t> AsSpan(const cdm::InputBuffer_2* input_buffer) {
  CHECK(input_buffer);
  // SAFETY: |input_buffer| is defined in the cdm interface submodule:
  // https://chromium.googlesource.com/chromium/cdm
  return UNSAFE_BUFFERS(
      base::span(input_buffer->data, input_buffer->data_size));
}

base::span<const cdm::SubsampleEntry> SubsamplesFrom(
    const cdm::InputBuffer_2* input_buffer) {
  CHECK(input_buffer);
  // SAFETY: |input_buffer| is defined in the cdm interface submodule:
  // https://chromium.googlesource.com/chromium/cdm
  return UNSAFE_BUFFERS(
      base::span(input_buffer->subsamples, input_buffer->num_subsamples));
}

base::span<const uint8_t> KeyIdFrom(const cdm::InputBuffer_2* input_buffer) {
  CHECK(input_buffer);
  // SAFETY: |input_buffer| is defined in the cdm interface submodule:
  // https://chromium.googlesource.com/chromium/cdm
  return UNSAFE_BUFFERS(
      base::span(input_buffer->key_id, input_buffer->key_id_size));
}

}  // namespace media
