diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java index feb380883e..b83db52943 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java @@ -116,30 +116,10 @@ public final class LibvpxVideoRenderer extends BaseRenderer { outputMode = VpxDecoder.OUTPUT_MODE_NONE; } - /** - * Returns whether the underlying libvpx library is available. - */ - public static boolean isLibvpxAvailable() { - return VpxDecoder.IS_AVAILABLE; - } - - /** - * Returns the version of the underlying libvpx library if available, otherwise {@code null}. - */ - public static String getLibvpxVersion() { - return isLibvpxAvailable() ? VpxDecoder.getLibvpxVersion() : null; - } - - /** - * Returns the configuration string with which the underlying libvpx library was built. - */ - public static String getLibvpxConfig() { - return isLibvpxAvailable() ? VpxDecoder.getLibvpxConfig() : null; - } - @Override public int supportsFormat(Format format) { - return isLibvpxAvailable() && MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType) + return VpxNativeLibraryHelper.isLibvpxAvailable() + && MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType) ? (FORMAT_HANDLED | ADAPTIVE_SEAMLESS) : FORMAT_UNSUPPORTED_TYPE; } diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxDecoder.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxDecoder.java index 48606b25ea..6bee22b988 100644 --- a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxDecoder.java +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxDecoder.java @@ -30,32 +30,6 @@ import java.nio.ByteBuffer; public static final int OUTPUT_MODE_YUV = 0; public static final int OUTPUT_MODE_RGB = 1; - /** - * Whether the underlying libvpx library is available. - */ - public static final boolean IS_AVAILABLE; - static { - boolean isAvailable; - try { - System.loadLibrary("vpx"); - System.loadLibrary("vpxJNI"); - isAvailable = true; - } catch (UnsatisfiedLinkError exception) { - isAvailable = false; - } - IS_AVAILABLE = isAvailable; - } - - /** - * Returns the version string of the underlying libvpx decoder. - */ - public static native String getLibvpxVersion(); - - /** - * Returns the configuration string with which the underlying libvpx library was built. - */ - public static native String getLibvpxConfig(); - private final long vpxDecContext; private volatile int outputMode; @@ -71,6 +45,9 @@ import java.nio.ByteBuffer; public VpxDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize) throws VpxDecoderException { super(new DecoderInputBuffer[numInputBuffers], new VpxOutputBuffer[numOutputBuffers]); + if (!VpxNativeLibraryHelper.isLibvpxAvailable()) { + throw new VpxDecoderException("Failed to load decoder native libraries."); + } vpxDecContext = vpxInit(); if (vpxDecContext == 0) { throw new VpxDecoderException("Failed to initialize decoder"); @@ -80,7 +57,7 @@ import java.nio.ByteBuffer; @Override public String getName() { - return "libvpx" + getLibvpxVersion(); + return "libvpx" + VpxNativeLibraryHelper.getLibvpxVersion(); } /** @@ -134,5 +111,4 @@ import java.nio.ByteBuffer; private native long vpxDecode(long context, ByteBuffer encoded, int length); private native int vpxGetFrame(long context, VpxOutputBuffer outputBuffer); private native String vpxGetErrorMessage(long context); - } diff --git a/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxNativeLibraryHelper.java b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxNativeLibraryHelper.java new file mode 100644 index 0000000000..81f356e1bb --- /dev/null +++ b/extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxNativeLibraryHelper.java @@ -0,0 +1,88 @@ +/* + * 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 com.google.android.exoplayer2.util.Assertions; + +/** + * Configure the native libraries that are used by the {@link VpxDecoder}. + */ +public final class VpxNativeLibraryHelper { + + private static boolean loadAttempted; + private static boolean isAvailable; + private static String[] nativeLibs = { "vpx", "vpxJNI" }; + + private static final Object lock = new Object(); + + private VpxNativeLibraryHelper() {} + + /** + * Override the default set of native libraries loaded for Vpx decoder support. + * If an application wishes to call this method, it must do so before calling any other method + * defined by this class, and before instantiating a {@link LibvpxVideoRenderer} instance. + */ + public static void setNativeLibraries(String... libs) { + synchronized (lock) { + Assertions.checkState(!loadAttempted, + "Vpx native libs must be set earlier, they have already been loaded."); + nativeLibs = libs; + } + } + + /** + * Returns whether the underlying libvpx library is available. + */ + public static boolean isLibvpxAvailable() { + return loadNativeLibraries(); + } + + /** + * Returns the version of the underlying libvpx library if available, otherwise {@code null}. + */ + public static String getLibvpxVersion() { + return isLibvpxAvailable() ? nativeGetLibvpxConfig() : null; + } + + /** + * Returns the configuration string with which the underlying libvpx library was built. + */ + public static String getLibvpxConfig() { + return isLibvpxAvailable() ? nativeGetLibvpxVersion() : null; + } + + private static boolean loadNativeLibraries() { + synchronized (lock) { + if (loadAttempted) { + return isAvailable; + } + + loadAttempted = true; + try { + for (String lib : VpxNativeLibraryHelper.nativeLibs) { + System.loadLibrary(lib); + } + isAvailable = true; + } catch (UnsatisfiedLinkError exception) { + isAvailable = false; + } + return isAvailable; + } + } + + private static native String nativeGetLibvpxConfig(); + private static native String nativeGetLibvpxVersion(); +} diff --git a/extensions/vp9/src/main/jni/vpx_jni.cc b/extensions/vp9/src/main/jni/vpx_jni.cc index edee954667..e3ecccc447 100644 --- a/extensions/vp9/src/main/jni/vpx_jni.cc +++ b/extensions/vp9/src/main/jni/vpx_jni.cc @@ -34,7 +34,7 @@ #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \ __VA_ARGS__)) -#define FUNC(RETURN_TYPE, NAME, ...) \ +#define VPX_DECODER_FUNC(RETURN_TYPE, NAME, ...) \ extern "C" { \ JNIEXPORT RETURN_TYPE \ Java_com_google_android_exoplayer2_ext_vp9_VpxDecoder_ ## NAME \ @@ -44,6 +44,16 @@ Java_com_google_android_exoplayer2_ext_vp9_VpxDecoder_ ## NAME \ (JNIEnv* env, jobject thiz, ##__VA_ARGS__)\ +#define VPX_NATIVE_LIBRARY_HELPER_FUNC(RETURN_TYPE, NAME, ...) \ + extern "C" { \ + JNIEXPORT RETURN_TYPE \ + Java_com_google_android_exoplayer2_ext_vp9_VpxNativeLibraryHelper_ ## NAME \ + (JNIEnv* env, jobject thiz, ##__VA_ARGS__);\ + } \ + JNIEXPORT RETURN_TYPE \ + Java_com_google_android_exoplayer2_ext_vp9_VpxNativeLibraryHelper_ ## NAME \ + (JNIEnv* env, jobject thiz, ##__VA_ARGS__)\ + // JNI references for VpxOutputBuffer class. static jmethodID initForRgbFrame; static jmethodID initForYuvFrame; @@ -58,7 +68,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { return JNI_VERSION_1_6; } -FUNC(jlong, vpxInit) { +VPX_DECODER_FUNC(jlong, vpxInit) { vpx_codec_ctx_t* context = new vpx_codec_ctx_t(); vpx_codec_dec_cfg_t cfg = {0, 0, 0}; cfg.threads = android_getCpuCount(); @@ -81,7 +91,7 @@ FUNC(jlong, vpxInit) { return reinterpret_cast(context); } -FUNC(jlong, vpxDecode, jlong jContext, jobject encoded, jint len) { +VPX_DECODER_FUNC(jlong, vpxDecode, jlong jContext, jobject encoded, jint len) { vpx_codec_ctx_t* const context = reinterpret_cast(jContext); const uint8_t* const buffer = reinterpret_cast(env->GetDirectBufferAddress(encoded)); @@ -94,14 +104,14 @@ FUNC(jlong, vpxDecode, jlong jContext, jobject encoded, jint len) { return 0; } -FUNC(jlong, vpxClose, jlong jContext) { +VPX_DECODER_FUNC(jlong, vpxClose, jlong jContext) { vpx_codec_ctx_t* const context = reinterpret_cast(jContext); vpx_codec_destroy(context); delete context; return 0; } -FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) { +VPX_DECODER_FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) { vpx_codec_ctx_t* const context = reinterpret_cast(jContext); vpx_codec_iter_t iter = NULL; const vpx_image_t* const img = vpx_codec_get_frame(context, &iter); @@ -166,15 +176,16 @@ FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) { return 0; } -FUNC(jstring, getLibvpxVersion) { - return env->NewStringUTF(vpx_codec_version_str()); -} - -FUNC(jstring, getLibvpxConfig) { - return env->NewStringUTF(vpx_codec_build_config()); -} - -FUNC(jstring, vpxGetErrorMessage, jlong jContext) { +VPX_DECODER_FUNC(jstring, vpxGetErrorMessage, jlong jContext) { vpx_codec_ctx_t* const context = reinterpret_cast(jContext); return env->NewStringUTF(vpx_codec_error(context)); } + +VPX_NATIVE_LIBRARY_HELPER_FUNC(jstring, nativeGetLibvpxVersion) { + return env->NewStringUTF(vpx_codec_version_str()); +} + +VPX_NATIVE_LIBRARY_HELPER_FUNC(jstring, nativeGetLibvpxConfig) { + return env->NewStringUTF(vpx_codec_build_config()); +} +