• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright 2017 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.view.SurfaceHolder;
14 import java.util.concurrent.CountDownLatch;
15 
16 /**
17  * Display the video stream on a Surface.
18  * renderFrame() is asynchronous to avoid blocking the calling thread.
19  * This class is thread safe and handles access from potentially three different threads:
20  * Interaction from the main app in init, release and setMirror.
21  * Interaction from C++ rtc::VideoSinkInterface in renderFrame.
22  * Interaction from SurfaceHolder lifecycle in surfaceCreated, surfaceChanged, and surfaceDestroyed.
23  */
24 public class SurfaceEglRenderer extends EglRenderer implements SurfaceHolder.Callback {
25   private static final String TAG = "SurfaceEglRenderer";
26 
27   // Callback for reporting renderer events. Read-only after initialization so no lock required.
28   private RendererCommon.RendererEvents rendererEvents;
29 
30   private final Object layoutLock = new Object();
31   private boolean isRenderingPaused;
32   private boolean isFirstFrameRendered;
33   private int rotatedFrameWidth;
34   private int rotatedFrameHeight;
35   private int frameRotation;
36 
37   /**
38    * In order to render something, you must first call init().
39    */
SurfaceEglRenderer(String name)40   public SurfaceEglRenderer(String name) {
41     super(name);
42   }
43 
44   /**
45    * Initialize this class, sharing resources with `sharedContext`. The custom `drawer` will be used
46    * for drawing frames on the EGLSurface. This class is responsible for calling release() on
47    * `drawer`. It is allowed to call init() to reinitialize the renderer after a previous
48    * init()/release() cycle.
49    */
init(final EglBase.Context sharedContext, RendererCommon.RendererEvents rendererEvents, final int[] configAttributes, RendererCommon.GlDrawer drawer)50   public void init(final EglBase.Context sharedContext,
51       RendererCommon.RendererEvents rendererEvents, final int[] configAttributes,
52       RendererCommon.GlDrawer drawer) {
53     ThreadUtils.checkIsOnMainThread();
54     this.rendererEvents = rendererEvents;
55     synchronized (layoutLock) {
56       isFirstFrameRendered = false;
57       rotatedFrameWidth = 0;
58       rotatedFrameHeight = 0;
59       frameRotation = 0;
60     }
61     super.init(sharedContext, configAttributes, drawer);
62   }
63 
64   @Override
init(final EglBase.Context sharedContext, final int[] configAttributes, RendererCommon.GlDrawer drawer)65   public void init(final EglBase.Context sharedContext, final int[] configAttributes,
66       RendererCommon.GlDrawer drawer) {
67     init(sharedContext, null /* rendererEvents */, configAttributes, drawer);
68   }
69 
70   /**
71    * Limit render framerate.
72    *
73    * @param fps Limit render framerate to this value, or use Float.POSITIVE_INFINITY to disable fps
74    *            reduction.
75    */
76   @Override
setFpsReduction(float fps)77   public void setFpsReduction(float fps) {
78     synchronized (layoutLock) {
79       isRenderingPaused = fps == 0f;
80     }
81     super.setFpsReduction(fps);
82   }
83 
84   @Override
disableFpsReduction()85   public void disableFpsReduction() {
86     synchronized (layoutLock) {
87       isRenderingPaused = false;
88     }
89     super.disableFpsReduction();
90   }
91 
92   @Override
pauseVideo()93   public void pauseVideo() {
94     synchronized (layoutLock) {
95       isRenderingPaused = true;
96     }
97     super.pauseVideo();
98   }
99 
100   // VideoSink interface.
101   @Override
onFrame(VideoFrame frame)102   public void onFrame(VideoFrame frame) {
103     updateFrameDimensionsAndReportEvents(frame);
104     super.onFrame(frame);
105   }
106 
107   // SurfaceHolder.Callback interface.
108   @Override
surfaceCreated(final SurfaceHolder holder)109   public void surfaceCreated(final SurfaceHolder holder) {
110     ThreadUtils.checkIsOnMainThread();
111     createEglSurface(holder.getSurface());
112   }
113 
114   @Override
surfaceDestroyed(SurfaceHolder holder)115   public void surfaceDestroyed(SurfaceHolder holder) {
116     ThreadUtils.checkIsOnMainThread();
117     final CountDownLatch completionLatch = new CountDownLatch(1);
118     releaseEglSurface(completionLatch::countDown);
119     ThreadUtils.awaitUninterruptibly(completionLatch);
120   }
121 
122   @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)123   public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
124     ThreadUtils.checkIsOnMainThread();
125     logD("surfaceChanged: format: " + format + " size: " + width + "x" + height);
126   }
127 
128   // Update frame dimensions and report any changes to `rendererEvents`.
updateFrameDimensionsAndReportEvents(VideoFrame frame)129   private void updateFrameDimensionsAndReportEvents(VideoFrame frame) {
130     synchronized (layoutLock) {
131       if (isRenderingPaused) {
132         return;
133       }
134       if (!isFirstFrameRendered) {
135         isFirstFrameRendered = true;
136         logD("Reporting first rendered frame.");
137         if (rendererEvents != null) {
138           rendererEvents.onFirstFrameRendered();
139         }
140       }
141       if (rotatedFrameWidth != frame.getRotatedWidth()
142           || rotatedFrameHeight != frame.getRotatedHeight()
143           || frameRotation != frame.getRotation()) {
144         logD("Reporting frame resolution changed to " + frame.getBuffer().getWidth() + "x"
145             + frame.getBuffer().getHeight() + " with rotation " + frame.getRotation());
146         if (rendererEvents != null) {
147           rendererEvents.onFrameResolutionChanged(
148               frame.getBuffer().getWidth(), frame.getBuffer().getHeight(), frame.getRotation());
149         }
150         rotatedFrameWidth = frame.getRotatedWidth();
151         rotatedFrameHeight = frame.getRotatedHeight();
152         frameRotation = frame.getRotation();
153       }
154     }
155   }
156 
logD(String string)157   private void logD(String string) {
158     Logging.d(TAG, name + ": " + string);
159   }
160 }
161