1 /*
2 * Copyright (C) 2016 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
17 #include <android/log.h>
18 #include <jni.h>
19
20 #include <cstdlib>
21
22 #include "opus.h" // NOLINT
23 #include "opus_multistream.h" // NOLINT
24
25 #define LOG_TAG "opus_jni"
26 #define LOGE(...) \
27 ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
28
29 #define DECODER_FUNC(RETURN_TYPE, NAME, ...) \
30 extern "C" { \
31 JNIEXPORT RETURN_TYPE \
32 Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_##NAME( \
33 JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
34 } \
35 JNIEXPORT RETURN_TYPE \
36 Java_com_google_android_exoplayer2_ext_opus_OpusDecoder_##NAME( \
37 JNIEnv* env, jobject thiz, ##__VA_ARGS__)
38
39 #define LIBRARY_FUNC(RETURN_TYPE, NAME, ...) \
40 extern "C" { \
41 JNIEXPORT RETURN_TYPE \
42 Java_com_google_android_exoplayer2_ext_opus_OpusLibrary_##NAME( \
43 JNIEnv* env, jobject thiz, ##__VA_ARGS__); \
44 } \
45 JNIEXPORT RETURN_TYPE \
46 Java_com_google_android_exoplayer2_ext_opus_OpusLibrary_##NAME( \
47 JNIEnv* env, jobject thiz, ##__VA_ARGS__)
48
49 // JNI references for SimpleOutputBuffer class.
50 static jmethodID outputBufferInit;
51
JNI_OnLoad(JavaVM * vm,void * reserved)52 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
53 JNIEnv* env;
54 if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
55 return -1;
56 }
57 return JNI_VERSION_1_6;
58 }
59
60 static const int kBytesPerIntPcmSample = 2;
61 static const int kBytesPerFloatSample = 4;
62 static const int kMaxOpusOutputPacketSizeSamples = 960 * 6;
63 static int channelCount;
64 static int errorCode;
65 static bool outputFloat = false;
66
DECODER_FUNC(jlong,opusInit,jint sampleRate,jint channelCount,jint numStreams,jint numCoupled,jint gain,jbyteArray jStreamMap)67 DECODER_FUNC(jlong, opusInit, jint sampleRate, jint channelCount,
68 jint numStreams, jint numCoupled, jint gain,
69 jbyteArray jStreamMap) {
70 int status = OPUS_INVALID_STATE;
71 ::channelCount = channelCount;
72 errorCode = 0;
73 jbyte* streamMapBytes = env->GetByteArrayElements(jStreamMap, 0);
74 uint8_t* streamMap = reinterpret_cast<uint8_t*>(streamMapBytes);
75 OpusMSDecoder* decoder = opus_multistream_decoder_create(
76 sampleRate, channelCount, numStreams, numCoupled, streamMap, &status);
77 env->ReleaseByteArrayElements(jStreamMap, streamMapBytes, 0);
78 if (!decoder || status != OPUS_OK) {
79 LOGE("Failed to create Opus Decoder; status=%s", opus_strerror(status));
80 return 0;
81 }
82 status = opus_multistream_decoder_ctl(decoder, OPUS_SET_GAIN(gain));
83 if (status != OPUS_OK) {
84 LOGE("Failed to set Opus header gain; status=%s", opus_strerror(status));
85 return 0;
86 }
87
88 // Populate JNI References.
89 const jclass outputBufferClass = env->FindClass(
90 "com/google/android/exoplayer2/decoder/SimpleDecoderOutputBuffer");
91 outputBufferInit =
92 env->GetMethodID(outputBufferClass, "init", "(JI)Ljava/nio/ByteBuffer;");
93
94 return reinterpret_cast<intptr_t>(decoder);
95 }
96
DECODER_FUNC(jint,opusDecode,jlong jDecoder,jlong jTimeUs,jobject jInputBuffer,jint inputSize,jobject jOutputBuffer)97 DECODER_FUNC(jint, opusDecode, jlong jDecoder, jlong jTimeUs,
98 jobject jInputBuffer, jint inputSize, jobject jOutputBuffer) {
99 OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
100 const uint8_t* inputBuffer = reinterpret_cast<const uint8_t*>(
101 env->GetDirectBufferAddress(jInputBuffer));
102
103 const int byteSizePerSample =
104 outputFloat ? kBytesPerFloatSample : kBytesPerIntPcmSample;
105 const jint outputSize =
106 kMaxOpusOutputPacketSizeSamples * byteSizePerSample * channelCount;
107
108 env->CallObjectMethod(jOutputBuffer, outputBufferInit, jTimeUs, outputSize);
109 if (env->ExceptionCheck()) {
110 // Exception is thrown in Java when returning from the native call.
111 return -1;
112 }
113 const jobject jOutputBufferData = env->CallObjectMethod(
114 jOutputBuffer, outputBufferInit, jTimeUs, outputSize);
115 if (env->ExceptionCheck()) {
116 // Exception is thrown in Java when returning from the native call.
117 return -1;
118 }
119
120 int sampleCount;
121 if (outputFloat) {
122 float* outputBufferData = reinterpret_cast<float*>(
123 env->GetDirectBufferAddress(jOutputBufferData));
124 sampleCount = opus_multistream_decode_float(
125 decoder, inputBuffer, inputSize, outputBufferData,
126 kMaxOpusOutputPacketSizeSamples, 0);
127 } else {
128 int16_t* outputBufferData = reinterpret_cast<int16_t*>(
129 env->GetDirectBufferAddress(jOutputBufferData));
130 sampleCount = opus_multistream_decode(decoder, inputBuffer, inputSize,
131 outputBufferData,
132 kMaxOpusOutputPacketSizeSamples, 0);
133 }
134
135 // record error code
136 errorCode = (sampleCount < 0) ? sampleCount : 0;
137 return (sampleCount < 0) ? sampleCount
138 : sampleCount * byteSizePerSample * channelCount;
139 }
140
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)141 DECODER_FUNC(jint, opusSecureDecode, jlong jDecoder, jlong jTimeUs,
142 jobject jInputBuffer, jint inputSize, jobject jOutputBuffer,
143 jint sampleRate, jobject mediaCrypto, jint inputMode,
144 jbyteArray key, jbyteArray javaIv, jint inputNumSubSamples,
145 jintArray numBytesOfClearData, jintArray numBytesOfEncryptedData) {
146 // Doesn't support
147 // Java client should have checked vpxSupportSecureDecode
148 // and avoid calling this
149 // return -2 (DRM Error)
150 return -2;
151 }
152
DECODER_FUNC(void,opusClose,jlong jDecoder)153 DECODER_FUNC(void, opusClose, jlong jDecoder) {
154 OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
155 opus_multistream_decoder_destroy(decoder);
156 }
157
DECODER_FUNC(void,opusReset,jlong jDecoder)158 DECODER_FUNC(void, opusReset, jlong jDecoder) {
159 OpusMSDecoder* decoder = reinterpret_cast<OpusMSDecoder*>(jDecoder);
160 opus_multistream_decoder_ctl(decoder, OPUS_RESET_STATE);
161 }
162
DECODER_FUNC(jstring,opusGetErrorMessage,jlong jContext)163 DECODER_FUNC(jstring, opusGetErrorMessage, jlong jContext) {
164 return env->NewStringUTF(opus_strerror(errorCode));
165 }
166
DECODER_FUNC(jint,opusGetErrorCode,jlong jContext)167 DECODER_FUNC(jint, opusGetErrorCode, jlong jContext) { return errorCode; }
168
DECODER_FUNC(void,opusSetFloatOutput)169 DECODER_FUNC(void, opusSetFloatOutput) { outputFloat = true; }
170
LIBRARY_FUNC(jstring,opusIsSecureDecodeSupported)171 LIBRARY_FUNC(jstring, opusIsSecureDecodeSupported) {
172 // Doesn't support
173 return 0;
174 }
175
LIBRARY_FUNC(jstring,opusGetVersion)176 LIBRARY_FUNC(jstring, opusGetVersion) {
177 return env->NewStringUTF(opus_get_version_string());
178 }
179