Android: Remove use of EGLContexts in PeerConnectionFactory

Since the legacy video codecs seem to be around for some time more, we
need to make them injectable and provide a migration path for clients
that still use them so that we can clean up PeerConnectionFactory.

This CL moves the creation of EglContexts into the legacy codec
factories. Clients can then migrate to setEGLContext() instead of using
setVideoHwAccelerationOptions().

Bug: webrtc:9502
Change-Id: I608607b32db73ce3df7704a061e66d9d53946af5
Reviewed-on: https://webrtc-review.googlesource.com/87941
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23934}
This commit is contained in:
Magnus Jedvert
2018-07-11 14:53:21 +02:00
committed by Commit Bot
parent 2234121cfb
commit 0f0e7a6f18
14 changed files with 119 additions and 203 deletions

View File

@ -36,6 +36,7 @@ import org.webrtc.VideoFrame;
// Java-side of peerconnection.cc:MediaCodecVideoDecoder.
// This class is an implementation detail of the Java PeerConnection API.
@SuppressWarnings("deprecation")
@Deprecated
public class MediaCodecVideoDecoder {
// This class is constructed, operated, and destroyed by its C++ incarnation,
// so the class and its methods have non-public visibility. The API this
@ -79,6 +80,7 @@ public class MediaCodecVideoDecoder {
private static int codecErrors = 0;
// List of disabled codec types - can be set from application.
private static Set<String> hwDecoderDisabledTypes = new HashSet<String>();
@Nullable private static EglBase eglBase;
@Nullable private Thread mediaCodecThread;
@Nullable private MediaCodec mediaCodec;
@ -141,7 +143,6 @@ public class MediaCodecVideoDecoder {
private int sliceHeight;
private boolean hasDecodedFirstFrame;
private final Queue<TimeStamps> decodeStartTimeMs = new ArrayDeque<TimeStamps>();
private boolean useSurface;
// The below variables are only used when decoding to a Surface.
@Nullable private TextureListener textureListener;
@ -157,6 +158,28 @@ public class MediaCodecVideoDecoder {
void onMediaCodecVideoDecoderCriticalError(int codecErrors);
}
/** Set EGL context used by HW decoding. The EGL context must be shared with the remote render. */
public static void setEglContext(EglBase.Context eglContext) {
if (eglBase != null) {
Logging.w(TAG, "Egl context already set.");
eglBase.release();
}
eglBase = EglBase.create(eglContext);
}
/** Dispose the EGL context used by HW decoding. */
public static void disposeEglContext() {
if (eglBase != null) {
eglBase.release();
eglBase = null;
}
}
@CalledByNative
static boolean useSurface() {
return eglBase != null;
}
public static void setErrorCallback(MediaCodecVideoDecoderErrorCallback errorCallback) {
Logging.d(TAG, "Set error callback");
MediaCodecVideoDecoder.errorCallback = errorCallback;
@ -323,16 +346,13 @@ public class MediaCodecVideoDecoder {
}
}
// Pass null in |eglContext| to configure the codec for ByteBuffer output.
@CalledByNativeUnchecked
private boolean initDecode(
VideoCodecType type, int width, int height, @Nullable EglBase.Context eglContext) {
private boolean initDecode(VideoCodecType type, int width, int height) {
if (mediaCodecThread != null) {
throw new RuntimeException("initDecode: Forgot to release()?");
}
String mime = null;
useSurface = (eglContext != null);
String[] supportedCodecPrefixes = null;
if (type == VideoCodecType.VIDEO_CODEC_VP8) {
mime = VP8_MIME_TYPE;
@ -351,8 +371,9 @@ public class MediaCodecVideoDecoder {
throw new RuntimeException("Cannot find HW decoder for " + type);
}
Logging.d(TAG, "Java initDecode: " + type + " : " + width + " x " + height + ". Color: 0x"
+ Integer.toHexString(properties.colorFormat) + ". Use Surface: " + useSurface);
Logging.d(TAG,
"Java initDecode: " + type + " : " + width + " x " + height + ". Color: 0x"
+ Integer.toHexString(properties.colorFormat) + ". Use Surface: " + useSurface());
runningInstance = this; // Decoder is now running and can be queried for stack traces.
mediaCodecThread = Thread.currentThread();
@ -362,10 +383,10 @@ public class MediaCodecVideoDecoder {
stride = width;
sliceHeight = height;
if (useSurface) {
if (useSurface()) {
@Nullable
final SurfaceTextureHelper surfaceTextureHelper =
SurfaceTextureHelper.create("Decoder SurfaceTextureHelper", eglContext);
final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(
"Decoder SurfaceTextureHelper", eglBase.getEglBaseContext());
if (surfaceTextureHelper != null) {
textureListener = new TextureListener(surfaceTextureHelper);
textureListener.setSize(width, height);
@ -374,7 +395,7 @@ public class MediaCodecVideoDecoder {
}
MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
if (!useSurface) {
if (!useSurface()) {
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
}
Logging.d(TAG, " Format: " + format);
@ -461,7 +482,7 @@ public class MediaCodecVideoDecoder {
mediaCodec = null;
mediaCodecThread = null;
runningInstance = null;
if (useSurface) {
if (useSurface()) {
surface.release();
surface = null;
textureListener.release();
@ -773,7 +794,7 @@ public class MediaCodecVideoDecoder {
textureListener.setSize(width, height);
}
if (!useSurface && format.containsKey(MediaFormat.KEY_COLOR_FORMAT)) {
if (!useSurface() && format.containsKey(MediaFormat.KEY_COLOR_FORMAT)) {
colorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
Logging.d(TAG, "Color: 0x" + Integer.toHexString(colorFormat));
if (!supportedColorList.contains(colorFormat)) {
@ -817,7 +838,7 @@ public class MediaCodecVideoDecoder {
@CalledByNativeUnchecked
private @Nullable DecodedTextureBuffer dequeueTextureBuffer(int dequeueTimeoutMs) {
checkOnMediaCodecThread();
if (!useSurface) {
if (!useSurface()) {
throw new IllegalStateException("dequeueTexture() called for byte buffer decoding.");
}
DecodedOutputBuffer outputBuffer = dequeueOutputBuffer(dequeueTimeoutMs);
@ -881,7 +902,7 @@ public class MediaCodecVideoDecoder {
private void returnDecodedOutputBuffer(int index)
throws IllegalStateException, MediaCodec.CodecException {
checkOnMediaCodecThread();
if (useSurface) {
if (useSurface()) {
throw new IllegalStateException("returnDecodedOutputBuffer() called for surface decoding.");
}
mediaCodec.releaseOutputBuffer(index, false /* render */);

View File

@ -30,6 +30,7 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
import org.webrtc.EglBase;
import org.webrtc.EglBase14;
import org.webrtc.VideoFrame;
@ -37,6 +38,7 @@ import org.webrtc.VideoFrame;
// This class is an implementation detail of the Java PeerConnection API.
@TargetApi(19)
@SuppressWarnings("deprecation")
@Deprecated
public class MediaCodecVideoEncoder {
// This class is constructed, operated, and destroyed by its C++ incarnation,
// so the class and its methods have non-public visibility. The API this
@ -79,6 +81,7 @@ public class MediaCodecVideoEncoder {
private static int codecErrors = 0;
// List of disabled codec types - can be set from application.
private static Set<String> hwEncoderDisabledTypes = new HashSet<String>();
@Nullable private static EglBase staticEglBase;
@Nullable private Thread mediaCodecThread;
@Nullable private MediaCodec mediaCodec;
@ -147,6 +150,36 @@ public class MediaCodecVideoEncoder {
}
}
/**
* Set EGL context used by HW encoding. The EGL context must be shared with the video capturer
* and any local render.
*/
public static void setEglContext(EglBase.Context eglContext) {
if (staticEglBase != null) {
Logging.w(TAG, "Egl context already set.");
staticEglBase.release();
}
staticEglBase = EglBase.create(eglContext);
}
/** Dispose the EGL context used by HW encoding. */
public static void disposeEglContext() {
if (staticEglBase != null) {
staticEglBase.release();
staticEglBase = null;
}
}
@Nullable
static EglBase.Context getEglContext() {
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);
@ -446,8 +479,7 @@ public class MediaCodecVideoEncoder {
@CalledByNativeUnchecked
boolean initEncode(VideoCodecType type, int profile, int width, int height, int kbps, int fps,
@Nullable EglBase14.Context sharedContext) {
final boolean useSurface = sharedContext != null;
boolean useSurface) {
Logging.d(TAG,
"Java initEncode: " + type + ". Profile: " + profile + " : " + width + " x " + height
+ ". @ " + kbps + " kbps. Fps: " + fps + ". Encode from texture : " + useSurface);
@ -548,7 +580,7 @@ public class MediaCodecVideoEncoder {
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
if (useSurface) {
eglBase = new EglBase14(sharedContext, EglBase.CONFIG_RECORDABLE);
eglBase = new EglBase14((EglBase14.Context) getEglContext(), EglBase.CONFIG_RECORDABLE);
// Create an input surface and keep a reference since we must release the surface when done.
inputSurface = mediaCodec.createInputSurface();
eglBase.createSurface(inputSurface);

View File

@ -33,8 +33,6 @@ public class PeerConnectionFactory {
@Nullable private static Thread networkThread;
@Nullable private static Thread workerThread;
@Nullable private static Thread signalingThread;
private EglBase localEglbase;
private EglBase remoteEglbase;
public static class InitializationOptions {
final Context applicationContext;
@ -88,6 +86,8 @@ public class PeerConnectionFactory {
return this;
}
// Deprecated, this method only affects the deprecated HW codecs and not the new ones.
@Deprecated
public Builder setEnableVideoHwAcceleration(boolean enableVideoHwAcceleration) {
this.enableVideoHwAcceleration = enableVideoHwAcceleration;
return this;
@ -365,10 +365,8 @@ public class PeerConnectionFactory {
@Deprecated
public VideoSource createVideoSource(VideoCapturer capturer) {
final EglBase.Context eglContext =
localEglbase == null ? null : localEglbase.getEglBaseContext();
final SurfaceTextureHelper surfaceTextureHelper =
SurfaceTextureHelper.create(VIDEO_CAPTURER_THREAD_NAME, eglContext);
final SurfaceTextureHelper surfaceTextureHelper = SurfaceTextureHelper.create(
VIDEO_CAPTURER_THREAD_NAME, MediaCodecVideoEncoder.getEglContext());
final VideoSource videoSource = new VideoSource(
nativeCreateVideoSource(nativeFactory, capturer.isScreencast()), surfaceTextureHelper);
capturer.initialize(surfaceTextureHelper, ContextUtils.getApplicationContext(),
@ -401,26 +399,20 @@ public class PeerConnectionFactory {
nativeStopAecDump(nativeFactory);
}
/** Set the EGL context used by HW Video encoding and decoding.
/**
* Set the EGL context used by HW Video encoding and decoding.
*
* @param localEglContext Must be the same as used by VideoCapturerAndroid and any local video
* renderer.
* @param remoteEglContext Must be the same as used by any remote video renderer.
* @deprecated Use new HW video encoded/decoder instead, and use createVideoSource(boolean
* isScreencast) instead of createVideoSource(VideoCapturer).
*/
@Deprecated
public void setVideoHwAccelerationOptions(
EglBase.Context localEglContext, EglBase.Context remoteEglContext) {
if (localEglbase != null) {
Logging.w(TAG, "Egl context already set.");
localEglbase.release();
}
if (remoteEglbase != null) {
Logging.w(TAG, "Egl context already set.");
remoteEglbase.release();
}
localEglbase = EglBase.create(localEglContext);
remoteEglbase = EglBase.create(remoteEglContext);
nativeSetVideoHwAccelerationOptions(
nativeFactory, localEglbase.getEglBaseContext(), remoteEglbase.getEglBaseContext());
MediaCodecVideoEncoder.setEglContext(localEglContext);
MediaCodecVideoDecoder.setEglContext(remoteEglContext);
}
public void dispose() {
@ -428,10 +420,8 @@ public class PeerConnectionFactory {
networkThread = null;
workerThread = null;
signalingThread = null;
if (localEglbase != null)
localEglbase.release();
if (remoteEglbase != null)
remoteEglbase.release();
MediaCodecVideoEncoder.disposeEglContext();
MediaCodecVideoDecoder.disposeEglContext();
}
public void threadsCallbacks() {
@ -510,8 +500,6 @@ public class PeerConnectionFactory {
private static native boolean nativeStartAecDump(
long factory, int file_descriptor, int filesize_limit_bytes);
private static native void nativeStopAecDump(long factory);
private static native void nativeSetVideoHwAccelerationOptions(
long factory, Object localEGLContext, Object remoteEGLContext);
private static native void nativeInvokeThreadsCallbacks(long factory);
private static native void nativeFreeFactory(long factory);
private static native long nativeGetNativePeerConnectionFactory(long factory);