blob: 3e8a1e4d8d5d1c8f82757fb1d7b1f8cab973924c [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 */
Magnus Jedvert84d8ae52017-12-20 15:12:10 +010028@JNINamespace("webrtc::jni")
magjed55220212017-06-02 02:45:56 -070029public class VideoFrame {
30 public interface Buffer {
31 /**
32 * Resolution of the buffer in pixels.
33 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010034 @CalledByNative("Buffer") int getWidth();
35 @CalledByNative("Buffer") int getHeight();
magjed55220212017-06-02 02:45:56 -070036
37 /**
38 * Returns a memory-backed frame in I420 format. If the pixel data is in another format, a
39 * conversion will take place. All implementations must provide a fallback to I420 for
40 * compatibility with e.g. the internal WebRTC software encoders.
41 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010042 @CalledByNative("Buffer") I420Buffer toI420();
magjed55220212017-06-02 02:45:56 -070043
44 /**
45 * Reference counting is needed since a video buffer can be shared between multiple VideoSinks,
46 * and the buffer needs to be returned to the VideoSource as soon as all references are gone.
47 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010048 @CalledByNative("Buffer") void retain();
49 @CalledByNative("Buffer") void release();
sakal836f60c2017-07-28 07:12:23 -070050
51 /**
52 * Crops a region defined by |cropx|, |cropY|, |cropWidth| and |cropHeight|. Scales it to size
53 * |scaleWidth| x |scaleHeight|.
54 */
Magnus Jedvert202be392017-11-18 16:09:17 +010055 @CalledByNative("Buffer")
sakal836f60c2017-07-28 07:12:23 -070056 Buffer cropAndScale(
57 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -070058 }
59
60 /**
61 * Interface for I420 buffers.
62 */
63 public interface I420Buffer extends Buffer {
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020064 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020065 * Returns a direct ByteBuffer containing Y-plane data. The buffer capacity is at least
66 * getStrideY() * getHeight() bytes. The position of the returned buffer is ignored and must
67 * be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
68 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020069 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010070 @CalledByNative("I420Buffer") ByteBuffer getDataY();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020071 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020072 * Returns a direct ByteBuffer containing U-plane data. The buffer capacity is at least
73 * getStrideU() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
74 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
75 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020076 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010077 @CalledByNative("I420Buffer") ByteBuffer getDataU();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020078 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020079 * Returns a direct ByteBuffer containing V-plane data. The buffer capacity is at least
80 * getStrideV() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
81 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
82 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020083 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010084 @CalledByNative("I420Buffer") ByteBuffer getDataV();
magjed55220212017-06-02 02:45:56 -070085
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +010086 @CalledByNative("I420Buffer") int getStrideY();
87 @CalledByNative("I420Buffer") int getStrideU();
88 @CalledByNative("I420Buffer") int getStrideV();
magjed55220212017-06-02 02:45:56 -070089 }
90
91 /**
92 * Interface for buffers that are stored as a single texture, either in OES or RGB format.
93 */
94 public interface TextureBuffer extends Buffer {
Sami Kalliomäkicb98b112017-10-16 11:20:26 +020095 enum Type {
96 OES(GLES11Ext.GL_TEXTURE_EXTERNAL_OES),
97 RGB(GLES20.GL_TEXTURE_2D);
98
99 private final int glTarget;
100
101 private Type(final int glTarget) {
102 this.glTarget = glTarget;
103 }
104
105 public int getGlTarget() {
106 return glTarget;
107 }
108 }
magjed55220212017-06-02 02:45:56 -0700109
110 Type getType();
111 int getTextureId();
sakal836f60c2017-07-28 07:12:23 -0700112
113 /**
114 * Retrieve the transform matrix associated with the frame. This transform matrix maps 2D
115 * homogeneous coordinates of the form (s, t, 1) with s and t in the inclusive range [0, 1] to
116 * the coordinate that should be used to sample that location from the buffer.
117 */
118 public Matrix getTransformMatrix();
magjed55220212017-06-02 02:45:56 -0700119 }
120
121 private final Buffer buffer;
122 private final int rotation;
123 private final long timestampNs;
magjed55220212017-06-02 02:45:56 -0700124
Magnus Jedvert1f2a3e72017-11-23 16:56:44 +0100125 @CalledByNative
sakal836f60c2017-07-28 07:12:23 -0700126 public VideoFrame(Buffer buffer, int rotation, long timestampNs) {
magjed55220212017-06-02 02:45:56 -0700127 if (buffer == null) {
128 throw new IllegalArgumentException("buffer not allowed to be null");
129 }
sakal6bdcefc2017-08-15 01:56:02 -0700130 if (rotation % 90 != 0) {
131 throw new IllegalArgumentException("rotation must be a multiple of 90");
132 }
magjed55220212017-06-02 02:45:56 -0700133 this.buffer = buffer;
134 this.rotation = rotation;
135 this.timestampNs = timestampNs;
magjed55220212017-06-02 02:45:56 -0700136 }
137
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100138 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700139 public Buffer getBuffer() {
140 return buffer;
141 }
142
143 /**
144 * Rotation of the frame in degrees.
145 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100146 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700147 public int getRotation() {
148 return rotation;
149 }
150
151 /**
152 * Timestamp of the frame in nano seconds.
153 */
Magnus Jedvertc2ac3c62017-11-14 17:08:59 +0100154 @CalledByNative
magjed55220212017-06-02 02:45:56 -0700155 public long getTimestampNs() {
156 return timestampNs;
157 }
158
sakal6bdcefc2017-08-15 01:56:02 -0700159 public int getRotatedWidth() {
160 if (rotation % 180 == 0) {
161 return buffer.getWidth();
162 }
163 return buffer.getHeight();
164 }
165
166 public int getRotatedHeight() {
167 if (rotation % 180 == 0) {
168 return buffer.getHeight();
169 }
170 return buffer.getWidth();
171 }
172
magjed55220212017-06-02 02:45:56 -0700173 /**
magjed55220212017-06-02 02:45:56 -0700174 * Reference counting of the underlying buffer.
175 */
176 public void retain() {
177 buffer.retain();
178 }
179
180 public void release() {
181 buffer.release();
182 }
sakal836f60c2017-07-28 07:12:23 -0700183
184 public static VideoFrame.Buffer cropAndScaleI420(final I420Buffer buffer, int cropX, int cropY,
185 int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
186 if (cropWidth == scaleWidth && cropHeight == scaleHeight) {
187 // No scaling.
188 ByteBuffer dataY = buffer.getDataY();
189 ByteBuffer dataU = buffer.getDataU();
190 ByteBuffer dataV = buffer.getDataV();
191
192 dataY.position(cropX + cropY * buffer.getStrideY());
193 dataU.position(cropX / 2 + cropY / 2 * buffer.getStrideU());
194 dataV.position(cropX / 2 + cropY / 2 * buffer.getStrideV());
195
196 buffer.retain();
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200197 return JavaI420Buffer.wrap(buffer.getWidth(), buffer.getHeight(), dataY.slice(),
sakal836f60c2017-07-28 07:12:23 -0700198 buffer.getStrideY(), dataU.slice(), buffer.getStrideU(), dataV.slice(),
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200199 buffer.getStrideV(), buffer::release);
sakal836f60c2017-07-28 07:12:23 -0700200 }
201
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200202 JavaI420Buffer newBuffer = JavaI420Buffer.allocate(scaleWidth, scaleHeight);
Magnus Jedvert84d8ae52017-12-20 15:12:10 +0100203 nativeCropAndScaleI420(buffer.getDataY(), buffer.getStrideY(), buffer.getDataU(),
sakal836f60c2017-07-28 07:12:23 -0700204 buffer.getStrideU(), buffer.getDataV(), buffer.getStrideV(), cropX, cropY, cropWidth,
205 cropHeight, newBuffer.getDataY(), newBuffer.getStrideY(), newBuffer.getDataU(),
206 newBuffer.getStrideU(), newBuffer.getDataV(), newBuffer.getStrideV(), scaleWidth,
207 scaleHeight);
208 return newBuffer;
209 }
210
Magnus Jedvert84d8ae52017-12-20 15:12:10 +0100211 private static native void nativeCropAndScaleI420(ByteBuffer srcY, int srcStrideY,
sakal836f60c2017-07-28 07:12:23 -0700212 ByteBuffer srcU, int srcStrideU, ByteBuffer srcV, int srcStrideV, int cropX, int cropY,
213 int cropWidth, int cropHeight, ByteBuffer dstY, int dstStrideY, ByteBuffer dstU,
214 int dstStrideU, ByteBuffer dstV, int dstStrideV, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -0700215}