android_media_AmrInputStream.cpp revision 49b6fbaebd5413b92c6dcff423da95d5525455ee
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
28using namespace android;
29
30// Corresponds to max bit rate of 12.2 kbps.
31static const int MAX_OUTPUT_BUFFER_SIZE = 32;
32static const int FRAME_DURATION_MS = 20;
33static const int SAMPLING_RATE_HZ = 8000;
34static const int SAMPLES_PER_FRAME = ((SAMPLING_RATE_HZ * FRAME_DURATION_MS) / 1000);
35static const int BYTES_PER_SAMPLE = 2;  // Assume 16-bit PCM samples
36static const int BYTES_PER_FRAME = (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE);
37
38struct 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//
55static 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
64static 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
74static 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
84static 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
120static 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
128static void android_media_AmrInputStream_GsmAmrEncoderDelete
129        (JNIEnv *env, jclass clazz, jint gae) {
130    delete (GsmAmrEncoderState*)gae;
131}
132
133// ----------------------------------------------------------------------------
134
135static 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
144int 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