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.opengl.GLES11Ext; 15 import android.opengl.GLES20; 16 import androidx.annotation.Nullable; 17 import java.nio.ByteBuffer; 18 19 /** 20 * Java version of webrtc::VideoFrame and webrtc::VideoFrameBuffer. A difference from the C++ 21 * version is that no explicit tag is used, and clients are expected to use 'instanceof' to find the 22 * right subclass of the buffer. This allows clients to create custom VideoFrame.Buffer in 23 * arbitrary format in their custom VideoSources, and then cast it back to the correct subclass in 24 * their custom VideoSinks. All implementations must also implement the toI420() function, 25 * converting from the underlying representation if necessary. I420 is the most widely accepted 26 * format and serves as a fallback for video sinks that can only handle I420, e.g. the internal 27 * WebRTC software encoders. 28 */ 29 public class VideoFrame implements RefCounted { 30 /** 31 * Implements image storage medium. Might be for example an OpenGL texture or a memory region 32 * containing I420-data. 33 * 34 * <p>Reference counting is needed since a video buffer can be shared between multiple VideoSinks, 35 * and the buffer needs to be returned to the VideoSource as soon as all references are gone. 36 */ 37 public interface Buffer extends RefCounted { 38 /** 39 * Representation of the underlying buffer. Currently, only NATIVE and I420 are supported. 40 */ 41 @CalledByNative("Buffer") 42 @VideoFrameBufferType getBufferType()43 default int getBufferType() { 44 return VideoFrameBufferType.NATIVE; 45 } 46 47 /** 48 * Resolution of the buffer in pixels. 49 */ getWidth()50 @CalledByNative("Buffer") int getWidth(); getHeight()51 @CalledByNative("Buffer") int getHeight(); 52 53 /** 54 * Returns a memory-backed frame in I420 format. If the pixel data is in another format, a 55 * conversion will take place. All implementations must provide a fallback to I420 for 56 * compatibility with e.g. the internal WebRTC software encoders. 57 * 58 * <p> Conversion may fail, for example if reading the pixel data from a texture fails. If the 59 * conversion fails, null is returned. 60 */ toI420()61 @Nullable @CalledByNative("Buffer") I420Buffer toI420(); 62 retain()63 @Override @CalledByNative("Buffer") void retain(); release()64 @Override @CalledByNative("Buffer") void release(); 65 66 /** 67 * Crops a region defined by `cropx`, `cropY`, `cropWidth` and `cropHeight`. Scales it to size 68 * `scaleWidth` x `scaleHeight`. 69 */ 70 @CalledByNative("Buffer") cropAndScale( int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight)71 Buffer cropAndScale( 72 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight); 73 } 74 75 /** 76 * Interface for I420 buffers. 77 */ 78 public interface I420Buffer extends Buffer { 79 @Override getBufferType()80 default int getBufferType() { 81 return VideoFrameBufferType.I420; 82 } 83 84 /** 85 * Returns a direct ByteBuffer containing Y-plane data. The buffer capacity is at least 86 * getStrideY() * getHeight() bytes. The position of the returned buffer is ignored and must 87 * be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so 88 * implementations must return a new ByteBuffer or slice for each call. 89 */ getDataY()90 @CalledByNative("I420Buffer") ByteBuffer getDataY(); 91 /** 92 * Returns a direct ByteBuffer containing U-plane data. The buffer capacity is at least 93 * getStrideU() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored 94 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so 95 * implementations must return a new ByteBuffer or slice for each call. 96 */ getDataU()97 @CalledByNative("I420Buffer") ByteBuffer getDataU(); 98 /** 99 * Returns a direct ByteBuffer containing V-plane data. The buffer capacity is at least 100 * getStrideV() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored 101 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so 102 * implementations must return a new ByteBuffer or slice for each call. 103 */ getDataV()104 @CalledByNative("I420Buffer") ByteBuffer getDataV(); 105 getStrideY()106 @CalledByNative("I420Buffer") int getStrideY(); getStrideU()107 @CalledByNative("I420Buffer") int getStrideU(); getStrideV()108 @CalledByNative("I420Buffer") int getStrideV(); 109 } 110 111 /** 112 * Interface for buffers that are stored as a single texture, either in OES or RGB format. 113 */ 114 public interface TextureBuffer extends Buffer { 115 enum Type { 116 OES(GLES11Ext.GL_TEXTURE_EXTERNAL_OES), 117 RGB(GLES20.GL_TEXTURE_2D); 118 119 private final int glTarget; 120 Type(final int glTarget)121 private Type(final int glTarget) { 122 this.glTarget = glTarget; 123 } 124 getGlTarget()125 public int getGlTarget() { 126 return glTarget; 127 } 128 } 129 getType()130 Type getType(); getTextureId()131 int getTextureId(); 132 133 /** 134 * Retrieve the transform matrix associated with the frame. This transform matrix maps 2D 135 * homogeneous coordinates of the form (s, t, 1) with s and t in the inclusive range [0, 1] to 136 * the coordinate that should be used to sample that location from the buffer. 137 */ getTransformMatrix()138 Matrix getTransformMatrix(); 139 140 /** 141 * Create a new TextureBufferImpl with an applied transform matrix and a new size. The existing 142 * buffer is unchanged. The given transform matrix is applied first when texture coordinates are 143 * still in the unmodified [0, 1] range. 144 */ applyTransformMatrix( Matrix transformMatrix, int newWidth, int newHeight)145 default TextureBuffer applyTransformMatrix( 146 Matrix transformMatrix, int newWidth, int newHeight) { 147 throw new UnsupportedOperationException("Not implemented"); 148 } 149 } 150 151 private final Buffer buffer; 152 private final int rotation; 153 private final long timestampNs; 154 155 /** 156 * Constructs a new VideoFrame backed by the given {@code buffer}. 157 * 158 * @note Ownership of the buffer object is tranferred to the new VideoFrame. 159 */ 160 @CalledByNative VideoFrame(Buffer buffer, int rotation, long timestampNs)161 public VideoFrame(Buffer buffer, int rotation, long timestampNs) { 162 if (buffer == null) { 163 throw new IllegalArgumentException("buffer not allowed to be null"); 164 } 165 if (rotation % 90 != 0) { 166 throw new IllegalArgumentException("rotation must be a multiple of 90"); 167 } 168 this.buffer = buffer; 169 this.rotation = rotation; 170 this.timestampNs = timestampNs; 171 } 172 173 @CalledByNative getBuffer()174 public Buffer getBuffer() { 175 return buffer; 176 } 177 178 /** 179 * Rotation of the frame in degrees. 180 */ 181 @CalledByNative getRotation()182 public int getRotation() { 183 return rotation; 184 } 185 186 /** 187 * Timestamp of the frame in nano seconds. 188 */ 189 @CalledByNative getTimestampNs()190 public long getTimestampNs() { 191 return timestampNs; 192 } 193 getRotatedWidth()194 public int getRotatedWidth() { 195 if (rotation % 180 == 0) { 196 return buffer.getWidth(); 197 } 198 return buffer.getHeight(); 199 } 200 getRotatedHeight()201 public int getRotatedHeight() { 202 if (rotation % 180 == 0) { 203 return buffer.getHeight(); 204 } 205 return buffer.getWidth(); 206 } 207 208 @Override retain()209 public void retain() { 210 buffer.retain(); 211 } 212 213 @Override 214 @CalledByNative release()215 public void release() { 216 buffer.release(); 217 } 218 } 219