| // Copyright 2017 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 "starboard/shared/starboard/media/media_util.h" |
| |
| #include <cctype> |
| |
| #include "starboard/character.h" |
| #include "starboard/common/log.h" |
| #include "starboard/common/string.h" |
| #include "starboard/log.h" |
| #include "starboard/memory.h" |
| #include "starboard/shared/starboard/media/codec_util.h" |
| #include "starboard/shared/starboard/media/media_support_internal.h" |
| #include "starboard/shared/starboard/media/mime_type.h" |
| |
| namespace starboard { |
| namespace shared { |
| namespace starboard { |
| namespace media { |
| |
| namespace { |
| |
| const int64_t kDefaultBitRate = 0; |
| const int64_t kDefaultAudioChannels = 2; |
| |
| bool IsSupportedAudioCodec(const MimeType& mime_type, |
| const std::string& codec, |
| const char* key_system) { |
| SbMediaAudioCodec audio_codec = GetAudioCodecFromString(codec.c_str()); |
| if (audio_codec == kSbMediaAudioCodecNone) { |
| return false; |
| } |
| |
| // TODO: allow platform-specific rejection of a combination of codec & |
| // number of channels, by passing channels to SbMediaAudioIsSupported and / |
| // or SbMediaIsSupported. |
| |
| if (SbStringGetLength(key_system) != 0) { |
| if (!SbMediaIsSupported(kSbMediaVideoCodecNone, audio_codec, key_system)) { |
| return false; |
| } |
| } |
| |
| int channels = mime_type.GetParamIntValue("channels", kDefaultAudioChannels); |
| if (!IsAudioOutputSupported(kSbMediaAudioCodingTypePcm, channels)) { |
| return false; |
| } |
| |
| int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate); |
| |
| if (!SbMediaIsAudioSupported(audio_codec, |
| #if SB_API_VERSION >= 12 |
| mime_type.raw_content_type().c_str(), |
| #endif // SB_API_VERSION >= 12 |
| bitrate)) { |
| return false; |
| } |
| |
| switch (audio_codec) { |
| case kSbMediaAudioCodecNone: |
| SB_NOTREACHED(); |
| return false; |
| case kSbMediaAudioCodecAac: |
| return mime_type.subtype() == "mp4"; |
| #if SB_API_VERSION >= 12 || SB_HAS(AC3_AUDIO) |
| case kSbMediaAudioCodecAc3: |
| if (!kSbHasAc3Audio) { |
| SB_NOTREACHED() << "AC3 audio is not enabled on this platform. To " |
| << "enable it, set kSbHasAc3Audio to |true|."; |
| return false; |
| } |
| return mime_type.subtype() == "mp4"; |
| case kSbMediaAudioCodecEac3: |
| if (!kSbHasAc3Audio) { |
| SB_NOTREACHED() << "AC3 audio is not enabled on this platform. To " |
| << "enable it, set kSbHasAc3Audio to |true|."; |
| return false; |
| } |
| return mime_type.subtype() == "mp4"; |
| #endif // SB_API_VERSION >= 12 || |
| // SB_HAS(AC3_AUDIO) |
| case kSbMediaAudioCodecOpus: |
| case kSbMediaAudioCodecVorbis: |
| return mime_type.subtype() == "webm"; |
| } |
| |
| SB_NOTREACHED(); |
| return false; |
| } |
| |
| bool IsSupportedVideoCodec(const MimeType& mime_type, |
| const std::string& codec, |
| const char* key_system, |
| bool decode_to_texture_required) { |
| SbMediaVideoCodec video_codec; |
| int profile = -1; |
| int level = -1; |
| int bit_depth = 8; |
| SbMediaPrimaryId primary_id = kSbMediaPrimaryIdUnspecified; |
| SbMediaTransferId transfer_id = kSbMediaTransferIdUnspecified; |
| SbMediaMatrixId matrix_id = kSbMediaMatrixIdUnspecified; |
| |
| if (!ParseVideoCodec(codec.c_str(), &video_codec, &profile, &level, |
| &bit_depth, &primary_id, &transfer_id, &matrix_id)) { |
| return false; |
| } |
| SB_DCHECK(video_codec != kSbMediaVideoCodecNone); |
| |
| if (SbStringGetLength(key_system) != 0) { |
| if (!SbMediaIsSupported(video_codec, kSbMediaAudioCodecNone, key_system)) { |
| return false; |
| } |
| } |
| |
| std::string eotf = mime_type.GetParamStringValue("eotf", ""); |
| if (!eotf.empty()) { |
| SbMediaTransferId transfer_id_from_eotf = GetTransferIdFromString(eotf); |
| // If the eotf is not known, reject immediately - without checking with |
| // the platform. |
| if (transfer_id_from_eotf == kSbMediaTransferIdUnknown) { |
| return false; |
| } |
| if (transfer_id != kSbMediaTransferIdUnspecified && |
| transfer_id != transfer_id_from_eotf) { |
| SB_LOG_IF(WARNING, transfer_id != kSbMediaTransferIdUnspecified) |
| << "transfer_id " << transfer_id << " set by the codec string \"" |
| << codec << "\" will be overwritten by the eotf attribute " << eotf; |
| } |
| transfer_id = transfer_id_from_eotf; |
| #if !SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT) |
| if (!SbMediaIsTransferCharacteristicsSupported(transfer_id)) { |
| return false; |
| } |
| #endif // !SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT) |
| } |
| |
| std::string cryptoblockformat = |
| mime_type.GetParamStringValue("cryptoblockformat", ""); |
| if (!cryptoblockformat.empty()) { |
| if (mime_type.subtype() != "webm" || cryptoblockformat != "subsample") { |
| return false; |
| } |
| } |
| |
| int width = mime_type.GetParamIntValue("width", 0); |
| int height = mime_type.GetParamIntValue("height", 0); |
| int fps = mime_type.GetParamIntValue("framerate", 0); |
| |
| int bitrate = mime_type.GetParamIntValue("bitrate", kDefaultBitRate); |
| |
| if (width < 0 || height < 0 || fps < 0 || bitrate < 0) { |
| return false; |
| } |
| |
| #if SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT) |
| if (!SbMediaIsVideoSupported(video_codec, |
| #if SB_API_VERSION >= 12 |
| mime_type.raw_content_type().c_str(), |
| #endif // SB_API_VERSION >= 12 |
| profile, level, bit_depth, primary_id, |
| transfer_id, matrix_id, width, height, bitrate, |
| fps, decode_to_texture_required)) { |
| return false; |
| } |
| #else // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT) |
| if (!SbMediaIsVideoSupported(video_codec, width, height, bitrate, fps, |
| decode_to_texture_required)) { |
| return false; |
| } |
| #endif // SB_HAS(MEDIA_IS_VIDEO_SUPPORTED_REFINEMENT) |
| |
| switch (video_codec) { |
| case kSbMediaVideoCodecNone: |
| SB_NOTREACHED(); |
| return false; |
| case kSbMediaVideoCodecH264: |
| case kSbMediaVideoCodecH265: |
| return mime_type.subtype() == "mp4"; |
| case kSbMediaVideoCodecMpeg2: |
| case kSbMediaVideoCodecTheora: |
| return false; // No associated container in YT. |
| case kSbMediaVideoCodecVc1: |
| #if SB_API_VERSION < 11 |
| case kSbMediaVideoCodecVp10: |
| #else // SB_API_VERSION < 11 |
| case kSbMediaVideoCodecAv1: |
| #endif // SB_API_VERSION < 11 |
| return mime_type.subtype() == "mp4"; |
| case kSbMediaVideoCodecVp8: |
| return mime_type.subtype() == "webm"; |
| case kSbMediaVideoCodecVp9: |
| return mime_type.subtype() == "mp4" || mime_type.subtype() == "webm"; |
| } |
| |
| SB_NOTREACHED(); |
| return false; |
| } |
| |
| } // namespace |
| |
| AudioSampleInfo::AudioSampleInfo() { |
| SbMemorySet(this, 0, sizeof(SbMediaAudioSampleInfo)); |
| #if SB_API_VERSION >= 11 |
| codec = kSbMediaAudioCodecNone; |
| #endif // SB_API_VERSION >= 11 |
| } |
| |
| AudioSampleInfo::AudioSampleInfo(const SbMediaAudioSampleInfo& that) { |
| *this = that; |
| } |
| |
| AudioSampleInfo& AudioSampleInfo::operator=( |
| const SbMediaAudioSampleInfo& that) { |
| *static_cast<SbMediaAudioSampleInfo*>(this) = that; |
| if (audio_specific_config_size > 0) { |
| audio_specific_config_storage.resize(audio_specific_config_size); |
| SbMemoryCopy(audio_specific_config_storage.data(), audio_specific_config, |
| audio_specific_config_size); |
| audio_specific_config = audio_specific_config_storage.data(); |
| } |
| #if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) |
| if (codec == kSbMediaAudioCodecNone) { |
| mime_storage.clear(); |
| } else { |
| SB_DCHECK(that.mime); |
| mime_storage = that.mime; |
| } |
| mime = mime_storage.c_str(); |
| #endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) |
| return *this; |
| } |
| |
| VideoSampleInfo::VideoSampleInfo() { |
| SbMemorySet(this, 0, sizeof(SbMediaAudioSampleInfo)); |
| #if SB_API_VERSION >= 11 |
| codec = kSbMediaVideoCodecNone; |
| #endif // SB_API_VERSION >= 11 |
| } |
| |
| VideoSampleInfo::VideoSampleInfo(const SbMediaVideoSampleInfo& that) { |
| *this = that; |
| } |
| |
| VideoSampleInfo& VideoSampleInfo::operator=( |
| const SbMediaVideoSampleInfo& that) { |
| *static_cast<SbMediaVideoSampleInfo*>(this) = that; |
| #if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) |
| if (codec == kSbMediaVideoCodecNone) { |
| mime_storage.clear(); |
| max_video_capabilities_storage.clear(); |
| } else { |
| SB_DCHECK(that.mime); |
| mime_storage = that.mime; |
| max_video_capabilities_storage = that.max_video_capabilities; |
| } |
| mime = mime_storage.c_str(); |
| max_video_capabilities = max_video_capabilities_storage.c_str(); |
| #endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) |
| #if SB_API_VERSION < 11 |
| if (color_metadata) { |
| color_metadata_storage = *color_metadata; |
| color_metadata = &color_metadata_storage; |
| } |
| #endif // SB_API_VERSION < 11 |
| return *this; |
| } |
| |
| bool IsAudioOutputSupported(SbMediaAudioCodingType coding_type, int channels) { |
| int count = SbMediaGetAudioOutputCount(); |
| |
| for (int output_index = 0; output_index < count; ++output_index) { |
| SbMediaAudioConfiguration configuration; |
| if (!SbMediaGetAudioConfiguration(output_index, &configuration)) { |
| continue; |
| } |
| |
| if (configuration.coding_type == coding_type && |
| configuration.number_of_channels >= channels) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| bool IsSDRVideo(int bit_depth, |
| SbMediaPrimaryId primary_id, |
| SbMediaTransferId transfer_id, |
| SbMediaMatrixId matrix_id) { |
| if (bit_depth != 8) { |
| return false; |
| } |
| |
| if (primary_id != kSbMediaPrimaryIdBt709 && |
| primary_id != kSbMediaPrimaryIdUnspecified && |
| primary_id != kSbMediaPrimaryIdSmpte170M) { |
| return false; |
| } |
| |
| if (transfer_id != kSbMediaTransferIdBt709 && |
| transfer_id != kSbMediaTransferIdUnspecified && |
| transfer_id != kSbMediaTransferIdSmpte170M) { |
| return false; |
| } |
| |
| if (matrix_id != kSbMediaMatrixIdBt709 && |
| matrix_id != kSbMediaMatrixIdUnspecified && |
| matrix_id != kSbMediaMatrixIdSmpte170M) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| SbMediaTransferId GetTransferIdFromString(const std::string& transfer_id) { |
| if (transfer_id == "bt709") { |
| return kSbMediaTransferIdBt709; |
| } else if (transfer_id == "smpte2084") { |
| return kSbMediaTransferIdSmpteSt2084; |
| } else if (transfer_id == "arib-std-b67") { |
| return kSbMediaTransferIdAribStdB67; |
| } |
| return kSbMediaTransferIdUnknown; |
| } |
| |
| int GetBytesPerSample(SbMediaAudioSampleType sample_type) { |
| switch (sample_type) { |
| case kSbMediaAudioSampleTypeInt16Deprecated: |
| return 2; |
| case kSbMediaAudioSampleTypeFloat32: |
| return 4; |
| } |
| |
| SB_NOTREACHED(); |
| return 4; |
| } |
| |
| SbMediaSupportType CanPlayMimeAndKeySystem(const MimeType& mime_type, |
| const char* key_system) { |
| SB_DCHECK(mime_type.is_valid()); |
| |
| if (mime_type.type() != "audio" && mime_type.type() != "video") { |
| return kSbMediaSupportTypeNotSupported; |
| } |
| |
| auto codecs = mime_type.GetCodecs(); |
| |
| // Pre-filter for |key_system|. |
| if (SbStringGetLength(key_system) != 0) { |
| if (!SbMediaIsSupported(kSbMediaVideoCodecNone, kSbMediaAudioCodecNone, |
| key_system)) { |
| return kSbMediaSupportTypeNotSupported; |
| } |
| } |
| |
| bool decode_to_texture_required = false; |
| std::string decode_to_texture_value = |
| mime_type.GetParamStringValue("decode-to-texture", "false"); |
| if (decode_to_texture_value == "true") { |
| decode_to_texture_required = true; |
| } else if (decode_to_texture_value != "false") { |
| // If an invalid value (e.g. not "true" or "false") is passed in for |
| // decode-to-texture, trivially reject. |
| return kSbMediaSupportTypeNotSupported; |
| } |
| |
| if (codecs.size() == 0) { |
| // This happens when the H5 player is either querying for progressive |
| // playback support, or probing for generic mp4 support without specific |
| // codecs. We only support "audio/mp4" and "video/mp4" for these cases. |
| if ((mime_type.type() == "audio" || mime_type.type() == "video") && |
| mime_type.subtype() == "mp4") { |
| return kSbMediaSupportTypeMaybe; |
| } |
| return kSbMediaSupportTypeNotSupported; |
| } |
| |
| if (codecs.size() > 2) { |
| return kSbMediaSupportTypeNotSupported; |
| } |
| |
| bool has_audio_codec = false; |
| bool has_video_codec = false; |
| for (const auto& codec : codecs) { |
| if (IsSupportedAudioCodec(mime_type, codec, key_system)) { |
| if (has_audio_codec) { |
| // We don't support two audio codecs in one stream. |
| return kSbMediaSupportTypeNotSupported; |
| } |
| has_audio_codec = true; |
| continue; |
| } |
| if (IsSupportedVideoCodec(mime_type, codec, key_system, |
| decode_to_texture_required)) { |
| if (mime_type.type() != "video") { |
| // Video can only be contained in "video/*", while audio can be |
| // contained in both "audio/*" and "video/*". |
| return kSbMediaSupportTypeNotSupported; |
| } |
| if (has_video_codec) { |
| // We don't support two video codecs in one stream. |
| return kSbMediaSupportTypeNotSupported; |
| } |
| has_video_codec = true; |
| continue; |
| } |
| return kSbMediaSupportTypeNotSupported; |
| } |
| |
| if (has_audio_codec || has_video_codec) { |
| return kSbMediaSupportTypeProbably; |
| } |
| return kSbMediaSupportTypeNotSupported; |
| } |
| |
| const char* GetCodecName(SbMediaAudioCodec codec) { |
| switch (codec) { |
| case kSbMediaAudioCodecNone: |
| return "none"; |
| case kSbMediaAudioCodecAac: |
| return "aac"; |
| #if SB_API_VERSION >= 12 || SB_HAS(AC3_AUDIO) |
| case kSbMediaAudioCodecAc3: |
| if (!kSbHasAc3Audio) { |
| SB_NOTREACHED() << "AC3 audio is not enabled on this platform. To " |
| << "enable it, set kSbHasAc3Audio to |true|."; |
| return "invalid"; |
| } |
| return "ac3"; |
| case kSbMediaAudioCodecEac3: |
| if (!kSbHasAc3Audio) { |
| SB_NOTREACHED() << "AC3 audio is not enabled on this platform. To " |
| << "enable it, set kSbHasAc3Audio to |true|."; |
| return "invalid"; |
| } |
| return "ec3"; |
| #endif // SB_API_VERSION >= 12 || |
| // SB_HAS(AC3_AUDIO) |
| case kSbMediaAudioCodecOpus: |
| return "opus"; |
| case kSbMediaAudioCodecVorbis: |
| return "vorbis"; |
| } |
| SB_NOTREACHED(); |
| return "invalid"; |
| } |
| |
| const char* GetCodecName(SbMediaVideoCodec codec) { |
| switch (codec) { |
| case kSbMediaVideoCodecNone: |
| return "none"; |
| case kSbMediaVideoCodecH264: |
| return "avc"; |
| case kSbMediaVideoCodecH265: |
| return "hevc"; |
| case kSbMediaVideoCodecMpeg2: |
| return "mpeg2"; |
| case kSbMediaVideoCodecTheora: |
| return "theora"; |
| case kSbMediaVideoCodecVc1: |
| return "vc1"; |
| #if SB_API_VERSION < 11 |
| case kSbMediaVideoCodecVp10: |
| return "vp10"; |
| #else // SB_API_VERSION < 11 |
| case kSbMediaVideoCodecAv1: |
| return "av1"; |
| #endif // SB_API_VERSION < 11 |
| case kSbMediaVideoCodecVp8: |
| return "vp8"; |
| case kSbMediaVideoCodecVp9: |
| return "vp9"; |
| } |
| SB_NOTREACHED(); |
| return "invalid"; |
| } |
| |
| const char* GetPrimaryIdName(SbMediaPrimaryId primary_id) { |
| switch (primary_id) { |
| case kSbMediaPrimaryIdReserved0: |
| return "Reserved0"; |
| case kSbMediaPrimaryIdBt709: |
| return "Bt709"; |
| case kSbMediaPrimaryIdUnspecified: |
| return "Unspecified"; |
| case kSbMediaPrimaryIdReserved: |
| return "Reserved"; |
| case kSbMediaPrimaryIdBt470M: |
| return "Bt470M"; |
| case kSbMediaPrimaryIdBt470Bg: |
| return "Bt470Bg"; |
| case kSbMediaPrimaryIdSmpte170M: |
| return "Smpte170M"; |
| case kSbMediaPrimaryIdSmpte240M: |
| return "Smpte240M"; |
| case kSbMediaPrimaryIdFilm: |
| return "Film"; |
| case kSbMediaPrimaryIdBt2020: |
| return "Bt2020"; |
| case kSbMediaPrimaryIdSmpteSt4281: |
| return "SmpteSt4281"; |
| case kSbMediaPrimaryIdSmpteSt4312: |
| return "SmpteSt4312"; |
| case kSbMediaPrimaryIdSmpteSt4321: |
| return "SmpteSt4321"; |
| case kSbMediaPrimaryIdUnknown: |
| return "Unknown"; |
| case kSbMediaPrimaryIdXyzD50: |
| return "XyzD50"; |
| case kSbMediaPrimaryIdCustom: |
| return "Custom"; |
| } |
| SB_NOTREACHED(); |
| return "Invalid"; |
| } |
| |
| const char* GetTransferIdName(SbMediaTransferId transfer_id) { |
| switch (transfer_id) { |
| case kSbMediaTransferIdReserved0: |
| return "Reserved0"; |
| case kSbMediaTransferIdBt709: |
| return "Bt709"; |
| case kSbMediaTransferIdUnspecified: |
| return "Unspecified"; |
| case kSbMediaTransferIdReserved: |
| return "Reserved"; |
| case kSbMediaTransferIdGamma22: |
| return "Gamma22"; |
| case kSbMediaTransferIdGamma28: |
| return "Gamma28"; |
| case kSbMediaTransferIdSmpte170M: |
| return "Smpte170M"; |
| case kSbMediaTransferIdSmpte240M: |
| return "Smpte240M"; |
| case kSbMediaTransferIdLinear: |
| return "Linear"; |
| case kSbMediaTransferIdLog: |
| return "Log"; |
| case kSbMediaTransferIdLogSqrt: |
| return "LogSqrt"; |
| case kSbMediaTransferIdIec6196624: |
| return "Iec6196624"; |
| case kSbMediaTransferIdBt1361Ecg: |
| return "Bt1361Ecg"; |
| case kSbMediaTransferIdIec6196621: |
| return "Iec6196621"; |
| case kSbMediaTransferId10BitBt2020: |
| return "10BitBt2020"; |
| case kSbMediaTransferId12BitBt2020: |
| return "12BitBt2020"; |
| case kSbMediaTransferIdSmpteSt2084: |
| return "SmpteSt2084"; |
| case kSbMediaTransferIdSmpteSt4281: |
| return "SmpteSt4281"; |
| case kSbMediaTransferIdAribStdB67: |
| return "AribStdB67/HLG"; |
| case kSbMediaTransferIdUnknown: |
| return "Unknown"; |
| case kSbMediaTransferIdGamma24: |
| return "Gamma24"; |
| case kSbMediaTransferIdSmpteSt2084NonHdr: |
| return "SmpteSt2084NonHdr"; |
| case kSbMediaTransferIdCustom: |
| return "Custom"; |
| } |
| SB_NOTREACHED(); |
| return "Invalid"; |
| } |
| |
| const char* GetMatrixIdName(SbMediaMatrixId matrix_id) { |
| switch (matrix_id) { |
| case kSbMediaMatrixIdRgb: |
| return "Rgb"; |
| case kSbMediaMatrixIdBt709: |
| return "Bt709"; |
| case kSbMediaMatrixIdUnspecified: |
| return "Unspecified"; |
| case kSbMediaMatrixIdReserved: |
| return "Reserved"; |
| case kSbMediaMatrixIdFcc: |
| return "Fcc"; |
| case kSbMediaMatrixIdBt470Bg: |
| return "Bt470Bg"; |
| case kSbMediaMatrixIdSmpte170M: |
| return "Smpte170M"; |
| case kSbMediaMatrixIdSmpte240M: |
| return "Smpte240M"; |
| case kSbMediaMatrixIdYCgCo: |
| return "YCgCo"; |
| case kSbMediaMatrixIdBt2020NonconstantLuminance: |
| return "Bt2020NonconstantLuminance"; |
| case kSbMediaMatrixIdBt2020ConstantLuminance: |
| return "Bt2020ConstantLuminance"; |
| case kSbMediaMatrixIdYDzDx: |
| return "YDzDx"; |
| case kSbMediaMatrixIdUnknown: |
| return "Unknown"; |
| } |
| SB_NOTREACHED(); |
| return "Invalid"; |
| } |
| |
| const char* GetRangeIdName(SbMediaRangeId range_id) { |
| switch (range_id) { |
| case kSbMediaRangeIdUnspecified: |
| return "Unspecified"; |
| case kSbMediaRangeIdLimited: |
| return "Limited"; |
| case kSbMediaRangeIdFull: |
| return "Full"; |
| case kSbMediaRangeIdDerived: |
| return "Derived"; |
| } |
| SB_NOTREACHED(); |
| return "Invalid"; |
| } |
| |
| #if SB_API_VERSION >= 11 |
| bool IsAudioSampleInfoSubstantiallyDifferent( |
| const SbMediaAudioSampleInfo& left, |
| const SbMediaAudioSampleInfo& right) { |
| return left.codec != right.codec || |
| left.samples_per_second != right.samples_per_second || |
| left.number_of_channels != right.number_of_channels || |
| left.audio_specific_config_size != right.audio_specific_config_size || |
| SbMemoryCompare(left.audio_specific_config, |
| right.audio_specific_config, |
| left.audio_specific_config_size) != 0; |
| } |
| #endif // SB_API_VERSION < 11 |
| |
| } // namespace media |
| } // namespace starboard |
| } // namespace shared |
| } // namespace starboard |
| |
| bool operator==(const SbMediaColorMetadata& metadata_1, |
| const SbMediaColorMetadata& metadata_2) { |
| return SbMemoryCompare(&metadata_1, &metadata_2, |
| sizeof(SbMediaColorMetadata)) == 0; |
| } |
| |
| bool operator==(const SbMediaVideoSampleInfo& sample_info_1, |
| const SbMediaVideoSampleInfo& sample_info_2) { |
| #if SB_API_VERSION >= 11 |
| if (sample_info_1.codec != sample_info_2.codec) { |
| return false; |
| } |
| if (sample_info_1.codec == kSbMediaVideoCodecNone) { |
| return true; |
| } |
| #endif // SB_API_VERSION >= 11 |
| |
| #if SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) |
| if (SbStringCompareAll(sample_info_1.mime, sample_info_2.mime) != 0) { |
| return false; |
| } |
| if (SbStringCompareAll(sample_info_1.max_video_capabilities, |
| sample_info_2.max_video_capabilities) != 0) { |
| return false; |
| } |
| #endif // SB_HAS(PLAYER_CREATION_AND_OUTPUT_MODE_QUERY_IMPROVEMENT) |
| |
| if (sample_info_1.is_key_frame != sample_info_2.is_key_frame) { |
| return false; |
| } |
| if (sample_info_1.frame_width != sample_info_2.frame_width) { |
| return false; |
| } |
| if (sample_info_1.frame_height != sample_info_2.frame_height) { |
| return false; |
| } |
| #if SB_API_VERSION >= 11 |
| return sample_info_1.color_metadata == sample_info_2.color_metadata; |
| #else // SB_API_VERSION >= 11 |
| return *sample_info_1.color_metadata == *sample_info_2.color_metadata; |
| #endif // SB_API_VERSION >= 11 |
| } |
| |
| bool operator!=(const SbMediaColorMetadata& metadata_1, |
| const SbMediaColorMetadata& metadata_2) { |
| return !(metadata_1 == metadata_2); |
| } |
| |
| bool operator!=(const SbMediaVideoSampleInfo& sample_info_1, |
| const SbMediaVideoSampleInfo& sample_info_2) { |
| return !(sample_info_1 == sample_info_2); |
| } |
| |
| std::ostream& operator<<(std::ostream& os, |
| const SbMediaMasteringMetadata& metadata) { |
| os << "r(" << metadata.primary_r_chromaticity_x << ", " |
| << metadata.primary_r_chromaticity_y << "), g(" |
| << metadata.primary_g_chromaticity_x << ", " |
| << metadata.primary_g_chromaticity_y << "), b(" |
| << metadata.primary_b_chromaticity_x << ", " |
| << metadata.primary_b_chromaticity_y << "), white(" |
| << metadata.white_point_chromaticity_x << ", " |
| << metadata.white_point_chromaticity_y << "), luminance(" |
| << metadata.luminance_min << " to " << metadata.luminance_max << ")"; |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, |
| const SbMediaColorMetadata& metadata) { |
| using starboard::shared::starboard::media::GetPrimaryIdName; |
| using starboard::shared::starboard::media::GetTransferIdName; |
| using starboard::shared::starboard::media::GetMatrixIdName; |
| using starboard::shared::starboard::media::GetRangeIdName; |
| os << metadata.bits_per_channel |
| << " bits, mastering metadata: " << metadata.mastering_metadata |
| << ", primary: " << GetPrimaryIdName(metadata.primaries) |
| << ", transfer: " << GetTransferIdName(metadata.transfer) |
| << ", matrix: " << GetMatrixIdName(metadata.matrix) |
| << ", range: " << GetRangeIdName(metadata.range); |
| return os; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, |
| const SbMediaVideoSampleInfo& sample_info) { |
| using starboard::shared::starboard::media::GetCodecName; |
| #if SB_API_VERSION >= 11 |
| os << GetCodecName(sample_info.codec) << ", "; |
| #endif // SB_API_VERSION >= 11 |
| if (sample_info.is_key_frame) { |
| os << "key frame, "; |
| } |
| os << sample_info.frame_width << 'x' << sample_info.frame_height << ' '; |
| #if SB_API_VERSION >= 11 |
| os << '(' << sample_info.color_metadata << ')'; |
| #else // SB_API_VERSION >= 11 |
| os << '(' << *sample_info.color_metadata << ')'; |
| #endif // SB_API_VERSION >= 11 |
| return os; |
| } |
| |
| std::string GetHexRepresentation(const uint8_t* data, int size) { |
| const char kBinToHex[] = "0123456789abcdef"; |
| |
| std::string result; |
| |
| for (int i = 0; i < size; ++i) { |
| result += kBinToHex[data[i] / 16]; |
| result += kBinToHex[data[i] % 16]; |
| if (i != size - 1) { |
| result += ' '; |
| } |
| } |
| |
| return result; |
| } |
| |
| std::string GetStringRepresentation(const uint8_t* data, int size) { |
| std::string result; |
| |
| for (int i = 0; i < size; ++i) { |
| if (std::isspace(data[i])) { |
| result += ' '; |
| } else if (std::isprint(data[i])) { |
| result += data[i]; |
| } else { |
| result += '?'; |
| } |
| } |
| |
| return result; |
| } |
| |
| std::string GetMixedRepresentation(const uint8_t* data, |
| int size, |
| int bytes_per_line) { |
| std::string result; |
| |
| for (int i = 0; i < size; i += bytes_per_line) { |
| if (i + bytes_per_line <= size) { |
| result += GetHexRepresentation(data + i, bytes_per_line); |
| result += " | "; |
| result += GetStringRepresentation(data + i, bytes_per_line); |
| result += '\n'; |
| } else { |
| int bytes_left = size - i; |
| result += GetHexRepresentation(data + i, bytes_left); |
| result += std::string((bytes_per_line - bytes_left) * 3, ' '); |
| result += " | "; |
| result += GetStringRepresentation(data + i, bytes_left); |
| result += std::string(bytes_per_line - bytes_left, ' '); |
| result += '\n'; |
| } |
| } |
| |
| return result; |
| } |