content: /*
   * 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. */
    public @C.VideoOutputMode 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);
    }
  }
  