From a895c26805e614f3d34a936024d0b912e804813f Mon Sep 17 00:00:00 2001 From: kanat Date: Thu, 17 Oct 2024 12:12:27 -0400 Subject: [PATCH 1/6] add DefaultBlacklistedVideoDecoderFactory --- sdk/android/BUILD.gn | 1 + ...DefaultBlacklistedVideoDecoderFactory.java | 143 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn index 7db8d413d3..9bbb7e76ad 100644 --- a/sdk/android/BUILD.gn +++ b/sdk/android/BUILD.gn @@ -367,6 +367,7 @@ if (is_android) { "api/org/webrtc/DefaultVideoDecoderFactory.java", "api/org/webrtc/DefaultVideoEncoderFactory.java", "api/org/webrtc/WrappedVideoDecoderFactory.java", + "api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java", "api/org/webrtc/DefaultAlignedVideoEncoderFactory.java", "api/org/webrtc/SimulcastAlignedVideoEncoderFactory.java", ] diff --git a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java new file mode 100644 index 0000000000..c2ad405e4b --- /dev/null +++ b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java @@ -0,0 +1,143 @@ +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; +import android.os.Build; +import androidx.annotation.Nullable; +import org.webrtc.EglBase; +import org.webrtc.HardwareVideoDecoderFactory; +import org.webrtc.Logging; +import org.webrtc.MediaCodecUtils; +import org.webrtc.PlatformSoftwareVideoDecoderFactory; +import org.webrtc.SoftwareVideoDecoderFactory; +import org.webrtc.VideoCodecInfo; +import org.webrtc.VideoCodecMimeType; +import org.webrtc.VideoDecoder; +import org.webrtc.VideoDecoderFactory; +import org.webrtc.VideoDecoderFallback; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.function.Predicate; + +/** Factory for decoders backed by Android MediaCodec API. */ +@SuppressWarnings("deprecation") // API level 16 requires use of deprecated methods. +public class DefaultBlacklistedVideoDecoderFactory implements VideoDecoderFactory { + + private static final String TAG = "DefaultBlacklistedVideoDecoderFactory"; + + private static final Predicate defaultBlacklistedPredicate = + new Predicate() { + @Override + public boolean test(MediaCodecInfo codec) { + // Use the existing isExynosVP9 method + if (isExynosVP9(codec)) { + return true; + } + return false; + } + }; + + private final VideoDecoderFactory hardwareVideoDecoderFactory; + private final VideoDecoderFactory softwareVideoDecoderFactory; + private final VideoDecoderFactory platformSoftwareVideoDecoderFactory; + private final Predicate isHardwareDecoderBlacklisted; + + public DefaultBlacklistedVideoDecoderFactory(@Nullable EglBase.Context eglContext) { + this(eglContext, null); + } + + public DefaultBlacklistedVideoDecoderFactory( + @Nullable EglBase.Context eglContext, + @Nullable Predicate codecBlacklistedPredicate) { + this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext); + this.softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory(); + this.platformSoftwareVideoDecoderFactory = new PlatformSoftwareVideoDecoderFactory(eglContext); + this.isHardwareDecoderBlacklisted = codecBlacklistedPredicate == null + ? defaultBlacklistedPredicate + : codecBlacklistedPredicate.and(defaultBlacklistedPredicate); + } + + @Override + public VideoDecoder createDecoder(VideoCodecInfo codecType) { + VideoCodecMimeType type = VideoCodecMimeType.valueOf(codecType.getName()); + MediaCodecInfo info = findCodecForType(type); + Logging.d(TAG, "[createDecoder] codecType: " + codecType + ", info: " + stringifyCodec(info)); + + VideoDecoder softwareDecoder = softwareVideoDecoderFactory.createDecoder(codecType); + VideoDecoder hardwareDecoder = hardwareVideoDecoderFactory.createDecoder(codecType); + if (softwareDecoder == null) { + softwareDecoder = platformSoftwareVideoDecoderFactory.createDecoder(codecType); + } + + if (isHardwareDecoderBlacklisted.test(info)) { + Logging.i(TAG, "[createDecoder] hardware decoder is blacklisted: " + stringifyCodec(info)); + return softwareDecoder; + } + + if (hardwareDecoder != null && softwareDecoder != null) { + return new VideoDecoderFallback(softwareDecoder, hardwareDecoder); + } else { + return hardwareDecoder != null ? hardwareDecoder : softwareDecoder; + } + } + + @Override + public VideoCodecInfo[] getSupportedCodecs() { + List supportedCodecInfos = new ArrayList<>(); + supportedCodecInfos.addAll(Arrays.asList(softwareVideoDecoderFactory.getSupportedCodecs())); + supportedCodecInfos.addAll(Arrays.asList(hardwareVideoDecoderFactory.getSupportedCodecs())); + supportedCodecInfos.addAll(Arrays.asList(platformSoftwareVideoDecoderFactory.getSupportedCodecs())); + return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]); + } + + @Nullable + private MediaCodecInfo findCodecForType(VideoCodecMimeType type) { + for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { + MediaCodecInfo info = null; + try { + info = MediaCodecList.getCodecInfoAt(i); + } catch (IllegalArgumentException e) { + Logging.e(TAG, "[findCodecForType] cannot retrieve decoder codec info", e); + } + + if (info == null || info.isEncoder()) { + continue; + } + + if (isSupportedCodec(info, type)) { + return info; + } + } + return null; // No support for this type. + } + + // Returns true if the given MediaCodecInfo indicates a supported encoder for the given type. + private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecMimeType type) { + if (!MediaCodecUtils.codecSupportsType(info, type)) { + return false; + } + // Check for a supported color format. + if (MediaCodecUtils.selectColorFormat( + MediaCodecUtils.DECODER_COLOR_FORMATS, info.getCapabilitiesForType(type.mimeType())) + == null) { + return false; + } + return isCodecAllowed(info); + } + + private boolean isCodecAllowed(MediaCodecInfo info) { + return MediaCodecUtils.isHardwareAccelerated(info) || MediaCodecUtils.isSoftwareOnly(info); + } + + private static boolean isExynosVP9(MediaCodecInfo codec) { + final String codecName = codec.getName().toLowerCase(); + return !codec.isEncoder() && codecName.contains("exynos") && codecName.contains("vp9"); + } + + private static String stringifyCodec(MediaCodecInfo codec) { + if (codec == null) { + return "null"; + } + return "MediaCodecInfo(name=" + codec.getName() + ", isEncoder=" + codec.isEncoder() + ")"; + } +} From 48d395561eef09239963d15b4b312e4c5d6f031e Mon Sep 17 00:00:00 2001 From: kanat Date: Thu, 17 Oct 2024 13:01:03 -0400 Subject: [PATCH 2/6] use decoder.implementationName --- ...DefaultBlacklistedVideoDecoderFactory.java | 93 ++++--------------- 1 file changed, 18 insertions(+), 75 deletions(-) diff --git a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java index c2ad405e4b..301c156738 100644 --- a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java +++ b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java @@ -1,39 +1,29 @@ -import android.media.MediaCodecInfo; -import android.media.MediaCodecList; -import android.os.Build; import androidx.annotation.Nullable; import org.webrtc.EglBase; import org.webrtc.HardwareVideoDecoderFactory; import org.webrtc.Logging; -import org.webrtc.MediaCodecUtils; import org.webrtc.PlatformSoftwareVideoDecoderFactory; import org.webrtc.SoftwareVideoDecoderFactory; import org.webrtc.VideoCodecInfo; -import org.webrtc.VideoCodecMimeType; import org.webrtc.VideoDecoder; import org.webrtc.VideoDecoderFactory; import org.webrtc.VideoDecoderFallback; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import java.util.function.Predicate; -/** Factory for decoders backed by Android MediaCodec API. */ -@SuppressWarnings("deprecation") // API level 16 requires use of deprecated methods. public class DefaultBlacklistedVideoDecoderFactory implements VideoDecoderFactory { private static final String TAG = "DefaultBlacklistedVideoDecoderFactory"; - private static final Predicate defaultBlacklistedPredicate = - new Predicate() { + private static final Predicate defaultBlacklistedPredicate = + new Predicate() { @Override - public boolean test(MediaCodecInfo codec) { - // Use the existing isExynosVP9 method - if (isExynosVP9(codec)) { - return true; - } - return false; + public boolean test(@Nullable VideoDecoder decoder) { + // if the decoder is Exynos VP9, then blacklist it + return isExynosVP9(decoder); } }; @@ -48,28 +38,24 @@ public DefaultBlacklistedVideoDecoderFactory(@Nullable EglBase.Context eglContex public DefaultBlacklistedVideoDecoderFactory( @Nullable EglBase.Context eglContext, - @Nullable Predicate codecBlacklistedPredicate) { + @Nullable Predicate decoderBlacklistedPredicate) { this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext); this.softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory(); this.platformSoftwareVideoDecoderFactory = new PlatformSoftwareVideoDecoderFactory(eglContext); - this.isHardwareDecoderBlacklisted = codecBlacklistedPredicate == null - ? defaultBlacklistedPredicate - : codecBlacklistedPredicate.and(defaultBlacklistedPredicate); + this.isHardwareDecoderBlacklisted = decoderBlacklistedPredicate == null + ? defaultBlacklistedPredicate + : decoderBlacklistedPredicate.or(defaultBlacklistedPredicate); } @Override public VideoDecoder createDecoder(VideoCodecInfo codecType) { - VideoCodecMimeType type = VideoCodecMimeType.valueOf(codecType.getName()); - MediaCodecInfo info = findCodecForType(type); - Logging.d(TAG, "[createDecoder] codecType: " + codecType + ", info: " + stringifyCodec(info)); - VideoDecoder softwareDecoder = softwareVideoDecoderFactory.createDecoder(codecType); VideoDecoder hardwareDecoder = hardwareVideoDecoderFactory.createDecoder(codecType); if (softwareDecoder == null) { softwareDecoder = platformSoftwareVideoDecoderFactory.createDecoder(codecType); } - if (isHardwareDecoderBlacklisted.test(info)) { + if (isHardwareDecoderBlacklisted.test(hardwareDecoder)) { Logging.i(TAG, "[createDecoder] hardware decoder is blacklisted: " + stringifyCodec(info)); return softwareDecoder; } @@ -83,61 +69,18 @@ public VideoDecoder createDecoder(VideoCodecInfo codecType) { @Override public VideoCodecInfo[] getSupportedCodecs() { - List supportedCodecInfos = new ArrayList<>(); + Set supportedCodecInfos = new HashSet<>(); supportedCodecInfos.addAll(Arrays.asList(softwareVideoDecoderFactory.getSupportedCodecs())); supportedCodecInfos.addAll(Arrays.asList(hardwareVideoDecoderFactory.getSupportedCodecs())); supportedCodecInfos.addAll(Arrays.asList(platformSoftwareVideoDecoderFactory.getSupportedCodecs())); - return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]); - } - - @Nullable - private MediaCodecInfo findCodecForType(VideoCodecMimeType type) { - for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) { - MediaCodecInfo info = null; - try { - info = MediaCodecList.getCodecInfoAt(i); - } catch (IllegalArgumentException e) { - Logging.e(TAG, "[findCodecForType] cannot retrieve decoder codec info", e); - } - - if (info == null || info.isEncoder()) { - continue; - } - - if (isSupportedCodec(info, type)) { - return info; - } - } - return null; // No support for this type. + return supportedCodecInfos.toArray(new VideoCodecInfo[0]); } - // Returns true if the given MediaCodecInfo indicates a supported encoder for the given type. - private boolean isSupportedCodec(MediaCodecInfo info, VideoCodecMimeType type) { - if (!MediaCodecUtils.codecSupportsType(info, type)) { + private static boolean isExynosVP9(@Nullable VideoDecoder decoder) { + if (decoder == null) { return false; } - // Check for a supported color format. - if (MediaCodecUtils.selectColorFormat( - MediaCodecUtils.DECODER_COLOR_FORMATS, info.getCapabilitiesForType(type.mimeType())) - == null) { - return false; - } - return isCodecAllowed(info); - } - - private boolean isCodecAllowed(MediaCodecInfo info) { - return MediaCodecUtils.isHardwareAccelerated(info) || MediaCodecUtils.isSoftwareOnly(info); - } - - private static boolean isExynosVP9(MediaCodecInfo codec) { - final String codecName = codec.getName().toLowerCase(); - return !codec.isEncoder() && codecName.contains("exynos") && codecName.contains("vp9"); - } - - private static String stringifyCodec(MediaCodecInfo codec) { - if (codec == null) { - return "null"; - } - return "MediaCodecInfo(name=" + codec.getName() + ", isEncoder=" + codec.isEncoder() + ")"; + final String name = decoder.getImplementationName().toLowerCase(); + return name.contains("exynos") && name.contains("vp9"); } } From bd871d2478f3529a8eb556dfc70fbd5c52967637 Mon Sep 17 00:00:00 2001 From: kanat Date: Thu, 17 Oct 2024 13:14:27 -0400 Subject: [PATCH 3/6] fix compilation --- .../api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java index 301c156738..78f1e8343a 100644 --- a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java +++ b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java @@ -30,7 +30,7 @@ public boolean test(@Nullable VideoDecoder decoder) { private final VideoDecoderFactory hardwareVideoDecoderFactory; private final VideoDecoderFactory softwareVideoDecoderFactory; private final VideoDecoderFactory platformSoftwareVideoDecoderFactory; - private final Predicate isHardwareDecoderBlacklisted; + private final Predicate isHardwareDecoderBlacklisted; public DefaultBlacklistedVideoDecoderFactory(@Nullable EglBase.Context eglContext) { this(eglContext, null); From 1405207b395e4a958d618a8cae1986a6fd5bab6e Mon Sep 17 00:00:00 2001 From: kanat Date: Thu, 17 Oct 2024 13:22:35 -0400 Subject: [PATCH 4/6] fix logging --- .../api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java index 78f1e8343a..194c904c61 100644 --- a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java +++ b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java @@ -56,7 +56,7 @@ public VideoDecoder createDecoder(VideoCodecInfo codecType) { } if (isHardwareDecoderBlacklisted.test(hardwareDecoder)) { - Logging.i(TAG, "[createDecoder] hardware decoder is blacklisted: " + stringifyCodec(info)); + Logging.i(TAG, "[createDecoder] hardware decoder is blacklisted: " + hardwareDecoder.getImplementationName()); return softwareDecoder; } From 17976eac0e67e8062a05fc38e705c9f5255c5b75 Mon Sep 17 00:00:00 2001 From: kanat Date: Thu, 17 Oct 2024 13:31:37 -0400 Subject: [PATCH 5/6] fix logging func call --- .../api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java index 194c904c61..f14c1f883d 100644 --- a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java +++ b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java @@ -56,7 +56,7 @@ public VideoDecoder createDecoder(VideoCodecInfo codecType) { } if (isHardwareDecoderBlacklisted.test(hardwareDecoder)) { - Logging.i(TAG, "[createDecoder] hardware decoder is blacklisted: " + hardwareDecoder.getImplementationName()); + Logging.d(TAG, "Hardware decoder is blacklisted: " + hardwareDecoder.getImplementationName()); return softwareDecoder; } From da873d26e0cbe14f9a07a936c0dfb8f867790e23 Mon Sep 17 00:00:00 2001 From: kanat Date: Thu, 17 Oct 2024 13:40:09 -0400 Subject: [PATCH 6/6] fix imports + package --- .../webrtc/DefaultBlacklistedVideoDecoderFactory.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java index f14c1f883d..3b3874734a 100644 --- a/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java +++ b/sdk/android/api/org/webrtc/DefaultBlacklistedVideoDecoderFactory.java @@ -1,13 +1,6 @@ +package org.webrtc; + import androidx.annotation.Nullable; -import org.webrtc.EglBase; -import org.webrtc.HardwareVideoDecoderFactory; -import org.webrtc.Logging; -import org.webrtc.PlatformSoftwareVideoDecoderFactory; -import org.webrtc.SoftwareVideoDecoderFactory; -import org.webrtc.VideoCodecInfo; -import org.webrtc.VideoDecoder; -import org.webrtc.VideoDecoderFactory; -import org.webrtc.VideoDecoderFallback; import java.util.Arrays; import java.util.HashSet;