blob: 30304a3f5101750f0c0c5d1d89ef70c99a1e9fdd [file] [log] [blame]
magjed55220212017-06-02 02:45:56 -07001/*
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
11package org.webrtc;
12
13import android.graphics.Matrix;
Sami Kalliomäkicb98b112017-10-16 11:20:26 +020014import android.opengl.GLES11Ext;
15import android.opengl.GLES20;
magjed55220212017-06-02 02:45:56 -070016import java.nio.ByteBuffer;
Sami Kalliomäkie7592d82018-03-22 13:32:44 +010017import javax.annotation.Nullable;
magjed55220212017-06-02 02:45:56 -070018
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 */
Magnus Jedvert84d8ae52017-12-20 15:12:10 +010029@JNINamespace("webrtc::jni")
magjed55220212017-06-02 02:45:56 -070030public class VideoFrame {
31 public interface Buffer {
32 /**
33 * Resolution of the buffer in pixels.
34 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010035 @CalledByNative("Buffer") int getWidth();
36 @CalledByNative("Buffer") int getHeight();
magjed55220212017-06-02 02:45:56 -070037
38 /**
39 * Returns a memory-backed frame in I420 format. If the pixel data is in another format, a
40 * conversion will take place. All implementations must provide a fallback to I420 for
41 * compatibility with e.g. the internal WebRTC software encoders.
42 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010043 @CalledByNative("Buffer") I420Buffer toI420();
magjed55220212017-06-02 02:45:56 -070044
45 /**
46 * Reference counting is needed since a video buffer can be shared between multiple VideoSinks,
47 * and the buffer needs to be returned to the VideoSource as soon as all references are gone.
48 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010049 @CalledByNative("Buffer") void retain();
50 @CalledByNative("Buffer") void release();
sakal836f60c2017-07-28 07:12:23 -070051
52 /**
53 * Crops a region defined by |cropx|, |cropY|, |cropWidth| and |cropHeight|. Scales it to size
54 * |scaleWidth| x |scaleHeight|.
55 */
Magnus Jedvert202be392017-11-18 16:09:17 +010056 @CalledByNative("Buffer")
sakal836f60c2017-07-28 07:12:23 -070057 Buffer cropAndScale(
58 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -070059 }
60
61 /**
62 * Interface for I420 buffers.
63 */
64 public interface I420Buffer extends Buffer {
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020065 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020066 * Returns a direct ByteBuffer containing Y-plane data. The buffer capacity is at least
67 * getStrideY() * getHeight() bytes. The position of the returned buffer is ignored and must
68 * be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
69 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020070 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010071 @CalledByNative("I420Buffer") ByteBuffer getDataY();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020072 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020073 * Returns a direct ByteBuffer containing U-plane data. The buffer capacity is at least
74 * getStrideU() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
75 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
76 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020077 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010078 @CalledByNative("I420Buffer") ByteBuffer getDataU();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020079 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020080 * Returns a direct ByteBuffer containing V-plane data. The buffer capacity is at least
81 * getStrideV() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
82 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
83 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020084 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010085 @CalledByNative("I420Buffer") ByteBuffer getDataV();
magjed55220212017-06-02 02:45:56 -070086
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010087 @CalledByNative("I420Buffer") int getStrideY();
88 @CalledByNative("I420Buffer") int getStrideU();
89 @CalledByNative("I420Buffer") int getStrideV();
magjed55220212017-06-02 02:45:56 -070090 }
91
92 /**
93 * Interface for buffers that are stored as a single texture, either in OES or RGB format.
94 */
95 public interface TextureBuffer extends Buffer {
Sami Kalliomäkicb98b112017-10-16 11:20:26 +020096 enum Type {
97 OES(GLES11Ext.GL_TEXTURE_EXTERNAL_OES),
98 RGB(GLES20.GL_TEXTURE_2D);
99
100 private final int glTarget;
101
102 private Type(final int glTarget) {
103 this.glTarget = glTarget;
104 }
105
106 public int getGlTarget() {
107 return glTarget;
108 }
109 }
magjed55220212017-06-02 02:45:56 -0700110
111 Type getType();
112 int getTextureId();
sakal836f60c2017-07-28 07:12:23 -0700113
114 /**
115 * Retrieve the transform matrix associated with the frame. This transform matrix maps 2D
116 * homogeneous coordinates of the form (s, t, 1) with s and t in the inclusive range [0, 1] to
117 * the coordinate that should be used to sample that location from the buffer.
118 */
119 public Matrix getTransformMatrix();
magjed55220212017-06-02 02:45:56 -0700120 }
121
122 private final Buffer buffer;
123 private final int rotation;
124 private final long timestampNs;
magjed55220212017-06-02 02:45:56 -0700125
Magnus Jedvert1f2a3e72017-11-23 16:56:44 +0100126 @CalledByNative
sakal836f60c2017-07-28 07:12:23 -0700127 public VideoFrame(Buffer buffer, int rotation, long timestampNs) {
magjed55220212017-06-02 02:45:56 -0700128 if (buffer == null) {
129 throw new IllegalArgumentException("buffer not allowed to be null");
130 }
sakal6bdcefc2017-08-15 01:56:02 -0700131 if (rotation % 90 != 0) {
132 throw new IllegalArgumentException("rotation must be a multiple of 90");
133 }
magjed55220212017-06-02 02:45:56 -0700134 this.buffer = buffer;
135 this.rotation = rotation;
136 this.timestampNs = timestampNs;
magjed55220212017-06-02 02:45:56 -0700137 }
138
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100139 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700140 public Buffer getBuffer() {
141 return buffer;
142 }
143
144 /**
145 * Rotation of the frame in degrees.
146 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100147 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700148 public int getRotation() {
149 return rotation;
150 }
151
152 /**
153 * Timestamp of the frame in nano seconds.
154 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100155 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700156 public long getTimestampNs() {
157 return timestampNs;
158 }
159
sakal6bdcefc2017-08-15 01:56:02 -0700160 public int getRotatedWidth() {
161 if (rotation % 180 == 0) {
162 return buffer.getWidth();
163 }
164 return buffer.getHeight();
165 }
166
167 public int getRotatedHeight() {
168 if (rotation % 180 == 0) {
169 return buffer.getHeight();
170 }
171 return buffer.getWidth();
172 }
173
magjed55220212017-06-02 02:45:56 -0700174 /**
magjed55220212017-06-02 02:45:56 -0700175 * Reference counting of the underlying buffer.
176 */
177 public void retain() {
178 buffer.retain();
179 }
180
Sami Kalliomäki2bde8502018-02-15 13:58:15 +0100181 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700182 public void release() {
183 buffer.release();
184 }
sakal836f60c2017-07-28 07:12:23 -0700185
186 public static VideoFrame.Buffer cropAndScaleI420(final I420Buffer buffer, int cropX, int cropY,
187 int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
188 if (cropWidth == scaleWidth && cropHeight == scaleHeight) {
189 // No scaling.
190 ByteBuffer dataY = buffer.getDataY();
191 ByteBuffer dataU = buffer.getDataU();
192 ByteBuffer dataV = buffer.getDataV();
193
194 dataY.position(cropX + cropY * buffer.getStrideY());
195 dataU.position(cropX / 2 + cropY / 2 * buffer.getStrideU());
196 dataV.position(cropX / 2 + cropY / 2 * buffer.getStrideV());
197
198 buffer.retain();
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200199 return JavaI420Buffer.wrap(buffer.getWidth(), buffer.getHeight(), dataY.slice(),
sakal836f60c2017-07-28 07:12:23 -0700200 buffer.getStrideY(), dataU.slice(), buffer.getStrideU(), dataV.slice(),
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200201 buffer.getStrideV(), buffer::release);
sakal836f60c2017-07-28 07:12:23 -0700202 }
203
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200204 JavaI420Buffer newBuffer = JavaI420Buffer.allocate(scaleWidth, scaleHeight);
Magnus Jedvert84d8ae52017-12-20 15:12:10 +0100205 nativeCropAndScaleI420(buffer.getDataY(), buffer.getStrideY(), buffer.getDataU(),
sakal836f60c2017-07-28 07:12:23 -0700206 buffer.getStrideU(), buffer.getDataV(), buffer.getStrideV(), cropX, cropY, cropWidth,
207 cropHeight, newBuffer.getDataY(), newBuffer.getStrideY(), newBuffer.getDataU(),
208 newBuffer.getStrideU(), newBuffer.getDataV(), newBuffer.getStrideV(), scaleWidth,
209 scaleHeight);
210 return newBuffer;
211 }
212
Magnus Jedvert84d8ae52017-12-20 15:12:10 +0100213 private static native void nativeCropAndScaleI420(ByteBuffer srcY, int srcStrideY,
sakal836f60c2017-07-28 07:12:23 -0700214 ByteBuffer srcU, int srcStrideU, ByteBuffer srcV, int srcStrideV, int cropX, int cropY,
215 int cropWidth, int cropHeight, ByteBuffer dstY, int dstStrideY, ByteBuffer dstU,
216 int dstStrideU, ByteBuffer dstV, int dstStrideV, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -0700217}