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