Home | History | Annotate | Download | only in jni
      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 //
     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)) {
     57         char msg[128];
     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) {
     66     GsmAmrEncoderState* gae = new GsmAmrEncoderState();
     67     if (gae == NULL) {
     68         throwException(env, "java/lang/RuntimeException",
     69                 "Out of memory", 0);
     70     }
     71     return (jint)gae;
     72 }
     73 
     74 static void android_media_AmrInputStream_GsmAmrEncoderInitialize
     75         (JNIEnv *env, jclass clazz, jint gae) {
     76     GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
     77     int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false);
     78     if (nResult != OK) {
     79         throwException(env, "java/lang/IllegalArgumentException",
     80                 "GsmAmrEncoder initialization failed %d", nResult);
     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 
     88     jbyte inBuf[BYTES_PER_FRAME];
     89     jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE];
     90 
     91     env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf);
     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);
    102         return -1;
    103     }
    104 
    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) {
    122     GsmAmrEncoderState *state = (GsmAmrEncoderState *)gae;
    123     AMREncodeExit(&state->mEncState, &state->mSidState);
    124     state->mEncState = NULL;
    125     state->mSidState = NULL;
    126 }
    127 
    128 static void android_media_AmrInputStream_GsmAmrEncoderDelete
    129         (JNIEnv *env, jclass clazz, jint gae) {
    130     delete (GsmAmrEncoderState*)gae;
    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";
    147 
    148     return AndroidRuntime::registerNativeMethods(env,
    149             kClassPathName, gMethods, NELEM(gMethods));
    150 }
    151 
    152 
    153