diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp b/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp index 837efc0941c24..a1803c409c254 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.cpp @@ -138,6 +138,17 @@ static VideoDecodingLimits* resolveVideoDecodingLimits() return limits ? &*limits : nullptr; } +// Returns true if the given mp4a codec string refers to USAC (xHE-AAC, AudioObjectType=42). +// Format: mp4a.. e.g. "mp4a.40.42" +static bool isUsacMp4aCodec(const String& codec) +{ + auto parts = codec.split('.'); + if (parts.size() != 3) + return false; + auto aot = parseInteger(parts[2]); + return aot && *aot == 42; +} + // We shouldn't accept media that the player can't actually play. // AAC supports up to 96 channels. #define MEDIA_MAX_AAC_CHANNELS 96 @@ -565,6 +576,7 @@ void GStreamerRegistryScanner::initializeDecoders(const GStreamerRegistryScanner Vector mseCompatibleMapping = { { ElementFactories::Type::AudioDecoder, "audio/x-ac3"_s, { }, { "x-ac3"_s, "ac-3"_s, "ac3"_s } }, { ElementFactories::Type::AudioDecoder, "audio/x-eac3"_s, { "audio/x-ac3"_s }, { "x-eac3"_s, "ec3"_s, "ec-3"_s, "eac3"_s } }, + { ElementFactories::Type::AudioDecoder, "audio/x-ac4"_s, { }, { "x-ac4"_s, "ac-4*"_s, "ac4"_s } }, { ElementFactories::Type::AudioDecoder, "audio/x-flac"_s, { "audio/x-flac"_s, "audio/flac"_s }, { "x-flac"_s, "flac"_s, "fLaC"_s } }, }; fillMimeTypeSetFromCapsMapping(factories, mseCompatibleMapping); @@ -797,6 +809,10 @@ GStreamerRegistryScanner::CodecLookupResult GStreamerRegistryScanner::isCodecSup result = isAVC1CodecSupported(configuration, codecName, shouldCheckForHardwareUse); else if (codecName.startsWith("hev1"_s) || codecName.startsWith("hvc1"_s)) result = isHEVCCodecSupported(configuration, codecName, shouldCheckForHardwareUse); + else if (codecName.startsWith("mp4a"_s) && isUsacMp4aCodec(codecName)) + result = isUSACCodecSupported(configuration, shouldCheckForHardwareUse); + else if (codecName.startsWith("ac-4"_s) && !parseAc4LevelAndProfile(codecName)) + result = { false, nullptr }; #if PLATFORM(WPE) else if ((codecName.startsWith("dvhe"_s) || codecName.startsWith("dvh1"_s)) && !supportsDVHCodec) result = { false, nullptr }; @@ -946,23 +962,21 @@ bool GStreamerRegistryScanner::areAllCodecsSupported(Configuration configuration return true; } -GStreamerRegistryScanner::CodecLookupResult GStreamerRegistryScanner::areCapsSupported(Configuration configuration, const GRefPtr& caps, bool shouldCheckForHardwareUse) const +GStreamerRegistryScanner::CodecLookupResult GStreamerRegistryScanner::areCapsSupported(ElementFactories::Type factoryType, const GRefPtr& caps, bool shouldCheckForHardwareUse) const { - OptionSet factoryTypes; - switch (configuration) { - case Configuration::Decoding: - factoryTypes.add(ElementFactories::Type::VideoDecoder); - break; - case Configuration::Encoding: - factoryTypes.add(ElementFactories::Type::VideoEncoder); - break; - } + OptionSet factoryTypes = { factoryType }; auto lookupResult = ElementFactories(factoryTypes).hasElementForCaps(factoryTypes.toSingleValue().value(), caps, ElementFactories::CheckHardwareClassifier::Yes); bool supported = lookupResult && (shouldCheckForHardwareUse ? lookupResult.isUsingHardware : true); GST_DEBUG("%s decoding supported for caps %" GST_PTR_FORMAT ": %s", shouldCheckForHardwareUse ? "Hardware" : "Software", caps.get(), boolForPrinting(supported)); return { supported, supported ? lookupResult.factory : nullptr }; } +GStreamerRegistryScanner::CodecLookupResult GStreamerRegistryScanner::areCapsSupported(Configuration configuration, const GRefPtr& caps, bool shouldCheckForHardwareUse) const +{ + auto factoryType = configuration == Configuration::Decoding ? ElementFactories::Type::VideoDecoder : ElementFactories::Type::VideoEncoder; + return areCapsSupported(factoryType, caps, shouldCheckForHardwareUse); +} + GStreamerRegistryScanner::CodecLookupResult GStreamerRegistryScanner::isAVC1CodecSupported(Configuration configuration, const String& codec, bool shouldCheckForHardwareUse) const { auto h264Caps = adoptGRef(gst_caps_new_empty_simple("video/x-h264")); @@ -1018,6 +1032,46 @@ ASCIILiteral GStreamerRegistryScanner::configurationNameForLogging(Configuration return ""_s; } +GStreamerRegistryScanner::CodecLookupResult GStreamerRegistryScanner::isUSACCodecSupported(Configuration configuration, bool shouldCheckForHardwareUse) const +{ + // USAC (Unified Speech and Audio Coding / xHE-AAC) requires a decoder that explicitly + // supports xHE-AAC/USAC. Check stream-format=usac: used by platform decoders/sinks + auto factoryType = configuration == Configuration::Decoding ? ElementFactories::Type::AudioDecoder : ElementFactories::Type::AudioEncoder; + auto usacStreamFormatCaps = adoptGRef(gst_caps_from_string("audio/mpeg, mpegversion=(int)4, stream-format=(string)usac")); + auto result = areCapsSupported(factoryType, usacStreamFormatCaps, shouldCheckForHardwareUse); + GST_DEBUG("USAC (xHE-AAC) audio %s supported: %s", shouldCheckForHardwareUse ? "hardware" : "software", boolForPrinting(result.isSupported)); + return result; +} + +bool GStreamerRegistryScanner::parseAc4LevelAndProfile(const String& codec) const +{ + auto parts = codec.split('.'); + // sanity check + if (parts.isEmpty()) + return false; + // "ac-4" with no dots is valid (generic, unconstrained). + if (parts.size() == 1 && equalIgnoringASCIICase(parts[0], "ac-4"_s)) + return true; + // Full format requires exactly 4 components: ["ac-4", bitstream_version, presentation_version, mdcompat] + if (parts.size() != 4) { + GST_DEBUG("AC-4 codec string has wrong number of components: %s", codec.utf8().data()); + return false; + } + // presentation_version must be 1 (stereo/5.1); value 2 denotes IMS which is not supported. + auto presentationVersion = parseInteger(parts[2]); + if (!presentationVersion || *presentationVersion != 1) { + GST_DEBUG("AC-4 codec string has unsupported presentation_version: %s", codec.utf8().data()); + return false; + } + // mdcompat (level): only levels 0-3 are supported. + auto mdcompat = parseInteger(parts[3]); + if (!mdcompat || *mdcompat > 3) { + GST_DEBUG("AC-4 codec string has unsupported mdcompat level: %s", codec.utf8().data()); + return false; + } + return true; +} + GStreamerRegistryScanner::RegistryLookupResult GStreamerRegistryScanner::isConfigurationSupported(Configuration configuration, const MediaConfiguration& mediaConfiguration) const { bool isUsingHardware = false; diff --git a/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.h b/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.h index d0695a01573db..e1c9b90904e83 100644 --- a/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.h +++ b/Source/WebCore/platform/graphics/gstreamer/GStreamerRegistryScanner.h @@ -161,6 +161,7 @@ class GStreamerRegistryScanner { void initializeEncoders(const ElementFactories&); RegistryLookupResult isConfigurationSupported(Configuration, const MediaConfiguration&) const; + CodecLookupResult areCapsSupported(ElementFactories::Type, const GRefPtr&, bool shouldCheckForHardwareUse) const; struct GstCapsWebKitMapping { ElementFactories::Type elementType; @@ -174,6 +175,9 @@ class GStreamerRegistryScanner { CodecLookupResult isAVC1CodecSupported(Configuration, const String& codec, bool shouldCheckForHardwareUse) const; CodecLookupResult isHEVCCodecSupported(Configuration, const String& codec, bool shouldCheckForHardwareUse) const; + bool parseAc4LevelAndProfile(const String& codec) const; + CodecLookupResult isUSACCodecSupported(Configuration, bool shouldCheckForHardwareUse) const; + ASCIILiteral configurationNameForLogging(Configuration) const; bool supportsFeatures(const String& features) const; diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/GStreamerEMEUtilities.h b/Source/WebCore/platform/graphics/gstreamer/eme/GStreamerEMEUtilities.h index 7cd766870f370..5066a4a5ba0ca 100644 --- a/Source/WebCore/platform/graphics/gstreamer/eme/GStreamerEMEUtilities.h +++ b/Source/WebCore/platform/graphics/gstreamer/eme/GStreamerEMEUtilities.h @@ -114,8 +114,8 @@ class GStreamerEMEUtilities { static constexpr auto s_unspecifiedUUID = GST_PROTECTION_UNSPECIFIED_SYSTEM_ID ""_s; static constexpr auto s_unspecifiedKeySystem = GST_PROTECTION_UNSPECIFIED_SYSTEM_ID ""_s; - static constexpr std::array s_cencEncryptionMediaTypes = { "video/mp4"_s, "audio/mp4"_s, "video/x-h264"_s, "video/x-h265"_s, "audio/mpeg"_s, - "audio/x-eac3"_s, "audio/x-ac3"_s, "audio/x-flac"_s, "audio/x-opus"_s, "video/x-vp9"_s, "video/x-av1"_s }; + static constexpr std::array s_cencEncryptionMediaTypes = { "video/mp4"_s, "audio/mp4"_s, "video/x-h264"_s, "video/x-h265"_s, "audio/mpeg"_s, + "audio/x-eac3"_s, "audio/x-ac3"_s, "audio/x-ac4"_s, "audio/x-flac"_s, "audio/x-opus"_s, "video/x-vp9"_s, "video/x-av1"_s }; static constexpr std::array s_webmEncryptionMediaTypes = { "video/webm"_s, "audio/webm"_s, "video/x-vp9"_s, "video/x-av1"_s, "audio/x-opus"_s, "audio/x-vorbis"_s, "video/x-vp8"_s }; static bool isClearKeyKeySystem(const String& keySystem) diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp index 0c53197153be2..bd39cfd9243d2 100644 --- a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderDecryptorGStreamer.cpp @@ -59,6 +59,7 @@ static GstStaticPadTemplate srcTemplate = GST_STATIC_PAD_TEMPLATE("src", "audio/x-flac; " "audio/x-eac3; " "audio/x-ac3; " + "audio/x-ac4; " "video/x-h264; " "video/x-h265; " "video/x-vp9; video/x-vp8; " diff --git a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderParser.cpp b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderParser.cpp index ff2696092c32a..9b5383af3b091 100644 --- a/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderParser.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/eme/WebKitThunderParser.cpp @@ -64,6 +64,7 @@ static GstStaticPadTemplate thunderParseSrcTemplate = GST_STATIC_PAD_TEMPLATE("s "audio/x-flac; " "audio/x-eac3; " "audio/x-ac3; " + "audio/x-ac4; " "video/x-h264; " "video/x-h265; " "video/x-vp9; video/x-vp8; "