| Index: talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java | 
| diff --git a/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java b/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java | 
| index 021b8228a8921868f813a70a968dabb342ea4b93..0c910f14c7b4a51456d4ed98cd8ec53b2e3fa570 100644 | 
| --- a/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java | 
| +++ b/talk/app/webrtc/java/android/org/webrtc/VideoRendererGui.java | 
| @@ -42,6 +42,7 @@ | 
| import android.opengl.EGLContext; | 
| import android.opengl.GLES20; | 
| import android.opengl.GLSurfaceView; | 
| +import android.opengl.Matrix; | 
| import android.util.Log; | 
|  | 
| import org.webrtc.VideoRenderer.I420Frame; | 
| @@ -69,6 +70,20 @@ | 
| // List of yuv renderers. | 
| private ArrayList<YuvImageRenderer> yuvImageRenderers; | 
| private GlRectDrawer drawer; | 
| +  // 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.56f; | 
| +  // 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 } | 
| private static final int EGL14_SDK_VERSION = | 
| android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; | 
| // Current SDK version. | 
| @@ -108,7 +123,7 @@ | 
| // Type of video frame used for recent frame rendering. | 
| private static enum RendererType { RENDERER_YUV, RENDERER_TEXTURE }; | 
| private RendererType rendererType; | 
| -    private RendererCommon.ScalingType scalingType; | 
| +    private ScalingType scalingType; | 
| private boolean mirror; | 
| // Flag if renderFrame() was ever called. | 
| boolean seenFrame; | 
| @@ -151,7 +166,7 @@ | 
| private YuvImageRenderer( | 
| GLSurfaceView surface, int id, | 
| int x, int y, int width, int height, | 
| -        RendererCommon.ScalingType scalingType, boolean mirror) { | 
| +        ScalingType scalingType, boolean mirror) { | 
| Log.d(TAG, "YuvImageRenderer.Create id: " + id); | 
| this.surface = surface; | 
| this.id = id; | 
| @@ -182,6 +197,33 @@ | 
| GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE); | 
| } | 
| GlUtil.checkNoGLES2Error("y/u/v glGenTextures"); | 
| +    } | 
| + | 
| +    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(); | 
| +      } | 
| +    } | 
| + | 
| +    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) { | 
| +        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, | 
| +          (int) (maxDisplayHeight / minVisibleFraction * videoAspectRatio)); | 
| +      final int height = Math.min(maxDisplayHeight, | 
| +          (int) (maxDisplayWidth / minVisibleFraction / videoAspectRatio)); | 
| +      return new Point(width, height); | 
| } | 
|  | 
| private void checkAdjustTextureCoords() { | 
| @@ -203,14 +245,38 @@ | 
| ? (float) videoWidth / videoHeight | 
| : (float) videoHeight / videoWidth; | 
| // Adjust display size based on |scalingType|. | 
| -        final Point displaySize = RendererCommon.getDisplaySize(scalingType, | 
| -            videoAspectRatio, displayLayout.width(), displayLayout.height()); | 
| +        final float minVisibleFraction = convertScalingTypeToVisibleFraction(scalingType); | 
| +        final Point displaySize = getDisplaySize(minVisibleFraction, videoAspectRatio, | 
| +            displayLayout.width(), displayLayout.height()); | 
| displayLayout.inset((displayLayout.width() - displaySize.x) / 2, | 
| (displayLayout.height() - displaySize.y) / 2); | 
| Log.d(TAG, "  Adjusted display size: " + displayLayout.width() + " x " | 
| + displayLayout.height()); | 
| -        RendererCommon.getTextureMatrix(texMatrix, rotationDegree, mirror, videoAspectRatio, | 
| -            (float) displayLayout.width() / displayLayout.height()); | 
| +        // The matrix stack is using post-multiplication, which means that matrix operations: | 
| +        // A; B; C; will end up as A * B * C. When you apply this to a vertex, it will result in: | 
| +        // v' = A * B * C * v, i.e. the last matrix operation is the first thing that affects the | 
| +        // vertex. This is the opposite of what you might expect. | 
| +        Matrix.setIdentityM(texMatrix, 0); | 
| +        // Move coordinates back to [0,1]x[0,1]. | 
| +        Matrix.translateM(texMatrix, 0, 0.5f, 0.5f, 0.0f); | 
| +        // Rotate frame clockwise in the XY-plane (around the Z-axis). | 
| +        Matrix.rotateM(texMatrix, 0, -rotationDegree, 0, 0, 1); | 
| +        // Scale one dimension until video and display size have same aspect ratio. | 
| +        final float displayAspectRatio = (float) displayLayout.width() / displayLayout.height(); | 
| +        if (displayAspectRatio > videoAspectRatio) { | 
| +            Matrix.scaleM(texMatrix, 0, 1, videoAspectRatio / displayAspectRatio, 1); | 
| +        } else { | 
| +            Matrix.scaleM(texMatrix, 0, displayAspectRatio / videoAspectRatio, 1, 1); | 
| +        } | 
| +        // TODO(magjed): We currently ignore the texture transform matrix from the SurfaceTexture. | 
| +        // It contains a vertical flip that is hardcoded here instead. | 
| +        Matrix.scaleM(texMatrix, 0, 1, -1, 1); | 
| +        // Apply optional horizontal flip. | 
| +        if (mirror) { | 
| +          Matrix.scaleM(texMatrix, 0, -1, 1, 1); | 
| +        } | 
| +        // Center coordinates around origin. | 
| +        Matrix.translateM(texMatrix, 0, -0.5f, -0.5f, 0.0f); | 
| updateTextureProperties = false; | 
| Log.d(TAG, "  AdjustTextureCoords done"); | 
| } | 
| @@ -309,7 +375,7 @@ | 
| } | 
|  | 
| public void setPosition(int x, int y, int width, int height, | 
| -        RendererCommon.ScalingType scalingType, boolean mirror) { | 
| +        ScalingType scalingType, boolean mirror) { | 
| final Rect layoutInPercentage = | 
| new Rect(x, y, Math.min(100, x + width), Math.min(100, y + height)); | 
| synchronized(updateTextureLock) { | 
| @@ -349,9 +415,9 @@ | 
| frameToRenderQueue.poll(); | 
| // Re-allocate / allocate the frame. | 
| yuvFrameToRender = new I420Frame(videoWidth, videoHeight, rotationDegree, | 
| -                                         strides, null, 0); | 
| +                                         strides, null); | 
| textureFrameToRender = new I420Frame(videoWidth, videoHeight, rotationDegree, | 
| -                                             null, -1, 0); | 
| +                                             null, -1); | 
| updateTextureProperties = true; | 
| Log.d(TAG, "  YuvImageRenderer.setSize done."); | 
| } | 
| @@ -365,7 +431,6 @@ | 
| // Skip rendering of this frame if setSize() was not called. | 
| if (yuvFrameToRender == null || textureFrameToRender == null) { | 
| framesDropped++; | 
| -        VideoRenderer.renderFrameDone(frame); | 
| return; | 
| } | 
| // Check input frame parameters. | 
| @@ -375,7 +440,6 @@ | 
| frame.yuvStrides[2] < frame.width / 2) { | 
| Log.e(TAG, "Incorrect strides " + frame.yuvStrides[0] + ", " + | 
| frame.yuvStrides[1] + ", " + frame.yuvStrides[2]); | 
| -          VideoRenderer.renderFrameDone(frame); | 
| return; | 
| } | 
| // Check incoming frame dimensions. | 
| @@ -389,7 +453,6 @@ | 
| if (frameToRenderQueue.size() > 0) { | 
| // Skip rendering of this frame if previous frame was not rendered yet. | 
| framesDropped++; | 
| -        VideoRenderer.renderFrameDone(frame); | 
| return; | 
| } | 
|  | 
| @@ -405,7 +468,6 @@ | 
| } | 
| copyTimeNs += (System.nanoTime() - now); | 
| seenFrame = true; | 
| -      VideoRenderer.renderFrameDone(frame); | 
|  | 
| // Request rendering. | 
| surface.requestRender(); | 
| @@ -435,7 +497,7 @@ | 
| * (width, height). All parameters are in percentage of screen resolution. | 
| */ | 
| public static VideoRenderer createGui(int x, int y, int width, int height, | 
| -      RendererCommon.ScalingType scalingType, boolean mirror) throws Exception { | 
| +      ScalingType scalingType, boolean mirror) throws Exception { | 
| YuvImageRenderer javaGuiRenderer = create( | 
| x, y, width, height, scalingType, mirror); | 
| return new VideoRenderer(javaGuiRenderer); | 
| @@ -443,7 +505,7 @@ | 
|  | 
| public static VideoRenderer.Callbacks createGuiRenderer( | 
| int x, int y, int width, int height, | 
| -      RendererCommon.ScalingType scalingType, boolean mirror) { | 
| +      ScalingType scalingType, boolean mirror) { | 
| return create(x, y, width, height, scalingType, mirror); | 
| } | 
|  | 
| @@ -453,7 +515,7 @@ | 
| * screen resolution. | 
| */ | 
| public static YuvImageRenderer create(int x, int y, int width, int height, | 
| -      RendererCommon.ScalingType scalingType, boolean mirror) { | 
| +      ScalingType scalingType, boolean mirror) { | 
| // Check display region parameters. | 
| if (x < 0 || x > 100 || y < 0 || y > 100 || | 
| width < 0 || width > 100 || height < 0 || height > 100 || | 
| @@ -497,7 +559,7 @@ | 
|  | 
| public static void update( | 
| VideoRenderer.Callbacks renderer, | 
| -      int x, int y, int width, int height, RendererCommon.ScalingType scalingType, boolean mirror) { | 
| +      int x, int y, int width, int height, ScalingType scalingType, boolean mirror) { | 
| Log.d(TAG, "VideoRendererGui.update"); | 
| if (instance == null) { | 
| throw new RuntimeException( | 
|  |