• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
GsmAmrEncoderStateGsmAmrEncoderState39     GsmAmrEncoderState()
40         : mEncState(NULL),
41           mSidState(NULL),
42           mLastModeUsed(0) {
43     }
44 
~GsmAmrEncoderStateGsmAmrEncoderState45     ~GsmAmrEncoderState() {}
46 
47     void*   mEncState;
48     void*   mSidState;
49     int32_t mLastModeUsed;
50 };
51 
52 //
53 // helper function to throw an exception
54 //
throwException(JNIEnv * env,const char * ex,const char * fmt,int data)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 
android_media_AmrInputStream_GsmAmrEncoderNew(JNIEnv * env,jclass clazz)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 
android_media_AmrInputStream_GsmAmrEncoderInitialize(JNIEnv * env,jclass clazz,jint gae)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 
android_media_AmrInputStream_GsmAmrEncoderEncode(JNIEnv * env,jclass clazz,jint gae,jbyteArray pcm,jint pcmOffset,jbyteArray amr,jint amrOffset)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 
android_media_AmrInputStream_GsmAmrEncoderCleanup(JNIEnv * env,jclass clazz,jint gae)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 
android_media_AmrInputStream_GsmAmrEncoderDelete(JNIEnv * env,jclass clazz,jint gae)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 
register_android_media_AmrInputStream(JNIEnv * env)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