Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Unified Diff: webrtc/sdk/android/api/org/webrtc/VideoFrameDrawer.java

Issue 3008423002: Android: Add helper class VideoFrameDrawer that can render VideoFrames (Closed)
Patch Set: similarity=10 Created 3 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webrtc/sdk/android/api/org/webrtc/RendererCommon.java ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webrtc/sdk/android/api/org/webrtc/VideoFrameDrawer.java
diff --git a/webrtc/sdk/android/api/org/webrtc/RendererCommon.java b/webrtc/sdk/android/api/org/webrtc/VideoFrameDrawer.java
similarity index 29%
copy from webrtc/sdk/android/api/org/webrtc/RendererCommon.java
copy to webrtc/sdk/android/api/org/webrtc/VideoFrameDrawer.java
index 18d96c22e57fdc25ee9ae2f589b6500683a9d327..491fd054c26c5894c580c8252e72cd186898515b 100644
--- a/webrtc/sdk/android/api/org/webrtc/RendererCommon.java
+++ b/webrtc/sdk/android/api/org/webrtc/VideoFrameDrawer.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015 The WebRTC project authors. All Rights Reserved.
+ * Copyright 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
@@ -10,61 +10,29 @@
package org.webrtc;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.opengl.GLES20;
-import android.opengl.Matrix;
-import android.view.View;
import java.nio.ByteBuffer;
/**
- * Static helper functions for renderer implementations.
+ * Helper class to draw VideoFrames. Calls either drawer.drawOes, drawer.drawRgb, or
+ * drawer.drawYuv depending on the type of the buffer. The frame will be rendered with rotation
+ * taken into account. You can supply an additional render matrix for custom transformations.
*/
-public class RendererCommon {
- /** Interface for reporting rendering events. */
- public static interface RendererEvents {
- /**
- * Callback fired once first frame is rendered.
- */
- public void onFirstFrameRendered();
-
- /**
- * Callback fired when rendered frame resolution or rotation has changed.
- */
- public void onFrameResolutionChanged(int videoWidth, int videoHeight, int rotation);
- }
-
- /** Interface for rendering frames on an EGLSurface. */
- public static interface GlDrawer {
- /**
- * Functions for drawing frames with different sources. The rendering surface target is
- * implied by the current EGL context of the calling thread and requires no explicit argument.
- * The coordinates specify the viewport location on the surface target.
- */
- void drawOes(int oesTextureId, float[] texMatrix, int frameWidth, int frameHeight,
- int viewportX, int viewportY, int viewportWidth, int viewportHeight);
- void drawRgb(int textureId, float[] texMatrix, int frameWidth, int frameHeight, int viewportX,
- int viewportY, int viewportWidth, int viewportHeight);
- void drawYuv(int[] yuvTextures, float[] texMatrix, int frameWidth, int frameHeight,
- int viewportX, int viewportY, int viewportWidth, int viewportHeight);
-
- /**
- * Release all GL resources. This needs to be done manually, otherwise resources may leak.
- */
- void release();
- }
-
+public class VideoFrameDrawer {
/**
* Draws a VideoFrame.TextureBuffer. Calls either drawer.drawOes or drawer.drawRgb
* depending on the type of the buffer. You can supply an additional render matrix. This is
* used multiplied together with the transformation matrix of the frame. (M = renderMatrix *
* transformationMatrix)
*/
- static void drawTexture(GlDrawer drawer, VideoFrame.TextureBuffer buffer,
- android.graphics.Matrix renderMatrix, int frameWidth, int frameHeight, int viewportX,
- int viewportY, int viewportWidth, int viewportHeight) {
- android.graphics.Matrix finalMatrix = new android.graphics.Matrix(buffer.getTransformMatrix());
+ static void drawTexture(RendererCommon.GlDrawer drawer, VideoFrame.TextureBuffer buffer,
+ Matrix renderMatrix, int frameWidth, int frameHeight, int viewportX, int viewportY,
+ int viewportWidth, int viewportHeight) {
+ Matrix finalMatrix = new Matrix(buffer.getTransformMatrix());
finalMatrix.preConcat(renderMatrix);
- float[] finalGlMatrix = convertMatrixFromAndroidGraphicsMatrix(finalMatrix);
+ float[] finalGlMatrix = RendererCommon.convertMatrixFromAndroidGraphicsMatrix(finalMatrix);
switch (buffer.getType()) {
case OES:
drawer.drawOes(buffer.getTextureId(), finalGlMatrix, frameWidth, frameHeight, viewportX,
@@ -83,7 +51,7 @@ public class RendererCommon {
* Helper class for uploading YUV bytebuffer frames to textures that handles stride > width. This
* class keeps an internal ByteBuffer to avoid unnecessary allocations for intermediate copies.
*/
- public static class YuvUploader {
+ private static class YuvUploader {
// Intermediate copy buffer for uploading yuv frames that are not packed, i.e. stride > width.
// TODO(magjed): Investigate when GL_UNPACK_ROW_LENGTH is available, or make a custom shader
// that handles stride and compare performance with intermediate copy.
@@ -143,6 +111,10 @@ public class RendererCommon {
return uploadYuvData(buffer.getWidth(), buffer.getHeight(), strides, planes);
}
+ public int[] getYuvTextures() {
+ return yuvTextures;
+ }
+
/**
* Releases cached resources. Uploader can still be used and the resources will be reallocated
* on first use.
@@ -156,240 +128,100 @@ public class RendererCommon {
}
}
- /**
- * Helper class for determining layout size based on layout requirements, scaling type, and video
- * aspect ratio.
- */
- public static class VideoLayoutMeasure {
- // The scaling type determines how the video will fill the allowed layout area in measure(). It
- // can be specified separately for the case when video has matched orientation with layout size
- // and when there is an orientation mismatch.
- private ScalingType scalingTypeMatchOrientation = ScalingType.SCALE_ASPECT_BALANCED;
- private ScalingType scalingTypeMismatchOrientation = ScalingType.SCALE_ASPECT_BALANCED;
+ private static int distance(float x0, float y0, float x1, float y1) {
+ return (int) Math.round(Math.hypot(x1 - x0, y1 - y0));
+ }
- public void setScalingType(ScalingType scalingType) {
- this.scalingTypeMatchOrientation = scalingType;
- this.scalingTypeMismatchOrientation = scalingType;
- }
+ // These points are used to calculate the size of the part of the frame we are rendering.
+ final static float[] srcPoints =
+ new float[] {0f /* x0 */, 0f /* y0 */, 1f /* x1 */, 0f /* y1 */, 0f /* x2 */, 1f /* y2 */};
+ private final float[] dstPoints = new float[6];
+ private final Point renderSize = new Point();
+ private int renderWidth;
+ private int renderHeight;
- public void setScalingType(
- ScalingType scalingTypeMatchOrientation, ScalingType scalingTypeMismatchOrientation) {
- this.scalingTypeMatchOrientation = scalingTypeMatchOrientation;
- this.scalingTypeMismatchOrientation = scalingTypeMismatchOrientation;
+ // Calculate the frame size after |renderMatrix| is applied. Stores the output in member variables
+ // |renderWidth| and |renderHeight| to avoid allocations since this function is called for every
+ // frame.
+ private void calculateTransformedRenderSize(
+ int frameWidth, int frameHeight, Matrix renderMatrix) {
+ if (renderMatrix == null) {
+ renderWidth = frameWidth;
+ renderHeight = frameHeight;
+ return;
}
+ // Transform the texture coordinates (in the range [0, 1]) according to |renderMatrix|.
+ renderMatrix.mapPoints(dstPoints, srcPoints);
- public Point measure(int widthSpec, int heightSpec, int frameWidth, int frameHeight) {
- // Calculate max allowed layout size.
- final int maxWidth = View.getDefaultSize(Integer.MAX_VALUE, widthSpec);
- final int maxHeight = View.getDefaultSize(Integer.MAX_VALUE, heightSpec);
- if (frameWidth == 0 || frameHeight == 0 || maxWidth == 0 || maxHeight == 0) {
- return new Point(maxWidth, maxHeight);
- }
- // Calculate desired display size based on scaling type, video aspect ratio,
- // and maximum layout size.
- final float frameAspect = frameWidth / (float) frameHeight;
- final float displayAspect = maxWidth / (float) maxHeight;
- final ScalingType scalingType = (frameAspect > 1.0f) == (displayAspect > 1.0f)
- ? scalingTypeMatchOrientation
- : scalingTypeMismatchOrientation;
- final Point layoutSize = getDisplaySize(scalingType, frameAspect, maxWidth, maxHeight);
-
- // If the measure specification is forcing a specific size - yield.
- if (View.MeasureSpec.getMode(widthSpec) == View.MeasureSpec.EXACTLY) {
- layoutSize.x = maxWidth;
- }
- if (View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY) {
- layoutSize.y = maxHeight;
- }
- return layoutSize;
+ // Multiply with the width and height to get the positions in terms of pixels.
+ for (int i = 0; i < 3; ++i) {
+ dstPoints[i * 2 + 0] *= frameWidth;
+ dstPoints[i * 2 + 1] *= frameHeight;
}
- }
- // Types of video scaling:
- // SCALE_ASPECT_FIT - video frame is scaled to fit the size of the view by
- // maintaining the aspect ratio (black borders may be displayed).
- // SCALE_ASPECT_FILL - video frame is scaled to fill the size of the view by
- // maintaining the aspect ratio. Some portion of the video frame may be
- // clipped.
- // SCALE_ASPECT_BALANCED - Compromise between FIT and FILL. Video frame will fill as much as
- // possible of the view while maintaining aspect ratio, under the constraint that at least
- // |BALANCED_VISIBLE_FRACTION| of the frame content will be shown.
- public static enum ScalingType { SCALE_ASPECT_FIT, SCALE_ASPECT_FILL, SCALE_ASPECT_BALANCED }
- // The minimum fraction of the frame content that will be shown for |SCALE_ASPECT_BALANCED|.
- // This limits excessive cropping when adjusting display size.
- private static float BALANCED_VISIBLE_FRACTION = 0.5625f;
- // clang-format off
- public static final float[] identityMatrix() {
- return new float[] {
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 1};
- }
- // Matrix with transform y' = 1 - y.
- public static final float[] verticalFlipMatrix() {
- return new float[] {
- 1, 0, 0, 0,
- 0, -1, 0, 0,
- 0, 0, 1, 0,
- 0, 1, 0, 1};
+ // Get the length of the sides of the transformed rectangle in terms of pixels.
+ renderWidth = distance(dstPoints[0], dstPoints[1], dstPoints[2], dstPoints[3]);
+ renderHeight = distance(dstPoints[0], dstPoints[1], dstPoints[4], dstPoints[5]);
}
- // Matrix with transform x' = 1 - x.
- public static final float[] horizontalFlipMatrix() {
- return new float[] {
- -1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 1, 0, 0, 1};
- }
- // clang-format on
+ private final YuvUploader yuvUploader = new YuvUploader();
+ // This variable will only be used for checking reference equality and is used for caching I420
+ // textures.
+ private VideoFrame lastI420Frame;
+ private final Matrix renderMatrix = new Matrix();
- /**
- * Returns texture matrix that will have the effect of rotating the frame |rotationDegree|
- * clockwise when rendered.
- */
- public static float[] rotateTextureMatrix(float[] textureMatrix, float rotationDegree) {
- final float[] rotationMatrix = new float[16];
- Matrix.setRotateM(rotationMatrix, 0, rotationDegree, 0, 0, 1);
- adjustOrigin(rotationMatrix);
- return multiplyMatrices(textureMatrix, rotationMatrix);
+ public void drawFrame(VideoFrame frame, RendererCommon.GlDrawer drawer) {
+ drawFrame(frame, drawer, null /* additionalRenderMatrix */);
}
- /**
- * Returns new matrix with the result of a * b.
- */
- public static float[] multiplyMatrices(float[] a, float[] b) {
- final float[] resultMatrix = new float[16];
- Matrix.multiplyMM(resultMatrix, 0, a, 0, b, 0);
- return resultMatrix;
+ public void drawFrame(
+ VideoFrame frame, RendererCommon.GlDrawer drawer, Matrix additionalRenderMatrix) {
+ drawFrame(frame, drawer, additionalRenderMatrix, 0 /* viewportX */, 0 /* viewportY */,
+ frame.getRotatedWidth(), frame.getRotatedHeight());
}
- /**
- * Returns layout transformation matrix that applies an optional mirror effect and compensates
- * for video vs display aspect ratio.
- */
- public static float[] getLayoutMatrix(
- boolean mirror, float videoAspectRatio, float displayAspectRatio) {
- float scaleX = 1;
- float scaleY = 1;
- // Scale X or Y dimension so that video and display size have same aspect ratio.
- if (displayAspectRatio > videoAspectRatio) {
- scaleY = videoAspectRatio / displayAspectRatio;
- } else {
- scaleX = displayAspectRatio / videoAspectRatio;
- }
- // Apply optional horizontal flip.
- if (mirror) {
- scaleX *= -1;
- }
- final float matrix[] = new float[16];
- Matrix.setIdentityM(matrix, 0);
- Matrix.scaleM(matrix, 0, scaleX, scaleY, 1);
- adjustOrigin(matrix);
- return matrix;
- }
+ public void drawFrame(VideoFrame frame, RendererCommon.GlDrawer drawer,
+ Matrix additionalRenderMatrix, int viewportX, int viewportY, int viewportWidth,
+ int viewportHeight) {
+ final int width = frame.getRotatedWidth();
+ final int height = frame.getRotatedHeight();
- /** Converts a float[16] matrix array to android.graphics.Matrix. */
- public static android.graphics.Matrix convertMatrixToAndroidGraphicsMatrix(float[] matrix4x4) {
- // clang-format off
- float[] values = {
- matrix4x4[0 * 4 + 0], matrix4x4[1 * 4 + 0], matrix4x4[3 * 4 + 0],
- matrix4x4[0 * 4 + 1], matrix4x4[1 * 4 + 1], matrix4x4[3 * 4 + 1],
- matrix4x4[0 * 4 + 3], matrix4x4[1 * 4 + 3], matrix4x4[3 * 4 + 3],
- };
- // clang-format on
+ calculateTransformedRenderSize(width, height, additionalRenderMatrix);
- android.graphics.Matrix matrix = new android.graphics.Matrix();
- matrix.setValues(values);
- return matrix;
- }
-
- /** Converts android.graphics.Matrix to a float[16] matrix array. */
- public static float[] convertMatrixFromAndroidGraphicsMatrix(android.graphics.Matrix matrix) {
- float[] values = new float[9];
- matrix.getValues(values);
-
- // The android.graphics.Matrix looks like this:
- // [x1 y1 w1]
- // [x2 y2 w2]
- // [x3 y3 w3]
- // We want to contruct a matrix that looks like this:
- // [x1 y1 0 w1]
- // [x2 y2 0 w2]
- // [ 0 0 1 0]
- // [x3 y3 0 w3]
- // Since it is stored in column-major order, it looks like this:
- // [x1 x2 0 x3
- // y1 y2 0 y3
- // 0 0 1 0
- // w1 w2 0 w3]
- // clang-format off
- float[] matrix4x4 = {
- values[0 * 3 + 0], values[1 * 3 + 0], 0, values[2 * 3 + 0],
- values[0 * 3 + 1], values[1 * 3 + 1], 0, values[2 * 3 + 1],
- 0, 0, 1, 0,
- values[0 * 3 + 2], values[1 * 3 + 2], 0, values[2 * 3 + 2],
- };
- // clang-format on
- return matrix4x4;
- }
-
- /**
- * Calculate display size based on scaling type, video aspect ratio, and maximum display size.
- */
- public static Point getDisplaySize(
- ScalingType scalingType, float videoAspectRatio, int maxDisplayWidth, int maxDisplayHeight) {
- return getDisplaySize(convertScalingTypeToVisibleFraction(scalingType), videoAspectRatio,
- maxDisplayWidth, maxDisplayHeight);
- }
+ final boolean isTextureFrame = frame.getBuffer() instanceof VideoFrame.TextureBuffer;
+ renderMatrix.reset();
+ renderMatrix.preTranslate(0.5f, 0.5f);
+ if (!isTextureFrame) {
+ renderMatrix.preScale(1f, -1f); // I420-frames are upside down
+ }
+ renderMatrix.preRotate(frame.getRotation());
+ renderMatrix.preTranslate(-0.5f, -0.5f);
+ if (additionalRenderMatrix != null) {
+ renderMatrix.preConcat(additionalRenderMatrix);
+ }
- /**
- * Move |matrix| transformation origin to (0.5, 0.5). This is the origin for texture coordinates
- * that are in the range 0 to 1.
- */
- private static void adjustOrigin(float[] matrix) {
- // Note that OpenGL is using column-major order.
- // Pre translate with -0.5 to move coordinates to range [-0.5, 0.5].
- matrix[12] -= 0.5f * (matrix[0] + matrix[4]);
- matrix[13] -= 0.5f * (matrix[1] + matrix[5]);
- // Post translate with 0.5 to move coordinates to range [0, 1].
- matrix[12] += 0.5f;
- matrix[13] += 0.5f;
- }
+ if (isTextureFrame) {
+ lastI420Frame = null;
+ drawTexture(drawer, (VideoFrame.TextureBuffer) frame.getBuffer(), renderMatrix, renderWidth,
+ renderHeight, viewportX, viewportY, viewportWidth, viewportHeight);
+ } else {
+ // Only upload the I420 data to textures once per frame, if we are called multiple times
+ // with the same frame.
+ if (frame != lastI420Frame) {
+ lastI420Frame = frame;
+ final VideoFrame.I420Buffer i420Buffer = frame.getBuffer().toI420();
+ yuvUploader.uploadFromBuffer(i420Buffer);
+ i420Buffer.release();
+ }
- /**
- * Each scaling type has a one-to-one correspondence to a numeric minimum fraction of the video
- * that must remain visible.
- */
- private static float convertScalingTypeToVisibleFraction(ScalingType scalingType) {
- switch (scalingType) {
- case SCALE_ASPECT_FIT:
- return 1.0f;
- case SCALE_ASPECT_FILL:
- return 0.0f;
- case SCALE_ASPECT_BALANCED:
- return BALANCED_VISIBLE_FRACTION;
- default:
- throw new IllegalArgumentException();
+ drawer.drawYuv(yuvUploader.getYuvTextures(),
+ RendererCommon.convertMatrixFromAndroidGraphicsMatrix(renderMatrix), renderWidth,
+ renderHeight, viewportX, viewportY, viewportWidth, viewportHeight);
}
}
- /**
- * Calculate display size based on minimum fraction of the video that must remain visible,
- * video aspect ratio, and maximum display size.
- */
- private static Point getDisplaySize(
- float minVisibleFraction, float videoAspectRatio, int maxDisplayWidth, int maxDisplayHeight) {
- // If there is no constraint on the amount of cropping, fill the allowed display area.
- if (minVisibleFraction == 0 || videoAspectRatio == 0) {
- return new Point(maxDisplayWidth, maxDisplayHeight);
- }
- // Each dimension is constrained on max display size and how much we are allowed to crop.
- final int width = Math.min(
- maxDisplayWidth, Math.round(maxDisplayHeight / minVisibleFraction * videoAspectRatio));
- final int height = Math.min(
- maxDisplayHeight, Math.round(maxDisplayWidth / minVisibleFraction / videoAspectRatio));
- return new Point(width, height);
+ public void release() {
+ yuvUploader.release();
+ lastI420Frame = null;
}
}
« no previous file with comments | « webrtc/sdk/android/api/org/webrtc/RendererCommon.java ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698