mirror of
https://github.com/samsonjs/media.git
synced 2026-04-05 11:15:46 +00:00
Allow replacement of libraries for Opus/Flac/Ffmpeg too
- Also make some of the naming more concise + misc style cleanup. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=132899979
This commit is contained in:
parent
c46c6f18c2
commit
de67fa5017
20 changed files with 432 additions and 300 deletions
|
|
@ -62,11 +62,11 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
|
|||
|
||||
@Override
|
||||
public int supportsFormat(Format format) {
|
||||
if (!FfmpegDecoder.IS_AVAILABLE) {
|
||||
if (!FfmpegLibrary.isAvailable()) {
|
||||
return FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
String mimeType = format.sampleMimeType;
|
||||
return FfmpegDecoder.supportsFormat(mimeType) ? FORMAT_HANDLED
|
||||
return FfmpegLibrary.supportsFormat(mimeType) ? FORMAT_HANDLED
|
||||
: MimeTypes.isAudio(mimeType) ? FORMAT_UNSUPPORTED_SUBTYPE : FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,39 +23,11 @@ import java.nio.ByteBuffer;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JNI wrapper for FFmpeg. Only audio decoding is supported.
|
||||
* FFmpeg audio decoder.
|
||||
*/
|
||||
/* package */ final class FfmpegDecoder extends
|
||||
SimpleDecoder<DecoderInputBuffer, SimpleOutputBuffer, FfmpegDecoderException> {
|
||||
|
||||
private static final String TAG = "FfmpegDecoder";
|
||||
|
||||
/**
|
||||
* Whether the underlying FFmpeg library is available.
|
||||
*/
|
||||
public static final boolean IS_AVAILABLE;
|
||||
static {
|
||||
boolean isAvailable;
|
||||
try {
|
||||
System.loadLibrary("avutil");
|
||||
System.loadLibrary("avresample");
|
||||
System.loadLibrary("avcodec");
|
||||
System.loadLibrary("ffmpeg");
|
||||
isAvailable = true;
|
||||
} catch (UnsatisfiedLinkError exception) {
|
||||
isAvailable = false;
|
||||
}
|
||||
IS_AVAILABLE = isAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this decoder can decode samples in the specified MIME type.
|
||||
*/
|
||||
public static boolean supportsFormat(String mimeType) {
|
||||
String codecName = getCodecName(mimeType);
|
||||
return codecName != null && nativeHasDecoder(codecName);
|
||||
}
|
||||
|
||||
// Space for 64 ms of 6 channel 48 kHz 16-bit PCM audio.
|
||||
private static final int OUTPUT_BUFFER_SIZE = 1536 * 6 * 2 * 2;
|
||||
|
||||
|
|
@ -70,9 +42,12 @@ import java.util.List;
|
|||
public FfmpegDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize,
|
||||
String mimeType, List<byte[]> initializationData) throws FfmpegDecoderException {
|
||||
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
|
||||
codecName = getCodecName(mimeType);
|
||||
if (!FfmpegLibrary.isAvailable()) {
|
||||
throw new FfmpegDecoderException("Failed to load decoder native libraries.");
|
||||
}
|
||||
codecName = FfmpegLibrary.getCodecName(mimeType);
|
||||
extraData = getExtraData(mimeType, initializationData);
|
||||
nativeContext = nativeInitialize(codecName, extraData);
|
||||
nativeContext = ffmpegInitialize(codecName, extraData);
|
||||
if (nativeContext == 0) {
|
||||
throw new FfmpegDecoderException("Initialization failed.");
|
||||
}
|
||||
|
|
@ -81,7 +56,7 @@ import java.util.List;
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "ffmpeg" + nativeGetFfmpegVersion() + "-" + codecName;
|
||||
return "ffmpeg" + FfmpegLibrary.getVersion() + "-" + codecName;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -98,7 +73,7 @@ import java.util.List;
|
|||
public FfmpegDecoderException decode(DecoderInputBuffer inputBuffer,
|
||||
SimpleOutputBuffer outputBuffer, boolean reset) {
|
||||
if (reset) {
|
||||
nativeContext = nativeReset(nativeContext, extraData);
|
||||
nativeContext = ffmpegReset(nativeContext, extraData);
|
||||
if (nativeContext == 0) {
|
||||
return new FfmpegDecoderException("Error resetting (see logcat).");
|
||||
}
|
||||
|
|
@ -106,13 +81,13 @@ import java.util.List;
|
|||
ByteBuffer inputData = inputBuffer.data;
|
||||
int inputSize = inputData.limit();
|
||||
ByteBuffer outputData = outputBuffer.init(inputBuffer.timeUs, OUTPUT_BUFFER_SIZE);
|
||||
int result = nativeDecode(nativeContext, inputData, inputSize, outputData, OUTPUT_BUFFER_SIZE);
|
||||
int result = ffmpegDecode(nativeContext, inputData, inputSize, outputData, OUTPUT_BUFFER_SIZE);
|
||||
if (result < 0) {
|
||||
return new FfmpegDecoderException("Error decoding (see logcat). Code: " + result);
|
||||
}
|
||||
if (!hasOutputFormat) {
|
||||
channelCount = nativeGetChannelCount(nativeContext);
|
||||
sampleRate = nativeGetSampleRate(nativeContext);
|
||||
channelCount = ffmpegGetChannelCount(nativeContext);
|
||||
sampleRate = ffmpegGetSampleRate(nativeContext);
|
||||
hasOutputFormat = true;
|
||||
}
|
||||
outputBuffer.data.position(0);
|
||||
|
|
@ -123,7 +98,7 @@ import java.util.List;
|
|||
@Override
|
||||
public void release() {
|
||||
super.release();
|
||||
nativeRelease(nativeContext);
|
||||
ffmpegRelease(nativeContext);
|
||||
nativeContext = 0;
|
||||
}
|
||||
|
||||
|
|
@ -169,50 +144,12 @@ import java.util.List;
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the FFmpeg decoder that could be used to decode {@code mimeType}. The codec
|
||||
* can only be used if {@link #nativeHasDecoder(String)} returns true for the returned codec name.
|
||||
*/
|
||||
private static String getCodecName(String mimeType) {
|
||||
switch (mimeType) {
|
||||
case MimeTypes.AUDIO_AAC:
|
||||
return "aac";
|
||||
case MimeTypes.AUDIO_MPEG:
|
||||
case MimeTypes.AUDIO_MPEG_L1:
|
||||
case MimeTypes.AUDIO_MPEG_L2:
|
||||
return "mp3";
|
||||
case MimeTypes.AUDIO_AC3:
|
||||
return "ac3";
|
||||
case MimeTypes.AUDIO_E_AC3:
|
||||
return "eac3";
|
||||
case MimeTypes.AUDIO_TRUEHD:
|
||||
return "truehd";
|
||||
case MimeTypes.AUDIO_DTS:
|
||||
case MimeTypes.AUDIO_DTS_HD:
|
||||
return "dca";
|
||||
case MimeTypes.AUDIO_VORBIS:
|
||||
return "vorbis";
|
||||
case MimeTypes.AUDIO_OPUS:
|
||||
return "opus";
|
||||
case MimeTypes.AUDIO_AMR_NB:
|
||||
return "amrnb";
|
||||
case MimeTypes.AUDIO_AMR_WB:
|
||||
return "amrwb";
|
||||
case MimeTypes.AUDIO_FLAC:
|
||||
return "flac";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static native String nativeGetFfmpegVersion();
|
||||
private static native boolean nativeHasDecoder(String codecName);
|
||||
private native long nativeInitialize(String codecName, byte[] extraData);
|
||||
private native int nativeDecode(long context, ByteBuffer inputData, int inputSize,
|
||||
private native long ffmpegInitialize(String codecName, byte[] extraData);
|
||||
private native int ffmpegDecode(long context, ByteBuffer inputData, int inputSize,
|
||||
ByteBuffer outputData, int outputSize);
|
||||
private native int nativeGetChannelCount(long context);
|
||||
private native int nativeGetSampleRate(long context);
|
||||
private native long nativeReset(long context, byte[] extraData);
|
||||
private native void nativeRelease(long context);
|
||||
private native int ffmpegGetChannelCount(long context);
|
||||
private native int ffmpegGetSampleRate(long context);
|
||||
private native long ffmpegReset(long context, byte[] extraData);
|
||||
private native void ffmpegRelease(long context);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* 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.ffmpeg;
|
||||
|
||||
import com.google.android.exoplayer2.util.LibraryLoader;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
||||
/**
|
||||
* Configures and queries the underlying native library.
|
||||
*/
|
||||
public final class FfmpegLibrary {
|
||||
|
||||
private static final LibraryLoader LOADER =
|
||||
new LibraryLoader("avutil", "avresample", "avcodec", "ffmpeg");
|
||||
|
||||
private FfmpegLibrary() {}
|
||||
|
||||
/**
|
||||
* Override the names of the FFmpeg native libraries. 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 FfmpegAudioRenderer} instance.
|
||||
*/
|
||||
public static void setLibraries(String... libraries) {
|
||||
LOADER.setLibraries(libraries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying library is available, loading it if necessary.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return LOADER.isAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the underlying library if available, or null otherwise.
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return isAvailable() ? ffmpegGetVersion() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying library supports the specified MIME type.
|
||||
*/
|
||||
public static boolean supportsFormat(String mimeType) {
|
||||
if (!isAvailable()) {
|
||||
return false;
|
||||
}
|
||||
String codecName = getCodecName(mimeType);
|
||||
return codecName != null && ffmpegHasDecoder(codecName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the FFmpeg decoder that could be used to decode {@code mimeType}.
|
||||
*/
|
||||
/* package */ static String getCodecName(String mimeType) {
|
||||
switch (mimeType) {
|
||||
case MimeTypes.AUDIO_AAC:
|
||||
return "aac";
|
||||
case MimeTypes.AUDIO_MPEG:
|
||||
case MimeTypes.AUDIO_MPEG_L1:
|
||||
case MimeTypes.AUDIO_MPEG_L2:
|
||||
return "mp3";
|
||||
case MimeTypes.AUDIO_AC3:
|
||||
return "ac3";
|
||||
case MimeTypes.AUDIO_E_AC3:
|
||||
return "eac3";
|
||||
case MimeTypes.AUDIO_TRUEHD:
|
||||
return "truehd";
|
||||
case MimeTypes.AUDIO_DTS:
|
||||
case MimeTypes.AUDIO_DTS_HD:
|
||||
return "dca";
|
||||
case MimeTypes.AUDIO_VORBIS:
|
||||
return "vorbis";
|
||||
case MimeTypes.AUDIO_OPUS:
|
||||
return "opus";
|
||||
case MimeTypes.AUDIO_AMR_NB:
|
||||
return "amrnb";
|
||||
case MimeTypes.AUDIO_AMR_WB:
|
||||
return "amrwb";
|
||||
case MimeTypes.AUDIO_FLAC:
|
||||
return "flac";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static native String ffmpegGetVersion();
|
||||
private static native boolean ffmpegHasDecoder(String codecName);
|
||||
|
||||
}
|
||||
|
|
@ -34,7 +34,8 @@ extern "C" {
|
|||
#define LOG_TAG "ffmpeg_jni"
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
#define FUNC(RETURN_TYPE, NAME, ...) \
|
||||
|
||||
#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
extern "C" { \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegDecoder_ ## NAME \
|
||||
|
|
@ -44,6 +45,16 @@ extern "C" {
|
|||
Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegDecoder_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||
|
||||
#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
extern "C" { \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegLibrary_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__);\
|
||||
} \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_ffmpeg_FfmpegLibrary_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||
|
||||
#define ERROR_STRING_BUFFER_LENGTH 256
|
||||
|
||||
// Request a format corresponding to AudioFormat.ENCODING_PCM_16BIT.
|
||||
|
|
@ -88,15 +99,15 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
FUNC(jstring, nativeGetFfmpegVersion) {
|
||||
LIBRARY_FUNC(jstring, ffmpegGetVersion) {
|
||||
return env->NewStringUTF(LIBAVCODEC_IDENT);
|
||||
}
|
||||
|
||||
FUNC(jboolean, nativeHasDecoder, jstring codecName) {
|
||||
LIBRARY_FUNC(jboolean, ffmpegHasDecoder, jstring codecName) {
|
||||
return getCodecByName(env, codecName) != NULL;
|
||||
}
|
||||
|
||||
FUNC(jlong, nativeInitialize, jstring codecName, jbyteArray extraData) {
|
||||
DECODER_FUNC(jlong, ffmpegInitialize, jstring codecName, jbyteArray extraData) {
|
||||
AVCodec *codec = getCodecByName(env, codecName);
|
||||
if (!codec) {
|
||||
LOGE("Codec not found.");
|
||||
|
|
@ -105,8 +116,8 @@ FUNC(jlong, nativeInitialize, jstring codecName, jbyteArray extraData) {
|
|||
return (jlong) createContext(env, codec, extraData);
|
||||
}
|
||||
|
||||
FUNC(jint, nativeDecode, jlong context, jobject inputData, jint inputSize,
|
||||
jobject outputData, jint outputSize) {
|
||||
DECODER_FUNC(jint, ffmpegDecode, jlong context, jobject inputData,
|
||||
jint inputSize, jobject outputData, jint outputSize) {
|
||||
if (!context) {
|
||||
LOGE("Context must be non-NULL.");
|
||||
return -1;
|
||||
|
|
@ -133,7 +144,7 @@ FUNC(jint, nativeDecode, jlong context, jobject inputData, jint inputSize,
|
|||
outputSize);
|
||||
}
|
||||
|
||||
FUNC(jint, nativeGetChannelCount, jlong context) {
|
||||
DECODER_FUNC(jint, ffmpegGetChannelCount, jlong context) {
|
||||
if (!context) {
|
||||
LOGE("Context must be non-NULL.");
|
||||
return -1;
|
||||
|
|
@ -141,7 +152,7 @@ FUNC(jint, nativeGetChannelCount, jlong context) {
|
|||
return ((AVCodecContext *) context)->channels;
|
||||
}
|
||||
|
||||
FUNC(jint, nativeGetSampleRate, jlong context) {
|
||||
DECODER_FUNC(jint, ffmpegGetSampleRate, jlong context) {
|
||||
if (!context) {
|
||||
LOGE("Context must be non-NULL.");
|
||||
return -1;
|
||||
|
|
@ -149,7 +160,7 @@ FUNC(jint, nativeGetSampleRate, jlong context) {
|
|||
return ((AVCodecContext *) context)->sample_rate;
|
||||
}
|
||||
|
||||
FUNC(jlong, nativeReset, jlong jContext, jbyteArray extraData) {
|
||||
DECODER_FUNC(jlong, ffmpegReset, jlong jContext, jbyteArray extraData) {
|
||||
AVCodecContext *context = (AVCodecContext *) jContext;
|
||||
if (!context) {
|
||||
LOGE("Tried to reset without a context.");
|
||||
|
|
@ -173,7 +184,7 @@ FUNC(jlong, nativeReset, jlong jContext, jbyteArray extraData) {
|
|||
return (jlong) context;
|
||||
}
|
||||
|
||||
FUNC(void, nativeRelease, jlong context) {
|
||||
DECODER_FUNC(void, ffmpegRelease, jlong context) {
|
||||
if (context) {
|
||||
releaseContext((AVCodecContext *) context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,13 +45,10 @@ import java.util.List;
|
|||
throws FlacDecoderException {
|
||||
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
|
||||
if (initializationData.size() != 1) {
|
||||
throw new FlacDecoderException("Wrong number of initialization data");
|
||||
throw new FlacDecoderException("Initialization data must be of length 1");
|
||||
}
|
||||
|
||||
decoder = new FlacJni();
|
||||
|
||||
ByteBuffer metadata = ByteBuffer.wrap(initializationData.get(0));
|
||||
decoder.setData(metadata);
|
||||
decoder.setData(ByteBuffer.wrap(initializationData.get(0)));
|
||||
FlacStreamInfo streamInfo;
|
||||
try {
|
||||
streamInfo = decoder.decodeMetadata();
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ public final class FlacExtractor implements Extractor {
|
|||
extractorOutput = output;
|
||||
trackOutput = extractorOutput.track(0);
|
||||
extractorOutput.endTracks();
|
||||
|
||||
try {
|
||||
decoder = new FlacJni();
|
||||
} catch (FlacDecoderException e) {
|
||||
|
|
|
|||
|
|
@ -26,32 +26,19 @@ import java.nio.ByteBuffer;
|
|||
*/
|
||||
/* package */ final class FlacJni {
|
||||
|
||||
/**
|
||||
* Whether the underlying libflac library is available.
|
||||
*/
|
||||
public static final boolean IS_AVAILABLE;
|
||||
static {
|
||||
boolean isAvailable;
|
||||
try {
|
||||
System.loadLibrary("flacJNI");
|
||||
isAvailable = true;
|
||||
} catch (UnsatisfiedLinkError exception) {
|
||||
isAvailable = false;
|
||||
}
|
||||
IS_AVAILABLE = isAvailable;
|
||||
}
|
||||
|
||||
private static final int TEMP_BUFFER_SIZE = 8192; // The same buffer size which libflac has
|
||||
|
||||
private final long nativeDecoderContext;
|
||||
|
||||
private ByteBuffer byteBufferData;
|
||||
|
||||
private ExtractorInput extractorInput;
|
||||
private boolean endOfExtractorInput;
|
||||
private byte[] tempBuffer;
|
||||
|
||||
public FlacJni() throws FlacDecoderException {
|
||||
if (!FlacLibrary.isAvailable()) {
|
||||
throw new FlacDecoderException("Failed to load decoder native libraries.");
|
||||
}
|
||||
nativeDecoderContext = flacInit();
|
||||
if (nativeDecoderContext == 0) {
|
||||
throw new FlacDecoderException("Failed to initialize decoder");
|
||||
|
|
@ -194,28 +181,18 @@ import java.nio.ByteBuffer;
|
|||
}
|
||||
|
||||
private native long flacInit();
|
||||
|
||||
private native FlacStreamInfo flacDecodeMetadata(long context)
|
||||
throws IOException, InterruptedException;
|
||||
|
||||
private native int flacDecodeToBuffer(long context, ByteBuffer outputBuffer)
|
||||
throws IOException, InterruptedException;
|
||||
|
||||
private native int flacDecodeToArray(long context, byte[] outputArray)
|
||||
throws IOException, InterruptedException;
|
||||
|
||||
private native long flacGetDecodePosition(long context);
|
||||
|
||||
private native long flacGetLastTimestamp(long context);
|
||||
|
||||
private native long flacGetSeekPosition(long context, long timeUs);
|
||||
|
||||
private native String flacGetStateString(long context);
|
||||
|
||||
private native void flacFlush(long context);
|
||||
|
||||
private native void flacReset(long context, long newPosition);
|
||||
|
||||
private native void flacRelease(long context);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.flac;
|
||||
|
||||
import com.google.android.exoplayer2.util.LibraryLoader;
|
||||
|
||||
/**
|
||||
* Configures and queries the underlying native library.
|
||||
*/
|
||||
public final class FlacLibrary {
|
||||
|
||||
private static final LibraryLoader LOADER = new LibraryLoader("flacJNI");
|
||||
|
||||
private FlacLibrary() {}
|
||||
|
||||
/**
|
||||
* Override the names of the Flac native libraries. If an application wishes to call this method,
|
||||
* it must do so before calling any other method defined by this class, and before instantiating
|
||||
* any {@link LibflacAudioRenderer} and {@link FlacExtractor} instances.
|
||||
*/
|
||||
public static void setLibraries(String... libraries) {
|
||||
LOADER.setLibraries(libraries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying library is available, loading it if necessary.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return LOADER.isAvailable();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -30,13 +30,6 @@ public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
|||
|
||||
private static final int NUM_BUFFERS = 16;
|
||||
|
||||
/**
|
||||
* Returns whether the underlying libflac library is available.
|
||||
*/
|
||||
public static boolean isLibflacAvailable() {
|
||||
return FlacJni.IS_AVAILABLE;
|
||||
}
|
||||
|
||||
public LibflacAudioRenderer() {
|
||||
this(null, null);
|
||||
}
|
||||
|
|
@ -65,7 +58,7 @@ public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
|
|||
|
||||
@Override
|
||||
public int supportsFormat(Format format) {
|
||||
return isLibflacAvailable() && MimeTypes.AUDIO_FLAC.equalsIgnoreCase(format.sampleMimeType)
|
||||
return FlacLibrary.isAvailable() && MimeTypes.AUDIO_FLAC.equalsIgnoreCase(format.sampleMimeType)
|
||||
? FORMAT_HANDLED : FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "include/flac_parser.h"
|
||||
|
||||
#define LOG_TAG "FlacJniJNI"
|
||||
#define LOG_TAG "flac_jni"
|
||||
#define ALOGE(...) \
|
||||
((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#define ALOGV(...) \
|
||||
|
|
|
|||
|
|
@ -31,20 +31,6 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer {
|
|||
private static final int NUM_BUFFERS = 16;
|
||||
private static final int INITIAL_INPUT_BUFFER_SIZE = 960 * 6;
|
||||
|
||||
/**
|
||||
* Returns whether the underlying libopus library is available.
|
||||
*/
|
||||
public static boolean isLibopusAvailable() {
|
||||
return OpusDecoder.IS_AVAILABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the underlying libopus library if available, otherwise {@code null}.
|
||||
*/
|
||||
public static String getLibopusVersion() {
|
||||
return isLibopusAvailable() ? OpusDecoder.getLibopusVersion() : null;
|
||||
}
|
||||
|
||||
public LibopusAudioRenderer() {
|
||||
this(null, null);
|
||||
}
|
||||
|
|
@ -73,7 +59,7 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer {
|
|||
|
||||
@Override
|
||||
public int supportsFormat(Format format) {
|
||||
return isLibopusAvailable() && MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)
|
||||
return OpusLibrary.isAvailable() && MimeTypes.AUDIO_OPUS.equalsIgnoreCase(format.sampleMimeType)
|
||||
? FORMAT_HANDLED : FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,32 +24,11 @@ import java.nio.ByteOrder;
|
|||
import java.util.List;
|
||||
|
||||
/**
|
||||
* JNI wrapper for the libopus Opus decoder.
|
||||
* Opus decoder.
|
||||
*/
|
||||
/* package */ final class OpusDecoder extends
|
||||
SimpleDecoder<DecoderInputBuffer, SimpleOutputBuffer, OpusDecoderException> {
|
||||
|
||||
/**
|
||||
* Whether the underlying libopus library is available.
|
||||
*/
|
||||
public static final boolean IS_AVAILABLE;
|
||||
static {
|
||||
boolean isAvailable;
|
||||
try {
|
||||
System.loadLibrary("opus");
|
||||
System.loadLibrary("opusJNI");
|
||||
isAvailable = true;
|
||||
} catch (UnsatisfiedLinkError exception) {
|
||||
isAvailable = false;
|
||||
}
|
||||
IS_AVAILABLE = isAvailable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version string of the underlying libopus decoder.
|
||||
*/
|
||||
public static native String getLibopusVersion();
|
||||
|
||||
private static final int DEFAULT_SEEK_PRE_ROLL_SAMPLES = 3840;
|
||||
|
||||
/**
|
||||
|
|
@ -78,6 +57,9 @@ import java.util.List;
|
|||
public OpusDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize,
|
||||
List<byte[]> initializationData) throws OpusDecoderException {
|
||||
super(new DecoderInputBuffer[numInputBuffers], new SimpleOutputBuffer[numOutputBuffers]);
|
||||
if (!OpusLibrary.isAvailable()) {
|
||||
throw new OpusDecoderException("Failed to load decoder native libraries.");
|
||||
}
|
||||
byte[] headerBytes = initializationData.get(0);
|
||||
if (headerBytes.length < 19) {
|
||||
throw new OpusDecoderException("Header size is too small.");
|
||||
|
|
@ -90,7 +72,8 @@ import java.util.List;
|
|||
int gain = readLittleEndian16(headerBytes, 16);
|
||||
|
||||
byte[] streamMap = new byte[8];
|
||||
int numStreams, numCoupled;
|
||||
int numStreams;
|
||||
int numCoupled;
|
||||
if (headerBytes[18] == 0) { // Channel mapping
|
||||
// If there is no channel mapping, use the defaults.
|
||||
if (channelCount > 2) { // Maximum channel count with default layout.
|
||||
|
|
@ -133,7 +116,7 @@ import java.util.List;
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "libopus" + getLibopusVersion();
|
||||
return "libopus" + OpusLibrary.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -185,14 +168,6 @@ import java.util.List;
|
|||
opusClose(nativeDecoderContext);
|
||||
}
|
||||
|
||||
private native long opusInit(int sampleRate, int channelCount, int numStreams, int numCoupled,
|
||||
int gain, byte[] streamMap);
|
||||
private native int opusDecode(long decoder, long timeUs, ByteBuffer inputBuffer, int inputSize,
|
||||
SimpleOutputBuffer outputBuffer, int sampleRate);
|
||||
private native void opusClose(long decoder);
|
||||
private native void opusReset(long decoder);
|
||||
private native String opusGetErrorMessage(int errorCode);
|
||||
|
||||
private static int nsToSamples(long ns) {
|
||||
return (int) (ns * SAMPLE_RATE / 1000000000);
|
||||
}
|
||||
|
|
@ -203,4 +178,12 @@ import java.util.List;
|
|||
return value;
|
||||
}
|
||||
|
||||
private native long opusInit(int sampleRate, int channelCount, int numStreams, int numCoupled,
|
||||
int gain, byte[] streamMap);
|
||||
private native int opusDecode(long decoder, long timeUs, ByteBuffer inputBuffer, int inputSize,
|
||||
SimpleOutputBuffer outputBuffer, int sampleRate);
|
||||
private native void opusClose(long decoder);
|
||||
private native void opusReset(long decoder);
|
||||
private native String opusGetErrorMessage(int errorCode);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.opus;
|
||||
|
||||
import com.google.android.exoplayer2.util.LibraryLoader;
|
||||
|
||||
/**
|
||||
* Configures and queries the underlying native library.
|
||||
*/
|
||||
public final class OpusLibrary {
|
||||
|
||||
private static final LibraryLoader LOADER = new LibraryLoader("opus", "opusJNI");
|
||||
|
||||
private OpusLibrary() {}
|
||||
|
||||
/**
|
||||
* Override the names of the Opus native libraries. 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 LibopusAudioRenderer} instance.
|
||||
*/
|
||||
public static void setLibraries(String... libraries) {
|
||||
LOADER.setLibraries(libraries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying library is available, loading it if necessary.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return LOADER.isAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the underlying library if available, or null otherwise.
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return isAvailable() ? opusGetVersion() : null;
|
||||
}
|
||||
|
||||
public static native String opusGetVersion();
|
||||
|
||||
}
|
||||
|
|
@ -23,11 +23,11 @@
|
|||
#include "opus.h" // NOLINT
|
||||
#include "opus_multistream.h" // NOLINT
|
||||
|
||||
#define LOG_TAG "libopus_native"
|
||||
#define LOG_TAG "opus_jni"
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
|
||||
#define FUNC(RETURN_TYPE, NAME, ...) \
|
||||
#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
extern "C" { \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_ ## NAME \
|
||||
|
|
@ -37,6 +37,16 @@
|
|||
Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||
|
||||
#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
extern "C" { \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_opus_OpusLibrary_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__);\
|
||||
} \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_opus_OpusLibrary_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||
|
||||
// JNI references for SimpleOutputBuffer class.
|
||||
static jmethodID outputBufferInit;
|
||||
|
||||
|
|
@ -51,8 +61,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||
static const int kBytesPerSample = 2; // opus fixed point uses 16 bit samples.
|
||||
static int channelCount;
|
||||
|
||||
FUNC(jlong, opusInit, jint sampleRate, jint channelCount, jint numStreams,
|
||||
jint numCoupled, jint gain, jbyteArray jStreamMap) {
|
||||
DECODER_FUNC(jlong, opusInit, jint sampleRate, jint channelCount,
|
||||
jint numStreams, jint numCoupled, jint gain, jbyteArray jStreamMap) {
|
||||
int status = OPUS_INVALID_STATE;
|
||||
::channelCount = channelCount;
|
||||
jbyte* streamMapBytes = env->GetByteArrayElements(jStreamMap, 0);
|
||||
|
|
@ -79,8 +89,9 @@ FUNC(jlong, opusInit, jint sampleRate, jint channelCount, jint numStreams,
|
|||
return reinterpret_cast<intptr_t>(decoder);
|
||||
}
|
||||
|
||||
FUNC(jint, opusDecode, jlong jDecoder, jlong jTimeUs, jobject jInputBuffer,
|
||||
jint inputSize, jobject jOutputBuffer, jint sampleRate) {
|
||||
DECODER_FUNC(jint, opusDecode, jlong jDecoder, jlong jTimeUs,
|
||||
jobject jInputBuffer, jint inputSize, jobject jOutputBuffer,
|
||||
jint sampleRate) {
|
||||
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
|
||||
const uint8_t* inputBuffer =
|
||||
reinterpret_cast<const uint8_t*>(
|
||||
|
|
@ -102,20 +113,20 @@ FUNC(jint, opusDecode, jlong jDecoder, jlong jTimeUs, jobject jInputBuffer,
|
|||
: sampleCount * kBytesPerSample * channelCount;
|
||||
}
|
||||
|
||||
FUNC(void, opusClose, jlong jDecoder) {
|
||||
DECODER_FUNC(void, opusClose, jlong jDecoder) {
|
||||
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
|
||||
opus_multistream_decoder_destroy(decoder);
|
||||
}
|
||||
|
||||
FUNC(void, opusReset, jlong jDecoder) {
|
||||
DECODER_FUNC(void, opusReset, jlong jDecoder) {
|
||||
OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
|
||||
opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
|
||||
}
|
||||
|
||||
FUNC(jstring, getLibopusVersion) {
|
||||
return env->NewStringUTF(opus_get_version_string());
|
||||
}
|
||||
|
||||
FUNC(jstring, opusGetErrorMessage, jint errorCode) {
|
||||
DECODER_FUNC(jstring, opusGetErrorMessage, jint errorCode) {
|
||||
return env->NewStringUTF(opus_strerror(errorCode));
|
||||
}
|
||||
|
||||
LIBRARY_FUNC(jstring, opusGetVersion) {
|
||||
return env->NewStringUTF(opus_get_version_string());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,8 +118,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
|
|||
|
||||
@Override
|
||||
public int supportsFormat(Format format) {
|
||||
return VpxNativeLibraryHelper.isLibvpxAvailable()
|
||||
&& MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)
|
||||
return VpxLibrary.isAvailable() && MimeTypes.VIDEO_VP9.equalsIgnoreCase(format.sampleMimeType)
|
||||
? (FORMAT_HANDLED | ADAPTIVE_SEAMLESS) : FORMAT_UNSUPPORTED_TYPE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import com.google.android.exoplayer2.decoder.SimpleDecoder;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* JNI wrapper for the libvpx VP9 decoder.
|
||||
* Vpx decoder.
|
||||
*/
|
||||
/* package */ final class VpxDecoder extends
|
||||
SimpleDecoder<DecoderInputBuffer, VpxOutputBuffer, VpxDecoderException> {
|
||||
|
|
@ -45,7 +45,7 @@ import java.nio.ByteBuffer;
|
|||
public VpxDecoder(int numInputBuffers, int numOutputBuffers, int initialInputBufferSize)
|
||||
throws VpxDecoderException {
|
||||
super(new DecoderInputBuffer[numInputBuffers], new VpxOutputBuffer[numOutputBuffers]);
|
||||
if (!VpxNativeLibraryHelper.isLibvpxAvailable()) {
|
||||
if (!VpxLibrary.isAvailable()) {
|
||||
throw new VpxDecoderException("Failed to load decoder native libraries.");
|
||||
}
|
||||
vpxDecContext = vpxInit();
|
||||
|
|
@ -57,7 +57,7 @@ import java.nio.ByteBuffer;
|
|||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "libvpx" + VpxNativeLibraryHelper.getLibvpxVersion();
|
||||
return "libvpx" + VpxLibrary.getVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -111,4 +111,5 @@ 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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.LibraryLoader;
|
||||
|
||||
/**
|
||||
* Configures and queries the underlying native library.
|
||||
*/
|
||||
public final class VpxLibrary {
|
||||
|
||||
private static final LibraryLoader LOADER = new LibraryLoader("vpx", "vpxJNI");
|
||||
|
||||
private VpxLibrary() {}
|
||||
|
||||
/**
|
||||
* Override the names of the Vpx native libraries. 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 setLibraries(String... libraries) {
|
||||
LOADER.setLibraries(libraries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying library is available, loading it if necessary.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
return LOADER.isAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version of the underlying library if available, or null otherwise.
|
||||
*/
|
||||
public static String getVersion() {
|
||||
return isAvailable() ? vpxGetVersion() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration string with which the underlying library was built if available, or
|
||||
* null otherwise.
|
||||
*/
|
||||
public static String getBuildConfig() {
|
||||
return isAvailable() ? vpxGetBuildConfig() : null;
|
||||
}
|
||||
|
||||
private static native String vpxGetVersion();
|
||||
private static native String vpxGetBuildConfig();
|
||||
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
|
|
@ -30,11 +30,11 @@
|
|||
#include "vpx/vpx_decoder.h"
|
||||
#include "vpx/vp8dx.h"
|
||||
|
||||
#define LOG_TAG "LIBVPX_DEC"
|
||||
#define LOG_TAG "vpx_jni"
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, \
|
||||
__VA_ARGS__))
|
||||
|
||||
#define VPX_DECODER_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
#define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
extern "C" { \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_vp9_VpxDecoder_ ## NAME \
|
||||
|
|
@ -44,14 +44,14 @@
|
|||
Java_com_google_android_exoplayer2_ext_vp9_VpxDecoder_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||
|
||||
#define VPX_NATIVE_LIBRARY_HELPER_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
#define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
|
||||
extern "C" { \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_vp9_VpxNativeLibraryHelper_ ## NAME \
|
||||
Java_com_google_android_exoplayer2_ext_vp9_VpxLibrary_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__);\
|
||||
} \
|
||||
JNIEXPORT RETURN_TYPE \
|
||||
Java_com_google_android_exoplayer2_ext_vp9_VpxNativeLibraryHelper_ ## NAME \
|
||||
Java_com_google_android_exoplayer2_ext_vp9_VpxLibrary_ ## NAME \
|
||||
(JNIEnv* env, jobject thiz, ##__VA_ARGS__)\
|
||||
|
||||
// JNI references for VpxOutputBuffer class.
|
||||
|
|
@ -68,7 +68,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
VPX_DECODER_FUNC(jlong, vpxInit) {
|
||||
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();
|
||||
|
|
@ -91,7 +91,7 @@ VPX_DECODER_FUNC(jlong, vpxInit) {
|
|||
return reinterpret_cast<intptr_t>(context);
|
||||
}
|
||||
|
||||
VPX_DECODER_FUNC(jlong, vpxDecode, jlong jContext, jobject encoded, jint len) {
|
||||
DECODER_FUNC(jlong, vpxDecode, jlong jContext, jobject encoded, jint len) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
const uint8_t* const buffer =
|
||||
reinterpret_cast<const uint8_t*>(env->GetDirectBufferAddress(encoded));
|
||||
|
|
@ -104,14 +104,14 @@ VPX_DECODER_FUNC(jlong, vpxDecode, jlong jContext, jobject encoded, jint len) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
VPX_DECODER_FUNC(jlong, vpxClose, jlong jContext) {
|
||||
DECODER_FUNC(jlong, vpxClose, jlong jContext) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
vpx_codec_destroy(context);
|
||||
delete context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
VPX_DECODER_FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) {
|
||||
DECODER_FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
vpx_codec_iter_t iter = NULL;
|
||||
const vpx_image_t* const img = vpx_codec_get_frame(context, &iter);
|
||||
|
|
@ -176,16 +176,15 @@ VPX_DECODER_FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
VPX_DECODER_FUNC(jstring, vpxGetErrorMessage, jlong jContext) {
|
||||
DECODER_FUNC(jstring, vpxGetErrorMessage, jlong jContext) {
|
||||
vpx_codec_ctx_t* const context = reinterpret_cast<vpx_codec_ctx_t*>(jContext);
|
||||
return env->NewStringUTF(vpx_codec_error(context));
|
||||
}
|
||||
|
||||
VPX_NATIVE_LIBRARY_HELPER_FUNC(jstring, nativeGetLibvpxVersion) {
|
||||
LIBRARY_FUNC(jstring, vpxGetVersion) {
|
||||
return env->NewStringUTF(vpx_codec_version_str());
|
||||
}
|
||||
|
||||
VPX_NATIVE_LIBRARY_HELPER_FUNC(jstring, nativeGetLibvpxConfig) {
|
||||
LIBRARY_FUNC(jstring, vpxGetBuildConfig) {
|
||||
return env->NewStringUTF(vpx_codec_build_config());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.util;
|
||||
|
||||
/**
|
||||
* Configurable loader for native libraries.
|
||||
*/
|
||||
public final class LibraryLoader {
|
||||
|
||||
private String[] nativeLibraries;
|
||||
private boolean loadAttempted;
|
||||
private boolean isAvailable;
|
||||
|
||||
/**
|
||||
* @param libraries The names of the libraries to load.
|
||||
*/
|
||||
public LibraryLoader(String... libraries) {
|
||||
nativeLibraries = libraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the names of the libraries to load. Must be called before any call to
|
||||
* {@link #isAvailable()}.
|
||||
*/
|
||||
public synchronized void setLibraries(String... libraries) {
|
||||
Assertions.checkState(!loadAttempted, "Cannot set libraries after loading");
|
||||
nativeLibraries = libraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying libraries are available, loading them if necessary.
|
||||
*/
|
||||
public synchronized boolean isAvailable() {
|
||||
if (loadAttempted) {
|
||||
return isAvailable;
|
||||
}
|
||||
loadAttempted = true;
|
||||
try {
|
||||
for (String lib : nativeLibraries) {
|
||||
System.loadLibrary(lib);
|
||||
}
|
||||
isAvailable = true;
|
||||
} catch (UnsatisfiedLinkError exception) {
|
||||
// Do nothing.
|
||||
}
|
||||
return isAvailable;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue