| /* |
| * 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 com.google.android.exoplayer2.decoder; |
| |
| import androidx.annotation.Nullable; |
| import com.google.android.exoplayer2.C; |
| import com.google.android.exoplayer2.Format; |
| import java.nio.ByteBuffer; |
| |
| /** Video decoder output buffer containing video frame data. */ |
| public class VideoDecoderOutputBuffer extends DecoderOutputBuffer { |
| |
| 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; |
| |
| /** Decoder private data. Used from native code. */ |
| 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; |
| /** The format of the input from which this output buffer was decoded. */ |
| @Nullable public Format format; |
| |
| /** 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<VideoDecoderOutputBuffer> owner; |
| |
| /** |
| * Creates VideoDecoderOutputBuffer. |
| * |
| * @param owner Buffer owner. |
| */ |
| public VideoDecoderOutputBuffer(Owner<VideoDecoderOutputBuffer> 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); |
| } |
| } |