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