The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 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 | |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 21 | #include "jni.h" |
| 22 | #include "JNIHelp.h" |
| 23 | #include "android_runtime/AndroidRuntime.h" |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 24 | #include "gsmamr_enc.h" |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 25 | |
| 26 | // ---------------------------------------------------------------------------- |
| 27 | |
| 28 | using namespace android; |
| 29 | |
| 30 | // Corresponds to max bit rate of 12.2 kbps. |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 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); |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 37 | |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 38 | struct GsmAmrEncoderState { |
| 39 | GsmAmrEncoderState() |
| 40 | : mEncState(NULL), |
| 41 | mSidState(NULL), |
| 42 | mLastModeUsed(0) { |
| 43 | } |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 44 | |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 45 | ~GsmAmrEncoderState() {} |
| 46 | |
| 47 | void* mEncState; |
| 48 | void* mSidState; |
| 49 | int32_t mLastModeUsed; |
| 50 | }; |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 51 | |
| 52 | // |
| 53 | // helper function to throw an exception |
| 54 | // |
| 55 | static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) { |
| 56 | if (jclass cls = env->FindClass(ex)) { |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 57 | char msg[128]; |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 58 | sprintf(msg, fmt, data); |
| 59 | env->ThrowNew(cls, msg); |
| 60 | env->DeleteLocalRef(cls); |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | static jint android_media_AmrInputStream_GsmAmrEncoderNew |
| 65 | (JNIEnv *env, jclass clazz) { |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 66 | GsmAmrEncoderState* gae = new GsmAmrEncoderState(); |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 67 | if (gae == NULL) { |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 68 | throwException(env, "java/lang/RuntimeException", |
| 69 | "Out of memory", 0); |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 70 | } |
| 71 | return (jint)gae; |
| 72 | } |
| 73 | |
| 74 | static void android_media_AmrInputStream_GsmAmrEncoderInitialize |
| 75 | (JNIEnv *env, jclass clazz, jint gae) { |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 76 | GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; |
| 77 | int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false); |
| 78 | if (nResult != OK) { |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 79 | throwException(env, "java/lang/IllegalArgumentException", |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 80 | "GsmAmrEncoder initialization failed %d", nResult); |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 81 | } |
| 82 | } |
| 83 | |
| 84 | static jint android_media_AmrInputStream_GsmAmrEncoderEncode |
| 85 | (JNIEnv *env, jclass clazz, |
| 86 | jint gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) { |
| 87 | |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 88 | jbyte inBuf[BYTES_PER_FRAME]; |
| 89 | jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE]; |
| 90 | |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 91 | env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf); |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 92 | GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae; |
| 93 | int32_t length = AMREncode(state->mEncState, state->mSidState, |
| 94 | (Mode) MR122, |
| 95 | (int16_t *) inBuf, |
| 96 | (unsigned char *) outBuf, |
| 97 | (Frame_Type_3GPP*) &state->mLastModeUsed, |
| 98 | AMR_TX_WMF); |
| 99 | if (length < 0) { |
| 100 | throwException(env, "java/io/IOException", |
| 101 | "Failed to encode a frame with error code: %d", length); |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 102 | return -1; |
| 103 | } |
| 104 | |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 105 | // The 1st byte of PV AMR frames are WMF (Wireless Multimedia Forum) |
| 106 | // bitpacked, i.e.; |
| 107 | // [P(4) + FT(4)]. Q=1 for good frame, P=padding bit, 0 |
| 108 | // Here we are converting the header to be as specified in Section 5.3 of |
| 109 | // RFC 3267 (AMR storage format) i.e. |
| 110 | // [P(1) + FT(4) + Q(1) + P(2)]. |
| 111 | if (length > 0) { |
| 112 | outBuf[0] = (outBuf[0] << 3) | 0x4; |
| 113 | } |
| 114 | |
| 115 | env->SetByteArrayRegion(amr, amrOffset, length, outBuf); |
| 116 | |
| 117 | return length; |
| 118 | } |
| 119 | |
| 120 | static void android_media_AmrInputStream_GsmAmrEncoderCleanup |
| 121 | (JNIEnv *env, jclass clazz, jint gae) { |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 122 | GsmAmrEncoderState *state = (GsmAmrEncoderState *)gae; |
| 123 | AMREncodeExit(&state->mEncState, &state->mSidState); |
| 124 | state->mEncState = NULL; |
| 125 | state->mSidState = NULL; |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | static void android_media_AmrInputStream_GsmAmrEncoderDelete |
| 129 | (JNIEnv *env, jclass clazz, jint gae) { |
James Dong | 49b6fba | 2010-06-17 16:03:22 -0700 | [diff] [blame] | 130 | delete (GsmAmrEncoderState*)gae; |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | // ---------------------------------------------------------------------------- |
| 134 | |
| 135 | static JNINativeMethod gMethods[] = { |
| 136 | {"GsmAmrEncoderNew", "()I", (void*)android_media_AmrInputStream_GsmAmrEncoderNew}, |
| 137 | {"GsmAmrEncoderInitialize", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize}, |
| 138 | {"GsmAmrEncoderEncode", "(I[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode}, |
| 139 | {"GsmAmrEncoderCleanup", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderCleanup}, |
| 140 | {"GsmAmrEncoderDelete", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderDelete}, |
| 141 | }; |
| 142 | |
| 143 | |
| 144 | int register_android_media_AmrInputStream(JNIEnv *env) |
| 145 | { |
| 146 | const char* const kClassPathName = "android/media/AmrInputStream"; |
The Android Open Source Project | f013e1a | 2008-12-17 18:05:43 -0800 | [diff] [blame] | 147 | |
| 148 | return AndroidRuntime::registerNativeMethods(env, |
| 149 | kClassPathName, gMethods, NELEM(gMethods)); |
| 150 | } |
| 151 | |
| 152 | |