blob: 19dd3e91e21cfe777fcb93f1d72d445e0b474c9f [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
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020042 /** Wraps existing ByteBuffers into JavaI420Buffer object without copying the contents. */
43 public static JavaI420Buffer wrap(int width, int height, ByteBuffer dataY, int strideY,
44 ByteBuffer dataU, int strideU, ByteBuffer dataV, int strideV, Runnable releaseCallback) {
45 if (dataY == null || dataU == null || dataV == null) {
46 throw new IllegalArgumentException("Data buffers cannot be null.");
47 }
48 if (!dataY.isDirect() || !dataU.isDirect() || !dataV.isDirect()) {
49 throw new IllegalArgumentException("Data buffers must be direct byte buffers.");
50 }
51
52 // Slice the buffers to prevent external modifications to the position / limit of the buffer.
53 // Note that this doesn't protect the contents of the buffers from modifications.
54 dataY = dataY.slice();
55 dataU = dataU.slice();
56 dataV = dataV.slice();
57
58 final int chromaHeight = (height + 1) / 2;
59 final int minCapacityY = strideY * height;
60 final int minCapacityU = strideU * chromaHeight;
61 final int minCapacityV = strideV * chromaHeight;
62 if (dataY.capacity() < minCapacityY) {
63 throw new IllegalArgumentException("Y-buffer must be at least " + minCapacityY + " bytes.");
64 }
65 if (dataU.capacity() < minCapacityU) {
66 throw new IllegalArgumentException("U-buffer must be at least " + minCapacityU + " bytes.");
67 }
68 if (dataV.capacity() < minCapacityV) {
69 throw new IllegalArgumentException("V-buffer must be at least " + minCapacityV + " bytes.");
70 }
71
72 return new JavaI420Buffer(
73 width, height, dataY, strideY, dataU, strideU, dataV, strideV, releaseCallback);
74 }
75
Bjorn Mellem8fb23612017-07-18 11:33:39 -070076 /** Allocates an empty I420Buffer suitable for an image of the given dimensions. */
Sami Kalliomäki48b3c022017-10-04 17:01:00 +020077 public static JavaI420Buffer allocate(int width, int height) {
Bjorn Mellem8fb23612017-07-18 11:33:39 -070078 int chromaHeight = (height + 1) / 2;
79 int strideUV = (width + 1) / 2;
80 int yPos = 0;
81 int uPos = yPos + width * height;
82 int vPos = uPos + strideUV * chromaHeight;
sakal836f60c2017-07-28 07:12:23 -070083
Sami Kalliomäki06110652018-02-20 11:19:58 +010084 ByteBuffer buffer =
85 JniCommon.nativeAllocateByteBuffer(width * height + 2 * strideUV * chromaHeight);
sakal836f60c2017-07-28 07:12:23 -070086
87 buffer.position(yPos);
88 buffer.limit(uPos);
89 ByteBuffer dataY = buffer.slice();
90
91 buffer.position(uPos);
92 buffer.limit(vPos);
93 ByteBuffer dataU = buffer.slice();
94
95 buffer.position(vPos);
96 buffer.limit(vPos + strideUV * chromaHeight);
97 ByteBuffer dataV = buffer.slice();
98
Sami Kalliomäki06110652018-02-20 11:19:58 +010099 return new JavaI420Buffer(width, height, dataY, width, dataU, strideUV, dataV, strideUV,
100 () -> { JniCommon.nativeFreeByteBuffer(buffer); });
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700101 }
102
103 @Override
104 public int getWidth() {
105 return width;
106 }
107
108 @Override
109 public int getHeight() {
110 return height;
111 }
112
113 @Override
114 public ByteBuffer getDataY() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700115 // Return a slice to prevent relative reads from changing the position.
116 return dataY.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700117 }
118
119 @Override
120 public ByteBuffer getDataU() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700121 // Return a slice to prevent relative reads from changing the position.
122 return dataU.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700123 }
124
125 @Override
126 public ByteBuffer getDataV() {
Bjorn Mellemd6293142017-09-29 11:33:52 -0700127 // Return a slice to prevent relative reads from changing the position.
128 return dataV.slice();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700129 }
130
131 @Override
132 public int getStrideY() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700133 return strideY;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700134 }
135
136 @Override
137 public int getStrideU() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700138 return strideU;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700139 }
140
141 @Override
142 public int getStrideV() {
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700143 return strideV;
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700144 }
145
146 @Override
147 public I420Buffer toI420() {
sakal5ca60cc2017-08-09 05:25:49 -0700148 retain();
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700149 return this;
150 }
151
152 @Override
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700153 public void retain() {
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200154 refCountDelegate.retain();
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700155 }
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700156
157 @Override
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700158 public void release() {
Sami Kalliomäki61db3fd2018-04-09 17:51:19 +0200159 refCountDelegate.release();
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700160 }
161
sakal836f60c2017-07-28 07:12:23 -0700162 @Override
163 public VideoFrame.Buffer cropAndScale(
164 int cropX, int cropY, int cropWidth, int cropHeight, int scaleWidth, int scaleHeight) {
165 return VideoFrame.cropAndScaleI420(
166 this, cropX, cropY, cropWidth, cropHeight, scaleWidth, scaleHeight);
Bjorn Mellem8fb23612017-07-18 11:33:39 -0700167 }
Bjorn Mellem9fbbdc22017-06-16 09:23:50 -0700168}