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