Added user-defined predicate to filter video codec implementations.

Ability to provide user defined predicate to disable particular
codec in particular circumstances was added. This could help
addressing mysterious crashes on specific Android devices.

Bug: webrtc:10029
Change-Id: I7ad81f4b1351aa68f036c0ee3b6d32fbf0f697ed
Reviewed-on: https://webrtc-review.googlesource.com/c/111781
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Commit-Queue: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25820}
diff --git a/sdk/android/api/org/webrtc/HardwareVideoDecoderFactory.java b/sdk/android/api/org/webrtc/HardwareVideoDecoderFactory.java
index 80252e6..6907143 100644
--- a/sdk/android/api/org/webrtc/HardwareVideoDecoderFactory.java
+++ b/sdk/android/api/org/webrtc/HardwareVideoDecoderFactory.java
@@ -10,10 +10,29 @@
 
 package org.webrtc;
 
+import android.media.MediaCodecInfo;
+import java.util.Arrays;
 import javax.annotation.Nullable;
 
 /** Factory for Android hardware VideoDecoders. */
 public class HardwareVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
+  private final static Predicate<MediaCodecInfo> defaultAllowedPredicate =
+      new Predicate<MediaCodecInfo>() {
+        private String[] prefixBlacklist =
+            Arrays.copyOf(MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES,
+                MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES.length);
+        @Override
+        public boolean test(MediaCodecInfo arg) {
+          final String name = arg.getName();
+          for (String prefix : prefixBlacklist) {
+            if (name.startsWith(prefix)) {
+              return false;
+            }
+          }
+          return true;
+        }
+      };
+
   /** Creates a HardwareVideoDecoderFactory that does not use surface textures. */
   @Deprecated // Not removed yet to avoid breaking callers.
   public HardwareVideoDecoderFactory() {
@@ -27,7 +46,21 @@
    *                      this disables texture support.
    */
   public HardwareVideoDecoderFactory(@Nullable EglBase.Context sharedContext) {
-    super(sharedContext, /* prefixWhitelist= */ new String[] {""},
-        /* prefixBlacklist= */ MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES);
+    this(sharedContext, /* codecAllowedPredicate= */ null);
+  }
+
+  /**
+   * Creates a HardwareVideoDecoderFactory that supports surface texture rendering.
+   *
+   * @param sharedContext The textures generated will be accessible from this context. May be null,
+   *                      this disables texture support.
+   * @param codecAllowedPredicate predicate to filter codecs. It is combined with the default
+   *                              predicate that only allows hardware codecs.
+   */
+  public HardwareVideoDecoderFactory(@Nullable EglBase.Context sharedContext,
+      @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) {
+    super(sharedContext,
+        (codecAllowedPredicate == null ? defaultAllowedPredicate
+                                       : codecAllowedPredicate.and(defaultAllowedPredicate)));
   }
 }
diff --git a/sdk/android/api/org/webrtc/HardwareVideoEncoderFactory.java b/sdk/android/api/org/webrtc/HardwareVideoEncoderFactory.java
index 4d782a9..2f78126 100644
--- a/sdk/android/api/org/webrtc/HardwareVideoEncoderFactory.java
+++ b/sdk/android/api/org/webrtc/HardwareVideoEncoderFactory.java
@@ -42,9 +42,34 @@
   @Nullable private final EglBase14.Context sharedContext;
   private final boolean enableIntelVp8Encoder;
   private final boolean enableH264HighProfile;
+  @Nullable private final Predicate<MediaCodecInfo> codecAllowedPredicate;
 
+  /**
+   * Creates a HardwareVideoEncoderFactory that supports surface texture encoding.
+   *
+   * @param sharedContext The textures generated will be accessible from this context. May be null,
+   *                      this disables texture support.
+   * @param enableIntelVp8Encoder true if Intel's VP8 encoder enabled.
+   * @param enableH264HighProfile true if H264 High Profile enabled.
+   */
   public HardwareVideoEncoderFactory(
       EglBase.Context sharedContext, boolean enableIntelVp8Encoder, boolean enableH264HighProfile) {
+    this(sharedContext, enableIntelVp8Encoder, enableH264HighProfile,
+        /* codecAllowedPredicate= */ null);
+  }
+
+  /**
+   * Creates a HardwareVideoEncoderFactory that supports surface texture encoding.
+   *
+   * @param sharedContext The textures generated will be accessible from this context. May be null,
+   *                      this disables texture support.
+   * @param enableIntelVp8Encoder true if Intel's VP8 encoder enabled.
+   * @param enableH264HighProfile true if H264 High Profile enabled.
+   * @param codecAllowedPredicate optional predicate to filter codecs. All codecs are allowed
+   *                              when predicate is not provided.
+   */
+  public HardwareVideoEncoderFactory(EglBase.Context sharedContext, boolean enableIntelVp8Encoder,
+      boolean enableH264HighProfile, @Nullable Predicate<MediaCodecInfo> codecAllowedPredicate) {
     // Texture mode requires EglBase14.
     if (sharedContext instanceof EglBase14.Context) {
       this.sharedContext = (EglBase14.Context) sharedContext;
@@ -54,6 +79,7 @@
     }
     this.enableIntelVp8Encoder = enableIntelVp8Encoder;
     this.enableH264HighProfile = enableH264HighProfile;
+    this.codecAllowedPredicate = codecAllowedPredicate;
   }
 
   @Deprecated
