blob: e9f3b52455dd4e11494ffbcdbcd948554a4fb941 [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;
Fabian Bergmarkf7a76982021-09-20 14:09:53 +020016import androidx.annotation.Nullable;
magjed55220212017-06-02 02:45:56 -070017import 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 */
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020029public 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 {
magjed55220212017-06-02 02:45:56 -070038 /**
Byoungchan Leef740c252021-07-24 06:16:20 +090039 * Representation of the underlying buffer. Currently, only NATIVE and I420 are supported.
40 */
41 @CalledByNative("Buffer")
42 @VideoFrameBufferType
43 default int getBufferType() {
44 return VideoFrameBufferType.NATIVE;
45 }
46
47 /**
magjed55220212017-06-02 02:45:56 -070048 * Resolution of the buffer in pixels.
49 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010050 @CalledByNative("Buffer") int getWidth();
51 @CalledByNative("Buffer") int getHeight();
magjed55220212017-06-02 02:45:56 -070052
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.
Fabian Bergmarkf7a76982021-09-20 14:09:53 +020057 *
58 * <p> Conversion may fail, for example if reading the pixel data from a texture fails. If the
59 * conversion fails, null is returned.
magjed55220212017-06-02 02:45:56 -070060 */
Fabian Bergmarkf7a76982021-09-20 14:09:53 +020061 @Nullable @CalledByNative("Buffer") I420Buffer toI420();
magjed55220212017-06-02 02:45:56 -070062
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020063 @Override @CalledByNative("Buffer") void retain();
64 @Override @CalledByNative("Buffer") void release();
sakal836f60c2017-07-28 07:12:23 -070065
66 /**
Artem Titovd7ac5812021-07-27 12:23:39 +020067 * Crops a region defined by `cropx`, `cropY`, `cropWidth` and `cropHeight`. Scales it to size
68 * `scaleWidth` x `scaleHeight`.
sakal836f60c2017-07-28 07:12:23 -070069 */
Niels Möller299c8392020-10-12 14:47:46 +020070 @CalledByNative("Buffer")
sakal836f60c2017-07-28 07:12:23 -070071 Buffer cropAndScale(
72 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -070073 }
74
75 /**
76 * Interface for I420 buffers.
77 */
78 public interface I420Buffer extends Buffer {
Byoungchan Leef740c252021-07-24 06:16:20 +090079 @Override
80 default int getBufferType() {
81 return VideoFrameBufferType.I420;
82 }
83
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020084 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020085 * 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.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020089 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010090 @CalledByNative("I420Buffer") ByteBuffer getDataY();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020091 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020092 * 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.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020096 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010097 @CalledByNative("I420Buffer") ByteBuffer getDataU();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020098 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020099 * 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.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +0200103 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100104 @CalledByNative("I420Buffer") ByteBuffer getDataV();
magjed55220212017-06-02 02:45:56 -0700105
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100106 @CalledByNative("I420Buffer") int getStrideY();
107 @CalledByNative("I420Buffer") int getStrideU();
108 @CalledByNative("I420Buffer") int getStrideV();
magjed55220212017-06-02 02:45:56 -0700109 }
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 {
Sami Kalliomäkicb98b112017-10-16 11:20:26 +0200115 enum Type {
116 OES(GLES11Ext.GL_TEXTURE_EXTERNAL_OES),
117 RGB(GLES20.GL_TEXTURE_2D);
118
119 private final int glTarget;
120
121 private Type(final int glTarget) {
122 this.glTarget = glTarget;
123 }
124
125 public int getGlTarget() {
126 return glTarget;
127 }
128 }
magjed55220212017-06-02 02:45:56 -0700129
130 Type getType();
131 int getTextureId();
sakal836f60c2017-07-28 07:12:23 -0700132
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 */
Magnus Jedvert783c6e32018-07-05 13:34:17 +0200138 Matrix getTransformMatrix();
magjed55220212017-06-02 02:45:56 -0700139 }
140
141 private final Buffer buffer;
142 private final int rotation;
143 private final long timestampNs;
magjed55220212017-06-02 02:45:56 -0700144
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200145 /**
146 * Constructs a new VideoFrame backed by the given {@code buffer}.
147 *
148 * @note Ownership of the buffer object is tranferred to the new VideoFrame.
149 */
Magnus Jedvert1f2a3e72017-11-23 16:56:44 +0100150 @CalledByNative
sakal836f60c2017-07-28 07:12:23 -0700151 public VideoFrame(Buffer buffer, int rotation, long timestampNs) {
magjed55220212017-06-02 02:45:56 -0700152 if (buffer == null) {
153 throw new IllegalArgumentException("buffer not allowed to be null");
154 }
sakal6bdcefc2017-08-15 01:56:02 -0700155 if (rotation % 90 != 0) {
156 throw new IllegalArgumentException("rotation must be a multiple of 90");
157 }
magjed55220212017-06-02 02:45:56 -0700158 this.buffer = buffer;
159 this.rotation = rotation;
160 this.timestampNs = timestampNs;
magjed55220212017-06-02 02:45:56 -0700161 }
162
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100163 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700164 public Buffer getBuffer() {
165 return buffer;
166 }
167
168 /**
169 * Rotation of the frame in degrees.
170 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100171 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700172 public int getRotation() {
173 return rotation;
174 }
175
176 /**
177 * Timestamp of the frame in nano seconds.
178 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100179 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700180 public long getTimestampNs() {
181 return timestampNs;
182 }
183
sakal6bdcefc2017-08-15 01:56:02 -0700184 public int getRotatedWidth() {
185 if (rotation % 180 == 0) {
186 return buffer.getWidth();
187 }
188 return buffer.getHeight();
189 }
190
191 public int getRotatedHeight() {
192 if (rotation % 180 == 0) {
193 return buffer.getHeight();
194 }
195 return buffer.getWidth();
196 }
197
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200198 @Override
magjed55220212017-06-02 02:45:56 -0700199 public void retain() {
200 buffer.retain();
201 }
202
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200203 @Override
Sami Kalliomäki2bde8502018-02-15 13:58:15 +0100204 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700205 public void release() {
206 buffer.release();
207 }
magjed55220212017-06-02 02:45:56 -0700208}