1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.google.android.exoplayer2.video; 17 18 import androidx.annotation.Nullable; 19 import com.google.android.exoplayer2.C; 20 import com.google.android.exoplayer2.decoder.OutputBuffer; 21 import java.nio.ByteBuffer; 22 23 /** Video decoder output buffer containing video frame data. */ 24 public class VideoDecoderOutputBuffer extends OutputBuffer { 25 26 // LINT.IfChange 27 public static final int COLORSPACE_UNKNOWN = 0; 28 public static final int COLORSPACE_BT601 = 1; 29 public static final int COLORSPACE_BT709 = 2; 30 public static final int COLORSPACE_BT2020 = 3; 31 // LINT.ThenChange( 32 // ../../../../../../../../../../extensions/av1/src/main/jni/gav1_jni.cc, 33 // ../../../../../../../../../../extensions/vp9/src/main/jni/vpx_jni.cc 34 // ) 35 36 /** Decoder private data. */ 37 public int decoderPrivate; 38 39 /** Output mode. */ 40 @C.VideoOutputMode public int mode; 41 /** RGB buffer for RGB mode. */ 42 @Nullable public ByteBuffer data; 43 44 public int width; 45 public int height; 46 @Nullable public ColorInfo colorInfo; 47 48 /** YUV planes for YUV mode. */ 49 @Nullable public ByteBuffer[] yuvPlanes; 50 51 @Nullable public int[] yuvStrides; 52 public int colorspace; 53 54 /** 55 * Supplemental data related to the output frame, if {@link #hasSupplementalData()} returns true. 56 * If present, the buffer is populated with supplemental data from position 0 to its limit. 57 */ 58 @Nullable public ByteBuffer supplementalData; 59 60 private final Owner<VideoDecoderOutputBuffer> owner; 61 62 /** 63 * Creates VideoDecoderOutputBuffer. 64 * 65 * @param owner Buffer owner. 66 */ VideoDecoderOutputBuffer(Owner<VideoDecoderOutputBuffer> owner)67 public VideoDecoderOutputBuffer(Owner<VideoDecoderOutputBuffer> owner) { 68 this.owner = owner; 69 } 70 71 @Override release()72 public void release() { 73 owner.releaseOutputBuffer(this); 74 } 75 76 /** 77 * Initializes the buffer. 78 * 79 * @param timeUs The presentation timestamp for the buffer, in microseconds. 80 * @param mode The output mode. One of {@link C#VIDEO_OUTPUT_MODE_NONE}, {@link 81 * C#VIDEO_OUTPUT_MODE_YUV} and {@link C#VIDEO_OUTPUT_MODE_SURFACE_YUV}. 82 * @param supplementalData Supplemental data associated with the frame, or {@code null} if not 83 * present. It is safe to reuse the provided buffer after this method returns. 84 */ init( long timeUs, @C.VideoOutputMode int mode, @Nullable ByteBuffer supplementalData)85 public void init( 86 long timeUs, @C.VideoOutputMode int mode, @Nullable ByteBuffer supplementalData) { 87 this.timeUs = timeUs; 88 this.mode = mode; 89 if (supplementalData != null && supplementalData.hasRemaining()) { 90 addFlag(C.BUFFER_FLAG_HAS_SUPPLEMENTAL_DATA); 91 int size = supplementalData.limit(); 92 if (this.supplementalData == null || this.supplementalData.capacity() < size) { 93 this.supplementalData = ByteBuffer.allocate(size); 94 } else { 95 this.supplementalData.clear(); 96 } 97 this.supplementalData.put(supplementalData); 98 this.supplementalData.flip(); 99 supplementalData.position(0); 100 } else { 101 this.supplementalData = null; 102 } 103 } 104 105 /** 106 * Resizes the buffer based on the given stride. Called via JNI after decoding completes. 107 * 108 * @return Whether the buffer was resized successfully. 109 */ initForYuvFrame(int width, int height, int yStride, int uvStride, int colorspace)110 public boolean initForYuvFrame(int width, int height, int yStride, int uvStride, int colorspace) { 111 this.width = width; 112 this.height = height; 113 this.colorspace = colorspace; 114 int uvHeight = (int) (((long) height + 1) / 2); 115 if (!isSafeToMultiply(yStride, height) || !isSafeToMultiply(uvStride, uvHeight)) { 116 return false; 117 } 118 int yLength = yStride * height; 119 int uvLength = uvStride * uvHeight; 120 int minimumYuvSize = yLength + (uvLength * 2); 121 if (!isSafeToMultiply(uvLength, 2) || minimumYuvSize < yLength) { 122 return false; 123 } 124 125 // Initialize data. 126 if (data == null || data.capacity() < minimumYuvSize) { 127 data = ByteBuffer.allocateDirect(minimumYuvSize); 128 } else { 129 data.position(0); 130 data.limit(minimumYuvSize); 131 } 132 133 if (yuvPlanes == null) { 134 yuvPlanes = new ByteBuffer[3]; 135 } 136 137 ByteBuffer data = this.data; 138 ByteBuffer[] yuvPlanes = this.yuvPlanes; 139 140 // Rewrapping has to be done on every frame since the stride might have changed. 141 yuvPlanes[0] = data.slice(); 142 yuvPlanes[0].limit(yLength); 143 data.position(yLength); 144 yuvPlanes[1] = data.slice(); 145 yuvPlanes[1].limit(uvLength); 146 data.position(yLength + uvLength); 147 yuvPlanes[2] = data.slice(); 148 yuvPlanes[2].limit(uvLength); 149 if (yuvStrides == null) { 150 yuvStrides = new int[3]; 151 } 152 yuvStrides[0] = yStride; 153 yuvStrides[1] = uvStride; 154 yuvStrides[2] = uvStride; 155 return true; 156 } 157 158 /** 159 * Configures the buffer for the given frame dimensions when passing actual frame data via {@link 160 * #decoderPrivate}. Called via JNI after decoding completes. 161 */ initForPrivateFrame(int width, int height)162 public void initForPrivateFrame(int width, int height) { 163 this.width = width; 164 this.height = height; 165 } 166 167 /** 168 * Ensures that the result of multiplying individual numbers can fit into the size limit of an 169 * integer. 170 */ isSafeToMultiply(int a, int b)171 private static boolean isSafeToMultiply(int a, int b) { 172 return a >= 0 && b >= 0 && !(b > 0 && a >= Integer.MAX_VALUE / b); 173 } 174 } 175