@@ -164,7 +190,7 @@
         == null) {
       return false;
     }
-    return isHardwareSupportedInCurrentSdk(info, type);
+    return isHardwareSupportedInCurrentSdk(info, type) && isMediaCodecAllowed(info);
   }
 
   // Returns true if the given MediaCodecInfo indicates a hardware module that is supported on the
@@ -212,6 +238,13 @@
                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
   }
 
+  private boolean isMediaCodecAllowed(MediaCodecInfo info) {
+    if (codecAllowedPredicate == null) {
+      return true;
+    }
+    return codecAllowedPredicate.test(info);
+  }
+
   private int getKeyFrameIntervalSec(VideoCodecType type) {
     switch (type) {
       case VP8: // Fallthrough intended.
diff --git a/sdk/android/api/org/webrtc/PlatformSoftwareVideoDecoderFactory.java b/sdk/android/api/org/webrtc/PlatformSoftwareVideoDecoderFactory.java
index 856db68..1cb52ac 100644
--- a/sdk/android/api/org/webrtc/PlatformSoftwareVideoDecoderFactory.java
+++ b/sdk/android/api/org/webrtc/PlatformSoftwareVideoDecoderFactory.java
@@ -10,18 +10,40 @@
 
 package org.webrtc;
 
+import android.media.MediaCodecInfo;
+import java.util.Arrays;
 import javax.annotation.Nullable;
 
 /** Factory for Android platform software VideoDecoders. */
 public class PlatformSoftwareVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
   /**
+   * Default allowed predicate.
+   */
+  private static final Predicate<MediaCodecInfo> defaultAllowedPredicate =
+      new Predicate<MediaCodecInfo>() {
+        private String[] prefixWhitelist =
+            Arrays.copyOf(MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES,
+                MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES.length);
+
+        @Override
+        public boolean test(MediaCodecInfo arg) {
+          final String name = arg.getName();
+          for (String prefix : prefixWhitelist) {
+            if (name.startsWith(prefix)) {
+              return true;
+            }
+          }
+          return false;
+        }
+      };
+
+  /**
    * Creates a PlatformSoftwareVideoDecoderFactory that supports surface texture rendering.
    *
    * @param sharedContext The textures generated will be accessible from this context. May be null,
    *                      this disables texture support.
    */
   public PlatformSoftwareVideoDecoderFactory(@Nullable EglBase.Context sharedContext) {
-    super(sharedContext, /* prefixWhitelist= */ MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES,
-        /* prefixBlacklist= */ new String[] {});
+    super(sharedContext, defaultAllowedPredicate);
   }
 }
diff --git a/sdk/android/api/org/webrtc/Predicate.java b/sdk/android/api/org/webrtc/Predicate.java
new file mode 100644
index 0000000..50e6975
--- /dev/null
+++ b/sdk/android/api/org/webrtc/Predicate.java
@@ -0,0 +1,73 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+/**
+ * Represents a predicate (boolean-valued function) of one argument.
+ */
+public interface Predicate<T> {
+  /**
+   * Evaluates this predicate on the given argument.
+   *
+   * @param arg the input argument
+   * @return true if the input argument matches the predicate, otherwise false
+   */
+  boolean test(T arg);
+
+  /**
+   * Returns a composed predicate that represents a short-circuiting logical OR of this predicate
+   * and another. When evaluating the composed predicate, if this predicate is true, then the other
+   * predicate is not evaluated.
+   *
+   * @param other a predicate that will be logically-ORed with this predicate
+   * @return a composed predicate that represents the short-circuiting logical OR of this predicate
+   *     and the other predicate
+   */
+  default Predicate<T> or(Predicate<? super T> other) {
+    return new Predicate<T>() {
+      @Override
+      public boolean test(T arg) {
+        return Predicate.this.test(arg) || other.test(arg);
+      }
+    };
+  }
+
+  /**
+   * Returns a composed predicate that represents a short-circuiting logical AND of this predicate
+   * and another.
+   *
+   * @param other a predicate that will be logically-ANDed with this predicate
+   * @return a composed predicate that represents the short-circuiting logical AND of this predicate
+   *     and the other predicate
+   */
+  default Predicate<T> and(Predicate<? super T> other) {
+    return new Predicate<T>() {
+      @Override
+      public boolean test(T arg) {
+        return Predicate.this.test(arg) && other.test(arg);
+      }
+    };
+  }
+
+  /**
+   * Returns a predicate that represents the logical negation of this predicate.
+   *
+   * @return a predicate that represents the logical negation of this predicate
+   */
+  default Predicate<T> negate() {
+    return new Predicate<T>() {
+      @Override
+      public boolean test(T arg) {
+        return !Predicate.this.test(arg);
+      }
+    };
+  }
+}
\ No newline at end of file