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