OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2017 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.graphics.Matrix; |
13 import android.graphics.Point; | 14 import android.graphics.Point; |
14 import android.opengl.GLES20; | 15 import android.opengl.GLES20; |
15 import android.opengl.Matrix; | |
16 import android.view.View; | |
17 import java.nio.ByteBuffer; | 16 import java.nio.ByteBuffer; |
18 | 17 |
19 /** | 18 /** |
20 * Static helper functions for renderer implementations. | 19 * Helper class to draw VideoFrames. Calls either drawer.drawOes, drawer.drawRgb
, or |
| 20 * drawer.drawYuv depending on the type of the buffer. The frame will be rendere
d with rotation |
| 21 * taken into account. You can supply an additional render matrix for custom tra
nsformations. |
21 */ | 22 */ |
22 public class RendererCommon { | 23 public class VideoFrameDrawer { |
23 /** Interface for reporting rendering events. */ | |
24 public static interface RendererEvents { | |
25 /** | |
26 * Callback fired once first frame is rendered. | |
27 */ | |
28 public void onFirstFrameRendered(); | |
29 | |
30 /** | |
31 * Callback fired when rendered frame resolution or rotation has changed. | |
32 */ | |
33 public void onFrameResolutionChanged(int videoWidth, int videoHeight, int ro
tation); | |
34 } | |
35 | |
36 /** Interface for rendering frames on an EGLSurface. */ | |
37 public static interface GlDrawer { | |
38 /** | |
39 * Functions for drawing frames with different sources. The rendering surfac
e target is | |
40 * implied by the current EGL context of the calling thread and requires no
explicit argument. | |
41 * The coordinates specify the viewport location on the surface target. | |
42 */ | |
43 void drawOes(int oesTextureId, float[] texMatrix, int frameWidth, int frameH
eight, | |
44 int viewportX, int viewportY, int viewportWidth, int viewportHeight); | |
45 void drawRgb(int textureId, float[] texMatrix, int frameWidth, int frameHeig
ht, int viewportX, | |
46 int viewportY, int viewportWidth, int viewportHeight); | |
47 void drawYuv(int[] yuvTextures, float[] texMatrix, int frameWidth, int frame
Height, | |
48 int viewportX, int viewportY, int viewportWidth, int viewportHeight); | |
49 | |
50 /** | |
51 * Release all GL resources. This needs to be done manually, otherwise resou
rces may leak. | |
52 */ | |
53 void release(); | |
54 } | |
55 | |
56 /** | 24 /** |
57 * Draws a VideoFrame.TextureBuffer. Calls either drawer.drawOes or drawer.dra
wRgb | 25 * Draws a VideoFrame.TextureBuffer. Calls either drawer.drawOes or drawer.dra
wRgb |
58 * depending on the type of the buffer. You can supply an additional render ma
trix. This is | 26 * depending on the type of the buffer. You can supply an additional render ma
trix. This is |
59 * used multiplied together with the transformation matrix of the frame. (M =
renderMatrix * | 27 * used multiplied together with the transformation matrix of the frame. (M =
renderMatrix * |
60 * transformationMatrix) | 28 * transformationMatrix) |
61 */ | 29 */ |
62 static void drawTexture(GlDrawer drawer, VideoFrame.TextureBuffer buffer, | 30 static void drawTexture(RendererCommon.GlDrawer drawer, VideoFrame.TextureBuff
er buffer, |
63 android.graphics.Matrix renderMatrix, int frameWidth, int frameHeight, int
viewportX, | 31 Matrix renderMatrix, int frameWidth, int frameHeight, int viewportX, int v
iewportY, |
64 int viewportY, int viewportWidth, int viewportHeight) { | 32 int viewportWidth, int viewportHeight) { |
65 android.graphics.Matrix finalMatrix = new android.graphics.Matrix(buffer.get
TransformMatrix()); | 33 Matrix finalMatrix = new Matrix(buffer.getTransformMatrix()); |
66 finalMatrix.preConcat(renderMatrix); | 34 finalMatrix.preConcat(renderMatrix); |
67 float[] finalGlMatrix = convertMatrixFromAndroidGraphicsMatrix(finalMatrix); | 35 float[] finalGlMatrix = RendererCommon.convertMatrixFromAndroidGraphicsMatri
x(finalMatrix); |
68 switch (buffer.getType()) { | 36 switch (buffer.getType()) { |
69 case OES: | 37 case OES: |
70 drawer.drawOes(buffer.getTextureId(), finalGlMatrix, frameWidth, frameHe
ight, viewportX, | 38 drawer.drawOes(buffer.getTextureId(), finalGlMatrix, frameWidth, frameHe
ight, viewportX, |
71 viewportY, viewportWidth, viewportHeight); | 39 viewportY, viewportWidth, viewportHeight); |
72 break; | 40 break; |
73 case RGB: | 41 case RGB: |
74 drawer.drawRgb(buffer.getTextureId(), finalGlMatrix, frameWidth, frameHe
ight, viewportX, | 42 drawer.drawRgb(buffer.getTextureId(), finalGlMatrix, frameWidth, frameHe
ight, viewportX, |
75 viewportY, viewportWidth, viewportHeight); | 43 viewportY, viewportWidth, viewportHeight); |
76 break; | 44 break; |
77 default: | 45 default: |
78 throw new RuntimeException("Unknown texture type."); | 46 throw new RuntimeException("Unknown texture type."); |
79 } | 47 } |
80 } | 48 } |
81 | 49 |
82 /** | 50 /** |
83 * Helper class for uploading YUV bytebuffer frames to textures that handles s
tride > width. This | 51 * Helper class for uploading YUV bytebuffer frames to textures that handles s
tride > width. This |
84 * class keeps an internal ByteBuffer to avoid unnecessary allocations for int
ermediate copies. | 52 * class keeps an internal ByteBuffer to avoid unnecessary allocations for int
ermediate copies. |
85 */ | 53 */ |
86 public static class YuvUploader { | 54 private static class YuvUploader { |
87 // Intermediate copy buffer for uploading yuv frames that are not packed, i.
e. stride > width. | 55 // Intermediate copy buffer for uploading yuv frames that are not packed, i.
e. stride > width. |
88 // TODO(magjed): Investigate when GL_UNPACK_ROW_LENGTH is available, or make
a custom shader | 56 // TODO(magjed): Investigate when GL_UNPACK_ROW_LENGTH is available, or make
a custom shader |
89 // that handles stride and compare performance with intermediate copy. | 57 // that handles stride and compare performance with intermediate copy. |
90 private ByteBuffer copyBuffer; | 58 private ByteBuffer copyBuffer; |
91 private int[] yuvTextures; | 59 private int[] yuvTextures; |
92 | 60 |
93 /** | 61 /** |
94 * Upload |planes| into OpenGL textures, taking stride into consideration. | 62 * Upload |planes| into OpenGL textures, taking stride into consideration. |
95 * | 63 * |
96 * @return Array of three texture indices corresponding to Y-, U-, and V-pla
ne respectively. | 64 * @return Array of three texture indices corresponding to Y-, U-, and V-pla
ne respectively. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 } | 104 } |
137 return yuvTextures; | 105 return yuvTextures; |
138 } | 106 } |
139 | 107 |
140 public int[] uploadFromBuffer(VideoFrame.I420Buffer buffer) { | 108 public int[] uploadFromBuffer(VideoFrame.I420Buffer buffer) { |
141 int[] strides = {buffer.getStrideY(), buffer.getStrideU(), buffer.getStrid
eV()}; | 109 int[] strides = {buffer.getStrideY(), buffer.getStrideU(), buffer.getStrid
eV()}; |
142 ByteBuffer[] planes = {buffer.getDataY(), buffer.getDataU(), buffer.getDat
aV()}; | 110 ByteBuffer[] planes = {buffer.getDataY(), buffer.getDataU(), buffer.getDat
aV()}; |
143 return uploadYuvData(buffer.getWidth(), buffer.getHeight(), strides, plane
s); | 111 return uploadYuvData(buffer.getWidth(), buffer.getHeight(), strides, plane
s); |
144 } | 112 } |
145 | 113 |
| 114 public int[] getYuvTextures() { |
| 115 return yuvTextures; |
| 116 } |
| 117 |
146 /** | 118 /** |
147 * Releases cached resources. Uploader can still be used and the resources w
ill be reallocated | 119 * Releases cached resources. Uploader can still be used and the resources w
ill be reallocated |
148 * on first use. | 120 * on first use. |
149 */ | 121 */ |
150 public void release() { | 122 public void release() { |
151 copyBuffer = null; | 123 copyBuffer = null; |
152 if (yuvTextures != null) { | 124 if (yuvTextures != null) { |
153 GLES20.glDeleteTextures(3, yuvTextures, 0); | 125 GLES20.glDeleteTextures(3, yuvTextures, 0); |
154 yuvTextures = null; | 126 yuvTextures = null; |
155 } | 127 } |
156 } | 128 } |
157 } | 129 } |
158 | 130 |
159 /** | 131 private static int distance(float x0, float y0, float x1, float y1) { |
160 * Helper class for determining layout size based on layout requirements, scal
ing type, and video | 132 return (int) Math.round(Math.hypot(x1 - x0, y1 - y0)); |
161 * aspect ratio. | 133 } |
162 */ | |
163 public static class VideoLayoutMeasure { | |
164 // The scaling type determines how the video will fill the allowed layout ar
ea in measure(). It | |
165 // can be specified separately for the case when video has matched orientati
on with layout size | |
166 // and when there is an orientation mismatch. | |
167 private ScalingType scalingTypeMatchOrientation = ScalingType.SCALE_ASPECT_B
ALANCED; | |
168 private ScalingType scalingTypeMismatchOrientation = ScalingType.SCALE_ASPEC
T_BALANCED; | |
169 | 134 |
170 public void setScalingType(ScalingType scalingType) { | 135 // These points are used to calculate the size of the part of the frame we are
rendering. |
171 this.scalingTypeMatchOrientation = scalingType; | 136 final static float[] srcPoints = |
172 this.scalingTypeMismatchOrientation = scalingType; | 137 new float[] {0f /* x0 */, 0f /* y0 */, 1f /* x1 */, 0f /* y1 */, 0f /* x2
*/, 1f /* y2 */}; |
| 138 private final float[] dstPoints = new float[6]; |
| 139 private final Point renderSize = new Point(); |
| 140 private int renderWidth; |
| 141 private int renderHeight; |
| 142 |
| 143 // Calculate the frame size after |renderMatrix| is applied. Stores the output
in member variables |
| 144 // |renderWidth| and |renderHeight| to avoid allocations since this function i
s called for every |
| 145 // frame. |
| 146 private void calculateTransformedRenderSize( |
| 147 int frameWidth, int frameHeight, Matrix renderMatrix) { |
| 148 if (renderMatrix == null) { |
| 149 renderWidth = frameWidth; |
| 150 renderHeight = frameHeight; |
| 151 return; |
| 152 } |
| 153 // Transform the texture coordinates (in the range [0, 1]) according to |ren
derMatrix|. |
| 154 renderMatrix.mapPoints(dstPoints, srcPoints); |
| 155 |
| 156 // Multiply with the width and height to get the positions in terms of pixel
s. |
| 157 for (int i = 0; i < 3; ++i) { |
| 158 dstPoints[i * 2 + 0] *= frameWidth; |
| 159 dstPoints[i * 2 + 1] *= frameHeight; |
173 } | 160 } |
174 | 161 |
175 public void setScalingType( | 162 // Get the length of the sides of the transformed rectangle in terms of pixe
ls. |
176 ScalingType scalingTypeMatchOrientation, ScalingType scalingTypeMismatch
Orientation) { | 163 renderWidth = distance(dstPoints[0], dstPoints[1], dstPoints[2], dstPoints[3
]); |
177 this.scalingTypeMatchOrientation = scalingTypeMatchOrientation; | 164 renderHeight = distance(dstPoints[0], dstPoints[1], dstPoints[4], dstPoints[
5]); |
178 this.scalingTypeMismatchOrientation = scalingTypeMismatchOrientation; | 165 } |
| 166 |
| 167 private final YuvUploader yuvUploader = new YuvUploader(); |
| 168 // This variable will only be used for checking reference equality and is used
for caching I420 |
| 169 // textures. |
| 170 private VideoFrame lastI420Frame; |
| 171 private final Matrix renderMatrix = new Matrix(); |
| 172 |
| 173 public void drawFrame(VideoFrame frame, RendererCommon.GlDrawer drawer) { |
| 174 drawFrame(frame, drawer, null /* additionalRenderMatrix */); |
| 175 } |
| 176 |
| 177 public void drawFrame( |
| 178 VideoFrame frame, RendererCommon.GlDrawer drawer, Matrix additionalRenderM
atrix) { |
| 179 drawFrame(frame, drawer, additionalRenderMatrix, 0 /* viewportX */, 0 /* vie
wportY */, |
| 180 frame.getRotatedWidth(), frame.getRotatedHeight()); |
| 181 } |
| 182 |
| 183 public void drawFrame(VideoFrame frame, RendererCommon.GlDrawer drawer, |
| 184 Matrix additionalRenderMatrix, int viewportX, int viewportY, int viewportW
idth, |
| 185 int viewportHeight) { |
| 186 final int width = frame.getRotatedWidth(); |
| 187 final int height = frame.getRotatedHeight(); |
| 188 |
| 189 calculateTransformedRenderSize(width, height, additionalRenderMatrix); |
| 190 |
| 191 final boolean isTextureFrame = frame.getBuffer() instanceof VideoFrame.Textu
reBuffer; |
| 192 renderMatrix.reset(); |
| 193 renderMatrix.preTranslate(0.5f, 0.5f); |
| 194 if (!isTextureFrame) { |
| 195 renderMatrix.preScale(1f, -1f); // I420-frames are upside down |
| 196 } |
| 197 renderMatrix.preRotate(frame.getRotation()); |
| 198 renderMatrix.preTranslate(-0.5f, -0.5f); |
| 199 if (additionalRenderMatrix != null) { |
| 200 renderMatrix.preConcat(additionalRenderMatrix); |
179 } | 201 } |
180 | 202 |
181 public Point measure(int widthSpec, int heightSpec, int frameWidth, int fram
eHeight) { | 203 if (isTextureFrame) { |
182 // Calculate max allowed layout size. | 204 lastI420Frame = null; |
183 final int maxWidth = View.getDefaultSize(Integer.MAX_VALUE, widthSpec); | 205 drawTexture(drawer, (VideoFrame.TextureBuffer) frame.getBuffer(), renderMa
trix, renderWidth, |
184 final int maxHeight = View.getDefaultSize(Integer.MAX_VALUE, heightSpec); | 206 renderHeight, viewportX, viewportY, viewportWidth, viewportHeight); |
185 if (frameWidth == 0 || frameHeight == 0 || maxWidth == 0 || maxHeight == 0
) { | 207 } else { |
186 return new Point(maxWidth, maxHeight); | 208 // Only upload the I420 data to textures once per frame, if we are called
multiple times |
| 209 // with the same frame. |
| 210 if (frame != lastI420Frame) { |
| 211 lastI420Frame = frame; |
| 212 final VideoFrame.I420Buffer i420Buffer = frame.getBuffer().toI420(); |
| 213 yuvUploader.uploadFromBuffer(i420Buffer); |
| 214 i420Buffer.release(); |
187 } | 215 } |
188 // Calculate desired display size based on scaling type, video aspect rati
o, | |
189 // and maximum layout size. | |
190 final float frameAspect = frameWidth / (float) frameHeight; | |
191 final float displayAspect = maxWidth / (float) maxHeight; | |
192 final ScalingType scalingType = (frameAspect > 1.0f) == (displayAspect > 1
.0f) | |
193 ? scalingTypeMatchOrientation | |
194 : scalingTypeMismatchOrientation; | |
195 final Point layoutSize = getDisplaySize(scalingType, frameAspect, maxWidth
, maxHeight); | |
196 | 216 |
197 // If the measure specification is forcing a specific size - yield. | 217 drawer.drawYuv(yuvUploader.getYuvTextures(), |
198 if (View.MeasureSpec.getMode(widthSpec) == View.MeasureSpec.EXACTLY) { | 218 RendererCommon.convertMatrixFromAndroidGraphicsMatrix(renderMatrix), r
enderWidth, |
199 layoutSize.x = maxWidth; | 219 renderHeight, viewportX, viewportY, viewportWidth, viewportHeight); |
200 } | |
201 if (View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY) { | |
202 layoutSize.y = maxHeight; | |
203 } | |
204 return layoutSize; | |
205 } | 220 } |
206 } | 221 } |
207 | 222 |
208 // Types of video scaling: | 223 public void release() { |
209 // SCALE_ASPECT_FIT - video frame is scaled to fit the size of the view by | 224 yuvUploader.release(); |
210 // maintaining the aspect ratio (black borders may be displayed). | 225 lastI420Frame = null; |
211 // SCALE_ASPECT_FILL - video frame is scaled to fill the size of the view by | |
212 // maintaining the aspect ratio. Some portion of the video frame may be | |
213 // clipped. | |
214 // SCALE_ASPECT_BALANCED - Compromise between FIT and FILL. Video frame will f
ill as much as | |
215 // possible of the view while maintaining aspect ratio, under the constraint t
hat at least | |
216 // |BALANCED_VISIBLE_FRACTION| of the frame content will be shown. | |
217 public static enum ScalingType { SCALE_ASPECT_FIT, SCALE_ASPECT_FILL, SCALE_AS
PECT_BALANCED } | |
218 // The minimum fraction of the frame content that will be shown for |SCALE_ASP
ECT_BALANCED|. | |
219 // This limits excessive cropping when adjusting display size. | |
220 private static float BALANCED_VISIBLE_FRACTION = 0.5625f; | |
221 // clang-format off | |
222 public static final float[] identityMatrix() { | |
223 return new float[] { | |
224 1, 0, 0, 0, | |
225 0, 1, 0, 0, | |
226 0, 0, 1, 0, | |
227 0, 0, 0, 1}; | |
228 } | |
229 // Matrix with transform y' = 1 - y. | |
230 public static final float[] verticalFlipMatrix() { | |
231 return new float[] { | |
232 1, 0, 0, 0, | |
233 0, -1, 0, 0, | |
234 0, 0, 1, 0, | |
235 0, 1, 0, 1}; | |
236 } | |
237 | |
238 // Matrix with transform x' = 1 - x. | |
239 public static final float[] horizontalFlipMatrix() { | |
240 return new float[] { | |
241 -1, 0, 0, 0, | |
242 0, 1, 0, 0, | |
243 0, 0, 1, 0, | |
244 1, 0, 0, 1}; | |
245 } | |
246 // clang-format on | |
247 | |
248 /** | |
249 * Returns texture matrix that will have the effect of rotating the frame |rot
ationDegree| | |
250 * clockwise when rendered. | |
251 */ | |
252 public static float[] rotateTextureMatrix(float[] textureMatrix, float rotatio
nDegree) { | |
253 final float[] rotationMatrix = new float[16]; | |
254 Matrix.setRotateM(rotationMatrix, 0, rotationDegree, 0, 0, 1); | |
255 adjustOrigin(rotationMatrix); | |
256 return multiplyMatrices(textureMatrix, rotationMatrix); | |
257 } | |
258 | |
259 /** | |
260 * Returns new matrix with the result of a * b. | |
261 */ | |
262 public static float[] multiplyMatrices(float[] a, float[] b) { | |
263 final float[] resultMatrix = new float[16]; | |
264 Matrix.multiplyMM(resultMatrix, 0, a, 0, b, 0); | |
265 return resultMatrix; | |
266 } | |
267 | |
268 /** | |
269 * Returns layout transformation matrix that applies an optional mirror effect
and compensates | |
270 * for video vs display aspect ratio. | |
271 */ | |
272 public static float[] getLayoutMatrix( | |
273 boolean mirror, float videoAspectRatio, float displayAspectRatio) { | |
274 float scaleX = 1; | |
275 float scaleY = 1; | |
276 // Scale X or Y dimension so that video and display size have same aspect ra
tio. | |
277 if (displayAspectRatio > videoAspectRatio) { | |
278 scaleY = videoAspectRatio / displayAspectRatio; | |
279 } else { | |
280 scaleX = displayAspectRatio / videoAspectRatio; | |
281 } | |
282 // Apply optional horizontal flip. | |
283 if (mirror) { | |
284 scaleX *= -1; | |
285 } | |
286 final float matrix[] = new float[16]; | |
287 Matrix.setIdentityM(matrix, 0); | |
288 Matrix.scaleM(matrix, 0, scaleX, scaleY, 1); | |
289 adjustOrigin(matrix); | |
290 return matrix; | |
291 } | |
292 | |
293 /** Converts a float[16] matrix array to android.graphics.Matrix. */ | |
294 public static android.graphics.Matrix convertMatrixToAndroidGraphicsMatrix(flo
at[] matrix4x4) { | |
295 // clang-format off | |
296 float[] values = { | |
297 matrix4x4[0 * 4 + 0], matrix4x4[1 * 4 + 0], matrix4x4[3 * 4 + 0], | |
298 matrix4x4[0 * 4 + 1], matrix4x4[1 * 4 + 1], matrix4x4[3 * 4 + 1], | |
299 matrix4x4[0 * 4 + 3], matrix4x4[1 * 4 + 3], matrix4x4[3 * 4 + 3], | |
300 }; | |
301 // clang-format on | |
302 | |
303 android.graphics.Matrix matrix = new android.graphics.Matrix(); | |
304 matrix.setValues(values); | |
305 return matrix; | |
306 } | |
307 | |
308 /** Converts android.graphics.Matrix to a float[16] matrix array. */ | |
309 public static float[] convertMatrixFromAndroidGraphicsMatrix(android.graphics.
Matrix matrix) { | |
310 float[] values = new float[9]; | |
311 matrix.getValues(values); | |
312 | |
313 // The android.graphics.Matrix looks like this: | |
314 // [x1 y1 w1] | |
315 // [x2 y2 w2] | |
316 // [x3 y3 w3] | |
317 // We want to contruct a matrix that looks like this: | |
318 // [x1 y1 0 w1] | |
319 // [x2 y2 0 w2] | |
320 // [ 0 0 1 0] | |
321 // [x3 y3 0 w3] | |
322 // Since it is stored in column-major order, it looks like this: | |
323 // [x1 x2 0 x3 | |
324 // y1 y2 0 y3 | |
325 // 0 0 1 0 | |
326 // w1 w2 0 w3] | |
327 // clang-format off | |
328 float[] matrix4x4 = { | |
329 values[0 * 3 + 0], values[1 * 3 + 0], 0, values[2 * 3 + 0], | |
330 values[0 * 3 + 1], values[1 * 3 + 1], 0, values[2 * 3 + 1], | |
331 0, 0, 1, 0, | |
332 values[0 * 3 + 2], values[1 * 3 + 2], 0, values[2 * 3 + 2], | |
333 }; | |
334 // clang-format on | |
335 return matrix4x4; | |
336 } | |
337 | |
338 /** | |
339 * Calculate display size based on scaling type, video aspect ratio, and maxim
um display size. | |
340 */ | |
341 public static Point getDisplaySize( | |
342 ScalingType scalingType, float videoAspectRatio, int maxDisplayWidth, int
maxDisplayHeight) { | |
343 return getDisplaySize(convertScalingTypeToVisibleFraction(scalingType), vide
oAspectRatio, | |
344 maxDisplayWidth, maxDisplayHeight); | |
345 } | |
346 | |
347 /** | |
348 * Move |matrix| transformation origin to (0.5, 0.5). This is the origin for t
exture coordinates | |
349 * that are in the range 0 to 1. | |
350 */ | |
351 private static void adjustOrigin(float[] matrix) { | |
352 // Note that OpenGL is using column-major order. | |
353 // Pre translate with -0.5 to move coordinates to range [-0.5, 0.5]. | |
354 matrix[12] -= 0.5f * (matrix[0] + matrix[4]); | |
355 matrix[13] -= 0.5f * (matrix[1] + matrix[5]); | |
356 // Post translate with 0.5 to move coordinates to range [0, 1]. | |
357 matrix[12] += 0.5f; | |
358 matrix[13] += 0.5f; | |
359 } | |
360 | |
361 /** | |
362 * Each scaling type has a one-to-one correspondence to a numeric minimum frac
tion of the video | |
363 * that must remain visible. | |
364 */ | |
365 private static float convertScalingTypeToVisibleFraction(ScalingType scalingTy
pe) { | |
366 switch (scalingType) { | |
367 case SCALE_ASPECT_FIT: | |
368 return 1.0f; | |
369 case SCALE_ASPECT_FILL: | |
370 return 0.0f; | |
371 case SCALE_ASPECT_BALANCED: | |
372 return BALANCED_VISIBLE_FRACTION; | |
373 default: | |
374 throw new IllegalArgumentException(); | |
375 } | |
376 } | |
377 | |
378 /** | |
379 * Calculate display size based on minimum fraction of the video that must rem
ain visible, | |
380 * video aspect ratio, and maximum display size. | |
381 */ | |
382 private static Point getDisplaySize( | |
383 float minVisibleFraction, float videoAspectRatio, int maxDisplayWidth, int
maxDisplayHeight) { | |
384 // If there is no constraint on the amount of cropping, fill the allowed dis
play area. | |
385 if (minVisibleFraction == 0 || videoAspectRatio == 0) { | |
386 return new Point(maxDisplayWidth, maxDisplayHeight); | |
387 } | |
388 // Each dimension is constrained on max display size and how much we are all
owed to crop. | |
389 final int width = Math.min( | |
390 maxDisplayWidth, Math.round(maxDisplayHeight / minVisibleFraction * vide
oAspectRatio)); | |
391 final int height = Math.min( | |
392 maxDisplayHeight, Math.round(maxDisplayWidth / minVisibleFraction / vide
oAspectRatio)); | |
393 return new Point(width, height); | |
394 } | 226 } |
395 } | 227 } |
OLD | NEW |