Index: talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java |
diff --git a/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java b/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java |
index a1cc041c416d4699417e892b4acc8507f4533639..515186ac78a628357e983f6988716e967f4f764e 100644 |
--- a/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java |
+++ b/talk/app/webrtc/java/src/org/webrtc/MediaCodecVideoDecoder.java |
@@ -27,20 +27,23 @@ |
package org.webrtc; |
+import android.graphics.SurfaceTexture; |
import android.media.MediaCodec; |
import android.media.MediaCodecInfo; |
import android.media.MediaCodecInfo.CodecCapabilities; |
import android.media.MediaCodecList; |
import android.media.MediaFormat; |
+import android.opengl.EGLContext; |
+import android.opengl.GLES11Ext; |
+import android.opengl.GLES20; |
import android.os.Build; |
import android.view.Surface; |
import org.webrtc.Logging; |
import java.nio.ByteBuffer; |
-import java.util.Arrays; |
import java.util.List; |
-import java.util.concurrent.TimeUnit; |
+import java.util.Arrays; |
// Java-side of peerconnection_jni.cc:MediaCodecVideoDecoder. |
// This class is an implementation detail of the Java PeerConnection API. |
@@ -91,11 +94,10 @@ public class MediaCodecVideoDecoder { |
private int stride; |
private int sliceHeight; |
private boolean useSurface; |
- // |isWaitingForTexture| is true when waiting for the transition: |
- // MediaCodec.releaseOutputBuffer() -> onTextureFrameAvailable(). |
- private boolean isWaitingForTexture = false; |
- private TextureListener textureListener; |
+ private int textureID = 0; |
+ private SurfaceTexture surfaceTexture = null; |
private Surface surface = null; |
+ private EglBase eglBase; |
private MediaCodecVideoDecoder() { |
instance = this; |
@@ -193,13 +195,12 @@ public class MediaCodecVideoDecoder { |
} |
} |
- // Pass null in |surfaceTextureHelper| to configure the codec for ByteBuffer output. |
- private boolean initDecode( |
- VideoCodecType type, int width, int height, SurfaceTextureHelper surfaceTextureHelper) { |
+ // Pass null in |sharedContext| to configure the codec for ByteBuffer output. |
+ private boolean initDecode(VideoCodecType type, int width, int height, EGLContext sharedContext) { |
if (mediaCodecThread != null) { |
throw new RuntimeException("Forgot to release()?"); |
} |
- useSurface = (surfaceTextureHelper != null); |
+ useSurface = (sharedContext != null); |
String mime = null; |
String[] supportedCodecPrefixes = null; |
if (type == VideoCodecType.VIDEO_CODEC_VP8) { |
@@ -218,6 +219,9 @@ public class MediaCodecVideoDecoder { |
Logging.d(TAG, "Java initDecode: " + type + " : "+ width + " x " + height + |
". Color: 0x" + Integer.toHexString(properties.colorFormat) + |
". Use Surface: " + useSurface); |
+ if (sharedContext != null) { |
+ Logging.d(TAG, "Decoder shared EGL Context: " + sharedContext); |
+ } |
mediaCodecThread = Thread.currentThread(); |
try { |
this.width = width; |
@@ -226,8 +230,16 @@ public class MediaCodecVideoDecoder { |
sliceHeight = height; |
if (useSurface) { |
- textureListener = new TextureListener(surfaceTextureHelper); |
- surface = new Surface(surfaceTextureHelper.getSurfaceTexture()); |
+ // Create shared EGL context. |
+ eglBase = new EglBase(sharedContext, EglBase.ConfigType.PIXEL_BUFFER); |
+ eglBase.createDummyPbufferSurface(); |
+ eglBase.makeCurrent(); |
+ |
+ // Create output surface |
+ textureID = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); |
+ Logging.d(TAG, "Video decoder TextureID = " + textureID); |
+ surfaceTexture = new SurfaceTexture(textureID); |
+ surface = new Surface(surfaceTexture); |
} |
MediaFormat format = MediaFormat.createVideoFormat(mime, width, height); |
@@ -270,7 +282,11 @@ public class MediaCodecVideoDecoder { |
if (useSurface) { |
surface.release(); |
surface = null; |
- textureListener.release(); |
+ Logging.d(TAG, "Delete video decoder TextureID " + textureID); |
+ GLES20.glDeleteTextures(1, new int[] {textureID}, 0); |
+ textureID = 0; |
+ eglBase.release(); |
+ eglBase = null; |
} |
Logging.d(TAG, "Java releaseDecoder done"); |
} |
@@ -319,72 +335,11 @@ public class MediaCodecVideoDecoder { |
private static class DecodedTextureBuffer { |
private final int textureID; |
- private final float[] transformMatrix; |
- private final long timestampNs; |
+ private final long presentationTimestampUs; |
- public DecodedTextureBuffer(int textureID, float[] transformMatrix, long timestampNs) { |
+ public DecodedTextureBuffer(int textureID, long presentationTimestampUs) { |
this.textureID = textureID; |
- this.transformMatrix = transformMatrix; |
- this.timestampNs = timestampNs; |
- } |
- } |
- |
- // Poll based texture listener. |
- private static class TextureListener |
- implements SurfaceTextureHelper.OnTextureFrameAvailableListener { |
- private final SurfaceTextureHelper surfaceTextureHelper; |
- private DecodedTextureBuffer textureBuffer; |
- // |newFrameLock| is used to synchronize arrival of new frames with wait()/notifyAll(). |
- private final Object newFrameLock = new Object(); |
- |
- public TextureListener(SurfaceTextureHelper surfaceTextureHelper) { |
- this.surfaceTextureHelper = surfaceTextureHelper; |
- surfaceTextureHelper.setListener(this); |
- } |
- |
- // Callback from |surfaceTextureHelper|. May be called on an arbitrary thread. |
- @Override |
- public void onTextureFrameAvailable( |
- int oesTextureId, float[] transformMatrix, long timestampNs) { |
- synchronized (newFrameLock) { |
- if (textureBuffer != null) { |
- Logging.e(TAG, |
- "Unexpected onTextureFrameAvailable() called while already holding a texture."); |
- throw new IllegalStateException("Already holding a texture."); |
- } |
- textureBuffer = new DecodedTextureBuffer(oesTextureId, transformMatrix, timestampNs); |
- newFrameLock.notifyAll(); |
- } |
- } |
- |
- // Dequeues and returns a texture buffer if available, or null otherwise. |
- public DecodedTextureBuffer dequeueTextureFrame(int timeoutMs) { |
- synchronized (newFrameLock) { |
- if (textureBuffer == null && timeoutMs > 0) { |
- try { |
- newFrameLock.wait(timeoutMs); |
- } catch(InterruptedException e) { |
- // Restore the interrupted status by reinterrupting the thread. |
- Thread.currentThread().interrupt(); |
- } |
- } |
- final DecodedTextureBuffer textureBuffer = this.textureBuffer; |
- this.textureBuffer = null; |
- return textureBuffer; |
- } |
- } |
- |
- public void release() { |
- // SurfaceTextureHelper.disconnect() will block until any onTextureFrameAvailable() in |
- // progress is done. Therefore, the call to disconnect() must be outside any synchronized |
- // statement that is also used in the onTextureFrameAvailable() above to avoid deadlocks. |
- surfaceTextureHelper.disconnect(); |
- synchronized (newFrameLock) { |
- if (textureBuffer != null) { |
- surfaceTextureHelper.returnTextureFrame(); |
- textureBuffer = null; |
- } |
- } |
+ this.presentationTimestampUs = presentationTimestampUs; |
} |
} |
@@ -393,25 +348,14 @@ public class MediaCodecVideoDecoder { |
// Throws IllegalStateException if call is made on the wrong thread, if color format changes to an |
// unsupported format, or if |mediaCodec| is not in the Executing state. Throws CodecException |
// upon codec error. |
- private Object dequeueOutputBuffer(int dequeueTimeoutMs) |
+ private Object dequeueOutputBuffer(int dequeueTimeoutUs) |
throws IllegalStateException, MediaCodec.CodecException { |
checkOnMediaCodecThread(); |
- // Calling multiple MediaCodec.releaseOutputBuffer() with render=true in a row will result in |
- // dropped texture frames. Therefore, wait for any pending onTextureFrameAvailable() before |
- // proceeding. |
- if (isWaitingForTexture) { |
- final DecodedTextureBuffer textureBuffer = |
- textureListener.dequeueTextureFrame(dequeueTimeoutMs); |
- isWaitingForTexture = (textureBuffer == null); |
- return textureBuffer; |
- } |
- |
// Drain the decoder until receiving a decoded buffer or hitting |
// MediaCodec.INFO_TRY_AGAIN_LATER. |
final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); |
while (true) { |
- final int result = mediaCodec.dequeueOutputBuffer( |
- info, TimeUnit.MILLISECONDS.toMicros(dequeueTimeoutMs)); |
+ final int result = mediaCodec.dequeueOutputBuffer(info, dequeueTimeoutUs); |
switch (result) { |
case MediaCodec.INFO_TRY_AGAIN_LATER: |
return null; |
@@ -445,10 +389,9 @@ public class MediaCodecVideoDecoder { |
// Output buffer decoded. |
if (useSurface) { |
mediaCodec.releaseOutputBuffer(result, true /* render */); |
- final DecodedTextureBuffer textureBuffer = |
- textureListener.dequeueTextureFrame(dequeueTimeoutMs); |
- isWaitingForTexture = (textureBuffer == null); |
- return textureBuffer; |
+ // TODO(magjed): Wait for SurfaceTexture.onFrameAvailable() before returning a texture |
+ // frame. |
+ return new DecodedTextureBuffer(textureID, info.presentationTimeUs); |
} else { |
return new DecodedByteBuffer(result, info.offset, info.size, info.presentationTimeUs); |
} |