/* * 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. */ #ifdef __ANDROID__ #include #endif #include #include #include #include "opus.h" // NOLINT #include "opus_multistream.h" // NOLINT #ifdef __ANDROID__ #define LOG_TAG "opus_jni" #define LOGE(...) \ ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) #else // __ANDROID__ #define LOGE(...) \ do { \ } while (0) #endif // __ANDROID__ #define DECODER_FUNC(RETURN_TYPE, NAME, ...) \ extern "C" { \ JNIEXPORT RETURN_TYPE \ Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_##NAME( \ JNIEnv* env, jobject thiz, ##__VA_ARGS__); \ } \ JNIEXPORT RETURN_TYPE \ 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; static const int kBytesPerIntPcmSample = 2; static const int kBytesPerFloatSample = 4; static const int kMaxOpusOutputPacketSizeSamples = 960 * 6; static int channelCount; static int errorCode; static bool outputFloat = false; DECODER_FUNC(jlong, opusInit, jint sampleRate, jint channelCount, jint numStreams, jint numCoupled, jint gain, jbyteArray jStreamMap) { int status = OPUS_INVALID_STATE; ::channelCount = channelCount; errorCode = 0; jbyte* streamMapBytes = env->GetByteArrayElements(jStreamMap, 0); uint8_t* streamMap = reinterpret_cast(streamMapBytes); OpusMSDecoder* decoder = opus_multistream_decoder_create( sampleRate, channelCount, numStreams, numCoupled, streamMap, &status); env->ReleaseByteArrayElements(jStreamMap, streamMapBytes, 0); if (!decoder || status != OPUS_OK) { LOGE("Failed to create Opus Decoder; status=%s", opus_strerror(status)); return 0; } status = opus_multistream_decoder_ctl(decoder, OPUS_SET_GAIN(gain)); if (status != OPUS_OK) { LOGE("Failed to set Opus header gain; status=%s", opus_strerror(status)); return 0; } // Populate JNI References. const jclass outputBufferClass = env->FindClass( "com/google/android/exoplayer2/decoder/SimpleDecoderOutputBuffer"); outputBufferInit = env->GetMethodID(outputBufferClass, "init", "(JI)Ljava/nio/ByteBuffer;"); return reinterpret_cast(decoder); } DECODER_FUNC(jint, opusDecode, jlong jDecoder, jlong jTimeUs, jobject jInputBuffer, jint inputSize, jobject jOutputBuffer) { OpusMSDecoder* decoder = reinterpret_cast(jDecoder); const uint8_t* inputBuffer = reinterpret_cast( env->GetDirectBufferAddress(jInputBuffer)); const int byteSizePerSample = outputFloat ? kBytesPerFloatSample : kBytesPerIntPcmSample; const jint outputSize = kMaxOpusOutputPacketSizeSamples * byteSizePerSample * channelCount; env->CallObjectMethod(jOutputBuffer, outputBufferInit, jTimeUs, outputSize); if (env->ExceptionCheck()) { // Exception is thrown in Java when returning from the native call. return -1; } const jobject jOutputBufferData = env->CallObjectMethod( jOutputBuffer, outputBufferInit, jTimeUs, outputSize); if (env->ExceptionCheck()) { // Exception is thrown in Java when returning from the native call. return -1; } int sampleCount; if (outputFloat) { float* outputBufferData = reinterpret_cast( env->GetDirectBufferAddress(jOutputBufferData)); sampleCount = opus_multistream_decode_float( decoder, inputBuffer, inputSize, outputBufferData, kMaxOpusOutputPacketSizeSamples, 0); } else { int16_t* outputBufferData = reinterpret_cast( env->GetDirectBufferAddress(jOutputBufferData)); sampleCount = opus_multistream_decode(decoder, inputBuffer, inputSize, outputBufferData, kMaxOpusOutputPacketSizeSamples, 0); } // record error code errorCode = (sampleCount < 0) ? sampleCount : 0; return (sampleCount < 0) ? sampleCount : sampleCount * byteSizePerSample * channelCount; } DECODER_FUNC(jint, opusSecureDecode, jlong jDecoder, jlong jTimeUs, jobject jInputBuffer, jint inputSize, jobject jOutputBuffer, jint sampleRate, jobject mediaCrypto, jint inputMode, jbyteArray key, jbyteArray javaIv, jint inputNumSubSamples, jintArray numBytesOfClearData, jintArray numBytesOfEncryptedData) { // Doesn't support // Java client should have checked vpxSupportSecureDecode // and avoid calling this // return -2 (DRM Error) return -2; } DECODER_FUNC(void, opusClose, jlong jDecoder) { OpusMSDecoder* decoder = reinterpret_cast(jDecoder); opus_multistream_decoder_destroy(decoder); } DECODER_FUNC(void, opusReset, jlong jDecoder) { OpusMSDecoder* decoder = reinterpret_cast(jDecoder); opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE); } DECODER_FUNC(jstring, opusGetErrorMessage, jlong jContext) { return env->NewStringUTF(opus_strerror(errorCode)); } DECODER_FUNC(jint, opusGetErrorCode, jlong jContext) { return errorCode; } DECODER_FUNC(void, opusSetFloatOutput) { outputFloat = true; } LIBRARY_FUNC(jstring, opusIsSecureDecodeSupported) { // Doesn't support return 0; } LIBRARY_FUNC(jstring, opusGetVersion) { return env->NewStringUTF(opus_get_version_string()); }