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.graphics.Matrix;
14 import android.os.Handler;
15 import android.support.annotation.Nullable;
16 
17 /**
18  * Android texture buffer that glues together the necessary information together with a generic
19  * release callback. ToI420() is implemented by providing a Handler and a YuvConverter.
20  */
21 public class TextureBufferImpl implements VideoFrame.TextureBuffer {
22   interface RefCountMonitor {
onRetain(TextureBufferImpl textureBuffer)23     void onRetain(TextureBufferImpl textureBuffer);
onRelease(TextureBufferImpl textureBuffer)24     void onRelease(TextureBufferImpl textureBuffer);
onDestroy(TextureBufferImpl textureBuffer)25     void onDestroy(TextureBufferImpl textureBuffer);
26   }
27 
28   // This is the full resolution the texture has in memory after applying the transformation matrix
29   // that might include cropping. This resolution is useful to know when sampling the texture to
30   // avoid downscaling artifacts.
31   private final int unscaledWidth;
32   private final int unscaledHeight;
33   // This is the resolution that has been applied after cropAndScale().
34   private final int width;
35   private final int height;
36   private final Type type;
37   private final int id;
38   private final Matrix transformMatrix;
39   private final Handler toI420Handler;
40   private final YuvConverter yuvConverter;
41   private final RefCountDelegate refCountDelegate;
42   private final RefCountMonitor refCountMonitor;
43 
TextureBufferImpl(int width, int height, Type type, int id, Matrix transformMatrix, Handler toI420Handler, YuvConverter yuvConverter, @Nullable Runnable releaseCallback)44   public TextureBufferImpl(int width, int height, Type type, int id, Matrix transformMatrix,
45       Handler toI420Handler, YuvConverter yuvConverter, @Nullable Runnable releaseCallback) {
46     this(width, height, width, height, type, id, transformMatrix, toI420Handler, yuvConverter,
47         new RefCountMonitor() {
48           @Override
49           public void onRetain(TextureBufferImpl textureBuffer) {}
50 
51           @Override
52           public void onRelease(TextureBufferImpl textureBuffer) {}
53 
54           @Override
55           public void onDestroy(TextureBufferImpl textureBuffer) {
56             if (releaseCallback != null) {
57               releaseCallback.run();
58             }
59           }
60         });
61   }
62 
TextureBufferImpl(int width, int height, Type type, int id, Matrix transformMatrix, Handler toI420Handler, YuvConverter yuvConverter, RefCountMonitor refCountMonitor)63   TextureBufferImpl(int width, int height, Type type, int id, Matrix transformMatrix,
64       Handler toI420Handler, YuvConverter yuvConverter, RefCountMonitor refCountMonitor) {
65     this(width, height, width, height, type, id, transformMatrix, toI420Handler, yuvConverter,
66         refCountMonitor);
67   }
68 
TextureBufferImpl(int unscaledWidth, int unscaledHeight, int width, int height, Type type, int id, Matrix transformMatrix, Handler toI420Handler, YuvConverter yuvConverter, RefCountMonitor refCountMonitor)69   private TextureBufferImpl(int unscaledWidth, int unscaledHeight, int width, int height, Type type,
70       int id, Matrix transformMatrix, Handler toI420Handler, YuvConverter yuvConverter,
71       RefCountMonitor refCountMonitor) {
72     this.unscaledWidth = unscaledWidth;
73     this.unscaledHeight = unscaledHeight;
74     this.width = width;
75     this.height = height;
76     this.type = type;
77     this.id = id;
78     this.transformMatrix = transformMatrix;
79     this.toI420Handler = toI420Handler;
80     this.yuvConverter = yuvConverter;
81     this.refCountDelegate = new RefCountDelegate(() -> refCountMonitor.onDestroy(this));
82     this.refCountMonitor = refCountMonitor;
83   }
84 
85   @Override
getType()86   public VideoFrame.TextureBuffer.Type getType() {
87     return type;
88   }
89 
90   @Override
getTextureId()91   public int getTextureId() {
92     return id;
93   }
94 
95   @Override
getTransformMatrix()96   public Matrix getTransformMatrix() {
97     return transformMatrix;
98   }
99 
100   @Override
getWidth()101   public int getWidth() {
102     return width;
103   }
104 
105   @Override
getHeight()106   public int getHeight() {
107     return height;
108   }
109 
110   @Override
toI420()111   public VideoFrame.I420Buffer toI420() {
112     return ThreadUtils.invokeAtFrontUninterruptibly(
113         toI420Handler, () -> yuvConverter.convert(this));
114   }
115 
116   @Override
retain()117   public void retain() {
118     refCountMonitor.onRetain(this);
119     refCountDelegate.retain();
120   }
121 
122   @Override
release()123   public void release() {
124     refCountMonitor.onRelease(this);
125     refCountDelegate.release();
126   }
127 
128   @Override
cropAndScale( int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight)129   public VideoFrame.Buffer cropAndScale(
130       int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
131     final Matrix cropAndScaleMatrix = new Matrix();
132     // In WebRTC, Y=0 is the top row, while in OpenGL Y=0 is the bottom row. This means that the Y
133     // direction is effectively reversed.
134     final int cropYFromBottom = height - (cropY + cropHeight);
135     cropAndScaleMatrix.preTranslate(cropX / (float) width, cropYFromBottom / (float) height);
136     cropAndScaleMatrix.preScale(cropWidth / (float) width, cropHeight / (float) height);
137 
138     return applyTransformMatrix(cropAndScaleMatrix,
139         (int) Math.round(unscaledWidth * cropWidth / (float) width),
140         (int) Math.round(unscaledHeight * cropHeight / (float) height), scaleWidth, scaleHeight);
141   }
142 
143   /**
144    * Returns the width of the texture in memory. This should only be used for downscaling, and you
145    * should still respect the width from getWidth().
146    */
getUnscaledWidth()147   public int getUnscaledWidth() {
148     return unscaledWidth;
149   }
150 
151   /**
152    * Returns the height of the texture in memory. This should only be used for downscaling, and you
153    * should still respect the height from getHeight().
154    */
getUnscaledHeight()155   public int getUnscaledHeight() {
156     return unscaledHeight;
157   }
158 
getToI420Handler()159   public Handler getToI420Handler() {
160     return toI420Handler;
161   }
162 
getYuvConverter()163   public YuvConverter getYuvConverter() {
164     return yuvConverter;
165   }
166 
167   /**
168    * Create a new TextureBufferImpl with an applied transform matrix and a new size. The
169    * existing buffer is unchanged. The given transform matrix is applied first when texture
170    * coordinates are still in the unmodified [0, 1] range.
171    */
applyTransformMatrix( Matrix transformMatrix, int newWidth, int newHeight)172   public TextureBufferImpl applyTransformMatrix(
173       Matrix transformMatrix, int newWidth, int newHeight) {
174     return applyTransformMatrix(transformMatrix, /* unscaledWidth= */ newWidth,
175         /* unscaledHeight= */ newHeight, /* scaledWidth= */ newWidth,
176         /* scaledHeight= */ newHeight);
177   }
178 
applyTransformMatrix(Matrix transformMatrix, int unscaledWidth, int unscaledHeight, int scaledWidth, int scaledHeight)179   private TextureBufferImpl applyTransformMatrix(Matrix transformMatrix, int unscaledWidth,
180       int unscaledHeight, int scaledWidth, int scaledHeight) {
181     final Matrix newMatrix = new Matrix(this.transformMatrix);
182     newMatrix.preConcat(transformMatrix);
183     retain();
184     return new TextureBufferImpl(unscaledWidth, unscaledHeight, scaledWidth, scaledHeight, type, id,
185         newMatrix, toI420Handler, yuvConverter, new RefCountMonitor() {
186           @Override
187           public void onRetain(TextureBufferImpl textureBuffer) {
188             refCountMonitor.onRetain(TextureBufferImpl.this);
189           }
190 
191           @Override
192           public void onRelease(TextureBufferImpl textureBuffer) {
193             refCountMonitor.onRelease(TextureBufferImpl.this);
194           }
195 
196           @Override
197           public void onDestroy(TextureBufferImpl textureBuffer) {
198             release();
199           }
200         });
201   }
202 }
203