blob: 96fb524cc276a65ec1af8702d3bfe987805ba311 [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "media/base/starboard_utils.h"
#include <algorithm>
#include "base/logging.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
#include "starboard/common/media.h"
#include "starboard/configuration.h"
#include "starboard/memory.h"
using base::Time;
using base::TimeDelta;
namespace media {
namespace {
int GetBitsPerPixel(const std::string& mime_type) {
auto codecs = ExtractCodecs(mime_type);
SbMediaVideoCodec codec;
int profile;
int level;
int bit_depth;
SbMediaPrimaryId primary_id;
SbMediaTransferId transfer_id;
SbMediaMatrixId matrix_id;
if (starboard::ParseVideoCodec(codecs.c_str(), &codec, &profile, &level,
&bit_depth, &primary_id, &transfer_id,
&matrix_id)) {
return bit_depth;
}
// Assume SDR when there isn't enough information to determine the bit depth.
return 8;
}
} // namespace
SbMediaAudioCodec MediaAudioCodecToSbMediaAudioCodec(AudioCodec codec) {
switch (codec) {
case AudioCodec::kAAC:
return kSbMediaAudioCodecAac;
case AudioCodec::kAC3:
if (!kSbHasAc3Audio) {
DLOG(ERROR) << "Audio codec AC3 not enabled on this platform. To "
<< "enable it, set kSbHasAc3Audio to |true|.";
return kSbMediaAudioCodecNone;
}
return kSbMediaAudioCodecAc3;
case AudioCodec::kEAC3:
if (!kSbHasAc3Audio) {
DLOG(ERROR) << "Audio codec AC3 not enabled on this platform. To "
<< "enable it, set kSbHasAc3Audio to |true|.";
return kSbMediaAudioCodecNone;
}
return kSbMediaAudioCodecEac3;
case AudioCodec::kVorbis:
return kSbMediaAudioCodecVorbis;
case AudioCodec::kOpus:
return kSbMediaAudioCodecOpus;
#if SB_API_VERSION >= 14
case AudioCodec::kMP3:
return kSbMediaAudioCodecMp3;
case AudioCodec::kFLAC:
return kSbMediaAudioCodecFlac;
case AudioCodec::kPCM:
return kSbMediaAudioCodecPcm;
#endif // SB_API_VERSION >= 14
default:
// Cobalt only supports a subset of audio codecs defined by Chromium.
DLOG(ERROR) << "Unsupported audio codec " << GetCodecName(codec);
return kSbMediaAudioCodecNone;
}
NOTREACHED();
return kSbMediaAudioCodecNone;
}
SbMediaVideoCodec MediaVideoCodecToSbMediaVideoCodec(VideoCodec codec) {
switch (codec) {
case VideoCodec::kH264:
return kSbMediaVideoCodecH264;
case VideoCodec::kVC1:
return kSbMediaVideoCodecVc1;
case VideoCodec::kMPEG2:
return kSbMediaVideoCodecMpeg2;
case VideoCodec::kTheora:
return kSbMediaVideoCodecTheora;
case VideoCodec::kVP8:
return kSbMediaVideoCodecVp8;
case VideoCodec::kVP9:
return kSbMediaVideoCodecVp9;
case VideoCodec::kHEVC:
return kSbMediaVideoCodecH265;
case VideoCodec::kAV1:
return kSbMediaVideoCodecAv1;
default:
// Cobalt only supports a subset of video codecs defined by Chromium.
DLOG(ERROR) << "Unsupported video codec " << GetCodecName(codec);
return kSbMediaVideoCodecNone;
}
NOTREACHED();
return kSbMediaVideoCodecNone;
}
SbMediaAudioSampleInfo MediaAudioConfigToSbMediaAudioSampleInfo(
const AudioDecoderConfig& audio_decoder_config,
const char* mime_type) {
DCHECK(audio_decoder_config.IsValidConfig());
SbMediaAudioSampleInfo audio_sample_info;
audio_sample_info.codec =
MediaAudioCodecToSbMediaAudioCodec(audio_decoder_config.codec());
audio_sample_info.mime = mime_type;
// TODO: Make this work with non AAC audio.
audio_sample_info.format_tag = 0x00ff;
audio_sample_info.number_of_channels =
ChannelLayoutToChannelCount(audio_decoder_config.channel_layout());
audio_sample_info.samples_per_second =
audio_decoder_config.samples_per_second();
audio_sample_info.average_bytes_per_second = 1;
audio_sample_info.block_alignment = 4;
audio_sample_info.bits_per_sample = audio_decoder_config.bits_per_channel();
const auto& extra_data = audio_sample_info.codec == kSbMediaAudioCodecAac
? audio_decoder_config.aac_extra_data()
: audio_decoder_config.extra_data();
audio_sample_info.audio_specific_config_size =
static_cast<uint16_t>(extra_data.size());
if (audio_sample_info.audio_specific_config_size == 0) {
audio_sample_info.audio_specific_config = NULL;
} else {
audio_sample_info.audio_specific_config = extra_data.data();
}
return audio_sample_info;
}
DemuxerStream::Type SbMediaTypeToDemuxerStreamType(SbMediaType type) {
if (type == kSbMediaTypeAudio) {
return DemuxerStream::AUDIO;
}
DCHECK_EQ(type, kSbMediaTypeVideo);
return DemuxerStream::VIDEO;
}
SbMediaType DemuxerStreamTypeToSbMediaType(DemuxerStream::Type type) {
if (type == DemuxerStream::AUDIO) {
return kSbMediaTypeAudio;
}
DCHECK_EQ(type, DemuxerStream::VIDEO);
return kSbMediaTypeVideo;
}
void FillDrmSampleInfo(const scoped_refptr<DecoderBuffer>& buffer,
SbDrmSampleInfo* drm_info,
SbDrmSubSampleMapping* subsample_mapping) {
DCHECK(drm_info);
DCHECK(subsample_mapping);
const DecryptConfig* config = buffer->decrypt_config();
if (config->encryption_scheme() == EncryptionScheme::kCenc) {
drm_info->encryption_scheme = kSbDrmEncryptionSchemeAesCtr;
} else {
DCHECK_EQ(config->encryption_scheme(), EncryptionScheme::kCbcs);
drm_info->encryption_scheme = kSbDrmEncryptionSchemeAesCbc;
}
// Set content of |drm_info| to default or invalid values.
drm_info->encryption_pattern.crypt_byte_block = 0;
drm_info->encryption_pattern.skip_byte_block = 0;
drm_info->initialization_vector_size = 0;
drm_info->identifier_size = 0;
drm_info->subsample_count = 0;
drm_info->subsample_mapping = NULL;
if (!config || config->iv().empty() || config->key_id().empty()) {
return;
}
DCHECK_LE(config->iv().size(), sizeof(drm_info->initialization_vector));
DCHECK_LE(config->key_id().size(), sizeof(drm_info->identifier));
if (config->iv().size() > sizeof(drm_info->initialization_vector) ||
config->key_id().size() > sizeof(drm_info->identifier)) {
return;
}
memcpy(drm_info->initialization_vector, &config->iv()[0],
config->iv().size());
drm_info->initialization_vector_size = config->iv().size();
memcpy(drm_info->identifier, &config->key_id()[0], config->key_id().size());
drm_info->identifier_size = config->key_id().size();
drm_info->subsample_count = config->subsamples().size();
if (drm_info->subsample_count > 0) {
COMPILE_ASSERT(sizeof(SbDrmSubSampleMapping) == sizeof(SubsampleEntry),
SubSampleEntrySizesMatch);
drm_info->subsample_mapping =
reinterpret_cast<const SbDrmSubSampleMapping*>(
&config->subsamples()[0]);
} else {
drm_info->subsample_count = 1;
drm_info->subsample_mapping = subsample_mapping;
subsample_mapping->clear_byte_count = 0;
subsample_mapping->encrypted_byte_count = buffer->data_size();
}
if (buffer->decrypt_config()->HasPattern()) {
drm_info->encryption_pattern.crypt_byte_block =
config->encryption_pattern()->crypt_byte_block();
drm_info->encryption_pattern.skip_byte_block =
config->encryption_pattern()->skip_byte_block();
}
}
// Ensure that the enums in starboard/media.h match enums in
// VideoColorSpace and gfx::ColorSpace.
#define ENUM_EQ(a, b) \
COMPILE_ASSERT(static_cast<int>(a) == static_cast<int>(b), mismatching_enums)
// Ensure PrimaryId enums convert correctly.
ENUM_EQ(kSbMediaPrimaryIdReserved0, VideoColorSpace::PrimaryID::INVALID);
ENUM_EQ(kSbMediaPrimaryIdBt709, VideoColorSpace::PrimaryID::BT709);
ENUM_EQ(kSbMediaPrimaryIdUnspecified, VideoColorSpace::PrimaryID::UNSPECIFIED);
ENUM_EQ(kSbMediaPrimaryIdBt470M, VideoColorSpace::PrimaryID::BT470M);
ENUM_EQ(kSbMediaPrimaryIdBt470Bg, VideoColorSpace::PrimaryID::BT470BG);
ENUM_EQ(kSbMediaPrimaryIdSmpte170M, VideoColorSpace::PrimaryID::SMPTE170M);
ENUM_EQ(kSbMediaPrimaryIdSmpte240M, VideoColorSpace::PrimaryID::SMPTE240M);
ENUM_EQ(kSbMediaPrimaryIdFilm, VideoColorSpace::PrimaryID::FILM);
ENUM_EQ(kSbMediaPrimaryIdBt2020, VideoColorSpace::PrimaryID::BT2020);
ENUM_EQ(kSbMediaPrimaryIdSmpteSt4281, VideoColorSpace::PrimaryID::SMPTEST428_1);
ENUM_EQ(kSbMediaPrimaryIdSmpteSt4312, VideoColorSpace::PrimaryID::SMPTEST431_2);
ENUM_EQ(kSbMediaPrimaryIdSmpteSt4321, VideoColorSpace::PrimaryID::SMPTEST432_1);
// Ensure TransferId enums convert correctly.
ENUM_EQ(kSbMediaTransferIdReserved0, VideoColorSpace::TransferID::INVALID);
ENUM_EQ(kSbMediaTransferIdBt709, VideoColorSpace::TransferID::BT709);
ENUM_EQ(kSbMediaTransferIdUnspecified,
VideoColorSpace::TransferID::UNSPECIFIED);
ENUM_EQ(kSbMediaTransferIdGamma22, VideoColorSpace::TransferID::GAMMA22);
ENUM_EQ(kSbMediaTransferIdGamma28, VideoColorSpace::TransferID::GAMMA28);
ENUM_EQ(kSbMediaTransferIdSmpte170M, VideoColorSpace::TransferID::SMPTE170M);
ENUM_EQ(kSbMediaTransferIdSmpte240M, VideoColorSpace::TransferID::SMPTE240M);
ENUM_EQ(kSbMediaTransferIdLinear, VideoColorSpace::TransferID::LINEAR);
ENUM_EQ(kSbMediaTransferIdLog, VideoColorSpace::TransferID::LOG);
ENUM_EQ(kSbMediaTransferIdLogSqrt, VideoColorSpace::TransferID::LOG_SQRT);
ENUM_EQ(kSbMediaTransferIdIec6196624,
VideoColorSpace::TransferID::IEC61966_2_4);
ENUM_EQ(kSbMediaTransferIdBt1361Ecg, VideoColorSpace::TransferID::BT1361_ECG);
ENUM_EQ(kSbMediaTransferIdIec6196621,
VideoColorSpace::TransferID::IEC61966_2_1);
ENUM_EQ(kSbMediaTransferId10BitBt2020, VideoColorSpace::TransferID::BT2020_10);
ENUM_EQ(kSbMediaTransferId12BitBt2020, VideoColorSpace::TransferID::BT2020_12);
ENUM_EQ(kSbMediaTransferIdSmpteSt2084,
VideoColorSpace::TransferID::SMPTEST2084);
ENUM_EQ(kSbMediaTransferIdSmpteSt4281,
VideoColorSpace::TransferID::SMPTEST428_1);
ENUM_EQ(kSbMediaTransferIdAribStdB67,
VideoColorSpace::TransferID::ARIB_STD_B67);
// Ensure MatrixId enums convert correctly.
ENUM_EQ(kSbMediaMatrixIdRgb, VideoColorSpace::MatrixID::RGB);
ENUM_EQ(kSbMediaMatrixIdBt709, VideoColorSpace::MatrixID::BT709);
ENUM_EQ(kSbMediaMatrixIdUnspecified, VideoColorSpace::MatrixID::UNSPECIFIED);
ENUM_EQ(kSbMediaMatrixIdFcc, VideoColorSpace::MatrixID::FCC);
ENUM_EQ(kSbMediaMatrixIdBt470Bg, VideoColorSpace::MatrixID::BT470BG);
ENUM_EQ(kSbMediaMatrixIdSmpte170M, VideoColorSpace::MatrixID::SMPTE170M);
ENUM_EQ(kSbMediaMatrixIdSmpte240M, VideoColorSpace::MatrixID::SMPTE240M);
ENUM_EQ(kSbMediaMatrixIdYCgCo, VideoColorSpace::MatrixID::YCOCG);
ENUM_EQ(kSbMediaMatrixIdBt2020NonconstantLuminance,
VideoColorSpace::MatrixID::BT2020_NCL);
ENUM_EQ(kSbMediaMatrixIdBt2020ConstantLuminance,
VideoColorSpace::MatrixID::BT2020_CL);
ENUM_EQ(kSbMediaMatrixIdYDzDx, VideoColorSpace::MatrixID::YDZDX);
#if SB_API_VERSION >= 14
ENUM_EQ(kSbMediaMatrixIdInvalid, VideoColorSpace::MatrixID::INVALID);
#endif // SB_API_VERSION >= 14
// Ensure RangeId enums convert correctly.
ENUM_EQ(kSbMediaRangeIdUnspecified, gfx::ColorSpace::RangeID::INVALID);
ENUM_EQ(kSbMediaRangeIdLimited, gfx::ColorSpace::RangeID::LIMITED);
ENUM_EQ(kSbMediaRangeIdFull, gfx::ColorSpace::RangeID::FULL);
ENUM_EQ(kSbMediaRangeIdDerived, gfx::ColorSpace::RangeID::DERIVED);
SbMediaColorMetadata MediaToSbMediaColorMetadata(
const VideoColorSpace& color_space,
const absl::optional<gfx::HDRMetadata>& hdr_metadata,
const std::string& mime_type) {
SbMediaColorMetadata sb_media_color_metadata = {};
sb_media_color_metadata.bits_per_channel = GetBitsPerPixel(mime_type);
// Copy the other color metadata below.
// TODO(b/230915942): Revisit to ensure that the metadata is valid and
// consider deprecate them from `SbMediaColorMetadata`.
sb_media_color_metadata.chroma_subsampling_horizontal = 0;
sb_media_color_metadata.chroma_subsampling_vertical = 0;
sb_media_color_metadata.cb_subsampling_horizontal = 0;
sb_media_color_metadata.cb_subsampling_vertical = 0;
sb_media_color_metadata.chroma_siting_horizontal = 0;
sb_media_color_metadata.chroma_siting_vertical = 0;
// Copy the HDR Metadata below.
SbMediaMasteringMetadata sb_media_mastering_metadata = {};
if (hdr_metadata) {
const auto& color_volume_metadata = hdr_metadata->color_volume_metadata;
sb_media_mastering_metadata.primary_r_chromaticity_x =
color_volume_metadata.primary_r.x();
sb_media_mastering_metadata.primary_r_chromaticity_y =
color_volume_metadata.primary_r.y();
sb_media_mastering_metadata.primary_g_chromaticity_x =
color_volume_metadata.primary_g.x();
sb_media_mastering_metadata.primary_g_chromaticity_y =
color_volume_metadata.primary_g.y();
sb_media_mastering_metadata.primary_b_chromaticity_x =
color_volume_metadata.primary_b.x();
sb_media_mastering_metadata.primary_b_chromaticity_y =
color_volume_metadata.primary_b.y();
sb_media_mastering_metadata.white_point_chromaticity_x =
color_volume_metadata.white_point.x();
sb_media_mastering_metadata.white_point_chromaticity_y =
color_volume_metadata.white_point.y();
sb_media_mastering_metadata.luminance_max =
color_volume_metadata.luminance_max;
sb_media_mastering_metadata.luminance_min =
color_volume_metadata.luminance_min;
sb_media_color_metadata.mastering_metadata = sb_media_mastering_metadata;
sb_media_color_metadata.max_cll = hdr_metadata->max_content_light_level;
sb_media_color_metadata.max_fall =
hdr_metadata->max_frame_average_light_level;
}
// Copy the color space below.
sb_media_color_metadata.primaries =
static_cast<SbMediaPrimaryId>(color_space.primaries);
sb_media_color_metadata.transfer =
static_cast<SbMediaTransferId>(color_space.transfer);
sb_media_color_metadata.matrix =
static_cast<SbMediaMatrixId>(color_space.matrix);
#if SB_API_VERSION < 14
if (color_space.matrix == VideoColorSpace::MatrixID::INVALID) {
sb_media_color_metadata.matrix = kSbMediaMatrixIdUnknown;
}
#endif // SB_API_VERSION < 14
sb_media_color_metadata.range =
static_cast<SbMediaRangeId>(color_space.range);
// TODO(b/230915942): Revisit to see if we have to support custom primary id.
// if (sb_media_color_metadata.primaries == kSbMediaPrimaryIdCustom) {
// const float* custom_primary_matrix = color_space.custom_primary_matrix();
// memcpy(sb_media_color_metadata.custom_primary_matrix,
// custom_primary_matrix,
// sizeof(sb_media_color_metadata.custom_primary_matrix));
// }
return sb_media_color_metadata;
}
int GetSbMediaVideoBufferBudget(const VideoDecoderConfig* video_config,
const std::string& mime_type) {
if (!video_config) {
return DecoderBuffer::Allocator::GetInstance()->GetVideoBufferBudget(
kSbMediaVideoCodecH264, 1920, 1080, 8);
}
auto width = video_config->visible_rect().size().width();
auto height = video_config->visible_rect().size().height();
auto bits_per_pixel = GetBitsPerPixel(mime_type);
auto codec = MediaVideoCodecToSbMediaVideoCodec(video_config->codec());
return DecoderBuffer::Allocator::GetInstance()->GetVideoBufferBudget(
codec, width, height, bits_per_pixel);
}
std::string ExtractCodecs(const std::string& mime_type) {
static const char kCodecs[] = "codecs=";
// SplitString will also trim the results.
std::vector<std::string> tokens = ::base::SplitString(
mime_type, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
for (size_t i = 1; i < tokens.size(); ++i) {
if (base::strncasecmp(tokens[i].c_str(), kCodecs, strlen(kCodecs))) {
continue;
}
auto codec = tokens[i].substr(strlen(kCodecs));
base::TrimString(codec, " \"", &codec);
return codec;
}
LOG(WARNING) << "Failed to find codecs in mime type \"" << mime_type << '\"';
return "";
}
} // namespace media