blob: b49262b104d55b353c772ad1934336db9b632100 [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;
14import java.nio.ByteBuffer;
15
16/**
17 * Java version of webrtc::VideoFrame and webrtc::VideoFrameBuffer. A difference from the C++
18 * version is that no explicit tag is used, and clients are expected to use 'instanceof' to find the
19 * right subclass of the buffer. This allows clients to create custom VideoFrame.Buffer in
20 * arbitrary format in their custom VideoSources, and then cast it back to the correct subclass in
21 * their custom VideoSinks. All implementations must also implement the toI420() function,
22 * converting from the underlying representation if necessary. I420 is the most widely accepted
23 * format and serves as a fallback for video sinks that can only handle I420, e.g. the internal
24 * WebRTC software encoders.
25 */
26public class VideoFrame {
27 public interface Buffer {
28 /**
29 * Resolution of the buffer in pixels.
30 */
31 int getWidth();
32 int getHeight();
33
34 /**
35 * Returns a memory-backed frame in I420 format. If the pixel data is in another format, a
36 * conversion will take place. All implementations must provide a fallback to I420 for
37 * compatibility with e.g. the internal WebRTC software encoders.
38 */
39 I420Buffer toI420();
40
41 /**
42 * Reference counting is needed since a video buffer can be shared between multiple VideoSinks,
43 * and the buffer needs to be returned to the VideoSource as soon as all references are gone.
44 */
45 void retain();
46 void release();
sakal836f60c2017-07-28 07:12:23 -070047
48 /**
49 * Crops a region defined by |cropx|, |cropY|, |cropWidth| and |cropHeight|. Scales it to size
50 * |scaleWidth| x |scaleHeight|.
51 */
52 Buffer cropAndScale(
53 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -070054 }
55
56 /**
57 * Interface for I420 buffers.
58 */
59 public interface I420Buffer extends Buffer {
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020060 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020061 * Returns a direct ByteBuffer containing Y-plane data. The buffer capacity is at least
62 * getStrideY() * getHeight() bytes. The position of the returned buffer is ignored and must
63 * be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
64 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020065 */
magjed55220212017-06-02 02:45:56 -070066 ByteBuffer getDataY();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020067 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020068 * Returns a direct ByteBuffer containing U-plane data. The buffer capacity is at least
69 * getStrideU() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
70 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
71 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020072 */
magjed55220212017-06-02 02:45:56 -070073 ByteBuffer getDataU();
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020074 /**
Sami Kalliomäkie3044fe2017-10-02 09:41:55 +020075 * Returns a direct ByteBuffer containing V-plane data. The buffer capacity is at least
76 * getStrideV() * ((getHeight() + 1) / 2) bytes. The position of the returned buffer is ignored
77 * and must be 0. Callers may mutate the ByteBuffer (eg. through relative-read operations), so
78 * implementations must return a new ByteBuffer or slice for each call.
Sami Kalliomäkibc7a1a92017-09-27 12:50:47 +020079 */
magjed55220212017-06-02 02:45:56 -070080 ByteBuffer getDataV();
81
82 int getStrideY();
83 int getStrideU();
84 int getStrideV();
85 }
86
87 /**
88 * Interface for buffers that are stored as a single texture, either in OES or RGB format.
89 */
90 public interface TextureBuffer extends Buffer {
91 enum Type { OES, RGB }
92
93 Type getType();
94 int getTextureId();
sakal836f60c2017-07-28 07:12:23 -070095
96 /**
97 * Retrieve the transform matrix associated with the frame. This transform matrix maps 2D
98 * homogeneous coordinates of the form (s, t, 1) with s and t in the inclusive range [0, 1] to
99 * the coordinate that should be used to sample that location from the buffer.
100 */
101 public Matrix getTransformMatrix();
magjed55220212017-06-02 02:45:56 -0700102 }
103
104 private final Buffer buffer;
105 private final int rotation;
106 private final long timestampNs;
magjed55220212017-06-02 02:45:56 -0700107
sakal836f60c2017-07-28 07:12:23 -0700108 public VideoFrame(Buffer buffer, int rotation, long timestampNs) {
magjed55220212017-06-02 02:45:56 -0700109 if (buffer == null) {
110 throw new IllegalArgumentException("buffer not allowed to be null");
111 }
sakal6bdcefc2017-08-15 01:56:02 -0700112 if (rotation % 90 != 0) {
113 throw new IllegalArgumentException("rotation must be a multiple of 90");
114 }
magjed55220212017-06-02 02:45:56 -0700115 this.buffer = buffer;
116 this.rotation = rotation;
117 this.timestampNs = timestampNs;
magjed55220212017-06-02 02:45:56 -0700118 }
119
120 public Buffer getBuffer() {
121 return buffer;
122 }
123
124 /**
125 * Rotation of the frame in degrees.
126 */
127 public int getRotation() {
128 return rotation;
129 }
130
131 /**
132 * Timestamp of the frame in nano seconds.
133 */
134 public long getTimestampNs() {
135 return timestampNs;
136 }
137
sakal6bdcefc2017-08-15 01:56:02 -0700138 public int getRotatedWidth() {
139 if (rotation % 180 == 0) {
140 return buffer.getWidth();
141 }
142 return buffer.getHeight();
143 }
144
145 public int getRotatedHeight() {
146 if (rotation % 180 == 0) {
147 return buffer.getHeight();
148 }
149 return buffer.getWidth();
150 }
151
magjed55220212017-06-02 02:45:56 -0700152 /**
magjed55220212017-06-02 02:45:56 -0700153 * Reference counting of the underlying buffer.
154 */
155 public void retain() {
156 buffer.retain();
157 }
158
159 public void release() {
160 buffer.release();
161 }
sakal836f60c2017-07-28 07:12:23 -0700162
163 public static VideoFrame.Buffer cropAndScaleI420(final I420Buffer buffer, int cropX, int cropY,
164 int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
165 if (cropWidth == scaleWidth && cropHeight == scaleHeight) {
166 // No scaling.
167 ByteBuffer dataY = buffer.getDataY();
168 ByteBuffer dataU = buffer.getDataU();
169 ByteBuffer dataV = buffer.getDataV();
170
171 dataY.position(cropX + cropY * buffer.getStrideY());
172 dataU.position(cropX / 2 + cropY / 2 * buffer.getStrideU());
173 dataV.position(cropX / 2 + cropY / 2 * buffer.getStrideV());
174
175 buffer.retain();
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200176 return JavaI420Buffer.wrap(buffer.getWidth(), buffer.getHeight(), dataY.slice(),
sakal836f60c2017-07-28 07:12:23 -0700177 buffer.getStrideY(), dataU.slice(), buffer.getStrideU(), dataV.slice(),
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200178 buffer.getStrideV(), buffer::release);
sakal836f60c2017-07-28 07:12:23 -0700179 }
180
Sami Kalliomäki48b3c022017-10-04 17:01:00 +0200181 JavaI420Buffer newBuffer = JavaI420Buffer.allocate(scaleWidth, scaleHeight);
sakal836f60c2017-07-28 07:12:23 -0700182 nativeCropAndScaleI420(buffer.getDataY(), buffer.getStrideY(), buffer.getDataU(),
183 buffer.getStrideU(), buffer.getDataV(), buffer.getStrideV(), cropX, cropY, cropWidth,
184 cropHeight, newBuffer.getDataY(), newBuffer.getStrideY(), newBuffer.getDataU(),
185 newBuffer.getStrideU(), newBuffer.getDataV(), newBuffer.getStrideV(), scaleWidth,
186 scaleHeight);
187 return newBuffer;
188 }
189
190 private static native void nativeCropAndScaleI420(ByteBuffer srcY, int srcStrideY,
191 ByteBuffer srcU, int srcStrideU, ByteBuffer srcV, int srcStrideV, int cropX, int cropY,
192 int cropWidth, int cropHeight, ByteBuffer dstY, int dstStrideY, ByteBuffer dstU,
193 int dstStrideU, ByteBuffer dstV, int dstStrideV, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -0700194}