blob: 3498faf50ed923dd2c6c3a7af2015dbe2b31789f [file] [log] [blame]
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -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 java.nio.ByteBuffer;
14import org.webrtc.VideoFrame.I420Buffer;
15
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020016/** Implementation of VideoFrame.I420Buffer backed by Java direct byte buffers. */
17public class JavaI420Buffer implements VideoFrame.I420Buffer {
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -070018 private final int width;
19 private final int height;
sakal836f60c2017-07-28 07:12:23 -070020 private final ByteBuffer dataY;
21 private final ByteBuffer dataU;
22 private final ByteBuffer dataV;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070023 private final int strideY;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070024 private final int strideU;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070025 private final int strideV;
sakal836f60c2017-07-28 07:12:23 -070026 private final Runnable releaseCallback;
sakal2fe9dfa2017-08-21 08:02:58 -070027 private final Object refCountLock = new Object();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -070028
Bjorn Mellem8fb23612017-07-18 11:33:39 -070029 private int refCount;
30
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020031 private JavaI420Buffer(int width, int height, ByteBuffer dataY, int strideY, ByteBuffer dataU,
sakal836f60c2017-07-28 07:12:23 -070032 int strideU, ByteBuffer dataV, int strideV, Runnable releaseCallback) {
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -070033 this.width = width;
34 this.height = height;
sakal836f60c2017-07-28 07:12:23 -070035 this.dataY = dataY;
36 this.dataU = dataU;
37 this.dataV = dataV;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070038 this.strideY = strideY;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070039 this.strideU = strideU;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070040 this.strideV = strideV;
41 this.releaseCallback = releaseCallback;
42
43 this.refCount = 1;
44 }
45
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020046 /** Wraps existing ByteBuffers into JavaI420Buffer object without copying the contents. */
47 public static JavaI420Buffer wrap(int width, int height, ByteBuffer dataY, int strideY,
48 ByteBuffer dataU, int strideU, ByteBuffer dataV, int strideV, Runnable releaseCallback) {
49 if (dataY == null || dataU == null || dataV == null) {
50 throw new IllegalArgumentException("Data buffers cannot be null.");
51 }
52 if (!dataY.isDirect() || !dataU.isDirect() || !dataV.isDirect()) {
53 throw new IllegalArgumentException("Data buffers must be direct byte buffers.");
54 }
55
56 // Slice the buffers to prevent external modifications to the position / limit of the buffer.
57 // Note that this doesn't protect the contents of the buffers from modifications.
58 dataY = dataY.slice();
59 dataU = dataU.slice();
60 dataV = dataV.slice();
61
62 final int chromaHeight = (height + 1) / 2;
63 final int minCapacityY = strideY * height;
64 final int minCapacityU = strideU * chromaHeight;
65 final int minCapacityV = strideV * chromaHeight;
66 if (dataY.capacity() < minCapacityY) {
67 throw new IllegalArgumentException("Y-buffer must be at least " + minCapacityY + " bytes.");
68 }
69 if (dataU.capacity() < minCapacityU) {
70 throw new IllegalArgumentException("U-buffer must be at least " + minCapacityU + " bytes.");
71 }
72 if (dataV.capacity() < minCapacityV) {
73 throw new IllegalArgumentException("V-buffer must be at least " + minCapacityV + " bytes.");
74 }
75
76 return new JavaI420Buffer(
77 width, height, dataY, strideY, dataU, strideU, dataV, strideV, releaseCallback);
78 }
79
Bjorn Mellem8fb23612017-07-18 11:33:39 -070080 /** Allocates an empty I420Buffer suitable for an image of the given dimensions. */
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020081 public static JavaI420Buffer allocate(int width, int height) {
Bjorn Mellem8fb23612017-07-18 11:33:39 -070082 int chromaHeight = (height + 1) / 2;
83 int strideUV = (width + 1) / 2;
84 int yPos = 0;
85 int uPos = yPos + width * height;
86 int vPos = uPos + strideUV * chromaHeight;
sakal836f60c2017-07-28 07:12:23 -070087
Sami Kalliomäki06110652018-02-20 11:19:58 +010088 ByteBuffer buffer =
89 JniCommon.nativeAllocateByteBuffer(width * height + 2 * strideUV * chromaHeight);
sakal836f60c2017-07-28 07:12:23 -070090
91 buffer.position(yPos);
92 buffer.limit(uPos);
93 ByteBuffer dataY = buffer.slice();
94
95 buffer.position(uPos);
96 buffer.limit(vPos);
97 ByteBuffer dataU = buffer.slice();
98
99 buffer.position(vPos);
100 buffer.limit(vPos + strideUV * chromaHeight);
101 ByteBuffer dataV = buffer.slice();
102
Sami Kalliomäki06110652018-02-20 11:19:58 +0100103 return new JavaI420Buffer(width, height, dataY, width, dataU, strideUV, dataV, strideUV,
104 () -> { JniCommon.nativeFreeByteBuffer(buffer); });
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700105 }
106
107 @Override
108 public int getWidth() {
109 return width;
110 }
111
112 @Override
113 public int getHeight() {
114 return height;
115 }
116
117 @Override
118 public ByteBuffer getDataY() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700119 // Return a slice to prevent relative reads from changing the position.
120 return dataY.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700121 }
122
123 @Override
124 public ByteBuffer getDataU() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700125 // Return a slice to prevent relative reads from changing the position.
126 return dataU.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700127 }
128
129 @Override
130 public ByteBuffer getDataV() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700131 // Return a slice to prevent relative reads from changing the position.
132 return dataV.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700133 }
134
135 @Override
136 public int getStrideY() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700137 return strideY;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700138 }
139
140 @Override
141 public int getStrideU() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700142 return strideU;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700143 }
144
145 @Override
146 public int getStrideV() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700147 return strideV;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700148 }
149
150 @Override
151 public I420Buffer toI420() {
sakal5ca60cc2017-08-09 05:25:49 -0700152 retain();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700153 return this;
154 }
155
156 @Override
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700157 public void retain() {
sakal2fe9dfa2017-08-21 08:02:58 -0700158 synchronized (refCountLock) {
159 ++refCount;
160 }
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700161 }
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700162
163 @Override
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700164 public void release() {
sakal2fe9dfa2017-08-21 08:02:58 -0700165 synchronized (refCountLock) {
166 if (--refCount == 0 && releaseCallback != null) {
167 releaseCallback.run();
168 }
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700169 }
170 }
171
sakal836f60c2017-07-28 07:12:23 -0700172 @Override
173 public VideoFrame.Buffer cropAndScale(
174 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
175 return VideoFrame.cropAndScaleI420(
176 this, cropX, cropY, cropWidth, cropHeight, scaleWidth, scaleHeight);
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700177 }
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700178}