Chromium Code Reviews| 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 |