blob: 159d63d265339efd67e333ae6bad673f5945e01c [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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/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";
return cdm::ColorRange::kInvalid;
}
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";
return gfx::ColorSpace::RangeID::INVALID;
}
} // 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";
return cdm::kHdcpVersion2_3;
}
cdm::SessionType ToCdmSessionType(CdmSessionType session_type) {
switch (session_type) {
case CdmSessionType::kTemporary:
return cdm::kTemporary;
case CdmSessionType::kPersistentLicense:
return cdm::kPersistentLicense;
case CdmSessionType::kPersistentUsageRecord:
return cdm::kPersistentUsageRecord;
}
NOTREACHED() << "Unexpected session type " << static_cast<int>(session_type);
return cdm::kTemporary;
}
CdmSessionType ToMediaSessionType(cdm::SessionType session_type) {
switch (session_type) {
case cdm::kTemporary:
return CdmSessionType::kTemporary;
case cdm::kPersistentLicense:
return CdmSessionType::kPersistentLicense;
case cdm::kPersistentUsageRecord:
return CdmSessionType::kPersistentUsageRecord;
}
NOTREACHED() << "Unexpected cdm::SessionType " << session_type;
return CdmSessionType::kTemporary;
}
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";
return cdm::kKeyIds;
}
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;
return EmeInitDataType::UNKNOWN;
}
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;
return CdmKeyInformation::INTERNAL_ERROR;
}
cdm::KeyStatus ToCdmKeyStatus(CdmKeyInformation::KeyStatus status) {
switch (status) {
case CdmKeyInformation::KeyStatus::USABLE:
return cdm::kUsable;
case CdmKeyInformation::KeyStatus::INTERNAL_ERROR:
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;
return cdm::kInternalError;
}
cdm::EncryptionScheme ToCdmEncryptionScheme(const EncryptionScheme& scheme) {
switch (scheme.mode()) {
case EncryptionScheme::CIPHER_MODE_UNENCRYPTED:
return cdm::EncryptionScheme::kUnencrypted;
case EncryptionScheme::CIPHER_MODE_AES_CTR:
return cdm::EncryptionScheme::kCenc;
case EncryptionScheme::CIPHER_MODE_AES_CBC:
return cdm::EncryptionScheme::kCbcs;
}
NOTREACHED() << "Unexpected EncryptionScheme mode " << scheme.mode();
return cdm::EncryptionScheme::kUnencrypted;
}
cdm::EncryptionScheme ToCdmEncryptionScheme(const EncryptionMode& mode) {
switch (mode) {
case EncryptionMode::kUnencrypted:
return cdm::EncryptionScheme::kUnencrypted;
case EncryptionMode::kCenc:
return cdm::EncryptionScheme::kCenc;
case EncryptionMode::kCbcs:
return cdm::EncryptionScheme::kCbcs;
}
NOTREACHED() << "Unexpected EncryptionMode";
return cdm::EncryptionScheme::kUnencrypted;
}
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;
return CdmPromise::Exception::INVALID_STATE_ERROR;
}
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";
return cdm::kExceptionInvalidStateError;
}
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;
return CdmMessageType::LICENSE_REQUEST;
}
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";
return cdm::kLicenseRequest;
}
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;
return cdm::kStreamTypeVideo;
}
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;
return Decryptor::kError;
}
// Audio Converters
cdm::AudioCodec ToCdmAudioCodec(AudioCodec codec) {
switch (codec) {
case kCodecVorbis:
return cdm::kCodecVorbis;
case kCodecAAC:
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;
return kUnknownSampleFormat;
}
// Video Converters
cdm::VideoCodec ToCdmVideoCodec(VideoCodec codec) {
switch (codec) {
case kCodecVP8:
return cdm::kCodecVp8;
case kCodecH264:
return cdm::kCodecH264;
case kCodecVP9:
return cdm::kCodecVp9;
case kCodecAV1:
return cdm::kCodecAv1;
default:
DVLOG(1) << "Unsupported VideoCodec " << codec;
return cdm::kUnknownVideoCodec;
}
}
VideoCodec ToMediaVideoCodec(cdm::VideoCodec codec) {
switch (codec) {
case cdm::kUnknownVideoCodec:
return kUnknownVideoCodec;
case cdm::kCodecVp8:
return kCodecVP8;
case cdm::kCodecH264:
return kCodecH264;
case cdm::kCodecVp9:
return kCodecVP9;
case cdm::kCodecAv1:
return kCodecAV1;
}
NOTREACHED() << "Unexpected cdm::VideoCodec " << codec;
return kUnknownVideoCodec;
}
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;
return VIDEO_CODEC_PROFILE_UNKNOWN;
}
cdm::VideoFormat ToCdmVideoFormat(VideoPixelFormat format) {
switch (format) {
case PIXEL_FORMAT_YV12:
return cdm::kYv12;
case PIXEL_FORMAT_I420:
return cdm::kI420;
case PIXEL_FORMAT_YUV420P9:
return cdm::kYUV420P9;
case PIXEL_FORMAT_YUV420P10:
return cdm::kYUV420P10;
case PIXEL_FORMAT_YUV422P9:
return cdm::kYUV422P9;
case PIXEL_FORMAT_YUV422P10:
return cdm::kYUV422P10;
case PIXEL_FORMAT_YUV444P9:
return cdm::kYUV444P9;
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::kYUV420P9:
return PIXEL_FORMAT_YUV420P9;
case cdm::kYUV420P10:
return PIXEL_FORMAT_YUV420P10;
case cdm::kYUV422P9:
return PIXEL_FORMAT_YUV422P9;
case cdm::kYUV422P10:
return PIXEL_FORMAT_YUV422P10;
case cdm::kYUV444P9:
return PIXEL_FORMAT_YUV444P9;
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 =
ChannelLayoutToChannelCount(config.channel_layout());
cdm_config.bits_per_channel = config.bits_per_channel();
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.
DCHECK(!input_buffer->data);
if (encrypted_buffer.end_of_stream())
return;
input_buffer->data = encrypted_buffer.data();
input_buffer->data_size = encrypted_buffer.data_size();
input_buffer->timestamp = encrypted_buffer.timestamp().InMicroseconds();
const DecryptConfig* decrypt_config = encrypted_buffer.decrypt_config();
if (!decrypt_config) {
DVLOG(2) << __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();
DCHECK(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_mode());
if (decrypt_config->HasPattern()) {
input_buffer->pattern = {
decrypt_config->encryption_pattern()->crypt_byte_block(),
decrypt_config->encryption_pattern()->skip_byte_block()};
}
}
} // namespace media