Add support for platform software video decoder implementations.
Also enables support for all hardware implementations. Renames HardwareVideoDecoderFactory to MediaCodecVideoDecoderFactory. Renames HardwareVideoDecoder to AndroidVideoDecoder. Bug: webrtc:8538 Change-Id: I9b351f387526af4da61fb07c07fb4285bd833e19 Reviewed-on: https://webrtc-review.googlesource.com/97680 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Magnus Jedvert <magjed@webrtc.org> Commit-Queue: Sami Kalliomäki <sakal@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24586}
This commit is contained in:
committed by
Commit Bot
parent
906add4b25
commit
389d2261c3
2
BUILD.gn
2
BUILD.gn
@ -576,12 +576,12 @@ if (rtc_include_tests) {
|
||||
"examples/androidjunit/src/org/appspot/apprtc/BluetoothManagerTest.java",
|
||||
"examples/androidjunit/src/org/appspot/apprtc/DirectRTCClientTest.java",
|
||||
"examples/androidjunit/src/org/appspot/apprtc/TCPChannelClientTest.java",
|
||||
"sdk/android/tests/src/org/webrtc/AndroidVideoDecoderTest.java",
|
||||
"sdk/android/tests/src/org/webrtc/CameraEnumerationTest.java",
|
||||
"sdk/android/tests/src/org/webrtc/CodecTestHelper.java",
|
||||
"sdk/android/tests/src/org/webrtc/FakeMediaCodecWrapper.java",
|
||||
"sdk/android/tests/src/org/webrtc/GlGenericDrawerTest.java",
|
||||
"sdk/android/tests/src/org/webrtc/HardwareVideoEncoderTest.java",
|
||||
"sdk/android/tests/src/org/webrtc/HardwareVideoDecoderTest.java",
|
||||
"sdk/android/tests/src/org/webrtc/ScalingSettingsTest.java",
|
||||
]
|
||||
|
||||
|
||||
@ -385,16 +385,18 @@ if (is_android) {
|
||||
java_files = [
|
||||
"api/org/webrtc/HardwareVideoDecoderFactory.java",
|
||||
"api/org/webrtc/HardwareVideoEncoderFactory.java",
|
||||
"api/org/webrtc/PlatformSoftwareVideoDecoderFactory.java",
|
||||
"src/java/org/webrtc/AndroidVideoDecoder.java",
|
||||
"src/java/org/webrtc/BaseBitrateAdjuster.java",
|
||||
"src/java/org/webrtc/BitrateAdjuster.java",
|
||||
"src/java/org/webrtc/DynamicBitrateAdjuster.java",
|
||||
"src/java/org/webrtc/FramerateBitrateAdjuster.java",
|
||||
"src/java/org/webrtc/HardwareVideoDecoder.java",
|
||||
"src/java/org/webrtc/HardwareVideoEncoder.java",
|
||||
"src/java/org/webrtc/MediaCodecUtils.java",
|
||||
"src/java/org/webrtc/MediaCodecVideoDecoderFactory.java",
|
||||
"src/java/org/webrtc/MediaCodecWrapper.java",
|
||||
"src/java/org/webrtc/MediaCodecWrapperFactory.java",
|
||||
"src/java/org/webrtc/MediaCodecWrapperFactoryImpl.java",
|
||||
"src/java/org/webrtc/MediaCodecUtils.java",
|
||||
"src/java/org/webrtc/NV12Buffer.java",
|
||||
"src/java/org/webrtc/VideoCodecType.java",
|
||||
]
|
||||
@ -1253,17 +1255,15 @@ if (is_android) {
|
||||
android_manifest = "instrumentationtests/AndroidManifest.xml"
|
||||
|
||||
java_files = [
|
||||
"instrumentationtests/src/org/webrtc/AndroidVideoDecoderInstrumentationTest.java",
|
||||
"instrumentationtests/src/org/webrtc/Camera1CapturerUsingByteBufferTest.java",
|
||||
"instrumentationtests/src/org/webrtc/Camera1CapturerUsingTextureTest.java",
|
||||
"instrumentationtests/src/org/webrtc/Camera2CapturerTest.java",
|
||||
"instrumentationtests/src/org/webrtc/CameraVideoCapturerTestFixtures.java",
|
||||
"instrumentationtests/src/org/webrtc/TestConstants.java",
|
||||
"instrumentationtests/src/org/webrtc/DefaultVideoEncoderFactoryTest.java",
|
||||
"instrumentationtests/src/org/webrtc/EglRendererTest.java",
|
||||
"instrumentationtests/src/org/webrtc/FileVideoCapturerTest.java",
|
||||
"instrumentationtests/src/org/webrtc/GlRectDrawerTest.java",
|
||||
"instrumentationtests/src/org/webrtc/VideoFrameBufferTest.java",
|
||||
"instrumentationtests/src/org/webrtc/HardwareVideoDecoderTest.java",
|
||||
"instrumentationtests/src/org/webrtc/HardwareVideoEncoderTest.java",
|
||||
"instrumentationtests/src/org/webrtc/LoggableTest.java",
|
||||
"instrumentationtests/src/org/webrtc/MediaCodecVideoEncoderTest.java",
|
||||
@ -1273,7 +1273,9 @@ if (is_android) {
|
||||
"instrumentationtests/src/org/webrtc/RendererCommonTest.java",
|
||||
"instrumentationtests/src/org/webrtc/SurfaceTextureHelperTest.java",
|
||||
"instrumentationtests/src/org/webrtc/SurfaceViewRendererOnMeasureTest.java",
|
||||
"instrumentationtests/src/org/webrtc/TestConstants.java",
|
||||
"instrumentationtests/src/org/webrtc/VideoFileRendererTest.java",
|
||||
"instrumentationtests/src/org/webrtc/VideoFrameBufferTest.java",
|
||||
"instrumentationtests/src/org/webrtc/WebRtcJniBootTest.java",
|
||||
"instrumentationtests/src/org/webrtc/YuvHelperTest.java",
|
||||
]
|
||||
|
||||
@ -15,25 +15,37 @@ import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
/** Helper class that combines HW and SW decoders. */
|
||||
/**
|
||||
* Helper class that combines HW and SW decoders.
|
||||
*/
|
||||
public class DefaultVideoDecoderFactory implements VideoDecoderFactory {
|
||||
private final VideoDecoderFactory hardwareVideoDecoderFactory;
|
||||
private final VideoDecoderFactory softwareVideoDecoderFactory = new SoftwareVideoDecoderFactory();
|
||||
private final @Nullable VideoDecoderFactory platformSoftwareVideoDecoderFactory;
|
||||
|
||||
/** Create decoder factory using default hardware decoder factory. */
|
||||
public DefaultVideoDecoderFactory(EglBase.Context eglContext) {
|
||||
/**
|
||||
* Create decoder factory using default hardware decoder factory.
|
||||
*/
|
||||
public DefaultVideoDecoderFactory(@Nullable EglBase.Context eglContext) {
|
||||
this.hardwareVideoDecoderFactory = new HardwareVideoDecoderFactory(eglContext);
|
||||
this.platformSoftwareVideoDecoderFactory = new PlatformSoftwareVideoDecoderFactory(eglContext);
|
||||
}
|
||||
|
||||
/** Create decoder factory using explicit hardware decoder factory. */
|
||||
/**
|
||||
* Create decoder factory using explicit hardware decoder factory.
|
||||
*/
|
||||
DefaultVideoDecoderFactory(VideoDecoderFactory hardwareVideoDecoderFactory) {
|
||||
this.hardwareVideoDecoderFactory = hardwareVideoDecoderFactory;
|
||||
this.platformSoftwareVideoDecoderFactory = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable VideoDecoder createDecoder(VideoCodecInfo codecType) {
|
||||
final VideoDecoder softwareDecoder = softwareVideoDecoderFactory.createDecoder(codecType);
|
||||
VideoDecoder softwareDecoder = softwareVideoDecoderFactory.createDecoder(codecType);
|
||||
final VideoDecoder hardwareDecoder = hardwareVideoDecoderFactory.createDecoder(codecType);
|
||||
if (softwareDecoder == null && platformSoftwareVideoDecoderFactory != null) {
|
||||
softwareDecoder = platformSoftwareVideoDecoderFactory.createDecoder(codecType);
|
||||
}
|
||||
if (hardwareDecoder != null && softwareDecoder != null) {
|
||||
// Both hardware and software supported, wrap it in a software fallback
|
||||
return new VideoDecoderFallback(
|
||||
@ -48,6 +60,10 @@ public class DefaultVideoDecoderFactory implements VideoDecoderFactory {
|
||||
|
||||
supportedCodecInfos.addAll(Arrays.asList(softwareVideoDecoderFactory.getSupportedCodecs()));
|
||||
supportedCodecInfos.addAll(Arrays.asList(hardwareVideoDecoderFactory.getSupportedCodecs()));
|
||||
if (platformSoftwareVideoDecoderFactory != null) {
|
||||
supportedCodecInfos.addAll(
|
||||
Arrays.asList(platformSoftwareVideoDecoderFactory.getSupportedCodecs()));
|
||||
}
|
||||
|
||||
return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]);
|
||||
}
|
||||
|
||||
@ -10,26 +10,10 @@
|
||||
|
||||
package org.webrtc;
|
||||
|
||||
import static org.webrtc.MediaCodecUtils.EXYNOS_PREFIX;
|
||||
import static org.webrtc.MediaCodecUtils.INTEL_PREFIX;
|
||||
import static org.webrtc.MediaCodecUtils.NVIDIA_PREFIX;
|
||||
import static org.webrtc.MediaCodecUtils.QCOM_PREFIX;
|
||||
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaCodecList;
|
||||
import android.os.Build;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Factory for Android hardware VideoDecoders. */
|
||||
@SuppressWarnings("deprecation") // API level 16 requires use of deprecated methods.
|
||||
public class HardwareVideoDecoderFactory implements VideoDecoderFactory {
|
||||
private static final String TAG = "HardwareVideoDecoderFactory";
|
||||
|
||||
private final EglBase.Context sharedContext;
|
||||
|
||||
public class HardwareVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
|
||||
/** Creates a HardwareVideoDecoderFactory that does not use surface textures. */
|
||||
@Deprecated // Not removed yet to avoid breaking callers.
|
||||
public HardwareVideoDecoderFactory() {
|
||||
@ -37,120 +21,13 @@ public class HardwareVideoDecoderFactory implements VideoDecoderFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a HardwareVideoDecoderFactory that supports surface texture rendering using the given
|
||||
* shared context. The context may be null. If it is null, then surface support is disabled.
|
||||
* 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.
|
||||
*/
|
||||
public HardwareVideoDecoderFactory(EglBase.Context sharedContext) {
|
||||
this.sharedContext = sharedContext;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VideoDecoder createDecoder(VideoCodecInfo codecType) {
|
||||
VideoCodecType type = VideoCodecType.valueOf(codecType.getName());
|
||||
MediaCodecInfo info = findCodecForType(type);
|
||||
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CodecCapabilities capabilities = info.getCapabilitiesForType(type.mimeType());
|
||||
return new HardwareVideoDecoder(new MediaCodecWrapperFactoryImpl(), info.getName(), type,
|
||||
MediaCodecUtils.selectColorFormat(MediaCodecUtils.DECODER_COLOR_FORMATS, capabilities),
|
||||
sharedContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoCodecInfo[] getSupportedCodecs() {
|
||||
List<VideoCodecInfo> supportedCodecInfos = new ArrayList<VideoCodecInfo>();
|
||||
// Generate a list of supported codecs in order of preference:
|
||||
// VP8, VP9, H264 (high profile), and H264 (baseline profile).
|
||||
for (VideoCodecType type :
|
||||
new VideoCodecType[] {VideoCodecType.VP8, VideoCodecType.VP9, VideoCodecType.H264}) {
|
||||
MediaCodecInfo codec = findCodecForType(type);
|
||||
if (codec != null) {
|
||||
String name = type.name();
|
||||
if (type == VideoCodecType.H264 && isH264HighProfileSupported(codec)) {
|
||||
supportedCodecInfos.add(new VideoCodecInfo(
|
||||
name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ true)));
|
||||
}
|
||||
|
||||
supportedCodecInfos.add(new VideoCodecInfo(
|
||||
name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ false)));
|
||||
}
|
||||
}
|
||||
|
||||
return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]);
|
||||
}
|
||||
|
||||
private @Nullable MediaCodecInfo findCodecForType(VideoCodecType type) {
|
||||
// HW decoding is not supported on builds before KITKAT.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
|
||||
MediaCodecInfo info = null;
|
||||
try {
|
||||
info = MediaCodecList.getCodecInfoAt(i);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logging.e(TAG, "Cannot retrieve encoder 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, VideoCodecType 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 isHardwareSupported(info, type);
|
||||
}
|
||||
|
||||
private boolean isHardwareSupported(MediaCodecInfo info, VideoCodecType type) {
|
||||
String name = info.getName();
|
||||
switch (type) {
|
||||
case VP8:
|
||||
// QCOM, Intel, Exynos, and Nvidia all supported for VP8.
|
||||
return name.startsWith(QCOM_PREFIX) || name.startsWith(INTEL_PREFIX)
|
||||
|| name.startsWith(EXYNOS_PREFIX) || name.startsWith(NVIDIA_PREFIX);
|
||||
case VP9:
|
||||
// QCOM and Exynos supported for VP9.
|
||||
return name.startsWith(QCOM_PREFIX) || name.startsWith(EXYNOS_PREFIX);
|
||||
case H264:
|
||||
// QCOM, Intel, and Exynos supported for H264.
|
||||
return name.startsWith(QCOM_PREFIX) || name.startsWith(INTEL_PREFIX)
|
||||
|| name.startsWith(EXYNOS_PREFIX);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isH264HighProfileSupported(MediaCodecInfo info) {
|
||||
String name = info.getName();
|
||||
// Support H.264 HP decoding on QCOM chips for Android L and above.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && name.startsWith(QCOM_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
// Support H.264 HP decoding on Exynos chips for Android M and above.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && name.startsWith(EXYNOS_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
public HardwareVideoDecoderFactory(@Nullable EglBase.Context sharedContext) {
|
||||
super(sharedContext, /* prefixWhitelist= */ new String[] {""},
|
||||
/* prefixBlacklist= */ MediaCodecUtils.SOFTWARE_IMPLEMENTATION_PREFIXES);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Factory for Android platform software VideoDecoders. */
|
||||
public class PlatformSoftwareVideoDecoderFactory extends MediaCodecVideoDecoderFactory {
|
||||
/**
|
||||
* 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[] {});
|
||||
}
|
||||
}
|
||||
@ -32,10 +32,10 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Unit tests for {@link HardwareVideoDecoder}. */
|
||||
/** Unit tests for {@link AndroidVideoDecoder}. */
|
||||
@RunWith(ParameterizedRunner.class)
|
||||
@UseRunnerDelegate(BaseJUnit4RunnerDelegate.class)
|
||||
public final class HardwareVideoDecoderTest {
|
||||
public final class AndroidVideoDecoderInstrumentationTest {
|
||||
@ClassParameter private static List<ParameterSet> CLASS_PARAMS = new ArrayList<>();
|
||||
|
||||
static {
|
||||
@ -56,7 +56,7 @@ public final class HardwareVideoDecoderTest {
|
||||
private final VideoCodecInfo codecType;
|
||||
private final boolean useEglContext;
|
||||
|
||||
public HardwareVideoDecoderTest(String codecName, boolean useEglContext) {
|
||||
public AndroidVideoDecoderInstrumentationTest(String codecName, boolean useEglContext) {
|
||||
if (codecName.equals("H264")) {
|
||||
this.codecType = H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC;
|
||||
} else {
|
||||
@ -65,7 +65,7 @@ public final class HardwareVideoDecoderTest {
|
||||
this.useEglContext = useEglContext;
|
||||
}
|
||||
|
||||
private static final String TAG = "HardwareVideoDecoderTest";
|
||||
private static final String TAG = "AndroidVideoDecoderInstrumentationTest";
|
||||
|
||||
private static final int TEST_FRAME_COUNT = 10;
|
||||
private static final int TEST_FRAME_WIDTH = 640;
|
||||
@ -79,7 +79,7 @@ public class HardwareVideoEncoderTest {
|
||||
// # Mock classes
|
||||
/**
|
||||
* Mock encoder callback that allows easy verification of the general properties of the encoded
|
||||
* frame such as width and height. Also used from HardwareVideoDecoderTest.
|
||||
* frame such as width and height. Also used from AndroidVideoDecoderInstrumentationTest.
|
||||
*/
|
||||
static class MockEncoderCallback implements VideoEncoder.Callback {
|
||||
private BlockingQueue<EncodedImage> frameQueue = new LinkedBlockingQueue<>();
|
||||
|
||||
@ -24,11 +24,15 @@ import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.Nullable;
|
||||
import org.webrtc.ThreadUtils.ThreadChecker;
|
||||
|
||||
/** Android hardware video decoder. */
|
||||
/**
|
||||
* Android hardware video decoder.
|
||||
*/
|
||||
@TargetApi(16)
|
||||
@SuppressWarnings("deprecation") // Cannot support API 16 without using deprecated methods.
|
||||
class HardwareVideoDecoder implements VideoDecoder, VideoSink {
|
||||
private static final String TAG = "HardwareVideoDecoder";
|
||||
@SuppressWarnings("deprecation")
|
||||
// Cannot support API 16 without using deprecated methods.
|
||||
// TODO(sakal): Rename to MediaCodecVideoDecoder once the deprecated implementation is removed.
|
||||
class AndroidVideoDecoder implements VideoDecoder, VideoSink {
|
||||
private static final String TAG = "AndroidVideoDecoder";
|
||||
|
||||
// TODO(magjed): Use MediaFormat.KEY_* constants when part of the public API.
|
||||
private static final String MEDIA_FORMAT_KEY_STRIDE = "stride";
|
||||
@ -100,7 +104,7 @@ class HardwareVideoDecoder implements VideoDecoder, VideoSink {
|
||||
// on the decoder thread.
|
||||
private boolean keyFrameRequired;
|
||||
|
||||
private final EglBase.Context sharedContext;
|
||||
private final @Nullable EglBase.Context sharedContext;
|
||||
// Valid and immutable while the decoder is running.
|
||||
@Nullable private SurfaceTextureHelper surfaceTextureHelper;
|
||||
@Nullable private Surface surface = null;
|
||||
@ -126,11 +130,14 @@ class HardwareVideoDecoder implements VideoDecoder, VideoSink {
|
||||
// Valid and immutable while the decoder is running.
|
||||
@Nullable private MediaCodecWrapper codec = null;
|
||||
|
||||
HardwareVideoDecoder(MediaCodecWrapperFactory mediaCodecWrapperFactory, String codecName,
|
||||
VideoCodecType codecType, int colorFormat, EglBase.Context sharedContext) {
|
||||
AndroidVideoDecoder(MediaCodecWrapperFactory mediaCodecWrapperFactory, String codecName,
|
||||
VideoCodecType codecType, int colorFormat, @Nullable EglBase.Context sharedContext) {
|
||||
if (!isSupportedColorFormat(colorFormat)) {
|
||||
throw new IllegalArgumentException("Unsupported color format: " + colorFormat);
|
||||
}
|
||||
Logging.d(TAG,
|
||||
"ctor name: " + codecName + " type: " + codecType + " color format: " + colorFormat
|
||||
+ " context: " + sharedContext);
|
||||
this.mediaCodecWrapperFactory = mediaCodecWrapperFactory;
|
||||
this.codecName = codecName;
|
||||
this.codecType = codecType;
|
||||
@ -155,7 +162,9 @@ class HardwareVideoDecoder implements VideoDecoder, VideoSink {
|
||||
// Internal variant is used when restarting the codec due to reconfiguration.
|
||||
private VideoCodecStatus initDecodeInternal(int width, int height) {
|
||||
decoderThreadChecker.checkIsOnValidThread();
|
||||
Logging.d(TAG, "initDecodeInternal");
|
||||
Logging.d(TAG,
|
||||
"initDecodeInternal name: " + codecName + " type: " + codecType + " width: " + width
|
||||
+ " height: " + height);
|
||||
if (outputThread != null) {
|
||||
Logging.e(TAG, "initDecodeInternal called while the codec is already running");
|
||||
return VideoCodecStatus.FALLBACK_SOFTWARE;
|
||||
@ -295,7 +304,7 @@ class HardwareVideoDecoder implements VideoDecoder, VideoSink {
|
||||
|
||||
@Override
|
||||
public String getImplementationName() {
|
||||
return "HWDecoder";
|
||||
return codecName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -358,7 +367,7 @@ class HardwareVideoDecoder implements VideoDecoder, VideoSink {
|
||||
}
|
||||
|
||||
private Thread createOutputThread() {
|
||||
return new Thread("HardwareVideoDecoder.outputThread") {
|
||||
return new Thread("AndroidVideoDecoder.outputThread") {
|
||||
@Override
|
||||
public void run() {
|
||||
outputThreadChecker = new ThreadChecker();
|
||||
@ -30,6 +30,7 @@ class MediaCodecUtils {
|
||||
static final String INTEL_PREFIX = "OMX.Intel.";
|
||||
static final String NVIDIA_PREFIX = "OMX.Nvidia.";
|
||||
static final String QCOM_PREFIX = "OMX.qcom.";
|
||||
static final String[] SOFTWARE_IMPLEMENTATION_PREFIXES = {"OMX.google.", "OMX.SEC."};
|
||||
|
||||
// NV12 color format supported by QCOM codec, but not declared in MediaCodec -
|
||||
// see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
|
||||
|
||||
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 static org.webrtc.MediaCodecUtils.EXYNOS_PREFIX;
|
||||
import static org.webrtc.MediaCodecUtils.QCOM_PREFIX;
|
||||
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaCodecList;
|
||||
import android.os.Build;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Factory for decoders backed by Android MediaCodec API. */
|
||||
@SuppressWarnings("deprecation") // API level 16 requires use of deprecated methods.
|
||||
class MediaCodecVideoDecoderFactory implements VideoDecoderFactory {
|
||||
private static final String TAG = "MediaCodecVideoDecoderFactory";
|
||||
|
||||
private final @Nullable EglBase.Context sharedContext;
|
||||
private final String[] prefixWhitelist;
|
||||
private final String[] prefixBlacklist;
|
||||
|
||||
/**
|
||||
* MediaCodecVideoDecoderFactory will support codecs whitelisted excluding those blacklisted.
|
||||
*
|
||||
* @param sharedContext The textures generated will be accessible from this context. May be null,
|
||||
* this disables texture support.
|
||||
* @param prefixWhitelist List of codec prefixes to be whitelisted.
|
||||
* @param prefixBlacklist List of codec prefixes to be blacklisted.
|
||||
*/
|
||||
public MediaCodecVideoDecoderFactory(
|
||||
@Nullable EglBase.Context sharedContext, String[] prefixWhitelist, String[] prefixBlacklist) {
|
||||
this.sharedContext = sharedContext;
|
||||
this.prefixWhitelist = Arrays.copyOf(prefixWhitelist, prefixWhitelist.length);
|
||||
this.prefixBlacklist = Arrays.copyOf(prefixBlacklist, prefixBlacklist.length);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VideoDecoder createDecoder(VideoCodecInfo codecType) {
|
||||
VideoCodecType type = VideoCodecType.valueOf(codecType.getName());
|
||||
MediaCodecInfo info = findCodecForType(type);
|
||||
|
||||
if (info == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CodecCapabilities capabilities = info.getCapabilitiesForType(type.mimeType());
|
||||
return new AndroidVideoDecoder(new MediaCodecWrapperFactoryImpl(), info.getName(), type,
|
||||
MediaCodecUtils.selectColorFormat(MediaCodecUtils.DECODER_COLOR_FORMATS, capabilities),
|
||||
sharedContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VideoCodecInfo[] getSupportedCodecs() {
|
||||
List<VideoCodecInfo> supportedCodecInfos = new ArrayList<VideoCodecInfo>();
|
||||
// Generate a list of supported codecs in order of preference:
|
||||
// VP8, VP9, H264 (high profile), and H264 (baseline profile).
|
||||
for (VideoCodecType type :
|
||||
new VideoCodecType[] {VideoCodecType.VP8, VideoCodecType.VP9, VideoCodecType.H264}) {
|
||||
MediaCodecInfo codec = findCodecForType(type);
|
||||
if (codec != null) {
|
||||
String name = type.name();
|
||||
if (type == VideoCodecType.H264 && isH264HighProfileSupported(codec)) {
|
||||
supportedCodecInfos.add(new VideoCodecInfo(
|
||||
name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ true)));
|
||||
}
|
||||
|
||||
supportedCodecInfos.add(new VideoCodecInfo(
|
||||
name, MediaCodecUtils.getCodecProperties(type, /* highProfile= */ false)));
|
||||
}
|
||||
}
|
||||
|
||||
return supportedCodecInfos.toArray(new VideoCodecInfo[supportedCodecInfos.size()]);
|
||||
}
|
||||
|
||||
private @Nullable MediaCodecInfo findCodecForType(VideoCodecType type) {
|
||||
// HW decoding is not supported on builds before KITKAT.
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
|
||||
MediaCodecInfo info = null;
|
||||
try {
|
||||
info = MediaCodecList.getCodecInfoAt(i);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Logging.e(TAG, "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, VideoCodecType type) {
|
||||
String name = info.getName();
|
||||
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 isWhitelisted(name) && !isBlacklisted(name);
|
||||
}
|
||||
|
||||
private boolean isWhitelisted(String name) {
|
||||
for (String prefix : prefixWhitelist) {
|
||||
if (name.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isBlacklisted(String name) {
|
||||
for (String prefix : prefixBlacklist) {
|
||||
if (name.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isH264HighProfileSupported(MediaCodecInfo info) {
|
||||
String name = info.getName();
|
||||
// Support H.264 HP decoding on QCOM chips for Android L and above.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && name.startsWith(QCOM_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
// Support H.264 HP decoding on Exynos chips for Android M and above.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && name.startsWith(EXYNOS_PREFIX)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Subset of methods defined in {@link android.media.MediaCodec} needed by
|
||||
* {@link HardwareVideoEncoder} and {@link HardwareVideoDecoder}. This interface
|
||||
* {@link HardwareVideoEncoder} and {@link AndroidVideoDecoder}. This interface
|
||||
* exists to allow mocking and using a fake implementation in tests.
|
||||
*/
|
||||
interface MediaCodecWrapper {
|
||||
|
||||
@ -23,16 +23,12 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.media.MediaCodec.BufferInfo;
|
||||
import android.media.MediaCodecInfo.CodecCapabilities;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.Handler;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.chromium.testing.local.LocalRobolectricTestRunner;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@ -40,25 +36,15 @@ import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowSystemClock;
|
||||
import org.webrtc.EglBase;
|
||||
import org.webrtc.EncodedImage;
|
||||
import org.webrtc.EncodedImage.FrameType;
|
||||
import org.webrtc.FakeMediaCodecWrapper.State;
|
||||
import org.webrtc.SurfaceTextureHelper;
|
||||
import org.webrtc.TextureBufferImpl;
|
||||
import org.webrtc.VideoCodecStatus;
|
||||
import org.webrtc.VideoDecoder;
|
||||
import org.webrtc.VideoDecoder.DecodeInfo;
|
||||
import org.webrtc.VideoFrame;
|
||||
import org.webrtc.VideoFrame.I420Buffer;
|
||||
import org.webrtc.VideoFrame.TextureBuffer.Type;
|
||||
import org.webrtc.VideoSink;
|
||||
import org.webrtc.YuvConverter;
|
||||
|
||||
@RunWith(LocalRobolectricTestRunner.class)
|
||||
@Config(manifest = Config.NONE)
|
||||
public class HardwareVideoDecoderTest {
|
||||
public class AndroidVideoDecoderTest {
|
||||
private static final VideoDecoder.Settings TEST_DECODER_SETTINGS =
|
||||
new VideoDecoder.Settings(/* numberOfCores= */ 1, /* width= */ 640, /* height= */ 480);
|
||||
private static final int COLOR_FORMAT = CodecCapabilities.COLOR_FormatYUV420Planar;
|
||||
@ -67,7 +53,7 @@ public class HardwareVideoDecoderTest {
|
||||
|
||||
private static final byte[] ENCODED_TEST_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
|
||||
|
||||
private class TestDecoder extends HardwareVideoDecoder {
|
||||
private class TestDecoder extends AndroidVideoDecoder {
|
||||
private final Object deliverDecodedFrameLock = new Object();
|
||||
private boolean deliverDecodedFrameDone = true;
|
||||
|
||||
@ -198,8 +184,7 @@ public class HardwareVideoDecoderTest {
|
||||
@Test
|
||||
public void testInit() {
|
||||
// Set-up.
|
||||
HardwareVideoDecoder decoder =
|
||||
new TestDecoderBuilder().setCodecType(VideoCodecType.VP8).build();
|
||||
AndroidVideoDecoder decoder = new TestDecoderBuilder().setCodecType(VideoCodecType.VP8).build();
|
||||
|
||||
// Test.
|
||||
assertThat(decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback))
|
||||
@ -221,7 +206,7 @@ public class HardwareVideoDecoderTest {
|
||||
@Test
|
||||
public void testRelease() {
|
||||
// Set-up.
|
||||
HardwareVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
|
||||
|
||||
// Test.
|
||||
@ -234,7 +219,7 @@ public class HardwareVideoDecoderTest {
|
||||
@Test
|
||||
public void testReleaseMultipleTimes() {
|
||||
// Set-up.
|
||||
HardwareVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
|
||||
|
||||
// Test.
|
||||
@ -248,7 +233,7 @@ public class HardwareVideoDecoderTest {
|
||||
@Test
|
||||
public void testDecodeQueuesData() {
|
||||
// Set-up.
|
||||
HardwareVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback);
|
||||
|
||||
// Test.
|
||||
@ -401,7 +386,7 @@ public class HardwareVideoDecoderTest {
|
||||
.when(fakeMediaCodecWrapper)
|
||||
.configure(any(), any(), any(), anyInt());
|
||||
|
||||
HardwareVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
|
||||
// Test.
|
||||
assertThat(decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback))
|
||||
@ -413,7 +398,7 @@ public class HardwareVideoDecoderTest {
|
||||
// Set-up.
|
||||
doThrow(new IllegalStateException("Fake error")).when(fakeMediaCodecWrapper).start();
|
||||
|
||||
HardwareVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
AndroidVideoDecoder decoder = new TestDecoderBuilder().build();
|
||||
|
||||
// Test.
|
||||
assertThat(decoder.initDecode(TEST_DECODER_SETTINGS, mockDecoderCallback))
|
||||
@ -15,11 +15,9 @@ import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Random;
|
||||
import org.webrtc.JavaI420Buffer;
|
||||
import org.webrtc.VideoFrame;
|
||||
|
||||
/**
|
||||
* Helper methods for {@link HardwareVideoEncoderTest} and {@link HardwareVideoDecoderTest}.
|
||||
* Helper methods for {@link HardwareVideoEncoderTest} and {@link AndroidVideoDecoderTest}.
|
||||
*/
|
||||
class CodecTestHelper {
|
||||
static void assertEqualContents(byte[] expected, ByteBuffer actual, int offset, int size) {
|
||||
|
||||
Reference in New Issue
Block a user