• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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