blob: a0a0d4eecbe083198973059a14c05518566da4d3 [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 /**
38 * Resolution of the buffer in pixels.
39 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010040 @CalledByNative("Buffer") int getWidth();
41 @CalledByNative("Buffer") int getHeight();
magjed55220212017-06-02 02:45:56 -070042
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 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010048 @CalledByNative("Buffer") I420Buffer toI420();
magjed55220212017-06-02 02:45:56 -070049
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020050 @Override @CalledByNative("Buffer") void retain();
51 @Override @CalledByNative("Buffer") void release();
sakal836f60c2017-07-28 07:12:23 -070052
53 /**
54 * Crops a region defined by |cropx|, |cropY|, |cropWidth| and |cropHeight|. Scales it to size
55 * |scaleWidth| x |scaleHeight|.
56 */
Niels Möller299c8392020-10-12 14:47:46 +020057 @CalledByNative("Buffer")
sakal836f60c2017-07-28 07:12:23 -070058 Buffer cropAndScale(
59 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -070060 }
61
62 /**
63 * Interface for I420 buffers.
64 */
65 public interface I420Buffer extends Buffer {
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020066 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020067 * 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.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020071 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010072 @CalledByNative("I420Buffer") ByteBuffer getDataY();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020073 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020074 * 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.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020078 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010079 @CalledByNative("I420Buffer") ByteBuffer getDataU();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020080 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020081 * 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.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020085 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010086 @CalledByNative("I420Buffer") ByteBuffer getDataV();
magjed55220212017-06-02 02:45:56 -070087
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010088 @CalledByNative("I420Buffer") int getStrideY();
89 @CalledByNative("I420Buffer") int getStrideU();
90 @CalledByNative("I420Buffer") int getStrideV();
magjed55220212017-06-02 02:45:56 -070091 }
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 {
Sami Kalliomäkicb98b112017-10-16 11:20:26 +020097 enum Type {
98 OES(GLES11Ext.GL_TEXTURE_EXTERNAL_OES),
99 RGB(GLES20.GL_TEXTURE_2D);
100
101 private final int glTarget;
102
103 private Type(final int glTarget) {
104 this.glTarget = glTarget;
105 }
106
107 public int getGlTarget() {
108 return glTarget;
109 }
110 }
magjed55220212017-06-02 02:45:56 -0700111
112 Type getType();
113 int getTextureId();
sakal836f60c2017-07-28 07:12:23 -0700114
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 */
Magnus Jedvert783c6e32018-07-05 13:34:17 +0200120 Matrix getTransformMatrix();
magjed55220212017-06-02 02:45:56 -0700121 }
122
123 private final Buffer buffer;
124 private final int rotation;
125 private final long timestampNs;
magjed55220212017-06-02 02:45:56 -0700126
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200127 /**
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 */
Magnus Jedvert1f2a3e72017-11-23 16:56:44 +0100132 @CalledByNative
sakal836f60c2017-07-28 07:12:23 -0700133 public VideoFrame(Buffer buffer, int rotation, long timestampNs) {
magjed55220212017-06-02 02:45:56 -0700134 if (buffer == null) {
135 throw new IllegalArgumentException("buffer not allowed to be null");
136 }
sakal6bdcefc2017-08-15 01:56:02 -0700137 if (rotation % 90 != 0) {
138 throw new IllegalArgumentException("rotation must be a multiple of 90");
139 }
magjed55220212017-06-02 02:45:56 -0700140 this.buffer = buffer;
141 this.rotation = rotation;
142 this.timestampNs = timestampNs;
magjed55220212017-06-02 02:45:56 -0700143 }
144
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100145 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700146 public Buffer getBuffer() {
147 return buffer;
148 }
149
150 /**
151 * Rotation of the frame in degrees.
152 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100153 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700154 public int getRotation() {
155 return rotation;
156 }
157
158 /**
159 * Timestamp of the frame in nano seconds.
160 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100161 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700162 public long getTimestampNs() {
163 return timestampNs;
164 }
165
sakal6bdcefc2017-08-15 01:56:02 -0700166 public int getRotatedWidth() {
167 if (rotation % 180 == 0) {
168 return buffer.getWidth();
169 }
170 return buffer.getHeight();
171 }
172
173 public int getRotatedHeight() {
174 if (rotation % 180 == 0) {
175 return buffer.getHeight();
176 }
177 return buffer.getWidth();
178 }
179
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200180 @Override
magjed55220212017-06-02 02:45:56 -0700181 public void retain() {
182 buffer.retain();
183 }
184
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200185 @Override
Sami Kalliomäki2bde8502018-02-15 13:58:15 +0100186 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700187 public void release() {
188 buffer.release();
189 }
magjed55220212017-06-02 02:45:56 -0700190}