blob: 006635459e3611d20e7c97e44b3aa660099298b3 [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;
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 */
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020028public 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 {
magjed55220212017-06-02 02:45:56 -070037 /**
Byoungchan Leef740c252021-07-24 06:16:20 +090038 * Representation of the underlying buffer. Currently, only NATIVE and I420 are supported.
39 */
40 @CalledByNative("Buffer")
41 @VideoFrameBufferType
42 default int getBufferType() {
43 return VideoFrameBufferType.NATIVE;
44 }
45
46 /**
magjed55220212017-06-02 02:45:56 -070047 * Resolution of the buffer in pixels.
48 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010049 @CalledByNative("Buffer") int getWidth();
50 @CalledByNative("Buffer") int getHeight();
magjed55220212017-06-02 02:45:56 -070051
52 /**
53 * Returns a memory-backed frame in I420 format. If the pixel data is in another format, a
54 * conversion will take place. All implementations must provide a fallback to I420 for
55 * compatibility with e.g. the internal WebRTC software encoders.
56 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010057 @CalledByNative("Buffer") I420Buffer toI420();
magjed55220212017-06-02 02:45:56 -070058
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020059 @Override @CalledByNative("Buffer") void retain();
60 @Override @CalledByNative("Buffer") void release();
sakal836f60c2017-07-28 07:12:23 -070061
62 /**
Artem Titovd7ac5812021-07-27 12:23:39 +020063 * Crops a region defined by `cropx`, `cropY`, `cropWidth` and `cropHeight`. Scales it to size
64 * `scaleWidth` x `scaleHeight`.
sakal836f60c2017-07-28 07:12:23 -070065 */
Niels Möller299c8392020-10-12 14:47:46 +020066 @CalledByNative("Buffer")
sakal836f60c2017-07-28 07:12:23 -070067 Buffer cropAndScale(
68 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -070069 }
70
71 /**
72 * Interface for I420 buffers.
73 */
74 public interface I420Buffer extends Buffer {
Byoungchan Leef740c252021-07-24 06:16:20 +090075 @Override
76 default int getBufferType() {
77 return VideoFrameBufferType.I420;
78 }
79
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020080 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020081 * Returns a direct ByteBuffer containing Y-plane data. The buffer capacity is at least
82 * getStrideY() * getHeight() bytes. The position of the returned buffer is ignored and must
83 * 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.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020085 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010086 @CalledByNative("I420Buffer") ByteBuffer getDataY();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020087 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020088 * Returns a direct ByteBuffer containing U-plane data. The buffer capacity is at least
89 * getStrideU() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
90 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
91 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020092 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010093 @CalledByNative("I420Buffer") ByteBuffer getDataU();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020094 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020095 * Returns a direct ByteBuffer containing V-plane data. The buffer capacity is at least
96 * getStrideV() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
97 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
98 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020099 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100100 @CalledByNative("I420Buffer") ByteBuffer getDataV();
magjed55220212017-06-02 02:45:56 -0700101
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100102 @CalledByNative("I420Buffer") int getStrideY();
103 @CalledByNative("I420Buffer") int getStrideU();
104 @CalledByNative("I420Buffer") int getStrideV();
magjed55220212017-06-02 02:45:56 -0700105 }
106
107 /**
108 * Interface for buffers that are stored as a single texture, either in OES or RGB format.
109 */
110 public interface TextureBuffer extends Buffer {
Sami Kalliomäkicb98b112017-10-16 11:20:26 +0200111 enum Type {
112 OES(GLES11Ext.GL_TEXTURE_EXTERNAL_OES),
113 RGB(GLES20.GL_TEXTURE_2D);
114
115 private final int glTarget;
116
117 private Type(final int glTarget) {
118 this.glTarget = glTarget;
119 }
120
121 public int getGlTarget() {
122 return glTarget;
123 }
124 }
magjed55220212017-06-02 02:45:56 -0700125
126 Type getType();
127 int getTextureId();
sakal836f60c2017-07-28 07:12:23 -0700128
129 /**
130 * Retrieve the transform matrix associated with the frame. This transform matrix maps 2D
131 * homogeneous coordinates of the form (s, t, 1) with s and t in the inclusive range [0, 1] to
132 * the coordinate that should be used to sample that location from the buffer.
133 */
Magnus Jedvert783c6e32018-07-05 13:34:17 +0200134 Matrix getTransformMatrix();
magjed55220212017-06-02 02:45:56 -0700135 }
136
137 private final Buffer buffer;
138 private final int rotation;
139 private final long timestampNs;
magjed55220212017-06-02 02:45:56 -0700140
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200141 /**
142 * Constructs a new VideoFrame backed by the given {@code buffer}.
143 *
144 * @note Ownership of the buffer object is tranferred to the new VideoFrame.
145 */
Magnus Jedvert1f2a3e72017-11-23 16:56:44 +0100146 @CalledByNative
sakal836f60c2017-07-28 07:12:23 -0700147 public VideoFrame(Buffer buffer, int rotation, long timestampNs) {
magjed55220212017-06-02 02:45:56 -0700148 if (buffer == null) {
149 throw new IllegalArgumentException("buffer not allowed to be null");
150 }
sakal6bdcefc2017-08-15 01:56:02 -0700151 if (rotation % 90 != 0) {
152 throw new IllegalArgumentException("rotation must be a multiple of 90");
153 }
magjed55220212017-06-02 02:45:56 -0700154 this.buffer = buffer;
155 this.rotation = rotation;
156 this.timestampNs = timestampNs;
magjed55220212017-06-02 02:45:56 -0700157 }
158
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100159 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700160 public Buffer getBuffer() {
161 return buffer;
162 }
163
164 /**
165 * Rotation of the frame in degrees.
166 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100167 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700168 public int getRotation() {
169 return rotation;
170 }
171
172 /**
173 * Timestamp of the frame in nano seconds.
174 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100175 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700176 public long getTimestampNs() {
177 return timestampNs;
178 }
179
sakal6bdcefc2017-08-15 01:56:02 -0700180 public int getRotatedWidth() {
181 if (rotation % 180 == 0) {
182 return buffer.getWidth();
183 }
184 return buffer.getHeight();
185 }
186
187 public int getRotatedHeight() {
188 if (rotation % 180 == 0) {
189 return buffer.getHeight();
190 }
191 return buffer.getWidth();
192 }
193
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200194 @Override
magjed55220212017-06-02 02:45:56 -0700195 public void retain() {
196 buffer.retain();
197 }
198
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200199 @Override
Sami Kalliomäki2bde8502018-02-15 13:58:15 +0100200 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700201 public void release() {
202 buffer.release();
203 }
magjed55220212017-06-02 02:45:56 -0700204}