1 /* 2 ** 3 ** Copyright 2007, The Android Open Source Project 4 ** 5 ** Licensed under the Apache License, Version 2.0 (the "License"); 6 ** you may not use this file except in compliance with the License. 7 ** You may obtain a copy of the License at 8 ** 9 ** http://www.apache.org/licenses/LICENSE-2.0 10 ** 11 ** Unless required by applicable law or agreed to in writing, software 12 ** distributed under the License is distributed on an "AS IS" BASIS, 13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 ** See the License for the specific language governing permissions and 15 ** limitations under the License. 16 */ 17 18 #define LOG_TAG "AmrInputStream" 19 #include "utils/Log.h" 20 21 #include "jni.h" 22 #include "JNIHelp.h" 23 #include "android_runtime/AndroidRuntime.h" 24 #include "gsmamr_enc.h" 25 26 // ---------------------------------------------------------------------------- 27 28 using namespace android; 29 30 // Corresponds to max bit rate of 12.2 kbps. 31 static const int MAX_OUTPUT_BUFFER_SIZE = 32; 32 static const int FRAME_DURATION_MS = 20; 33 static const int SAMPLING_RATE_HZ = 8000; 34 static const int SAMPLES_PER_FRAME = ((SAMPLING_RATE_HZ * FRAME_DURATION_MS) / 1000); 35 static const int BYTES_PER_SAMPLE = 2; // Assume 16-bit PCM samples 36 static const int BYTES_PER_FRAME = (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE); 37 38 struct GsmAmrEncoderState { 39 GsmAmrEncoderState() 40 : mEncState(NULL), 41 mSidState(NULL), 42 mLastModeUsed(0) { 43 } 44 45 ~GsmAmrEncoderState() {} 46 47 void* mEncState; 48 void* mSidState; 49 int32_t mLastModeUsed; 50 }; 51 52 static jlong android_media_AmrInputStream_GsmAmrEncoderNew 53 (JNIEnv *env, jclass /* clazz */) { 54 GsmAmrEncoderState* gae = new GsmAmrEncoderState(); 55 if (gae == NULL) { 56 jniThrowRuntimeException(env, "Out of memory"); 57 } 58 return (jlong)gae; 59 } 60 61 static void android_media_AmrInputStream_GsmAmrEncoderInitialize 62 (JNIEnv *env, jclass /* clazz */, jlong gae) { 63 GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; 64 int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false); 65 if (nResult != OK) { 66 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", 67 "GsmAmrEncoder initialization failed %d", nResult); 68 } 69 } 70 71 static jint android_media_AmrInputStream_GsmAmrEncoderEncode 72 (JNIEnv *env, jclass /* clazz */, 73 jlong gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) { 74 75 jbyte inBuf[BYTES_PER_FRAME]; 76 jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE]; 77 78 env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf); 79 GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; 80 int32_t length = AMREncode(state->mEncState, state->mSidState, 81 (Mode) MR122, 82 (int16_t *) inBuf, 83 (unsigned char *) outBuf, 84 (Frame_Type_3GPP*) &state->mLastModeUsed, 85 AMR_TX_WMF); 86 if (length < 0) { 87 jniThrowExceptionFmt(env, "java/io/IOException", 88 "Failed to encode a frame with error code: %d", length); 89 return (jint)-1; 90 } 91 92 // The 1st byte of PV AMR frames are WMF (Wireless Multimedia Forum) 93 // bitpacked, i.e.; 94 // [P(4) + FT(4)]. Q=1 for good frame, P=padding bit, 0 95 // Here we are converting the header to be as specified in Section 5.3 of 96 // RFC 3267 (AMR storage format) i.e. 97 // [P(1) + FT(4) + Q(1) + P(2)]. 98 if (length > 0) { 99 outBuf[0] = (outBuf[0] << 3) | 0x4; 100 } 101 102 env->SetByteArrayRegion(amr, amrOffset, length, outBuf); 103 104 return (jint)length; 105 } 106 107 static void android_media_AmrInputStream_GsmAmrEncoderCleanup 108 (JNIEnv* /* env */, jclass /* clazz */, jlong gae) { 109 GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; 110 AMREncodeExit(&state->mEncState, &state->mSidState); 111 state->mEncState = NULL; 112 state->mSidState = NULL; 113 } 114 115 static void android_media_AmrInputStream_GsmAmrEncoderDelete 116 (JNIEnv* /* env */, jclass /* clazz */, jlong gae) { 117 delete (GsmAmrEncoderState*)gae; 118 } 119 120 // ---------------------------------------------------------------------------- 121 122 static const JNINativeMethod gMethods[] = { 123 {"GsmAmrEncoderNew", "()J", (void*)android_media_AmrInputStream_GsmAmrEncoderNew}, 124 {"GsmAmrEncoderInitialize", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize}, 125 {"GsmAmrEncoderEncode", "(J[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode}, 126 {"GsmAmrEncoderCleanup", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderCleanup}, 127 {"GsmAmrEncoderDelete", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderDelete}, 128 }; 129 130 131 int register_android_media_AmrInputStream(JNIEnv *env) 132 { 133 const char* const kClassPathName = "android/media/AmrInputStream"; 134 135 return AndroidRuntime::registerNativeMethods(env, 136 kClassPathName, gMethods, NELEM(gMethods)); 137 } 138