OLD | NEW |
---|---|
1 /* | 1 /* |
2 * libjingle | 2 * libjingle |
3 * Copyright 2015 Google Inc. | 3 * Copyright 2015 Google Inc. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
84 // Current size on screen in pixels. Updated in onLayout(), and should be cons istent with | 84 // Current size on screen in pixels. Updated in onLayout(), and should be cons istent with |
85 // |widthSpec|/|heightSpec| after that. | 85 // |widthSpec|/|heightSpec| after that. |
86 private int layoutWidth; | 86 private int layoutWidth; |
87 private int layoutHeight; | 87 private int layoutHeight; |
88 // Current surface size of the underlying Surface. Updated in surfaceChanged() , and should be | 88 // Current surface size of the underlying Surface. Updated in surfaceChanged() , and should be |
89 // consistent with |layoutWidth|/|layoutHeight| after that. | 89 // consistent with |layoutWidth|/|layoutHeight| after that. |
90 // TODO(magjed): Enable hardware scaler with SurfaceHolder.setFixedSize(). Thi s will decouple | 90 // TODO(magjed): Enable hardware scaler with SurfaceHolder.setFixedSize(). Thi s will decouple |
91 // layout and surface size. | 91 // layout and surface size. |
92 private int surfaceWidth; | 92 private int surfaceWidth; |
93 private int surfaceHeight; | 93 private int surfaceHeight; |
94 // |isSurfaceCreated| keeps track of the current status in surfaceCreated()/su rfaceDestroyed(). | |
95 private boolean isSurfaceCreated; | |
94 // Last rendered frame dimensions, or 0 if no frame has been rendered yet. | 96 // Last rendered frame dimensions, or 0 if no frame has been rendered yet. |
95 private int frameWidth; | 97 private int frameWidth; |
96 private int frameHeight; | 98 private int frameHeight; |
97 private int frameRotation; | 99 private int frameRotation; |
98 // |scalingType| determines how the video will fill the allowed layout area in onMeasure(). | 100 // |scalingType| determines how the video will fill the allowed layout area in onMeasure(). |
99 private RendererCommon.ScalingType scalingType = RendererCommon.ScalingType.SC ALE_ASPECT_BALANCED; | 101 private RendererCommon.ScalingType scalingType = RendererCommon.ScalingType.SC ALE_ASPECT_BALANCED; |
100 // If true, mirrors the video stream horizontally. | 102 // If true, mirrors the video stream horizontally. |
101 private boolean mirror; | 103 private boolean mirror; |
102 // Callback for reporting renderer events. | 104 // Callback for reporting renderer events. |
103 private RendererCommon.RendererEvents rendererEvents; | 105 private RendererCommon.RendererEvents rendererEvents; |
(...skipping 17 matching lines...) Expand all Loading... | |
121 @Override public void run() { | 123 @Override public void run() { |
122 renderFrameOnRenderThread(); | 124 renderFrameOnRenderThread(); |
123 } | 125 } |
124 }; | 126 }; |
125 | 127 |
126 /** | 128 /** |
127 * Standard View constructor. In order to render something, you must first cal l init(). | 129 * Standard View constructor. In order to render something, you must first cal l init(). |
128 */ | 130 */ |
129 public SurfaceViewRenderer(Context context) { | 131 public SurfaceViewRenderer(Context context) { |
130 super(context); | 132 super(context); |
133 getHolder().addCallback(this); | |
131 } | 134 } |
132 | 135 |
133 /** | 136 /** |
134 * Standard View constructor. In order to render something, you must first cal l init(). | 137 * Standard View constructor. In order to render something, you must first cal l init(). |
135 */ | 138 */ |
136 public SurfaceViewRenderer(Context context, AttributeSet attrs) { | 139 public SurfaceViewRenderer(Context context, AttributeSet attrs) { |
137 super(context, attrs); | 140 super(context, attrs); |
141 getHolder().addCallback(this); | |
138 } | 142 } |
139 | 143 |
140 /** | 144 /** |
141 * Initialize this class, sharing resources with |sharedContext|. | 145 * Initialize this class, sharing resources with |sharedContext|. It is allowe d to call init() to |
146 * reinitialize the renderer after a previous init()/release() cycle. | |
142 */ | 147 */ |
143 public void init( | 148 public void init( |
hbos
2015/10/08 07:54:36
You may call init() while it is already initialize
magjed_webrtc
2015/10/08 09:56:08
Done, I moved the check back to the top. I was thi
hbos
2015/10/08 11:19:35
You probably shouldn't consider exceptions in java
| |
144 EGLContext sharedContext, RendererCommon.RendererEvents rendererEvents) { | 149 EGLContext sharedContext, RendererCommon.RendererEvents rendererEvents) { |
145 if (renderThreadHandler != null) { | |
146 throw new IllegalStateException("Already initialized"); | |
147 } | |
148 Logging.d(TAG, "Initializing"); | 150 Logging.d(TAG, "Initializing"); |
149 this.rendererEvents = rendererEvents; | 151 this.rendererEvents = rendererEvents; |
150 renderThread = new HandlerThread(TAG); | 152 renderThread = new HandlerThread(TAG); |
151 renderThread.start(); | 153 renderThread.start(); |
152 renderThreadHandler = new Handler(renderThread.getLooper()); | 154 drawer = new GlRectDrawer(); |
153 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PLAIN); | 155 eglBase = new EglBase(sharedContext, EglBase.ConfigType.PLAIN); |
hbos
2015/10/08 07:54:36
What about threading swag? These variables are set
magjed_webrtc
2015/10/08 09:56:08
They are only accessed from |renderThread|, which
hbos
2015/10/08 11:19:35
Acknowledged.
| |
154 drawer = new GlRectDrawer(); | 156 synchronized (handlerLock) { |
155 getHolder().addCallback(this); | 157 if (renderThreadHandler != null) { |
158 throw new IllegalStateException("Already initialized"); | |
159 } | |
160 renderThreadHandler = new Handler(renderThread.getLooper()); | |
161 } | |
162 tryCreateEglSurface(); | |
156 } | 163 } |
157 | 164 |
158 /** | 165 /** |
166 * Create and make an EGLSurface current if both init() and surfaceCreated() h ave been called. | |
167 */ | |
168 public void tryCreateEglSurface() { | |
169 // |renderThreadHandler| is only created after |eglBase| is created in init( ), so the | |
170 // following code will only execute if eglBase != null. | |
171 runOnRenderThread(new Runnable() { | |
172 @Override public void run() { | |
173 synchronized (layoutLock) { | |
174 if (isSurfaceCreated) { | |
175 eglBase.createSurface(getHolder().getSurface()); | |
176 eglBase.makeCurrent(); | |
177 // Necessary for YUV frames with odd width. | |
178 GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); | |
179 } | |
180 } | |
181 } | |
182 }); | |
183 } | |
184 | |
185 /** | |
159 * Block until any pending frame is returned and all GL resources released, ev en if an interrupt | 186 * Block until any pending frame is returned and all GL resources released, ev en if an interrupt |
160 * occurs. If an interrupt occurs during release(), the interrupt flag will be set. This function | 187 * occurs. If an interrupt occurs during release(), the interrupt flag will be set. This function |
161 * should be called before the Activity is destroyed and the EGLContext is sti ll valid. If you | 188 * should be called before the Activity is destroyed and the EGLContext is sti ll valid. If you |
162 * don't call this function, the GL resources might leak. | 189 * don't call this function, the GL resources might leak. |
163 */ | 190 */ |
164 public void release() { | 191 public void release() { |
165 synchronized (handlerLock) { | 192 synchronized (handlerLock) { |
166 if (renderThreadHandler == null) { | 193 if (renderThreadHandler == null) { |
167 Logging.d(TAG, "Already released"); | 194 Logging.d(TAG, "Already released"); |
168 return; | 195 return; |
169 } | 196 } |
170 // Release EGL and GL resources on render thread. | 197 // Release EGL and GL resources on render thread. |
171 // TODO(magjed): This might not be necessary - all OpenGL resources are au tomatically deleted | 198 // TODO(magjed): This might not be necessary - all OpenGL resources are au tomatically deleted |
172 // when the EGL context is lost. It might be dangerous to delete them manu ally in | 199 // when the EGL context is lost. It might be dangerous to delete them manu ally in |
173 // Activity.onDestroy(). | 200 // Activity.onDestroy(). |
174 renderThreadHandler.postAtFrontOfQueue(new Runnable() { | 201 renderThreadHandler.postAtFrontOfQueue(new Runnable() { |
175 @Override public void run() { | 202 @Override public void run() { |
176 drawer.release(); | 203 drawer.release(); |
177 drawer = null; | 204 drawer = null; |
178 if (yuvTextures != null) { | 205 if (yuvTextures != null) { |
179 GLES20.glDeleteTextures(3, yuvTextures, 0); | 206 GLES20.glDeleteTextures(3, yuvTextures, 0); |
180 yuvTextures = null; | 207 yuvTextures = null; |
181 } | 208 } |
209 // Clear last rendered image to black. | |
210 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); | |
211 eglBase.swapBuffers(); | |
182 eglBase.release(); | 212 eglBase.release(); |
183 eglBase = null; | 213 eglBase = null; |
184 } | 214 } |
185 }); | 215 }); |
186 // Don't accept any more frames or messages to the render thread. | 216 // Don't accept any more frames or messages to the render thread. |
187 renderThreadHandler = null; | 217 renderThreadHandler = null; |
188 } | 218 } |
189 // Quit safely to make sure the EGL/GL cleanup posted above is executed. | 219 // Quit safely to make sure the EGL/GL cleanup posted above is executed. |
190 renderThread.quitSafely(); | 220 renderThread.quitSafely(); |
191 synchronized (frameLock) { | 221 synchronized (frameLock) { |
192 if (pendingFrame != null) { | 222 if (pendingFrame != null) { |
193 VideoRenderer.renderFrameDone(pendingFrame); | 223 VideoRenderer.renderFrameDone(pendingFrame); |
194 pendingFrame = null; | 224 pendingFrame = null; |
195 } | 225 } |
196 } | 226 } |
197 // The |renderThread| cleanup is not safe to cancel and we need to wait unti l it's done. | 227 // The |renderThread| cleanup is not safe to cancel and we need to wait unti l it's done. |
198 ThreadUtils.joinUninterruptibly(renderThread); | 228 ThreadUtils.joinUninterruptibly(renderThread); |
199 renderThread = null; | 229 renderThread = null; |
230 // Reset statistics and event reporting. | |
231 synchronized (layoutLock) { | |
232 frameWidth = 0; | |
233 frameHeight = 0; | |
234 frameRotation = 0; | |
235 rendererEvents = null; | |
236 } | |
237 synchronized (statisticsLock) { | |
238 framesReceived = 0; | |
239 framesDropped = 0; | |
240 framesRendered = 0; | |
241 firstFrameTimeNs = 0; | |
242 renderTimeNs = 0; | |
243 } | |
200 } | 244 } |
201 | 245 |
202 /** | 246 /** |
203 * Set if the video stream should be mirrored or not. | 247 * Set if the video stream should be mirrored or not. |
204 */ | 248 */ |
205 public void setMirror(final boolean mirror) { | 249 public void setMirror(final boolean mirror) { |
206 synchronized (layoutLock) { | 250 synchronized (layoutLock) { |
207 this.mirror = mirror; | 251 this.mirror = mirror; |
208 } | 252 } |
209 } | 253 } |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
279 layoutHeight = bottom - top; | 323 layoutHeight = bottom - top; |
280 } | 324 } |
281 // Might have a pending frame waiting for a layout of correct size. | 325 // Might have a pending frame waiting for a layout of correct size. |
282 runOnRenderThread(renderFrameRunnable); | 326 runOnRenderThread(renderFrameRunnable); |
283 } | 327 } |
284 | 328 |
285 // SurfaceHolder.Callback interface. | 329 // SurfaceHolder.Callback interface. |
286 @Override | 330 @Override |
287 public void surfaceCreated(final SurfaceHolder holder) { | 331 public void surfaceCreated(final SurfaceHolder holder) { |
288 Logging.d(TAG, "Surface created"); | 332 Logging.d(TAG, "Surface created"); |
289 runOnRenderThread(new Runnable() { | 333 synchronized (layoutLock) { |
290 @Override public void run() { | 334 isSurfaceCreated = true; |
291 eglBase.createSurface(holder.getSurface()); | 335 } |
292 eglBase.makeCurrent(); | 336 tryCreateEglSurface(); |
293 // Necessary for YUV frames with odd width. | |
294 GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1); | |
295 } | |
296 }); | |
297 } | 337 } |
298 | 338 |
299 @Override | 339 @Override |
300 public void surfaceDestroyed(SurfaceHolder holder) { | 340 public void surfaceDestroyed(SurfaceHolder holder) { |
301 Logging.d(TAG, "Surface destroyed"); | 341 Logging.d(TAG, "Surface destroyed"); |
302 synchronized (layoutLock) { | 342 synchronized (layoutLock) { |
343 isSurfaceCreated = false; | |
303 surfaceWidth = 0; | 344 surfaceWidth = 0; |
304 surfaceHeight = 0; | 345 surfaceHeight = 0; |
305 } | 346 } |
306 runOnRenderThread(new Runnable() { | 347 runOnRenderThread(new Runnable() { |
307 @Override public void run() { | 348 @Override public void run() { |
308 eglBase.releaseSurface(); | 349 eglBase.releaseSurface(); |
309 } | 350 } |
310 }); | 351 }); |
311 } | 352 } |
312 | 353 |
(...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 if (framesReceived > 0 && framesRendered > 0) { | 524 if (framesReceived > 0 && framesRendered > 0) { |
484 final long timeSinceFirstFrameNs = System.nanoTime() - firstFrameTimeNs; | 525 final long timeSinceFirstFrameNs = System.nanoTime() - firstFrameTimeNs; |
485 Logging.d(TAG, "Duration: " + (int) (timeSinceFirstFrameNs / 1e6) + | 526 Logging.d(TAG, "Duration: " + (int) (timeSinceFirstFrameNs / 1e6) + |
486 " ms. FPS: " + (float) framesRendered * 1e9 / timeSinceFirstFrameNs) ; | 527 " ms. FPS: " + (float) framesRendered * 1e9 / timeSinceFirstFrameNs) ; |
487 Logging.d(TAG, "Average render time: " | 528 Logging.d(TAG, "Average render time: " |
488 + (int) (renderTimeNs / (1000 * framesRendered)) + " us."); | 529 + (int) (renderTimeNs / (1000 * framesRendered)) + " us."); |
489 } | 530 } |
490 } | 531 } |
491 } | 532 } |
492 } | 533 } |
OLD | NEW |