Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(686)

Unified Diff: webrtc/api/android/java/src/org/webrtc/EglRenderer.java

Issue 2436503003: WIP. Android EglRenderer: Add Bitmap frame listener functionality (Closed)
Patch Set: Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/api/android/java/src/org/webrtc/EglRenderer.java
diff --git a/webrtc/api/android/java/src/org/webrtc/EglRenderer.java b/webrtc/api/android/java/src/org/webrtc/EglRenderer.java
index 415e1278541b0c7fe0fd59e9f0d01085e2e59399..d026baba2e507c5b3d9c7f07e3f2b666d12b1b7f 100644
--- a/webrtc/api/android/java/src/org/webrtc/EglRenderer.java
+++ b/webrtc/api/android/java/src/org/webrtc/EglRenderer.java
@@ -10,12 +10,16 @@
package org.webrtc;
+import android.graphics.Bitmap;
import android.graphics.SurfaceTexture;
import android.opengl.GLES20;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.view.Surface;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -29,6 +33,18 @@ public class EglRenderer implements VideoRenderer.Callbacks {
private static final long LOG_INTERVAL_SEC = 4;
private static final int MAX_SURFACE_CLEAR_COUNT = 3;
+ public interface FrameListener { void onFrame(Bitmap frame); }
+
+ private static class ScaleAndFrameListener {
+ public final float scale;
+ public final FrameListener listener;
+
+ public ScaleAndFrameListener(float scale, FrameListener listener) {
+ this.scale = scale;
+ this.listener = listener;
+ }
+ }
+
private class EglSurfaceCreation implements Runnable {
private Object surface;
@@ -60,6 +76,9 @@ public class EglRenderer implements VideoRenderer.Callbacks {
private final Object handlerLock = new Object();
private Handler renderThreadHandler;
+ private final Object frameListenerLock = new Object();
+ private final ArrayList<ScaleAndFrameListener> frameListeners = new ArrayList<>();
+
// Variables for fps reduction.
private final Object fpsReductionLock = new Object();
// Time for when next frame should be rendered.
@@ -72,6 +91,7 @@ public class EglRenderer implements VideoRenderer.Callbacks {
// from the render thread.
private EglBase eglBase;
private final RendererCommon.YuvUploader yuvUploader = new RendererCommon.YuvUploader();
+ private YuvConverter yuvConverter;
private RendererCommon.GlDrawer drawer;
// Texture ids for YUV frames. Allocated on first arrival of a YUV frame.
private int[] yuvTextures = null;
@@ -216,6 +236,10 @@ public class EglRenderer implements VideoRenderer.Callbacks {
drawer.release();
drawer = null;
}
+ if (yuvConverter != null) {
+ yuvConverter.release();
+ yuvConverter = null;
+ }
if (yuvTextures != null) {
GLES20.glDeleteTextures(3, yuvTextures, 0);
yuvTextures = null;
@@ -333,6 +357,36 @@ public class EglRenderer implements VideoRenderer.Callbacks {
setFpsReduction(0 /* fps */);
}
+ /**
+ * Register a callback to be invoked when a new video frame has been received.
+ *
+ * @param listener The callback to be invoked.
+ * @param scale The scale of the Bitmap passed to the callback, or null if no Bitmap is
+ * required.
+ */
+ public void addFrameListener(FrameListener listener, float scale) {
+ synchronized (frameListenerLock) {
+ frameListeners.add(new ScaleAndFrameListener(scale, listener));
+ }
+ }
+
+ /**
+ * Remove any pending callback that was added with addFrameListener. If the callback is not in
+ * the queue, nothing happens.
+ *
+ * @param runnable The callback to remove.
+ */
+ public void removeFrameListener(FrameListener listener) {
+ synchronized (frameListenerLock) {
+ final Iterator<ScaleAndFrameListener> iter = frameListeners.iterator();
+ while (iter.hasNext()) {
+ if (iter.next().listener == listener) {
+ iter.remove();
+ }
+ }
+ }
+ }
+
// VideoRenderer.Callbacks interface.
@Override
public void renderFrame(VideoRenderer.I420Frame frame) {
@@ -466,6 +520,8 @@ public class EglRenderer implements VideoRenderer.Callbacks {
return;
}
+ notifyFrameListeners(frame);
+
final long startTimeNs = System.nanoTime();
float[] texMatrix =
RendererCommon.rotateTextureMatrix(frame.samplingMatrix, frame.rotationDegree);
@@ -527,6 +583,78 @@ public class EglRenderer implements VideoRenderer.Callbacks {
}
}
+ private int roundUp(float value, int multiple) {
+ return ((int) (value + multiple - 1) / multiple) * multiple;
+ }
+
+ private void notifyFrameListeners(VideoRenderer.I420Frame frame) {
+ synchronized (frameListenerLock) {
+ if (frameListeners.isEmpty()) {
+ return;
+ }
+ // Notify callbacks. Make temporary copy of callback list to avoid
+ // ConcurrentModificationException, in case callbacks call addFramelistener or
+ // removeFrameListener.
+ final ArrayList<ScaleAndFrameListener> tmpList = new ArrayList<>(frameListeners);
+ frameListeners.clear();
+
+ final float[] transformMatrix =
+ mirror ? RendererCommon.horizontalFlipMatrix() : RendererCommon.identityMatrix();
+
+ for (ScaleAndFrameListener scaleAndListener : tmpList) {
+ if (scaleAndListener.scale == 0) {
+ scaleAndListener.listener.onFrame(null /* frame */);
+ } else {
+ final ByteBuffer yPlane;
+ final ByteBuffer uPlane;
+ final ByteBuffer vPlane;
+ final int yStride;
+ final int uStride;
+ final int vStride;
+ final int scaledWidth;
+ final int scaledHeight;
+
+ if (frame.yuvFrame) {
+ scaledWidth = (int) (scaleAndListener.scale * frame.rotatedWidth());
+ scaledHeight = (int) (scaleAndListener.scale * frame.rotatedHeight());
+ yPlane = frame.yuvPlanes[0];
+ uPlane = frame.yuvPlanes[1];
+ vPlane = frame.yuvPlanes[2];
+ yStride = frame.yuvStrides[0];
+ uStride = frame.yuvStrides[1];
+ vStride = frame.yuvStrides[2];
+ } else {
+ if (yuvConverter == null) {
+ yuvConverter = new YuvConverter();
+ }
+ scaled_width = roundUp(scaleAndListener.scale * frame.rotatedWidth(), 8);
+ scaled_height = roundUp(scaleAndListener.scale * frame.rotatedHeight(), 2);
+ final int size = scaled_width * scaled_height * 3 / 2;
+
+ final ByteBuffer buf = ByteBuffer.allocateDirect(size);
+ yuvConverter.convert(
+ buf, scaled_width, scaled_height, scaled_width, frame.textureId, transformMatrix);
+ yPlane = buf;
+ buf.position(scaled_width * scaled_height);
+ uPlane = buf.slice();
+ buf.position(scaled_width * scaled_height + scaled_width / 2);
+ vPlane = buf.slice();
+ yStride = scaled_width;
+ uStride = scaled_width;
+ vStride = scaled_width;
+ }
+
+ final ByteBuffer argbBuffer = ByteBuffer.allocateDirect(scaled_width * scaled_height * 4);
+ // TODO. Call libyuv::I420ToARGB through JNI.
+ // nativeI420ToArgb(yPlane, yStride, uPlane, uStride, vPlane, vStride, argbBuffer);
+ final Bitmap bitmap = Bitmap.createBitmap(
+ argbBuffer.array(), scaled_width, scaled_height, Bitmap.Config.ARGB_8888);
+ scaleAndListener.listener.onFrame(bitmap);
+ }
+ }
+ }
+ }
+
private String averageTimeAsString(long sumTimeNs, int count) {
return (count <= 0) ? "NA" : TimeUnit.NANOSECONDS.toMicros(sumTimeNs / count) + " μs";
}
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698