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 |