blob: 438df0f23e124166403f3afe901e3c18ecabadc8 [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 {
60 ByteBuffer getDataY();
61 ByteBuffer getDataU();
62 ByteBuffer getDataV();
63
64 int getStrideY();
65 int getStrideU();
66 int getStrideV();
67 }
68
69 /**
70 * Interface for buffers that are stored as a single texture, either in OES or RGB format.
71 */
72 public interface TextureBuffer extends Buffer {
73 enum Type { OES, RGB }
74
75 Type getType();
76 int getTextureId();
sakal836f60c2017-07-28 07:12:23 -070077
78 /**
79 * Retrieve the transform matrix associated with the frame. This transform matrix maps 2D
80 * homogeneous coordinates of the form (s, t, 1) with s and t in the inclusive range [0, 1] to
81 * the coordinate that should be used to sample that location from the buffer.
82 */
83 public Matrix getTransformMatrix();
magjed55220212017-06-02 02:45:56 -070084 }
85
86 private final Buffer buffer;
87 private final int rotation;
88 private final long timestampNs;
magjed55220212017-06-02 02:45:56 -070089
sakal836f60c2017-07-28 07:12:23 -070090 public VideoFrame(Buffer buffer, int rotation, long timestampNs) {
magjed55220212017-06-02 02:45:56 -070091 if (buffer == null) {
92 throw new IllegalArgumentException("buffer not allowed to be null");
93 }
sakal6bdcefc2017-08-15 01:56:02 -070094 if (rotation % 90 != 0) {
95 throw new IllegalArgumentException("rotation must be a multiple of 90");
96 }
magjed55220212017-06-02 02:45:56 -070097 this.buffer = buffer;
98 this.rotation = rotation;
99 this.timestampNs = timestampNs;
magjed55220212017-06-02 02:45:56 -0700100 }
101
102 public Buffer getBuffer() {
103 return buffer;
104 }
105
106 /**
107 * Rotation of the frame in degrees.
108 */
109 public int getRotation() {
110 return rotation;
111 }
112
113 /**
114 * Timestamp of the frame in nano seconds.
115 */
116 public long getTimestampNs() {
117 return timestampNs;
118 }
119
sakal6bdcefc2017-08-15 01:56:02 -0700120 public int getRotatedWidth() {
121 if (rotation % 180 == 0) {
122 return buffer.getWidth();
123 }
124 return buffer.getHeight();
125 }
126
127 public int getRotatedHeight() {
128 if (rotation % 180 == 0) {
129 return buffer.getHeight();
130 }
131 return buffer.getWidth();
132 }
133
magjed55220212017-06-02 02:45:56 -0700134 /**
magjed55220212017-06-02 02:45:56 -0700135 * Reference counting of the underlying buffer.
136 */
137 public void retain() {
138 buffer.retain();
139 }
140
141 public void release() {
142 buffer.release();
143 }
sakal836f60c2017-07-28 07:12:23 -0700144
145 public static VideoFrame.Buffer cropAndScaleI420(final I420Buffer buffer, int cropX, int cropY,
146 int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
147 if (cropWidth == scaleWidth && cropHeight == scaleHeight) {
148 // No scaling.
149 ByteBuffer dataY = buffer.getDataY();
150 ByteBuffer dataU = buffer.getDataU();
151 ByteBuffer dataV = buffer.getDataV();
152
153 dataY.position(cropX + cropY * buffer.getStrideY());
154 dataU.position(cropX / 2 + cropY / 2 * buffer.getStrideU());
155 dataV.position(cropX / 2 + cropY / 2 * buffer.getStrideV());
156
157 buffer.retain();
158 return new I420BufferImpl(buffer.getWidth(), buffer.getHeight(), dataY.slice(),
159 buffer.getStrideY(), dataU.slice(), buffer.getStrideU(), dataV.slice(),
160 buffer.getStrideV(), new Runnable() {
161 @Override
162 public void run() {
163 buffer.release();
164 }
165 });
166 }
167
168 I420BufferImpl newBuffer = I420BufferImpl.allocate(scaleWidth, scaleHeight);
169 nativeCropAndScaleI420(buffer.getDataY(), buffer.getStrideY(), buffer.getDataU(),
170 buffer.getStrideU(), buffer.getDataV(), buffer.getStrideV(), cropX, cropY, cropWidth,
171 cropHeight, newBuffer.getDataY(), newBuffer.getStrideY(), newBuffer.getDataU(),
172 newBuffer.getStrideU(), newBuffer.getDataV(), newBuffer.getStrideV(), scaleWidth,
173 scaleHeight);
174 return newBuffer;
175 }
176
177 private static native void nativeCropAndScaleI420(ByteBuffer srcY, int srcStrideY,
178 ByteBuffer srcU, int srcStrideU, ByteBuffer srcV, int srcStrideV, int cropX, int cropY,
179 int cropWidth, int cropHeight, ByteBuffer dstY, int dstStrideY, ByteBuffer dstU,
180 int dstStrideU, ByteBuffer dstV, int dstStrideV, int scaleWidth, int scaleHeight);
magjed55220212017-06-02 02:45:56 -0700181}