Create VideoDecoderFactory interface and implementation.
The implementation creates an Android hardware video decoder. It is built around the same patterns as the HardwareVideoEncoderFactory. This change pulls some shared code and constants into a common "utils" class. Finally, adds an instrumentation test for the HardwareVideoDecoder. BUG=webrtc:7760 Change-Id: Iea6eaae7727925743cb54f7c3153a6c07d62f55d Reviewed-on: https://chromium-review.googlesource.com/536254 Commit-Queue: Bjorn Mellem <mellem@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Reviewed-by: Peter Thatcher <pthatcher@webrtc.org> Cr-Commit-Position: refs/heads/master@{#18686}
This commit is contained in:
@ -38,13 +38,6 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
private static final String MEDIA_FORMAT_KEY_CROP_TOP = "crop-top";
|
||||
private static final String MEDIA_FORMAT_KEY_CROP_BOTTOM = "crop-bottom";
|
||||
|
||||
// NV12 color format supported by QCOM codec, but not declared in MediaCodec -
|
||||
// see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
|
||||
private static final int COLOR_QCOM_FORMATYVU420PackedSemiPlanar32m4ka = 0x7FA30C01;
|
||||
private static final int COLOR_QCOM_FORMATYVU420PackedSemiPlanar16m4ka = 0x7FA30C02;
|
||||
private static final int COLOR_QCOM_FORMATYVU420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03;
|
||||
private static final int COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04;
|
||||
|
||||
// MediaCodec.release() occasionally hangs. Release stops waiting and reports failure after
|
||||
// this timeout.
|
||||
private static final int MEDIA_CODEC_RELEASE_TIMEOUT_MS = 5000;
|
||||
@ -132,7 +125,7 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
try {
|
||||
codec = MediaCodec.createByCodecName(codecName);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Logging.e(TAG, "Cannot create media decoder");
|
||||
Logging.e(TAG, "Cannot create media decoder " + codecName);
|
||||
return VideoCodecStatus.ERROR;
|
||||
}
|
||||
try {
|
||||
@ -168,8 +161,8 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
|
||||
int size = frame.buffer.remaining();
|
||||
if (size == 0) {
|
||||
Logging.e(TAG, "decode() - input buffer empty");
|
||||
return VideoCodecStatus.ERR_PARAMETER;
|
||||
Logging.e(TAG, "decode() - no input data");
|
||||
return VideoCodecStatus.ERROR;
|
||||
}
|
||||
|
||||
// Load dimensions from shared memory under the dimension lock.
|
||||
@ -215,13 +208,13 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
index = codec.dequeueInputBuffer(0 /* timeout */);
|
||||
} catch (IllegalStateException e) {
|
||||
Logging.e(TAG, "dequeueInputBuffer failed", e);
|
||||
return VideoCodecStatus.ERROR;
|
||||
return VideoCodecStatus.FALLBACK_SOFTWARE;
|
||||
}
|
||||
if (index < 0) {
|
||||
// Decoder is falling behind. No input buffers available.
|
||||
// The decoder can't simply drop frames; it might lose a key frame.
|
||||
Logging.e(TAG, "decode() - no HW buffers available; decoder falling behind");
|
||||
return VideoCodecStatus.ERROR;
|
||||
return VideoCodecStatus.FALLBACK_SOFTWARE;
|
||||
}
|
||||
|
||||
ByteBuffer buffer;
|
||||
@ -229,12 +222,12 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
buffer = codec.getInputBuffers()[index];
|
||||
} catch (IllegalStateException e) {
|
||||
Logging.e(TAG, "getInputBuffers failed", e);
|
||||
return VideoCodecStatus.ERROR;
|
||||
return VideoCodecStatus.FALLBACK_SOFTWARE;
|
||||
}
|
||||
|
||||
if (buffer.capacity() < size) {
|
||||
Logging.e(TAG, "decode() - HW buffer too small");
|
||||
return VideoCodecStatus.ERROR;
|
||||
return VideoCodecStatus.FALLBACK_SOFTWARE;
|
||||
}
|
||||
buffer.put(frame.buffer);
|
||||
|
||||
@ -245,7 +238,7 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
} catch (IllegalStateException e) {
|
||||
Logging.e(TAG, "queueInputBuffer failed", e);
|
||||
decodeStartTimes.pollLast();
|
||||
return VideoCodecStatus.ERROR;
|
||||
return VideoCodecStatus.FALLBACK_SOFTWARE;
|
||||
}
|
||||
if (keyFrameRequired) {
|
||||
keyFrameRequired = false;
|
||||
@ -472,18 +465,12 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
}
|
||||
|
||||
private boolean isSupportedColorFormat(int colorFormat) {
|
||||
switch (colorFormat) {
|
||||
case CodecCapabilities.COLOR_FormatYUV420Planar:
|
||||
case CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
|
||||
case CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
|
||||
case COLOR_QCOM_FORMATYVU420PackedSemiPlanar32m4ka:
|
||||
case COLOR_QCOM_FORMATYVU420PackedSemiPlanar16m4ka:
|
||||
case COLOR_QCOM_FORMATYVU420PackedSemiPlanar64x32Tile2m8ka:
|
||||
case COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m:
|
||||
for (int supported : MediaCodecUtils.DECODER_COLOR_FORMATS) {
|
||||
if (supported == colorFormat) {
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void copyI420(ByteBuffer src, int offset, VideoFrame.I420Buffer frameBuffer,
|
||||
@ -505,15 +492,14 @@ class HardwareVideoDecoder implements VideoDecoder {
|
||||
copyPlane(src, vPos, uvStride, frameBuffer.getDataV(), 0, frameBuffer.getStrideV(), chromaWidth,
|
||||
chromaHeight);
|
||||
|
||||
// If the sliceHeight is odd, duplicate the last rows of chroma. Copy the last row of the U and
|
||||
// V channels and append them at the end of each channel.
|
||||
// If the sliceHeight is odd, duplicate the last rows of chroma.
|
||||
if (sliceHeight % 2 != 0) {
|
||||
int strideU = frameBuffer.getStrideU();
|
||||
int endU = chromaHeight * strideU;
|
||||
copyRow(frameBuffer.getDataU(), endU - strideU, frameBuffer.getDataU(), endU, chromaWidth);
|
||||
uPos = chromaHeight * strideU;
|
||||
copyRow(frameBuffer.getDataU(), uPos - strideU, frameBuffer.getDataU(), uPos, chromaWidth);
|
||||
int strideV = frameBuffer.getStrideV();
|
||||
int endV = chromaHeight * strideV;
|
||||
copyRow(frameBuffer.getDataV(), endV - strideV, frameBuffer.getDataV(), endV, chromaWidth);
|
||||
vPos = chromaHeight * strideV;
|
||||
copyRow(frameBuffer.getDataV(), vPos - strideV, frameBuffer.getDataV(), vPos, chromaWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -36,10 +36,6 @@ class HardwareVideoEncoder implements VideoEncoder {
|
||||
// constant until API level 21.
|
||||
private static final String KEY_BITRATE_MODE = "bitrate-mode";
|
||||
|
||||
// NV12 color format supported by QCOM codec, but not declared in MediaCodec -
|
||||
// see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
|
||||
private static final int COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04;
|
||||
|
||||
private static final int MAX_VIDEO_FRAMERATE = 30;
|
||||
|
||||
// See MAX_ENCODER_Q_SIZE in androidmediaencoder_jni.cc.
|
||||
@ -133,8 +129,9 @@ class HardwareVideoEncoder implements VideoEncoder {
|
||||
|
||||
lastKeyFrameMs = -1;
|
||||
|
||||
codec = createCodecByName(codecName);
|
||||
if (codec == null) {
|
||||
try {
|
||||
codec = MediaCodec.createByCodecName(codecName);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Logging.e(TAG, "Cannot create media encoder " + codecName);
|
||||
return VideoCodecStatus.ERROR;
|
||||
}
|
||||
@ -418,15 +415,6 @@ class HardwareVideoEncoder implements VideoEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
private static MediaCodec createCodecByName(String codecName) {
|
||||
try {
|
||||
return MediaCodec.createByCodecName(codecName);
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
Logging.e(TAG, "createCodecByName failed", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration of supported color formats used for MediaCodec's input.
|
||||
*/
|
||||
@ -463,7 +451,7 @@ class HardwareVideoEncoder implements VideoEncoder {
|
||||
return I420;
|
||||
case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
|
||||
case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
|
||||
case COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m:
|
||||
case MediaCodecUtils.COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m:
|
||||
return NV12;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported colorFormat: " + colorFormat);
|
||||
|
||||
74
webrtc/sdk/android/src/java/org/webrtc/MediaCodecUtils.java
Normal file
74
webrtc/sdk/android/src/java/org/webrtc/MediaCodecUtils.java
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2017 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;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaCodec;
|
||||
|
||||
/** Container class for static constants and helpers used with MediaCodec. */
|
||||
class MediaCodecUtils {
|
||||
private static final String TAG = "MediaCodecUtils";
|
||||
|
||||
// Prefixes for supported hardware encoder/decoder component names.
|
||||
static final String EXYNOS_PREFIX = "OMX.Exynos.";
|
||||
static final String INTEL_PREFIX = "OMX.Intel.";
|
||||
static final String NVIDIA_PREFIX = "OMX.Nvidia.";
|
||||
static final String QCOM_PREFIX = "OMX.qcom.";
|
||||
|
||||
// NV12 color format supported by QCOM codec, but not declared in MediaCodec -
|
||||
// see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
|
||||
static final int COLOR_QCOM_FORMATYVU420PackedSemiPlanar32m4ka = 0x7FA30C01;
|
||||
static final int COLOR_QCOM_FORMATYVU420PackedSemiPlanar16m4ka = 0x7FA30C02;
|
||||
static final int COLOR_QCOM_FORMATYVU420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03;
|
||||
static final int COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04;
|
||||
|
||||
// Color formats supported by hardware decoder - in order of preference.
|
||||
static final int[] DECODER_COLOR_FORMATS = new int[] {CodecCapabilities.COLOR_FormatYUV420Planar,
|
||||
CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
|
||||
CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
|
||||
MediaCodecUtils.COLOR_QCOM_FORMATYVU420PackedSemiPlanar32m4ka,
|
||||
MediaCodecUtils.COLOR_QCOM_FORMATYVU420PackedSemiPlanar16m4ka,
|
||||
MediaCodecUtils.COLOR_QCOM_FORMATYVU420PackedSemiPlanar64x32Tile2m8ka,
|
||||
MediaCodecUtils.COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m};
|
||||
|
||||
// Color formats supported by hardware encoder - in order of preference.
|
||||
static final int[] ENCODER_COLOR_FORMATS = {
|
||||
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar,
|
||||
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
|
||||
MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
|
||||
MediaCodecUtils.COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m};
|
||||
|
||||
static Integer selectColorFormat(int[] supportedColorFormats, CodecCapabilities capabilities) {
|
||||
for (int supportedColorFormat : supportedColorFormats) {
|
||||
for (int codecColorFormat : capabilities.colorFormats) {
|
||||
if (codecColorFormat == supportedColorFormat) {
|
||||
return codecColorFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static boolean codecSupportsType(MediaCodecInfo info, VideoCodecType type) {
|
||||
for (String mimeType : info.getSupportedTypes()) {
|
||||
if (type.mimeType().equals(mimeType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private MediaCodecUtils() {
|
||||
// This class should not be instantiated.
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user