blob: 7231fdab911f92a15fe6c4919cc6acc6cd30e03e [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;
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020014import javax.annotation.Nullable;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -070015import org.webrtc.VideoFrame.I420Buffer;
16
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020017/** Implementation of VideoFrame.I420Buffer backed by Java direct byte buffers. */
18public class JavaI420Buffer implements VideoFrame.I420Buffer {
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -070019 private final int width;
20 private final int height;
sakal836f60c2017-07-28 07:12:23 -070021 private final ByteBuffer dataY;
22 private final ByteBuffer dataU;
23 private final ByteBuffer dataV;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070024 private final int strideY;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070025 private final int strideU;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070026 private final int strideV;
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020027 private final RefCountDelegate refCountDelegate;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070028
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020029 private JavaI420Buffer(int width, int height, ByteBuffer dataY, int strideY, ByteBuffer dataU,
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020030 int strideU, ByteBuffer dataV, int strideV, @Nullable Runnable releaseCallback) {
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -070031 this.width = width;
32 this.height = height;
sakal836f60c2017-07-28 07:12:23 -070033 this.dataY = dataY;
34 this.dataU = dataU;
35 this.dataV = dataV;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070036 this.strideY = strideY;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070037 this.strideU = strideU;
Bjorn Mellem8fb23612017-07-18 11:33:39 -070038 this.strideV = strideV;
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +020039 this.refCountDelegate = new RefCountDelegate(releaseCallback);
Bjorn Mellem8fb23612017-07-18 11:33:39 -070040 }
41
Magnus Jedvert65070542018-06-14 12:23:01 +020042 private static void checkCapacity(ByteBuffer data, int width, int height, int stride) {
43 // The last row does not necessarily need padding.
44 final int minCapacity = stride * (height - 1) + width;
45 if (data.capacity() < minCapacity) {
46 throw new IllegalArgumentException(
47 "Buffer must be at least " + minCapacity + " bytes, but was " + data.capacity());
48 }
49 }
50
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020051 /** Wraps existing ByteBuffers into JavaI420Buffer object without copying the contents. */
52 public static JavaI420Buffer wrap(int width, int height, ByteBuffer dataY, int strideY,
53 ByteBuffer dataU, int strideU, ByteBuffer dataV, int strideV, Runnable releaseCallback) {
54 if (dataY == null || dataU == null || dataV == null) {
55 throw new IllegalArgumentException("Data buffers cannot be null.");
56 }
57 if (!dataY.isDirect() || !dataU.isDirect() || !dataV.isDirect()) {
58 throw new IllegalArgumentException("Data buffers must be direct byte buffers.");
59 }
60
61 // Slice the buffers to prevent external modifications to the position / limit of the buffer.
62 // Note that this doesn't protect the contents of the buffers from modifications.
63 dataY = dataY.slice();
64 dataU = dataU.slice();
65 dataV = dataV.slice();
66
Magnus Jedvert65070542018-06-14 12:23:01 +020067 final int chromaWidth = (width + 1) / 2;
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020068 final int chromaHeight = (height + 1) / 2;
Magnus Jedvert65070542018-06-14 12:23:01 +020069 checkCapacity(dataY, width, height, strideY);
70 checkCapacity(dataU, chromaWidth, chromaHeight, strideU);
71 checkCapacity(dataV, chromaWidth, chromaHeight, strideV);
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020072
73 return new JavaI420Buffer(
74 width, height, dataY, strideY, dataU, strideU, dataV, strideV, releaseCallback);
75 }
76
Bjorn Mellem8fb23612017-07-18 11:33:39 -070077 /** Allocates an empty I420Buffer suitable for an image of the given dimensions. */
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020078 public static JavaI420Buffer allocate(int width, int height) {
Bjorn Mellem8fb23612017-07-18 11:33:39 -070079 int chromaHeight = (height + 1) / 2;
80 int strideUV = (width + 1) / 2;
81 int yPos = 0;
82 int uPos = yPos + width * height;
83 int vPos = uPos + strideUV * chromaHeight;
sakal836f60c2017-07-28 07:12:23 -070084
Sami Kalliomäki06110652018-02-20 11:19:58 +010085 ByteBuffer buffer =
86 JniCommon.nativeAllocateByteBuffer(width * height + 2 * strideUV * chromaHeight);
sakal836f60c2017-07-28 07:12:23 -070087
88 buffer.position(yPos);
89 buffer.limit(uPos);
90 ByteBuffer dataY = buffer.slice();
91
92 buffer.position(uPos);
93 buffer.limit(vPos);
94 ByteBuffer dataU = buffer.slice();
95
96 buffer.position(vPos);
97 buffer.limit(vPos + strideUV * chromaHeight);
98 ByteBuffer dataV = buffer.slice();
99
Sami Kalliomäki06110652018-02-20 11:19:58 +0100100 return new JavaI420Buffer(width, height, dataY, width, dataU, strideUV, dataV, strideUV,
101 () -> { JniCommon.nativeFreeByteBuffer(buffer); });
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700102 }
103
104 @Override
105 public int getWidth() {
106 return width;
107 }
108
109 @Override
110 public int getHeight() {
111 return height;
112 }
113
114 @Override
115 public ByteBuffer getDataY() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700116 // Return a slice to prevent relative reads from changing the position.
117 return dataY.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700118 }
119
120 @Override
121 public ByteBuffer getDataU() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700122 // Return a slice to prevent relative reads from changing the position.
123 return dataU.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700124 }
125
126 @Override
127 public ByteBuffer getDataV() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700128 // Return a slice to prevent relative reads from changing the position.
129 return dataV.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700130 }
131
132 @Override
133 public int getStrideY() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700134 return strideY;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700135 }
136
137 @Override
138 public int getStrideU() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700139 return strideU;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700140 }
141
142 @Override
143 public int getStrideV() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700144 return strideV;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700145 }
146
147 @Override
148 public I420Buffer toI420() {
sakal5ca60cc2017-08-09 05:25:49 -0700149 retain();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700150 return this;
151 }
152
153 @Override
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700154 public void retain() {
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200155 refCountDelegate.retain();
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700156 }
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700157
158 @Override
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700159 public void release() {
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200160 refCountDelegate.release();
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700161 }
162
sakal836f60c2017-07-28 07:12:23 -0700163 @Override
164 public VideoFrame.Buffer cropAndScale(
165 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
166 return VideoFrame.cropAndScaleI420(
167 this, cropX, cropY, cropWidth, cropHeight, scaleWidth, scaleHeight);
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700168 }
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700169}