From eee86a6aa3c172e079a451d05c0e17001e3bb4af Mon Sep 17 00:00:00 2001 From: Alex Glaznev Date: Fri, 29 Jan 2016 14:17:07 -0800 Subject: [PATCH] Add option to disable particular HW video codec from app. Plus minor clean up / adding comments. BUG=b/26695339 R=jiayl@webrtc.org Review URL: https://codereview.webrtc.org/1644253003 . Cr-Commit-Position: refs/heads/master@{#11431} --- .../org/webrtc/MediaCodecVideoDecoder.java | 107 ++++++++++++------ .../org/webrtc/MediaCodecVideoEncoder.java | 80 ++++++++----- 2 files changed, 123 insertions(+), 64 deletions(-) diff --git a/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java b/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java index 19002f70e1..4b4e0478a1 100644 --- a/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java +++ b/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java @@ -27,7 +27,6 @@ package org.webrtc; -import android.graphics.SurfaceTexture; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecInfo.CodecCapabilities; @@ -41,8 +40,10 @@ import org.webrtc.Logging; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.Queue; import java.util.concurrent.TimeUnit; @@ -65,13 +66,19 @@ public class MediaCodecVideoDecoder { VIDEO_CODEC_H264 } - private static final int DEQUEUE_INPUT_TIMEOUT = 500000; // 500 ms timeout. - private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000; // Timeout for codec releasing. + // Timeout for input buffer dequeue. + private static final int DEQUEUE_INPUT_TIMEOUT = 500000; + // Timeout for codec releasing. + private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000; + // Max number of output buffers queued before starting to drop decoded frames. + private static final int MAX_QUEUED_OUTPUTBUFFERS = 3; // Active running decoder instance. Set in initDecode() (called from native code) // and reset to null in release() call. private static MediaCodecVideoDecoder runningInstance = null; private static MediaCodecVideoDecoderErrorCallback errorCallback = null; private static int codecErrors = 0; + // List of disabled codec types - can be set from application. + private static Set hwDecoderDisabledTypes = new HashSet(); private Thread mediaCodecThread; private MediaCodec mediaCodec; @@ -110,8 +117,6 @@ public class MediaCodecVideoDecoder { // The below variables are only used when decoding to a Surface. private TextureListener textureListener; - // Max number of output buffers queued before starting to drop decoded frames. - private static final int MAX_QUEUED_OUTPUTBUFFERS = 3; private int droppedFrames; private Surface surface = null; private final Queue @@ -129,7 +134,52 @@ public class MediaCodecVideoDecoder { MediaCodecVideoDecoder.errorCallback = errorCallback; } - // Helper struct for findVp8Decoder() below. + // Functions to disable HW decoding - can be called from applications for platforms + // which have known HW decoding problems. + public static void disableVp8HwCodec() { + Logging.w(TAG, "VP8 decoding is disabled by application."); + hwDecoderDisabledTypes.add(VP8_MIME_TYPE); + } + + public static void disableVp9HwCodec() { + Logging.w(TAG, "VP9 decoding is disabled by application."); + hwDecoderDisabledTypes.add(VP9_MIME_TYPE); + } + + public static void disableH264HwCodec() { + Logging.w(TAG, "H.264 decoding is disabled by application."); + hwDecoderDisabledTypes.add(H264_MIME_TYPE); + } + + // Functions to query if HW decoding is supported. + public static boolean isVp8HwSupported() { + return !hwDecoderDisabledTypes.contains(VP8_MIME_TYPE) && + (findDecoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes) != null); + } + + public static boolean isVp9HwSupported() { + return !hwDecoderDisabledTypes.contains(VP9_MIME_TYPE) && + (findDecoder(VP9_MIME_TYPE, supportedVp9HwCodecPrefixes) != null); + } + + public static boolean isH264HwSupported() { + return !hwDecoderDisabledTypes.contains(H264_MIME_TYPE) && + (findDecoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes) != null); + } + + public static void printStackTrace() { + if (runningInstance != null && runningInstance.mediaCodecThread != null) { + StackTraceElement[] mediaCodecStackTraces = runningInstance.mediaCodecThread.getStackTrace(); + if (mediaCodecStackTraces.length > 0) { + Logging.d(TAG, "MediaCodecVideoDecoder stacks trace:"); + for (StackTraceElement stackTrace : mediaCodecStackTraces) { + Logging.d(TAG, stackTrace.toString()); + } + } + } + } + + // Helper struct for findDecoder() below. private static class DecoderProperties { public DecoderProperties(String codecName, int colorFormat) { this.codecName = codecName; @@ -195,30 +245,6 @@ public class MediaCodecVideoDecoder { return null; // No HW decoder. } - public static boolean isVp8HwSupported() { - return findDecoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes) != null; - } - - public static boolean isVp9HwSupported() { - return findDecoder(VP9_MIME_TYPE, supportedVp9HwCodecPrefixes) != null; - } - - public static boolean isH264HwSupported() { - return findDecoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes) != null; - } - - public static void printStackTrace() { - if (runningInstance != null && runningInstance.mediaCodecThread != null) { - StackTraceElement[] mediaCodecStackTraces = runningInstance.mediaCodecThread.getStackTrace(); - if (mediaCodecStackTraces.length > 0) { - Logging.d(TAG, "MediaCodecVideoDecoder stacks trace:"); - for (StackTraceElement stackTrace : mediaCodecStackTraces) { - Logging.d(TAG, stackTrace.toString()); - } - } - } - } - private void checkOnMediaCodecThread() throws IllegalStateException { if (mediaCodecThread.getId() != Thread.currentThread().getId()) { throw new IllegalStateException( @@ -376,9 +402,12 @@ public class MediaCodecVideoDecoder { this.timeStampMs = timeStampMs; this.ntpTimeStampMs = ntpTimeStampMs; } - private final long decodeStartTimeMs; // Time when this frame was queued for decoding. - private final long timeStampMs; // Only used for bookkeeping in Java. Used in C++; - private final long ntpTimeStampMs; // Only used for bookkeeping in Java. Used in C++; + // Time when this frame was queued for decoding. + private final long decodeStartTimeMs; + // Only used for bookkeeping in Java. Stores C++ inputImage._timeStamp value for input frame. + private final long timeStampMs; + // Only used for bookkeeping in Java. Stores C++ inputImage.ntp_time_ms_ value for input frame. + private final long ntpTimeStampMs; } // Helper struct for dequeueOutputBuffer() below. @@ -397,11 +426,13 @@ public class MediaCodecVideoDecoder { private final int index; private final int offset; private final int size; + // C++ inputImage._timeStamp value for output frame. private final long timeStampMs; + // C++ inputImage.ntp_time_ms_ value for output frame. private final long ntpTimeStampMs; // Number of ms it took to decode this frame. private final long decodeTimeMs; - // System time when this frame finished decoding. + // System time when this frame decoding finished. private final long endDecodeTimeMs; } @@ -409,8 +440,11 @@ public class MediaCodecVideoDecoder { private static class DecodedTextureBuffer { private final int textureID; private final float[] transformMatrix; + // C++ inputImage._timeStamp value for output frame. private final long timeStampMs; + // C++ inputImage.ntp_time_ms_ value for output frame. private final long ntpTimeStampMs; + // Number of ms it took to decode this frame. private final long decodeTimeMs; // Interval from when the frame finished decoding until this buffer has been created. // Since there is only one texture, this interval depend on the time from when @@ -614,8 +648,9 @@ public class MediaCodecVideoDecoder { // Logging.w(TAG, "Draining decoder. Dropping frame with TS: " // + droppedFrame.timeStampMs + ". Total number of dropped frames: " + droppedFrames); } else { - Logging.w(TAG, "Too many output buffers. Dropping frame with TS: " - + droppedFrame.timeStampMs + ". Total number of dropped frames: " + droppedFrames); + Logging.w(TAG, "Too many output buffers " + dequeuedSurfaceOutputBuffers.size() + + ". Dropping frame with TS: " + droppedFrame.timeStampMs + + ". Total number of dropped frames: " + droppedFrames); } mediaCodec.releaseOutputBuffer(droppedFrame.index, false /* render */); diff --git a/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java b/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java index 5c8f9dc77e..8b8ee71dfe 100644 --- a/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java +++ b/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoEncoder.java @@ -42,7 +42,9 @@ import org.webrtc.Logging; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -72,6 +74,8 @@ public class MediaCodecVideoEncoder { private static MediaCodecVideoEncoder runningInstance = null; private static MediaCodecVideoEncoderErrorCallback errorCallback = null; private static int codecErrors = 0; + // List of disabled codec types - can be set from application. + private static Set hwEncoderDisabledTypes = new HashSet(); private Thread mediaCodecThread; private MediaCodec mediaCodec; @@ -104,7 +108,6 @@ public class MediaCodecVideoEncoder { // Bitrate modes - should be in sync with OMX_VIDEO_CONTROLRATETYPE defined // in OMX_Video.h - private static final int VIDEO_ControlRateVariable = 1; private static final int VIDEO_ControlRateConstant = 2; // NV12 color format supported by QCOM codec, but not declared in MediaCodec - // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h @@ -138,6 +141,54 @@ public class MediaCodecVideoEncoder { MediaCodecVideoEncoder.errorCallback = errorCallback; } + // Functions to disable HW encoding - can be called from applications for platforms + // which have known HW decoding problems. + public static void disableVp8HwCodec() { + Logging.w(TAG, "VP8 encoding is disabled by application."); + hwEncoderDisabledTypes.add(VP8_MIME_TYPE); + } + + public static void disableVp9HwCodec() { + Logging.w(TAG, "VP9 encoding is disabled by application."); + hwEncoderDisabledTypes.add(VP9_MIME_TYPE); + } + + public static void disableH264HwCodec() { + Logging.w(TAG, "H.264 encoding is disabled by application."); + hwEncoderDisabledTypes.add(H264_MIME_TYPE); + } + + // Functions to query if HW encoding is supported. + public static boolean isVp8HwSupported() { + return !hwEncoderDisabledTypes.contains(VP8_MIME_TYPE) && + (findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, supportedColorList) != null); + } + + public static boolean isVp9HwSupported() { + return !hwEncoderDisabledTypes.contains(VP9_MIME_TYPE) && + (findHwEncoder(VP9_MIME_TYPE, supportedVp9HwCodecPrefixes, supportedColorList) != null); + } + + public static boolean isH264HwSupported() { + return !hwEncoderDisabledTypes.contains(H264_MIME_TYPE) && + (findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, supportedColorList) != null); + } + + public static boolean isVp8HwSupportedUsingTextures() { + return !hwEncoderDisabledTypes.contains(VP8_MIME_TYPE) && (findHwEncoder( + VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, supportedSurfaceColorList) != null); + } + + public static boolean isVp9HwSupportedUsingTextures() { + return !hwEncoderDisabledTypes.contains(VP9_MIME_TYPE) && (findHwEncoder( + VP9_MIME_TYPE, supportedVp9HwCodecPrefixes, supportedSurfaceColorList) != null); + } + + public static boolean isH264HwSupportedUsingTextures() { + return !hwEncoderDisabledTypes.contains(H264_MIME_TYPE) && (findHwEncoder( + H264_MIME_TYPE, supportedH264HwCodecPrefixes, supportedSurfaceColorList) != null); + } + // Helper struct for findHwEncoder() below. private static class EncoderProperties { public EncoderProperties(String codecName, int colorFormat) { @@ -213,33 +264,6 @@ public class MediaCodecVideoEncoder { return null; // No HW encoder. } - public static boolean isVp8HwSupported() { - return findHwEncoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, supportedColorList) != null; - } - - public static boolean isVp9HwSupported() { - return findHwEncoder(VP9_MIME_TYPE, supportedVp9HwCodecPrefixes, supportedColorList) != null; - } - - public static boolean isH264HwSupported() { - return findHwEncoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes, supportedColorList) != null; - } - - public static boolean isVp8HwSupportedUsingTextures() { - return findHwEncoder( - VP8_MIME_TYPE, supportedVp8HwCodecPrefixes, supportedSurfaceColorList) != null; - } - - public static boolean isVp9HwSupportedUsingTextures() { - return findHwEncoder( - VP9_MIME_TYPE, supportedVp9HwCodecPrefixes, supportedSurfaceColorList) != null; - } - - public static boolean isH264HwSupportedUsingTextures() { - return findHwEncoder( - H264_MIME_TYPE, supportedH264HwCodecPrefixes, supportedSurfaceColorList) != null; - } - private void checkOnMediaCodecThread() { if (mediaCodecThread.getId() != Thread.currentThread().getId()) { throw new RuntimeException(