Android: Transform legacy codec factories into VideoCodecFactories
We want to have an easy migration path away from MediaCodecVideoEncoder and MediaCodecVideoDecoder and remove the special treatment of these in our JNI code. This CL transforms these video codecs into proper VideoCodecFactories that can be injected in the PeerConnectionFactory like any other external factory. To summarize, this CL: * Provides a trivial migration path for external clients. * Removes special treatment of the legacy factories in our JNI code. Bug: webrtc:7925 Change-Id: I7ee8a6b0ce5ac0f3dc9c06d1587b8a9e52e0b684 Reviewed-on: https://webrtc-review.googlesource.com/88442 Commit-Queue: Magnus Jedvert <magjed@webrtc.org> Reviewed-by: Sami Kalliomäki <sakal@webrtc.org> Cr-Commit-Position: refs/heads/master@{#23972}
This commit is contained in:
committed by
Commit Bot
parent
dfbced6504
commit
e26ff4b581
@ -21,6 +21,7 @@ import android.os.SystemClock;
|
||||
import android.view.Surface;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
@ -44,6 +45,86 @@ public class MediaCodecVideoDecoder {
|
||||
// possibly to minimize the amount of translation work necessary.
|
||||
|
||||
private static final String TAG = "MediaCodecVideoDecoder";
|
||||
|
||||
/**
|
||||
* Create a VideoDecoderFactory that can be injected in the PeerConnectionFactory and replicate
|
||||
* the old behavior.
|
||||
*/
|
||||
public static VideoDecoderFactory createFactory() {
|
||||
return new DefaultVideoDecoderFactory(new HwDecoderFactory());
|
||||
}
|
||||
|
||||
// Factory for creating HW MediaCodecVideoDecoder instances.
|
||||
static class HwDecoderFactory implements VideoDecoderFactory {
|
||||
private static boolean isSameCodec(VideoCodecInfo codecA, VideoCodecInfo codecB) {
|
||||
if (!codecA.name.equalsIgnoreCase(codecB.name)) {
|
||||
return false;
|
||||
}
|
||||
return codecA.name.equalsIgnoreCase("H264")
|
||||
? H264Utils.isSameH264Profile(codecA.params, codecB.params)
|
||||
: true;
|
||||
}
|
||||
|
||||
private static boolean isCodecSupported(
|
||||
VideoCodecInfo[] supportedCodecs, VideoCodecInfo codec) {
|
||||
for (VideoCodecInfo supportedCodec : supportedCodecs) {
|
||||
if (isSameCodec(supportedCodec, codec)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static VideoCodecInfo[] getSupportedHardwareCodecs() {
|
||||
final List<VideoCodecInfo> codecs = new ArrayList<VideoCodecInfo>();
|
||||
|
||||
if (isVp8HwSupported()) {
|
||||
Logging.d(TAG, "VP8 HW Decoder supported.");
|
||||
codecs.add(new VideoCodecInfo("VP8", new HashMap<>()));
|
||||
}
|
||||
|
||||
if (isVp9HwSupported()) {
|
||||
Logging.d(TAG, "VP9 HW Decoder supported.");
|
||||
codecs.add(new VideoCodecInfo("VP9", new HashMap<>()));
|
||||
}
|
||||
|
||||
if (isH264HighProfileHwSupported()) {
|
||||
Logging.d(TAG, "H.264 High Profile HW Decoder supported.");
|
||||
codecs.add(H264Utils.DEFAULT_H264_HIGH_PROFILE_CODEC);
|
||||
}
|
||||
|
||||
if (isH264HwSupported()) {
|
||||
Logging.d(TAG, "H.264 HW Decoder supported.");
|
||||
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
|
||||
}
|
||||
|
||||
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
|
||||
}
|
||||
|
||||
private final VideoCodecInfo[] supportedHardwareCodecs = getSupportedHardwareCodecs();
|
||||
|
||||
@Override
|
||||
public VideoCodecInfo[] getSupportedCodecs() {
|
||||
return supportedHardwareCodecs;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VideoDecoder createDecoder(VideoCodecInfo codec) {
|
||||
if (!isCodecSupported(supportedHardwareCodecs, codec)) {
|
||||
Logging.d(TAG, "No HW video decoder for codec " + codec.name);
|
||||
return null;
|
||||
}
|
||||
Logging.d(TAG, "Create HW video decoder for " + codec.name);
|
||||
return new WrappedNativeVideoDecoder() {
|
||||
@Override
|
||||
public long createNativeVideoDecoder() {
|
||||
return nativeCreateDecoder(codec.name, useSurface());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static final long MAX_DECODE_TIME_MS = 200;
|
||||
|
||||
// TODO(magjed): Use MediaFormat constants when part of the public API.
|
||||
@ -175,7 +256,6 @@ public class MediaCodecVideoDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
static boolean useSurface() {
|
||||
return eglBase != null;
|
||||
}
|
||||
@ -203,25 +283,21 @@ public class MediaCodecVideoDecoder {
|
||||
}
|
||||
|
||||
// Functions to query if HW decoding is supported.
|
||||
@CalledByNativeUnchecked
|
||||
public static boolean isVp8HwSupported() {
|
||||
return !hwDecoderDisabledTypes.contains(VP8_MIME_TYPE)
|
||||
&& (findDecoder(VP8_MIME_TYPE, supportedVp8HwCodecPrefixes()) != null);
|
||||
}
|
||||
|
||||
@CalledByNativeUnchecked
|
||||
public static boolean isVp9HwSupported() {
|
||||
return !hwDecoderDisabledTypes.contains(VP9_MIME_TYPE)
|
||||
&& (findDecoder(VP9_MIME_TYPE, supportedVp9HwCodecPrefixes) != null);
|
||||
}
|
||||
|
||||
@CalledByNativeUnchecked
|
||||
public static boolean isH264HwSupported() {
|
||||
return !hwDecoderDisabledTypes.contains(H264_MIME_TYPE)
|
||||
&& (findDecoder(H264_MIME_TYPE, supportedH264HwCodecPrefixes()) != null);
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
public static boolean isH264HighProfileHwSupported() {
|
||||
if (hwDecoderDisabledTypes.contains(H264_MIME_TYPE)) {
|
||||
return false;
|
||||
@ -942,4 +1018,6 @@ public class MediaCodecVideoDecoder {
|
||||
int getSliceHeight() {
|
||||
return sliceHeight;
|
||||
}
|
||||
|
||||
private static native long nativeCreateDecoder(String codec, boolean useSurface);
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import android.os.Bundle;
|
||||
import android.view.Surface;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -47,6 +48,93 @@ public class MediaCodecVideoEncoder {
|
||||
|
||||
private static final String TAG = "MediaCodecVideoEncoder";
|
||||
|
||||
/**
|
||||
* Create a VideoEncoderFactory that can be injected in the PeerConnectionFactory and replicate
|
||||
* the old behavior.
|
||||
*/
|
||||
public static VideoEncoderFactory createFactory() {
|
||||
return new DefaultVideoEncoderFactory(new HwEncoderFactory());
|
||||
}
|
||||
|
||||
// Factory for creating HW MediaCodecVideoEncoder instances.
|
||||
static class HwEncoderFactory implements VideoEncoderFactory {
|
||||
private static boolean isSameCodec(VideoCodecInfo codecA, VideoCodecInfo codecB) {
|
||||
if (!codecA.name.equalsIgnoreCase(codecB.name)) {
|
||||
return false;
|
||||
}
|
||||
return codecA.name.equalsIgnoreCase("H264")
|
||||
? H264Utils.isSameH264Profile(codecA.params, codecB.params)
|
||||
: true;
|
||||
}
|
||||
|
||||
private static boolean isCodecSupported(
|
||||
VideoCodecInfo[] supportedCodecs, VideoCodecInfo codec) {
|
||||
for (VideoCodecInfo supportedCodec : supportedCodecs) {
|
||||
if (isSameCodec(supportedCodec, codec)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static VideoCodecInfo[] getSupportedHardwareCodecs() {
|
||||
final List<VideoCodecInfo> codecs = new ArrayList<VideoCodecInfo>();
|
||||
|
||||
if (isVp8HwSupported()) {
|
||||
Logging.d(TAG, "VP8 HW Encoder supported.");
|
||||
codecs.add(new VideoCodecInfo("VP8", new HashMap<>()));
|
||||
}
|
||||
|
||||
if (isVp9HwSupported()) {
|
||||
Logging.d(TAG, "VP9 HW Encoder supported.");
|
||||
codecs.add(new VideoCodecInfo("VP9", new HashMap<>()));
|
||||
}
|
||||
|
||||
// Check if high profile is supported by decoder. If yes, encoder can always
|
||||
// fall back to baseline profile as a subset as high profile.
|
||||
if (MediaCodecVideoDecoder.isH264HighProfileHwSupported()) {
|
||||
Logging.d(TAG, "H.264 High Profile HW Encoder supported.");
|
||||
codecs.add(H264Utils.DEFAULT_H264_HIGH_PROFILE_CODEC);
|
||||
}
|
||||
|
||||
if (isH264HwSupported()) {
|
||||
Logging.d(TAG, "H.264 HW Encoder supported.");
|
||||
codecs.add(H264Utils.DEFAULT_H264_BASELINE_PROFILE_CODEC);
|
||||
}
|
||||
|
||||
return codecs.toArray(new VideoCodecInfo[codecs.size()]);
|
||||
}
|
||||
|
||||
private final VideoCodecInfo[] supportedHardwareCodecs = getSupportedHardwareCodecs();
|
||||
|
||||
@Override
|
||||
public VideoCodecInfo[] getSupportedCodecs() {
|
||||
return supportedHardwareCodecs;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VideoEncoder createEncoder(VideoCodecInfo info) {
|
||||
if (!isCodecSupported(supportedHardwareCodecs, info)) {
|
||||
Logging.d(TAG, "No HW video encoder for codec " + info.name);
|
||||
return null;
|
||||
}
|
||||
Logging.d(TAG, "Create HW video encoder for " + info.name);
|
||||
return new WrappedNativeVideoEncoder() {
|
||||
@Override
|
||||
public long createNativeVideoEncoder() {
|
||||
return nativeCreateEncoder(
|
||||
info, /* hasEgl14Context= */ staticEglBase instanceof EglBase14);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHardwareEncoder() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Tracks webrtc::VideoCodecType.
|
||||
public enum VideoCodecType {
|
||||
VIDEO_CODEC_UNKNOWN,
|
||||
@ -175,11 +263,6 @@ public class MediaCodecVideoEncoder {
|
||||
return staticEglBase == null ? null : staticEglBase.getEglBaseContext();
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
static boolean hasEgl14Context() {
|
||||
return staticEglBase instanceof EglBase14;
|
||||
}
|
||||
|
||||
// List of supported HW VP8 encoders.
|
||||
private static final MediaCodecProperties qcomVp8HwProperties = new MediaCodecProperties(
|
||||
"OMX.qcom.", Build.VERSION_CODES.KITKAT, BitrateAdjustmentType.NO_ADJUSTMENT);
|
||||
@ -297,7 +380,6 @@ public class MediaCodecVideoEncoder {
|
||||
}
|
||||
|
||||
// Functions to query if HW encoding is supported.
|
||||
@CalledByNative
|
||||
public static boolean isVp8HwSupported() {
|
||||
return !hwEncoderDisabledTypes.contains(VP8_MIME_TYPE)
|
||||
&& (findHwEncoder(VP8_MIME_TYPE, vp8HwList(), supportedColorList) != null);
|
||||
@ -311,13 +393,11 @@ public class MediaCodecVideoEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
public static boolean isVp9HwSupported() {
|
||||
return !hwEncoderDisabledTypes.contains(VP9_MIME_TYPE)
|
||||
&& (findHwEncoder(VP9_MIME_TYPE, vp9HwList, supportedColorList) != null);
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
public static boolean isH264HwSupported() {
|
||||
return !hwEncoderDisabledTypes.contains(H264_MIME_TYPE)
|
||||
&& (findHwEncoder(H264_MIME_TYPE, h264HwList(), supportedColorList) != null);
|
||||
@ -1022,4 +1102,5 @@ public class MediaCodecVideoEncoder {
|
||||
/** Fills an inputBuffer with the given index with data from the byte buffers. */
|
||||
private static native void nativeFillInputBuffer(long encoder, int inputBuffer, ByteBuffer dataY,
|
||||
int strideY, ByteBuffer dataU, int strideU, ByteBuffer dataV, int strideV);
|
||||
private static native long nativeCreateEncoder(VideoCodecInfo info, boolean hasEgl14Context);
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ public class PeerConnectionFactory {
|
||||
private static final String VIDEO_CAPTURER_THREAD_NAME = "VideoCapturerThread";
|
||||
|
||||
private final long nativeFactory;
|
||||
private static boolean enableVideoHwAcceleration;
|
||||
private static volatile boolean internalTracerInitialized = false;
|
||||
@Nullable private static Thread networkThread;
|
||||
@Nullable private static Thread workerThread;
|
||||
@ -201,6 +202,22 @@ public class PeerConnectionFactory {
|
||||
}
|
||||
|
||||
public PeerConnectionFactory createPeerConnectionFactory() {
|
||||
VideoEncoderFactory encoderFactory = this.encoderFactory;
|
||||
VideoDecoderFactory decoderFactory = this.decoderFactory;
|
||||
// For legacy reasons, we provide implicit built-in codec factories.
|
||||
// TODO(bugs.webrtc.org/9181): Remove code below. All codec factories should be injected
|
||||
// explicitly.
|
||||
if (encoderFactory == null && decoderFactory == null && !enableVideoHwAcceleration) {
|
||||
encoderFactory = new SoftwareVideoEncoderFactory();
|
||||
decoderFactory = new SoftwareVideoDecoderFactory();
|
||||
} else {
|
||||
if (encoderFactory == null) {
|
||||
encoderFactory = MediaCodecVideoEncoder.createFactory();
|
||||
}
|
||||
if (decoderFactory == null) {
|
||||
decoderFactory = MediaCodecVideoDecoder.createFactory();
|
||||
}
|
||||
}
|
||||
return new PeerConnectionFactory(options, audioDeviceModule, encoderFactory, decoderFactory,
|
||||
audioProcessingFactory, fecControllerFactoryFactory);
|
||||
}
|
||||
@ -218,7 +235,8 @@ public class PeerConnectionFactory {
|
||||
public static void initialize(InitializationOptions options) {
|
||||
ContextUtils.initialize(options.applicationContext);
|
||||
NativeLibrary.initialize(options.nativeLibraryLoader, options.nativeLibraryName);
|
||||
nativeInitializeAndroidGlobals(options.enableVideoHwAcceleration);
|
||||
enableVideoHwAcceleration = options.enableVideoHwAcceleration;
|
||||
nativeInitializeAndroidGlobals();
|
||||
nativeInitializeFieldTrials(options.fieldTrials);
|
||||
if (options.enableInternalTracer && !internalTracerInitialized) {
|
||||
initializeInternalTracer();
|
||||
@ -476,7 +494,7 @@ public class PeerConnectionFactory {
|
||||
|
||||
// Must be called at least once before creating a PeerConnectionFactory
|
||||
// (for example, at application startup time).
|
||||
private static native void nativeInitializeAndroidGlobals(boolean videoHwAcceleration);
|
||||
private static native void nativeInitializeAndroidGlobals();
|
||||
private static native void nativeInitializeFieldTrials(String fieldTrialsInitString);
|
||||
private static native String nativeFindFieldTrialsFullName(String name);
|
||||
private static native void nativeInitializeInternalTracer();
|
||||
|
||||
Reference in New Issue
Block a user