| /* |
| * Copyright (C) 2016 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.ext.vp9; |
| |
| import static java.lang.Runtime.getRuntime; |
| |
| import android.os.Handler; |
| import android.view.Surface; |
| import androidx.annotation.Nullable; |
| import com.google.android.exoplayer2.C; |
| import com.google.android.exoplayer2.Format; |
| import com.google.android.exoplayer2.RendererCapabilities; |
| import com.google.android.exoplayer2.drm.ExoMediaCrypto; |
| import com.google.android.exoplayer2.util.MimeTypes; |
| import com.google.android.exoplayer2.util.TraceUtil; |
| import com.google.android.exoplayer2.video.DecoderVideoRenderer; |
| import com.google.android.exoplayer2.video.VideoDecoderOutputBuffer; |
| import com.google.android.exoplayer2.video.VideoRendererEventListener; |
| |
| /** Decodes and renders video using the native VP9 decoder. */ |
| public class LibvpxVideoRenderer extends DecoderVideoRenderer { |
| |
| private static final String TAG = "LibvpxVideoRenderer"; |
| |
| /** The number of input buffers. */ |
| private final int numInputBuffers; |
| /** |
| * The number of output buffers. The renderer may limit the minimum possible value due to |
| * requiring multiple output buffers to be dequeued at a time for it to make progress. |
| */ |
| private final int numOutputBuffers; |
| /** |
| * The default input buffer size. The value is based on <a |
| * href="https://android.googlesource.com/platform/frameworks/av/+/d42b90c5183fbd9d6a28d9baee613fddbf8131d6/media/libstagefright/codecs/on2/dec/SoftVPX.cpp">SoftVPX.cpp</a>. |
| */ |
| private static final int DEFAULT_INPUT_BUFFER_SIZE = 768 * 1024; |
| |
| private final int threads; |
| |
| @Nullable private VpxDecoder decoder; |
| |
| /** |
| * Creates a new instance. |
| * |
| * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer |
| * can attempt to seamlessly join an ongoing playback. |
| */ |
| public LibvpxVideoRenderer(long allowedJoiningTimeMs) { |
| this(allowedJoiningTimeMs, null, null, 0); |
| } |
| |
| /** |
| * Creates a new instance. |
| * |
| * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer |
| * can attempt to seamlessly join an ongoing playback. |
| * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be |
| * null if delivery of events is not required. |
| * @param eventListener A listener of events. May be null if delivery of events is not required. |
| * @param maxDroppedFramesToNotify The maximum number of frames that can be dropped between |
| * invocations of {@link VideoRendererEventListener#onDroppedFrames(int, long)}. |
| */ |
| public LibvpxVideoRenderer( |
| long allowedJoiningTimeMs, |
| @Nullable Handler eventHandler, |
| @Nullable VideoRendererEventListener eventListener, |
| int maxDroppedFramesToNotify) { |
| this( |
| allowedJoiningTimeMs, |
| eventHandler, |
| eventListener, |
| maxDroppedFramesToNotify, |
| getRuntime().availableProcessors(), |
| /* numInputBuffers= */ 4, |
| /* numOutputBuffers= */ 4); |
| } |
| |
| /** |
| * Creates a new instance. |
| * |
| * @param allowedJoiningTimeMs The maximum duration in milliseconds for which this video renderer |
| * can attempt to seamlessly join an ongoing playback. |
| * @param eventHandler A handler to use when delivering events to {@code eventListener}. May be |
| * null if delivery of events is not required. |
| * @param eventListener A listener of events. May be null if delivery of events is not required. |
| * @param maxDroppedFramesToNotify The maximum number of frames that can be dropped between |
| * invocations of {@link VideoRendererEventListener#onDroppedFrames(int, long)}. |
| * @param threads Number of threads libvpx will use to decode. |
| * @param numInputBuffers Number of input buffers. |
| * @param numOutputBuffers Number of output buffers. |
| */ |
| @SuppressWarnings("deprecation") |
| public LibvpxVideoRenderer( |
| long allowedJoiningTimeMs, |
| @Nullable Handler eventHandler, |
| @Nullable VideoRendererEventListener eventListener, |
| int maxDroppedFramesToNotify, |
| int threads, |
| int numInputBuffers, |
| int numOutputBuffers) { |
| super(allowedJoiningTimeMs, eventHandler, eventListener, maxDroppedFramesToNotify); |
| this.threads = threads; |
| this.numInputBuffers = numInputBuffers; |
| this.numOutputBuffers = numOutputBuffers; |
| } |
| |
| @Override |
| public String getName() { |
| return TAG; |
| } |
| |
| @Override |
| @Capabilities |
| public final int supportsFormat(Format format) { |
| if (!VpxLibrary.isAvailable() || !MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)) { |
| return RendererCapabilities.create(FORMAT_UNSUPPORTED_TYPE); |
| } |
| boolean drmIsSupported = |
| format.drmInitData == null |
| || VpxLibrary.matchesExpectedExoMediaCryptoType(format.exoMediaCryptoType); |
| if (!drmIsSupported) { |
| return RendererCapabilities.create(FORMAT_UNSUPPORTED_DRM); |
| } |
| return RendererCapabilities.create(FORMAT_HANDLED, ADAPTIVE_SEAMLESS, TUNNELING_NOT_SUPPORTED); |
| } |
| |
| @Override |
| protected VpxDecoder createDecoder(Format format, @Nullable ExoMediaCrypto mediaCrypto) |
| throws VpxDecoderException { |
| TraceUtil.beginSection("createVpxDecoder"); |
| int initialInputBufferSize = |
| format.maxInputSize != Format.NO_VALUE ? format.maxInputSize : DEFAULT_INPUT_BUFFER_SIZE; |
| VpxDecoder decoder = |
| new VpxDecoder( |
| numInputBuffers, numOutputBuffers, initialInputBufferSize, mediaCrypto, threads); |
| this.decoder = decoder; |
| TraceUtil.endSection(); |
| return decoder; |
| } |
| |
| @Override |
| protected void renderOutputBufferToSurface(VideoDecoderOutputBuffer outputBuffer, Surface surface) |
| throws VpxDecoderException { |
| if (decoder == null) { |
| throw new VpxDecoderException( |
| "Failed to render output buffer to surface: decoder is not initialized."); |
| } |
| decoder.renderToSurface(outputBuffer, surface); |
| outputBuffer.release(); |
| } |
| |
| @Override |
| protected void setDecoderOutputMode(@C.VideoOutputMode int outputMode) { |
| if (decoder != null) { |
| decoder.setOutputMode(outputMode); |
| } |
| } |
| } |