diff options
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/VideoDecoderOutputBuffer.java')
-rw-r--r-- | mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/VideoDecoderOutputBuffer.java | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/VideoDecoderOutputBuffer.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/VideoDecoderOutputBuffer.java new file mode 100644 index 0000000000..b09e8b759a --- /dev/null +++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/video/VideoDecoderOutputBuffer.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.mozilla.thirdparty.com.google.android.exoplayer2.video; + +import androidx.annotation.Nullable; +import org.mozilla.thirdparty.com.google.android.exoplayer2.C; +import org.mozilla.thirdparty.com.google.android.exoplayer2.decoder.OutputBuffer; +import java.nio.ByteBuffer; + +/** Video decoder output buffer containing video frame data. */ +public class VideoDecoderOutputBuffer extends OutputBuffer { + + /** Buffer owner. */ + public interface Owner { + + /** + * Releases the buffer. + * + * @param outputBuffer Output buffer. + */ + void releaseOutputBuffer(VideoDecoderOutputBuffer outputBuffer); + } + + // LINT.IfChange + public static final int COLORSPACE_UNKNOWN = 0; + public static final int COLORSPACE_BT601 = 1; + public static final int COLORSPACE_BT709 = 2; + public static final int COLORSPACE_BT2020 = 3; + // LINT.ThenChange( + // ../../../../../../../../../../extensions/av1/src/main/jni/gav1_jni.cc, + // ../../../../../../../../../../extensions/vp9/src/main/jni/vpx_jni.cc + // ) + + /** Decoder private data. */ + public int decoderPrivate; + + /** Output mode. */ + @C.VideoOutputMode public int mode; + /** RGB buffer for RGB mode. */ + @Nullable public ByteBuffer data; + + public int width; + public int height; + @Nullable public ColorInfo colorInfo; + + /** YUV planes for YUV mode. */ + @Nullable public ByteBuffer[] yuvPlanes; + + @Nullable public int[] yuvStrides; + public int colorspace; + + /** + * Supplemental data related to the output frame, if {@link #hasSupplementalData()} returns true. + * If present, the buffer is populated with supplemental data from position 0 to its limit. + */ + @Nullable public ByteBuffer supplementalData; + + private final Owner owner; + + /** + * Creates VideoDecoderOutputBuffer. + * + * @param owner Buffer owner. + */ + public VideoDecoderOutputBuffer(Owner owner) { + this.owner = owner; + } + + @Override + public void release() { + owner.releaseOutputBuffer(this); + } + + /** + * Initializes the buffer. + * + * @param timeUs The presentation timestamp for the buffer, in microseconds. + * @param mode The output mode. One of {@link C#VIDEO_OUTPUT_MODE_NONE}, {@link + * C#VIDEO_OUTPUT_MODE_YUV} and {@link C#VIDEO_OUTPUT_MODE_SURFACE_YUV}. + * @param supplementalData Supplemental data associated with the frame, or {@code null} if not + * present. It is safe to reuse the provided buffer after this method returns. + */ + public void init( + long timeUs, @C.VideoOutputMode int mode, @Nullable ByteBuffer supplementalData) { + this.timeUs = timeUs; + this.mode = mode; + if (supplementalData != null && supplementalData.hasRemaining()) { + addFlag(C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA); + int size = supplementalData.limit(); + if (this.supplementalData == null || this.supplementalData.capacity() < size) { + this.supplementalData = ByteBuffer.allocate(size); + } else { + this.supplementalData.clear(); + } + this.supplementalData.put(supplementalData); + this.supplementalData.flip(); + supplementalData.position(0); + } else { + this.supplementalData = null; + } + } + + /** + * Resizes the buffer based on the given stride. Called via JNI after decoding completes. + * + * @return Whether the buffer was resized successfully. + */ + public boolean initForYuvFrame(int width, int height, int yStride, int uvStride, int colorspace) { + this.width = width; + this.height = height; + this.colorspace = colorspace; + int uvHeight = (int) (((long) height + 1) / 2); + if (!isSafeToMultiply(yStride, height) || !isSafeToMultiply(uvStride, uvHeight)) { + return false; + } + int yLength = yStride * height; + int uvLength = uvStride * uvHeight; + int minimumYuvSize = yLength + (uvLength * 2); + if (!isSafeToMultiply(uvLength, 2) || minimumYuvSize < yLength) { + return false; + } + + // Initialize data. + if (data == null || data.capacity() < minimumYuvSize) { + data = ByteBuffer.allocateDirect(minimumYuvSize); + } else { + data.position(0); + data.limit(minimumYuvSize); + } + + if (yuvPlanes == null) { + yuvPlanes = new ByteBuffer[3]; + } + + ByteBuffer data = this.data; + ByteBuffer[] yuvPlanes = this.yuvPlanes; + + // Rewrapping has to be done on every frame since the stride might have changed. + yuvPlanes[0] = data.slice(); + yuvPlanes[0].limit(yLength); + data.position(yLength); + yuvPlanes[1] = data.slice(); + yuvPlanes[1].limit(uvLength); + data.position(yLength + uvLength); + yuvPlanes[2] = data.slice(); + yuvPlanes[2].limit(uvLength); + if (yuvStrides == null) { + yuvStrides = new int[3]; + } + yuvStrides[0] = yStride; + yuvStrides[1] = uvStride; + yuvStrides[2] = uvStride; + return true; + } + + /** + * Configures the buffer for the given frame dimensions when passing actual frame data via {@link + * #decoderPrivate}. Called via JNI after decoding completes. + */ + public void initForPrivateFrame(int width, int height) { + this.width = width; + this.height = height; + } + + /** + * Ensures that the result of multiplying individual numbers can fit into the size limit of an + * integer. + */ + private static boolean isSafeToMultiply(int a, int b) { + return a >= 0 && b >= 0 && !(b > 0 && a >= Integer.MAX_VALUE / b); + } +} |