OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 package org.webrtc; | 11 package org.webrtc; |
12 | 12 |
13 import android.content.Context; | 13 import android.content.Context; |
14 import android.content.res.Resources.NotFoundException; | 14 import android.content.res.Resources.NotFoundException; |
15 import android.graphics.Matrix; | |
15 import android.graphics.Point; | 16 import android.graphics.Point; |
17 import android.graphics.SurfaceTexture; | |
16 import android.util.AttributeSet; | 18 import android.util.AttributeSet; |
19 import android.view.Surface; | |
17 import android.view.SurfaceHolder; | 20 import android.view.SurfaceHolder; |
18 import android.view.SurfaceView; | 21 import android.view.TextureView; |
22 import android.view.TextureView.SurfaceTextureListener; | |
23 | |
19 import java.util.concurrent.CountDownLatch; | 24 import java.util.concurrent.CountDownLatch; |
20 | 25 |
21 /** | 26 /** |
22 * Implements org.webrtc.VideoRenderer.Callbacks by displaying the video stream on a SurfaceView. | 27 * Implements org.webrtc.VideoRenderer.Callbacks by displaying the video stream on a SurfaceView. |
23 * renderFrame() is asynchronous to avoid blocking the calling thread. | 28 * renderFrame() is asynchronous to avoid blocking the calling thread. |
24 * This class is thread safe and handles access from potentially four different threads: | 29 * This class is thread safe and handles access from potentially four different threads: |
25 * Interaction from the main app in init, release, setMirror, and setScalingtype . | 30 * Interaction from the main app in init, release, setMirror, and setScalingtype . |
26 * Interaction from C++ rtc::VideoSinkInterface in renderFrame. | 31 * Interaction from C++ rtc::VideoSinkInterface in renderFrame. |
27 * Interaction from the Activity lifecycle in surfaceCreated, surfaceChanged, an d surfaceDestroyed. | 32 * Interaction from the Activity lifecycle in surfaceCreated, surfaceChanged, an d surfaceDestroyed. |
28 * Interaction with the layout framework in onMeasure and onSizeChanged. | 33 * Interaction with the layout framework in onMeasure and onSizeChanged. |
29 */ | 34 */ |
30 public class SurfaceViewRenderer | 35 public class TextureViewRenderer extends TextureView |
31 extends SurfaceView implements SurfaceHolder.Callback, VideoRenderer.Callbac ks { | 36 implements SurfaceHolder.Callback, SurfaceTextureListener, VideoRenderer.Cal lbacks { |
32 private static final String TAG = "SurfaceViewRenderer"; | 37 private static final String TAG = "SurfaceViewRenderer"; |
33 | 38 |
34 // Cached resource name. | 39 // Cached resource name. |
35 private final String resourceName; | 40 private final String resourceName; |
36 private final RendererCommon.VideoLayoutMeasure videoLayoutMeasure = | 41 private final RendererCommon.VideoLayoutMeasure videoLayoutMeasure = |
37 new RendererCommon.VideoLayoutMeasure(); | 42 new RendererCommon.VideoLayoutMeasure(); |
38 private final EglRenderer eglRenderer; | 43 private final EglRenderer eglRenderer; |
39 | 44 |
40 // Callback for reporting renderer events. Read-only after initilization so no lock required. | 45 // Callback for reporting renderer events. Read-only after initilization so no lock required. |
41 private RendererCommon.RendererEvents rendererEvents; | 46 private RendererCommon.RendererEvents rendererEvents; |
42 | 47 |
43 private final Object layoutLock = new Object(); | 48 private final Object layoutLock = new Object(); |
44 private boolean isFirstFrameRendered; | 49 private boolean isFirstFrameRendered; |
45 private int rotatedFrameWidth; | 50 private int rotatedFrameWidth; |
46 private int rotatedFrameHeight; | 51 private int rotatedFrameHeight; |
47 private int frameRotation; | 52 private int frameRotation; |
48 | 53 |
49 // Accessed only on the main thread. | 54 // Accessed only on the main thread. |
50 private boolean enableFixedSize; | 55 private boolean enableFixedSize; |
51 private int surfaceWidth; | 56 private int surfaceWidth; |
52 private int surfaceHeight; | 57 private int surfaceHeight; |
53 | 58 |
54 /** | 59 /** |
55 * Standard View constructor. In order to render something, you must first cal l init(). | 60 * Standard View constructor. In order to render something, you must first cal l init(). |
56 */ | 61 */ |
57 public SurfaceViewRenderer(Context context) { | 62 public TextureViewRenderer(Context context) { |
58 super(context); | 63 super(context); |
59 this.resourceName = getResourceName(); | 64 this.resourceName = getResourceName(); |
60 eglRenderer = new EglRenderer(resourceName); | 65 eglRenderer = new EglRenderer(resourceName); |
61 getHolder().addCallback(this); | 66 setSurfaceTextureListener(this); |
62 } | 67 } |
63 | 68 |
64 /** | 69 /** |
65 * Standard View constructor. In order to render something, you must first cal l init(). | 70 * Standard View constructor. In order to render something, you must first cal l init(). |
66 */ | 71 */ |
67 public SurfaceViewRenderer(Context context, AttributeSet attrs) { | 72 public TextureViewRenderer(Context context, AttributeSet attrs) { |
68 super(context, attrs); | 73 super(context, attrs); |
69 this.resourceName = getResourceName(); | 74 this.resourceName = getResourceName(); |
70 eglRenderer = new EglRenderer(resourceName); | 75 eglRenderer = new EglRenderer(resourceName); |
71 getHolder().addCallback(this); | 76 setSurfaceTextureListener(this); |
72 } | 77 } |
73 | 78 |
74 /** | 79 /** |
75 * Initialize this class, sharing resources with |sharedContext|. It is allowe d to call init() to | 80 * Initialize this class, sharing resources with |sharedContext|. It is allowe d to call init() to |
76 * reinitialize the renderer after a previous init()/release() cycle. | 81 * reinitialize the renderer after a previous init()/release() cycle. |
77 */ | 82 */ |
78 public void init(EglBase.Context sharedContext, RendererCommon.RendererEvents rendererEvents) { | 83 public void init(EglBase.Context sharedContext, RendererCommon.RendererEvents rendererEvents) { |
79 init(sharedContext, rendererEvents, EglBase.CONFIG_PLAIN, new GlRectDrawer() ); | 84 init(sharedContext, rendererEvents, EglBase.CONFIG_PLAIN, new GlRectDrawer() ); |
80 } | 85 } |
81 | 86 |
(...skipping 26 matching lines...) Expand all Loading... | |
108 eglRenderer.release(); | 113 eglRenderer.release(); |
109 } | 114 } |
110 | 115 |
111 /** | 116 /** |
112 * Register a callback to be invoked when a new video frame has been received. | 117 * Register a callback to be invoked when a new video frame has been received. |
113 * | 118 * |
114 * @param listener The callback to be invoked. The callback will be invoked on the render thread. | 119 * @param listener The callback to be invoked. The callback will be invoked on the render thread. |
115 * It should be lightweight and must not call removeFrameListe ner. | 120 * It should be lightweight and must not call removeFrameListe ner. |
116 * @param scale The scale of the Bitmap passed to the callback, or 0 if no Bitmap is | 121 * @param scale The scale of the Bitmap passed to the callback, or 0 if no Bitmap is |
117 * required. | 122 * required. |
118 * @param drawer Custom drawer to use for this frame listener. | 123 * @param drawerParam Custom drawer to use for this frame listener. |
119 */ | 124 */ |
120 public void addFrameListener( | 125 public void addFrameListener( |
121 EglRenderer.FrameListener listener, float scale, RendererCommon.GlDrawer d rawerParam) { | 126 EglRenderer.FrameListener listener, float scale, RendererCommon.GlDrawer d rawerParam) { |
122 eglRenderer.addFrameListener(listener, scale, drawerParam); | 127 eglRenderer.addFrameListener(listener, scale, drawerParam); |
123 } | 128 } |
124 | 129 |
125 /** | 130 /** |
126 * Register a callback to be invoked when a new video frame has been received. This version uses | 131 * Register a callback to be invoked when a new video frame has been received. This version uses |
127 * the drawer of the EglRenderer that was passed in init. | 132 * the drawer of the EglRenderer that was passed in init. |
128 * | 133 * |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
233 } | 238 } |
234 // Aspect ratio of the drawn frame and the view is the same. | 239 // Aspect ratio of the drawn frame and the view is the same. |
235 final int width = Math.min(getWidth(), drawnFrameWidth); | 240 final int width = Math.min(getWidth(), drawnFrameWidth); |
236 final int height = Math.min(getHeight(), drawnFrameHeight); | 241 final int height = Math.min(getHeight(), drawnFrameHeight); |
237 logD("updateSurfaceSize. Layout size: " + getWidth() + "x" + getHeight() + ", frame size: " | 242 logD("updateSurfaceSize. Layout size: " + getWidth() + "x" + getHeight() + ", frame size: " |
238 + rotatedFrameWidth + "x" + rotatedFrameHeight + ", requested surfac e size: " + width | 243 + rotatedFrameWidth + "x" + rotatedFrameHeight + ", requested surfac e size: " + width |
239 + "x" + height + ", old surface size: " + surfaceWidth + "x" + surfa ceHeight); | 244 + "x" + height + ", old surface size: " + surfaceWidth + "x" + surfa ceHeight); |
240 if (width != surfaceWidth || height != surfaceHeight) { | 245 if (width != surfaceWidth || height != surfaceHeight) { |
241 surfaceWidth = width; | 246 surfaceWidth = width; |
242 surfaceHeight = height; | 247 surfaceHeight = height; |
243 getHolder().setFixedSize(width, height); | 248 adjustAspectRatio(surfaceWidth, surfaceHeight); |
sakal
2017/06/16 07:47:02
This doesn't change the surface size?
| |
244 } | 249 } |
245 } else { | 250 } else { |
246 surfaceWidth = surfaceHeight = 0; | 251 surfaceWidth = surfaceHeight = 0; |
sakal
2017/06/16 07:47:02
We have to somehow reset the surface size
| |
247 getHolder().setSizeFromLayout(); | |
248 } | 252 } |
249 } | 253 } |
250 } | 254 } |
251 | 255 |
256 /** | |
257 * Sets the TextureView transform to preserve the aspect ratio of the video. | |
258 */ | |
259 private void adjustAspectRatio(int videoWidth, int videoHeight) { | |
sakal
2017/06/16 07:47:02
This method is not needed if you correctly call se
| |
260 int viewWidth = getWidth(); | |
261 int viewHeight = getHeight(); | |
262 double aspectRatio = (double) videoHeight / videoWidth; | |
263 | |
264 int newWidth, newHeight; | |
265 if (viewHeight > (int) (viewWidth * aspectRatio)) { | |
266 // limited by narrow width; restrict height | |
267 newWidth = viewWidth; | |
268 newHeight = (int) (viewWidth * aspectRatio); | |
269 } else { | |
270 // limited by short height; restrict width | |
271 newWidth = (int) (viewHeight / aspectRatio); | |
272 newHeight = viewHeight; | |
273 } | |
274 int xoff = (viewWidth - newWidth) / 2; | |
275 int yoff = (viewHeight - newHeight) / 2; | |
276 logD("video=" + videoWidth + "x" + videoHeight + " view=" + viewWidth + "x" + viewHeight | |
277 + " newView=" + newWidth + "x" + newHeight + " off=" + xoff + "," + yoff ); | |
278 | |
279 Matrix txform = new Matrix(); | |
280 getTransform(txform); | |
281 txform.setScale((float) newWidth / viewWidth, (float) newHeight / viewHeight ); | |
282 // txform.postRotate(10); // just for fun | |
283 txform.postTranslate(xoff, yoff); | |
284 setTransform(txform); | |
285 } | |
286 | |
252 // SurfaceHolder.Callback interface. | 287 // SurfaceHolder.Callback interface. |
253 @Override | 288 @Override |
254 public void surfaceCreated(final SurfaceHolder holder) { | 289 public void surfaceCreated(final SurfaceHolder holder) { |
255 ThreadUtils.checkIsOnMainThread(); | 290 ThreadUtils.checkIsOnMainThread(); |
256 eglRenderer.createEglSurface(holder.getSurface()); | 291 eglRenderer.createEglSurface(holder.getSurface()); |
257 surfaceWidth = surfaceHeight = 0; | 292 surfaceWidth = surfaceHeight = 0; |
258 updateSurfaceSize(); | 293 updateSurfaceSize(); |
259 } | 294 } |
260 | 295 |
261 @Override | 296 @Override |
262 public void surfaceDestroyed(SurfaceHolder holder) { | 297 public void surfaceDestroyed(SurfaceHolder holder) { |
263 ThreadUtils.checkIsOnMainThread(); | 298 ThreadUtils.checkIsOnMainThread(); |
264 final CountDownLatch completionLatch = new CountDownLatch(1); | 299 final CountDownLatch completionLatch = new CountDownLatch(1); |
265 eglRenderer.releaseEglSurface(new Runnable() { | 300 eglRenderer.releaseEglSurface(new Runnable() { |
266 @Override | 301 @Override |
267 public void run() { | 302 public void run() { |
268 completionLatch.countDown(); | 303 completionLatch.countDown(); |
269 } | 304 } |
270 }); | 305 }); |
271 ThreadUtils.awaitUninterruptibly(completionLatch); | 306 ThreadUtils.awaitUninterruptibly(completionLatch); |
272 } | 307 } |
273 | 308 |
274 @Override | 309 @Override |
275 public void surfaceChanged(SurfaceHolder holder, int format, int width, int he ight) { | 310 public void surfaceChanged(SurfaceHolder holder, int format, int width, int he ight) { |
276 ThreadUtils.checkIsOnMainThread(); | 311 ThreadUtils.checkIsOnMainThread(); |
277 logD("surfaceChanged: format: " + format + " size: " + width + "x" + height) ; | 312 logD("surfaceChanged: format: " + format + " size: " + width + "x" + height) ; |
278 } | 313 } |
279 | 314 |
315 // TextureView.SurfaceTextureListener implementation | |
316 @Override | |
317 public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, in t i1) { | |
318 ThreadUtils.checkIsOnMainThread(); | |
319 eglRenderer.createEglSurface(new Surface(surfaceTexture)); | |
320 surfaceWidth = surfaceHeight = 0; | |
321 updateSurfaceSize(); | |
322 } | |
323 | |
324 @Override | |
325 public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int wid th, int height) { | |
326 ThreadUtils.checkIsOnMainThread(); | |
327 logD("surfaceChanged: size: " + width + "x" + height); | |
328 } | |
329 | |
330 @Override | |
331 public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { | |
332 ThreadUtils.checkIsOnMainThread(); | |
333 final CountDownLatch completionLatch = new CountDownLatch(1); | |
334 eglRenderer.releaseEglSurface(new Runnable() { | |
335 @Override | |
336 public void run() { | |
337 completionLatch.countDown(); | |
338 } | |
339 }); | |
340 ThreadUtils.awaitUninterruptibly(completionLatch); | |
341 return true; | |
342 } | |
343 | |
344 @Override | |
345 public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {} | |
346 | |
280 private String getResourceName() { | 347 private String getResourceName() { |
281 try { | 348 try { |
282 return getResources().getResourceEntryName(getId()) + ": "; | 349 return getResources().getResourceEntryName(getId()) + ": "; |
283 } catch (NotFoundException e) { | 350 } catch (NotFoundException e) { |
284 return ""; | 351 return ""; |
285 } | 352 } |
286 } | 353 } |
287 | 354 |
288 /** | 355 /** |
289 * Post a task to clear the SurfaceView to a transparent uniform color. | 356 * Post a task to clear the SurfaceView to a transparent uniform color. |
(...skipping 30 matching lines...) Expand all Loading... | |
320 } | 387 } |
321 }); | 388 }); |
322 } | 389 } |
323 } | 390 } |
324 } | 391 } |
325 | 392 |
326 private void logD(String string) { | 393 private void logD(String string) { |
327 Logging.d(TAG, resourceName + string); | 394 Logging.d(TAG, resourceName + string); |
328 } | 395 } |
329 } | 396 } |
OLD | NEW |