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

Side by Side Diff: webrtc/api/android/java/src/org/webrtc/SurfaceTextureHelper.java

Issue 2547483003: Move /webrtc/api/android files to /webrtc/sdk/android (Closed)
Patch Set: Move to api folder under Android instead of src Created 4 years 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 unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 package org.webrtc;
12
13 import android.graphics.SurfaceTexture;
14 import android.opengl.GLES11Ext;
15 import android.opengl.GLES20;
16 import android.os.Build;
17 import android.os.Handler;
18 import android.os.HandlerThread;
19 import android.os.SystemClock;
20
21 import java.nio.ByteBuffer;
22 import java.nio.FloatBuffer;
23 import java.util.concurrent.Callable;
24 import java.util.concurrent.TimeUnit;
25
26 /**
27 * Helper class to create and synchronize access to a SurfaceTexture. The caller will get notified
28 * of new frames in onTextureFrameAvailable(), and should call returnTextureFram e() when done with
29 * the frame. Only one texture frame can be in flight at once, so returnTextureF rame() must be
30 * called in order to receive a new frame. Call stopListening() to stop receivei ng new frames. Call
31 * dispose to release all resources once the texture frame is returned.
32 * Note that there is a C++ counter part of this class that optionally can be us ed. It is used for
33 * wrapping texture frames into webrtc::VideoFrames and also handles calling ret urnTextureFrame()
34 * when the webrtc::VideoFrame is no longer used.
35 */
36 public class SurfaceTextureHelper {
37 private static final String TAG = "SurfaceTextureHelper";
38 /**
39 * Callback interface for being notified that a new texture frame is available . The calls will be
40 * made on a dedicated thread with a bound EGLContext. The thread will be the same throughout the
41 * lifetime of the SurfaceTextureHelper instance, but different from the threa d calling the
42 * SurfaceTextureHelper constructor. The callee is not allowed to make another EGLContext current
43 * on the calling thread.
44 */
45 public interface OnTextureFrameAvailableListener {
46 abstract void onTextureFrameAvailable(
47 int oesTextureId, float[] transformMatrix, long timestampNs);
48 }
49
50 /**
51 * Construct a new SurfaceTextureHelper sharing OpenGL resources with |sharedC ontext|. A dedicated
52 * thread and handler is created for handling the SurfaceTexture. May return n ull if EGL fails to
53 * initialize a pixel buffer surface and make it current.
54 */
55 public static SurfaceTextureHelper create(
56 final String threadName, final EglBase.Context sharedContext) {
57 final HandlerThread thread = new HandlerThread(threadName);
58 thread.start();
59 final Handler handler = new Handler(thread.getLooper());
60
61 // The onFrameAvailable() callback will be executed on the SurfaceTexture ct or thread. See:
62 // http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.andr oid/android/5.1.1_r1/android/graphics/SurfaceTexture.java#195.
63 // Therefore, in order to control the callback thread on API lvl < 21, the S urfaceTextureHelper
64 // is constructed on the |handler| thread.
65 return ThreadUtils.invokeAtFrontUninterruptibly(handler, new Callable<Surfac eTextureHelper>() {
66 @Override
67 public SurfaceTextureHelper call() {
68 try {
69 return new SurfaceTextureHelper(sharedContext, handler);
70 } catch (RuntimeException e) {
71 Logging.e(TAG, threadName + " create failure", e);
72 return null;
73 }
74 }
75 });
76 }
77
78 private final Handler handler;
79 private final EglBase eglBase;
80 private final SurfaceTexture surfaceTexture;
81 private final int oesTextureId;
82 private YuvConverter yuvConverter;
83
84 // These variables are only accessed from the |handler| thread.
85 private OnTextureFrameAvailableListener listener;
86 // The possible states of this class.
87 private boolean hasPendingTexture = false;
88 private volatile boolean isTextureInUse = false;
89 private boolean isQuitting = false;
90 // |pendingListener| is set in setListener() and the runnable is posted to the handler thread.
91 // setListener() is not allowed to be called again before stopListening(), so this is thread safe.
92 private OnTextureFrameAvailableListener pendingListener;
93 final Runnable setListenerRunnable = new Runnable() {
94 @Override
95 public void run() {
96 Logging.d(TAG, "Setting listener to " + pendingListener);
97 listener = pendingListener;
98 pendingListener = null;
99 // May have a pending frame from the previous capture session - drop it.
100 if (hasPendingTexture) {
101 // Calling updateTexImage() is neccessary in order to receive new frames .
102 updateTexImage();
103 hasPendingTexture = false;
104 }
105 }
106 };
107
108 private SurfaceTextureHelper(EglBase.Context sharedContext, Handler handler) {
109 if (handler.getLooper().getThread() != Thread.currentThread()) {
110 throw new IllegalStateException("SurfaceTextureHelper must be created on t he handler thread");
111 }
112 this.handler = handler;
113
114 eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
115 try {
116 // Both these statements have been observed to fail on rare occasions, see BUG=webrtc:5682.
117 eglBase.createDummyPbufferSurface();
118 eglBase.makeCurrent();
119 } catch (RuntimeException e) {
120 // Clean up before rethrowing the exception.
121 eglBase.release();
122 handler.getLooper().quit();
123 throw e;
124 }
125
126 oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
127 surfaceTexture = new SurfaceTexture(oesTextureId);
128 surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailab leListener() {
129 @Override
130 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
131 hasPendingTexture = true;
132 tryDeliverTextureFrame();
133 }
134 });
135 }
136
137 /**
138 * Start to stream textures to the given |listener|. If you need to change lis tener, you need to
139 * call stopListening() first.
140 */
141 public void startListening(final OnTextureFrameAvailableListener listener) {
142 if (this.listener != null || this.pendingListener != null) {
143 throw new IllegalStateException("SurfaceTextureHelper listener has already been set.");
144 }
145 this.pendingListener = listener;
146 handler.post(setListenerRunnable);
147 }
148
149 /**
150 * Stop listening. The listener set in startListening() is guaranteded to not receive any more
151 * onTextureFrameAvailable() callbacks after this function returns.
152 */
153 public void stopListening() {
154 Logging.d(TAG, "stopListening()");
155 handler.removeCallbacks(setListenerRunnable);
156 ThreadUtils.invokeAtFrontUninterruptibly(handler, new Runnable() {
157 @Override
158 public void run() {
159 listener = null;
160 pendingListener = null;
161 }
162 });
163 }
164
165 /**
166 * Retrieve the underlying SurfaceTexture. The SurfaceTexture should be passed in to a video
167 * producer such as a camera or decoder.
168 */
169 public SurfaceTexture getSurfaceTexture() {
170 return surfaceTexture;
171 }
172
173 /**
174 * Retrieve the handler that calls onTextureFrameAvailable(). This handler is valid until
175 * dispose() is called.
176 */
177 public Handler getHandler() {
178 return handler;
179 }
180
181 /**
182 * Call this function to signal that you are done with the frame received in
183 * onTextureFrameAvailable(). Only one texture frame can be in flight at once, so you must call
184 * this function in order to receive a new frame.
185 */
186 public void returnTextureFrame() {
187 handler.post(new Runnable() {
188 @Override
189 public void run() {
190 isTextureInUse = false;
191 if (isQuitting) {
192 release();
193 } else {
194 tryDeliverTextureFrame();
195 }
196 }
197 });
198 }
199
200 public boolean isTextureInUse() {
201 return isTextureInUse;
202 }
203
204 /**
205 * Call disconnect() to stop receiving frames. OpenGL resources are released a nd the handler is
206 * stopped when the texture frame has been returned by a call to returnTexture Frame(). You are
207 * guaranteed to not receive any more onTextureFrameAvailable() after this fun ction returns.
208 */
209 public void dispose() {
210 Logging.d(TAG, "dispose()");
211 ThreadUtils.invokeAtFrontUninterruptibly(handler, new Runnable() {
212 @Override
213 public void run() {
214 isQuitting = true;
215 if (!isTextureInUse) {
216 release();
217 }
218 }
219 });
220 }
221
222 public void textureToYUV(final ByteBuffer buf, final int width, final int heig ht,
223 final int stride, final int textureId, final float[] transformMatrix) {
224 if (textureId != oesTextureId) {
225 throw new IllegalStateException("textureToByteBuffer called with unexpecte d textureId");
226 }
227
228 ThreadUtils.invokeAtFrontUninterruptibly(handler, new Runnable() {
229 @Override
230 public void run() {
231 if (yuvConverter == null) {
232 yuvConverter = new YuvConverter();
233 }
234 yuvConverter.convert(buf, width, height, stride, textureId, transformMat rix);
235 }
236 });
237 }
238
239 private void updateTexImage() {
240 // SurfaceTexture.updateTexImage apparently can compete and deadlock with eg lSwapBuffers,
241 // as observed on Nexus 5. Therefore, synchronize it with the EGL functions.
242 // See https://bugs.chromium.org/p/webrtc/issues/detail?id=5702 for more inf o.
243 synchronized (EglBase.lock) {
244 surfaceTexture.updateTexImage();
245 }
246 }
247
248 private void tryDeliverTextureFrame() {
249 if (handler.getLooper().getThread() != Thread.currentThread()) {
250 throw new IllegalStateException("Wrong thread.");
251 }
252 if (isQuitting || !hasPendingTexture || isTextureInUse || listener == null) {
253 return;
254 }
255 isTextureInUse = true;
256 hasPendingTexture = false;
257
258 updateTexImage();
259
260 final float[] transformMatrix = new float[16];
261 surfaceTexture.getTransformMatrix(transformMatrix);
262 final long timestampNs = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_C REAM_SANDWICH)
263 ? surfaceTexture.getTimestamp()
264 : TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime());
265 listener.onTextureFrameAvailable(oesTextureId, transformMatrix, timestampNs) ;
266 }
267
268 private void release() {
269 if (handler.getLooper().getThread() != Thread.currentThread()) {
270 throw new IllegalStateException("Wrong thread.");
271 }
272 if (isTextureInUse || !isQuitting) {
273 throw new IllegalStateException("Unexpected release.");
274 }
275 if (yuvConverter != null) {
276 yuvConverter.release();
277 }
278 GLES20.glDeleteTextures(1, new int[] {oesTextureId}, 0);
279 surfaceTexture.release();
280 eglBase.release();
281 handler.getLooper().quit();
282 }
283 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698