OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright 2015 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.graphics.Point; | |
14 import android.opengl.GLES20; | |
15 import android.opengl.Matrix; | |
16 import android.view.View; | |
17 | |
18 import java.nio.ByteBuffer; | |
19 | |
20 /** | |
21 * Static helper functions for renderer implementations. | |
22 */ | |
23 public class RendererCommon { | |
24 /** Interface for reporting rendering events. */ | |
25 public static interface RendererEvents { | |
26 /** | |
27 * Callback fired once first frame is rendered. | |
28 */ | |
29 public void onFirstFrameRendered(); | |
30 | |
31 /** | |
32 * Callback fired when rendered frame resolution or rotation has changed. | |
33 */ | |
34 public void onFrameResolutionChanged(int videoWidth, int videoHeight, int ro
tation); | |
35 } | |
36 | |
37 /** Interface for rendering frames on an EGLSurface. */ | |
38 public static interface GlDrawer { | |
39 /** | |
40 * Functions for drawing frames with different sources. The rendering surfac
e target is | |
41 * implied by the current EGL context of the calling thread and requires no
explicit argument. | |
42 * The coordinates specify the viewport location on the surface target. | |
43 */ | |
44 void drawOes(int oesTextureId, float[] texMatrix, int frameWidth, int frameH
eight, | |
45 int viewportX, int viewportY, int viewportWidth, int viewportHeight); | |
46 void drawRgb(int textureId, float[] texMatrix, int frameWidth, int frameHeig
ht, int viewportX, | |
47 int viewportY, int viewportWidth, int viewportHeight); | |
48 void drawYuv(int[] yuvTextures, float[] texMatrix, int frameWidth, int frame
Height, | |
49 int viewportX, int viewportY, int viewportWidth, int viewportHeight); | |
50 | |
51 /** | |
52 * Release all GL resources. This needs to be done manually, otherwise resou
rces may leak. | |
53 */ | |
54 void release(); | |
55 } | |
56 | |
57 /** | |
58 * Helper class for uploading YUV bytebuffer frames to textures that handles s
tride > width. This | |
59 * class keeps an internal ByteBuffer to avoid unnecessary allocations for int
ermediate copies. | |
60 */ | |
61 public static class YuvUploader { | |
62 // Intermediate copy buffer for uploading yuv frames that are not packed, i.
e. stride > width. | |
63 // TODO(magjed): Investigate when GL_UNPACK_ROW_LENGTH is available, or make
a custom shader | |
64 // that handles stride and compare performance with intermediate copy. | |
65 private ByteBuffer copyBuffer; | |
66 | |
67 /** | |
68 * Upload |planes| into |outputYuvTextures|, taking stride into consideratio
n. | |
69 * |outputYuvTextures| must have been generated in advance. | |
70 */ | |
71 public void uploadYuvData( | |
72 int[] outputYuvTextures, int width, int height, int[] strides, ByteBuffe
r[] planes) { | |
73 final int[] planeWidths = new int[] {width, width / 2, width / 2}; | |
74 final int[] planeHeights = new int[] {height, height / 2, height / 2}; | |
75 // Make a first pass to see if we need a temporary copy buffer. | |
76 int copyCapacityNeeded = 0; | |
77 for (int i = 0; i < 3; ++i) { | |
78 if (strides[i] > planeWidths[i]) { | |
79 copyCapacityNeeded = Math.max(copyCapacityNeeded, planeWidths[i] * pla
neHeights[i]); | |
80 } | |
81 } | |
82 // Allocate copy buffer if necessary. | |
83 if (copyCapacityNeeded > 0 | |
84 && (copyBuffer == null || copyBuffer.capacity() < copyCapacityNeeded))
{ | |
85 copyBuffer = ByteBuffer.allocateDirect(copyCapacityNeeded); | |
86 } | |
87 // Upload each plane. | |
88 for (int i = 0; i < 3; ++i) { | |
89 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); | |
90 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, outputYuvTextures[i]); | |
91 // GLES only accepts packed data, i.e. stride == planeWidth. | |
92 final ByteBuffer packedByteBuffer; | |
93 if (strides[i] == planeWidths[i]) { | |
94 // Input is packed already. | |
95 packedByteBuffer = planes[i]; | |
96 } else { | |
97 VideoRenderer.nativeCopyPlane( | |
98 planes[i], planeWidths[i], planeHeights[i], strides[i], copyBuffer
, planeWidths[i]); | |
99 packedByteBuffer = copyBuffer; | |
100 } | |
101 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, planeW
idths[i], | |
102 planeHeights[i], 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, pa
ckedByteBuffer); | |
103 } | |
104 } | |
105 } | |
106 | |
107 /** | |
108 * Helper class for determining layout size based on layout requirements, scal
ing type, and video | |
109 * aspect ratio. | |
110 */ | |
111 public static class VideoLayoutMeasure { | |
112 // The scaling type determines how the video will fill the allowed layout ar
ea in measure(). It | |
113 // can be specified separately for the case when video has matched orientati
on with layout size | |
114 // and when there is an orientation mismatch. | |
115 private ScalingType scalingTypeMatchOrientation = ScalingType.SCALE_ASPECT_B
ALANCED; | |
116 private ScalingType scalingTypeMismatchOrientation = ScalingType.SCALE_ASPEC
T_BALANCED; | |
117 | |
118 public void setScalingType(ScalingType scalingType) { | |
119 this.scalingTypeMatchOrientation = scalingType; | |
120 this.scalingTypeMismatchOrientation = scalingType; | |
121 } | |
122 | |
123 public void setScalingType( | |
124 ScalingType scalingTypeMatchOrientation, ScalingType scalingTypeMismatch
Orientation) { | |
125 this.scalingTypeMatchOrientation = scalingTypeMatchOrientation; | |
126 this.scalingTypeMismatchOrientation = scalingTypeMismatchOrientation; | |
127 } | |
128 | |
129 public Point measure(int widthSpec, int heightSpec, int frameWidth, int fram
eHeight) { | |
130 // Calculate max allowed layout size. | |
131 final int maxWidth = View.getDefaultSize(Integer.MAX_VALUE, widthSpec); | |
132 final int maxHeight = View.getDefaultSize(Integer.MAX_VALUE, heightSpec); | |
133 if (frameWidth == 0 || frameHeight == 0 || maxWidth == 0 || maxHeight == 0
) { | |
134 return new Point(maxWidth, maxHeight); | |
135 } | |
136 // Calculate desired display size based on scaling type, video aspect rati
o, | |
137 // and maximum layout size. | |
138 final float frameAspect = frameWidth / (float) frameHeight; | |
139 final float displayAspect = maxWidth / (float) maxHeight; | |
140 final ScalingType scalingType = (frameAspect > 1.0f) == (displayAspect > 1
.0f) | |
141 ? scalingTypeMatchOrientation | |
142 : scalingTypeMismatchOrientation; | |
143 final Point layoutSize = getDisplaySize(scalingType, frameAspect, maxWidth
, maxHeight); | |
144 | |
145 // If the measure specification is forcing a specific size - yield. | |
146 if (View.MeasureSpec.getMode(widthSpec) == View.MeasureSpec.EXACTLY) { | |
147 layoutSize.x = maxWidth; | |
148 } | |
149 if (View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY) { | |
150 layoutSize.y = maxHeight; | |
151 } | |
152 return layoutSize; | |
153 } | |
154 } | |
155 | |
156 // Types of video scaling: | |
157 // SCALE_ASPECT_FIT - video frame is scaled to fit the size of the view by | |
158 // maintaining the aspect ratio (black borders may be displayed). | |
159 // SCALE_ASPECT_FILL - video frame is scaled to fill the size of the view by | |
160 // maintaining the aspect ratio. Some portion of the video frame may be | |
161 // clipped. | |
162 // SCALE_ASPECT_BALANCED - Compromise between FIT and FILL. Video frame will f
ill as much as | |
163 // possible of the view while maintaining aspect ratio, under the constraint t
hat at least | |
164 // |BALANCED_VISIBLE_FRACTION| of the frame content will be shown. | |
165 public static enum ScalingType { SCALE_ASPECT_FIT, SCALE_ASPECT_FILL, SCALE_AS
PECT_BALANCED } | |
166 // The minimum fraction of the frame content that will be shown for |SCALE_ASP
ECT_BALANCED|. | |
167 // This limits excessive cropping when adjusting display size. | |
168 private static float BALANCED_VISIBLE_FRACTION = 0.5625f; | |
169 // clang-format off | |
170 public static final float[] identityMatrix() { | |
171 return new float[] { | |
172 1, 0, 0, 0, | |
173 0, 1, 0, 0, | |
174 0, 0, 1, 0, | |
175 0, 0, 0, 1}; | |
176 } | |
177 // Matrix with transform y' = 1 - y. | |
178 public static final float[] verticalFlipMatrix() { | |
179 return new float[] { | |
180 1, 0, 0, 0, | |
181 0, -1, 0, 0, | |
182 0, 0, 1, 0, | |
183 0, 1, 0, 1}; | |
184 } | |
185 | |
186 // Matrix with transform x' = 1 - x. | |
187 public static final float[] horizontalFlipMatrix() { | |
188 return new float[] { | |
189 -1, 0, 0, 0, | |
190 0, 1, 0, 0, | |
191 0, 0, 1, 0, | |
192 1, 0, 0, 1}; | |
193 } | |
194 // clang-format on | |
195 | |
196 /** | |
197 * Returns texture matrix that will have the effect of rotating the frame |rot
ationDegree| | |
198 * clockwise when rendered. | |
199 */ | |
200 public static float[] rotateTextureMatrix(float[] textureMatrix, float rotatio
nDegree) { | |
201 final float[] rotationMatrix = new float[16]; | |
202 Matrix.setRotateM(rotationMatrix, 0, rotationDegree, 0, 0, 1); | |
203 adjustOrigin(rotationMatrix); | |
204 return multiplyMatrices(textureMatrix, rotationMatrix); | |
205 } | |
206 | |
207 /** | |
208 * Returns new matrix with the result of a * b. | |
209 */ | |
210 public static float[] multiplyMatrices(float[] a, float[] b) { | |
211 final float[] resultMatrix = new float[16]; | |
212 Matrix.multiplyMM(resultMatrix, 0, a, 0, b, 0); | |
213 return resultMatrix; | |
214 } | |
215 | |
216 /** | |
217 * Returns layout transformation matrix that applies an optional mirror effect
and compensates | |
218 * for video vs display aspect ratio. | |
219 */ | |
220 public static float[] getLayoutMatrix( | |
221 boolean mirror, float videoAspectRatio, float displayAspectRatio) { | |
222 float scaleX = 1; | |
223 float scaleY = 1; | |
224 // Scale X or Y dimension so that video and display size have same aspect ra
tio. | |
225 if (displayAspectRatio > videoAspectRatio) { | |
226 scaleY = videoAspectRatio / displayAspectRatio; | |
227 } else { | |
228 scaleX = displayAspectRatio / videoAspectRatio; | |
229 } | |
230 // Apply optional horizontal flip. | |
231 if (mirror) { | |
232 scaleX *= -1; | |
233 } | |
234 final float matrix[] = new float[16]; | |
235 Matrix.setIdentityM(matrix, 0); | |
236 Matrix.scaleM(matrix, 0, scaleX, scaleY, 1); | |
237 adjustOrigin(matrix); | |
238 return matrix; | |
239 } | |
240 | |
241 /** | |
242 * Calculate display size based on scaling type, video aspect ratio, and maxim
um display size. | |
243 */ | |
244 public static Point getDisplaySize( | |
245 ScalingType scalingType, float videoAspectRatio, int maxDisplayWidth, int
maxDisplayHeight) { | |
246 return getDisplaySize(convertScalingTypeToVisibleFraction(scalingType), vide
oAspectRatio, | |
247 maxDisplayWidth, maxDisplayHeight); | |
248 } | |
249 | |
250 /** | |
251 * Move |matrix| transformation origin to (0.5, 0.5). This is the origin for t
exture coordinates | |
252 * that are in the range 0 to 1. | |
253 */ | |
254 private static void adjustOrigin(float[] matrix) { | |
255 // Note that OpenGL is using column-major order. | |
256 // Pre translate with -0.5 to move coordinates to range [-0.5, 0.5]. | |
257 matrix[12] -= 0.5f * (matrix[0] + matrix[4]); | |
258 matrix[13] -= 0.5f * (matrix[1] + matrix[5]); | |
259 // Post translate with 0.5 to move coordinates to range [0, 1]. | |
260 matrix[12] += 0.5f; | |
261 matrix[13] += 0.5f; | |
262 } | |
263 | |
264 /** | |
265 * Each scaling type has a one-to-one correspondence to a numeric minimum frac
tion of the video | |
266 * that must remain visible. | |
267 */ | |
268 private static float convertScalingTypeToVisibleFraction(ScalingType scalingTy
pe) { | |
269 switch (scalingType) { | |
270 case SCALE_ASPECT_FIT: | |
271 return 1.0f; | |
272 case SCALE_ASPECT_FILL: | |
273 return 0.0f; | |
274 case SCALE_ASPECT_BALANCED: | |
275 return BALANCED_VISIBLE_FRACTION; | |
276 default: | |
277 throw new IllegalArgumentException(); | |
278 } | |
279 } | |
280 | |
281 /** | |
282 * Calculate display size based on minimum fraction of the video that must rem
ain visible, | |
283 * video aspect ratio, and maximum display size. | |
284 */ | |
285 private static Point getDisplaySize( | |
286 float minVisibleFraction, float videoAspectRatio, int maxDisplayWidth, int
maxDisplayHeight) { | |
287 // If there is no constraint on the amount of cropping, fill the allowed dis
play area. | |
288 if (minVisibleFraction == 0 || videoAspectRatio == 0) { | |
289 return new Point(maxDisplayWidth, maxDisplayHeight); | |
290 } | |
291 // Each dimension is constrained on max display size and how much we are all
owed to crop. | |
292 final int width = Math.min( | |
293 maxDisplayWidth, Math.round(maxDisplayHeight / minVisibleFraction * vide
oAspectRatio)); | |
294 final int height = Math.min( | |
295 maxDisplayHeight, Math.round(maxDisplayWidth / minVisibleFraction / vide
oAspectRatio)); | |
296 return new Point(width, height); | |
297 } | |
298 } | |
OLD | NEW |