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.ext.av1; 17 18 import android.view.Surface; 19 import androidx.annotation.Nullable; 20 import com.google.android.exoplayer2.C; 21 import com.google.android.exoplayer2.decoder.SimpleDecoder; 22 import com.google.android.exoplayer2.util.Util; 23 import com.google.android.exoplayer2.video.VideoDecoderInputBuffer; 24 import com.google.android.exoplayer2.video.VideoDecoderOutputBuffer; 25 import java.nio.ByteBuffer; 26 27 /** Gav1 decoder. */ 28 /* package */ final class Gav1Decoder 29 extends SimpleDecoder<VideoDecoderInputBuffer, VideoDecoderOutputBuffer, Gav1DecoderException> { 30 31 // LINT.IfChange 32 private static final int GAV1_ERROR = 0; 33 private static final int GAV1_OK = 1; 34 private static final int GAV1_DECODE_ONLY = 2; 35 // LINT.ThenChange(../../../../../../../jni/gav1_jni.cc) 36 37 private final long gav1DecoderContext; 38 39 @C.VideoOutputMode private volatile int outputMode; 40 41 /** 42 * Creates a Gav1Decoder. 43 * 44 * @param numInputBuffers Number of input buffers. 45 * @param numOutputBuffers Number of output buffers. 46 * @param initialInputBufferSize The initial size of each input buffer, in bytes. 47 * @param threads Number of threads libgav1 will use to decode. 48 * @throws Gav1DecoderException Thrown if an exception occurs when initializing the decoder. 49 */ Gav1Decoder( int numInputBuffers, int numOutputBuffers, int initialInputBufferSize, int threads)50 public Gav1Decoder( 51 int numInputBuffers, int numOutputBuffers, int initialInputBufferSize, int threads) 52 throws Gav1DecoderException { 53 super( 54 new VideoDecoderInputBuffer[numInputBuffers], 55 new VideoDecoderOutputBuffer[numOutputBuffers]); 56 if (!Gav1Library.isAvailable()) { 57 throw new Gav1DecoderException("Failed to load decoder native library."); 58 } 59 gav1DecoderContext = gav1Init(threads); 60 if (gav1DecoderContext == GAV1_ERROR || gav1CheckError(gav1DecoderContext) == GAV1_ERROR) { 61 throw new Gav1DecoderException( 62 "Failed to initialize decoder. Error: " + gav1GetErrorMessage(gav1DecoderContext)); 63 } 64 setInitialInputBufferSize(initialInputBufferSize); 65 } 66 67 @Override getName()68 public String getName() { 69 return "libgav1"; 70 } 71 72 /** 73 * Sets the output mode for frames rendered by the decoder. 74 * 75 * @param outputMode The output mode. 76 */ setOutputMode(@.VideoOutputMode int outputMode)77 public void setOutputMode(@C.VideoOutputMode int outputMode) { 78 this.outputMode = outputMode; 79 } 80 81 @Override createInputBuffer()82 protected VideoDecoderInputBuffer createInputBuffer() { 83 return new VideoDecoderInputBuffer(); 84 } 85 86 @Override createOutputBuffer()87 protected VideoDecoderOutputBuffer createOutputBuffer() { 88 return new VideoDecoderOutputBuffer(this::releaseOutputBuffer); 89 } 90 91 @Override 92 @Nullable decode( VideoDecoderInputBuffer inputBuffer, VideoDecoderOutputBuffer outputBuffer, boolean reset)93 protected Gav1DecoderException decode( 94 VideoDecoderInputBuffer inputBuffer, VideoDecoderOutputBuffer outputBuffer, boolean reset) { 95 ByteBuffer inputData = Util.castNonNull(inputBuffer.data); 96 int inputSize = inputData.limit(); 97 if (gav1Decode(gav1DecoderContext, inputData, inputSize) == GAV1_ERROR) { 98 return new Gav1DecoderException( 99 "gav1Decode error: " + gav1GetErrorMessage(gav1DecoderContext)); 100 } 101 102 boolean decodeOnly = inputBuffer.isDecodeOnly(); 103 if (!decodeOnly) { 104 outputBuffer.init(inputBuffer.timeUs, outputMode, /* supplementalData= */ null); 105 } 106 // We need to dequeue the decoded frame from the decoder even when the input data is 107 // decode-only. 108 int getFrameResult = gav1GetFrame(gav1DecoderContext, outputBuffer, decodeOnly); 109 if (getFrameResult == GAV1_ERROR) { 110 return new Gav1DecoderException( 111 "gav1GetFrame error: " + gav1GetErrorMessage(gav1DecoderContext)); 112 } 113 if (getFrameResult == GAV1_DECODE_ONLY) { 114 outputBuffer.addFlag(C.BUFFER_FLAG_DECODE_ONLY); 115 } 116 if (!decodeOnly) { 117 outputBuffer.colorInfo = inputBuffer.colorInfo; 118 } 119 120 return null; 121 } 122 123 @Override createUnexpectedDecodeException(Throwable error)124 protected Gav1DecoderException createUnexpectedDecodeException(Throwable error) { 125 return new Gav1DecoderException("Unexpected decode error", error); 126 } 127 128 @Override release()129 public void release() { 130 super.release(); 131 gav1Close(gav1DecoderContext); 132 } 133 134 @Override releaseOutputBuffer(VideoDecoderOutputBuffer buffer)135 protected void releaseOutputBuffer(VideoDecoderOutputBuffer buffer) { 136 // Decode only frames do not acquire a reference on the internal decoder buffer and thus do not 137 // require a call to gav1ReleaseFrame. 138 if (buffer.mode == C.VIDEO_OUTPUT_MODE_SURFACE_YUV && !buffer.isDecodeOnly()) { 139 gav1ReleaseFrame(gav1DecoderContext, buffer); 140 } 141 super.releaseOutputBuffer(buffer); 142 } 143 144 /** 145 * Renders output buffer to the given surface. Must only be called when in {@link 146 * C#VIDEO_OUTPUT_MODE_SURFACE_YUV} mode. 147 * 148 * @param outputBuffer Output buffer. 149 * @param surface Output surface. 150 * @throws Gav1DecoderException Thrown if called with invalid output mode or frame rendering 151 * fails. 152 */ renderToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface)153 public void renderToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface) 154 throws Gav1DecoderException { 155 if (outputBuffer.mode != C.VIDEO_OUTPUT_MODE_SURFACE_YUV) { 156 throw new Gav1DecoderException("Invalid output mode."); 157 } 158 if (gav1RenderFrame(gav1DecoderContext, surface, outputBuffer) == GAV1_ERROR) { 159 throw new Gav1DecoderException( 160 "Buffer render error: " + gav1GetErrorMessage(gav1DecoderContext)); 161 } 162 } 163 164 /** 165 * Initializes a libgav1 decoder. 166 * 167 * @param threads Number of threads to be used by a libgav1 decoder. 168 * @return The address of the decoder context or {@link #GAV1_ERROR} if there was an error. 169 */ gav1Init(int threads)170 private native long gav1Init(int threads); 171 172 /** 173 * Deallocates the decoder context. 174 * 175 * @param context Decoder context. 176 */ gav1Close(long context)177 private native void gav1Close(long context); 178 179 /** 180 * Decodes the encoded data passed. 181 * 182 * @param context Decoder context. 183 * @param encodedData Encoded data. 184 * @param length Length of the data buffer. 185 * @return {@link #GAV1_OK} if successful, {@link #GAV1_ERROR} if an error occurred. 186 */ gav1Decode(long context, ByteBuffer encodedData, int length)187 private native int gav1Decode(long context, ByteBuffer encodedData, int length); 188 189 /** 190 * Gets the decoded frame. 191 * 192 * @param context Decoder context. 193 * @param outputBuffer Output buffer for the decoded frame. 194 * @return {@link #GAV1_OK} if successful, {@link #GAV1_DECODE_ONLY} if successful but the frame 195 * is decode-only, {@link #GAV1_ERROR} if an error occurred. 196 */ gav1GetFrame( long context, VideoDecoderOutputBuffer outputBuffer, boolean decodeOnly)197 private native int gav1GetFrame( 198 long context, VideoDecoderOutputBuffer outputBuffer, boolean decodeOnly); 199 200 /** 201 * Renders the frame to the surface. Used with {@link C#VIDEO_OUTPUT_MODE_SURFACE_YUV} only. 202 * 203 * @param context Decoder context. 204 * @param surface Output surface. 205 * @param outputBuffer Output buffer with the decoded frame. 206 * @return {@link #GAV1_OK} if successful, {@link #GAV1_ERROR} if an error occurred. 207 */ gav1RenderFrame( long context, Surface surface, VideoDecoderOutputBuffer outputBuffer)208 private native int gav1RenderFrame( 209 long context, Surface surface, VideoDecoderOutputBuffer outputBuffer); 210 211 /** 212 * Releases the frame. Used with {@link C#VIDEO_OUTPUT_MODE_SURFACE_YUV} only. 213 * 214 * @param context Decoder context. 215 * @param outputBuffer Output buffer. 216 */ gav1ReleaseFrame(long context, VideoDecoderOutputBuffer outputBuffer)217 private native void gav1ReleaseFrame(long context, VideoDecoderOutputBuffer outputBuffer); 218 219 /** 220 * Returns a human-readable string describing the last error encountered in the given context. 221 * 222 * @param context Decoder context. 223 * @return A string describing the last encountered error. 224 */ gav1GetErrorMessage(long context)225 private native String gav1GetErrorMessage(long context); 226 227 /** 228 * Returns whether an error occurred. 229 * 230 * @param context Decoder context. 231 * @return {@link #GAV1_OK} if there was no error, {@link #GAV1_ERROR} if an error occurred. 232 */ gav1CheckError(long context)233 private native int gav1CheckError(long context); 234 } 235