blob: 3df653015fbddd8a4c8a2369a02c1f52ec784c90 [file] [log] [blame]
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -08001/*
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 Projectf013e1a2008-12-17 18:05:43 -080021#include "jni.h"
22#include "JNIHelp.h"
23#include "android_runtime/AndroidRuntime.h"
James Dong49b6fba2010-06-17 16:03:22 -070024#include "gsmamr_enc.h"
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080025
26// ----------------------------------------------------------------------------
27
28using namespace android;
29
30// Corresponds to max bit rate of 12.2 kbps.
James Dong49b6fba2010-06-17 16:03:22 -070031static 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);
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080037
James Dong49b6fba2010-06-17 16:03:22 -070038struct GsmAmrEncoderState {
39 GsmAmrEncoderState()
40 : mEncState(NULL),
41 mSidState(NULL),
42 mLastModeUsed(0) {
43 }
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080044
James Dong49b6fba2010-06-17 16:03:22 -070045 ~GsmAmrEncoderState() {}
46
47 void* mEncState;
48 void* mSidState;
49 int32_t mLastModeUsed;
50};
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080051
Ashok Bhatb348c3f2013-12-17 14:10:25 +000052static jlong android_media_AmrInputStream_GsmAmrEncoderNew
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080053 (JNIEnv *env, jclass clazz) {
James Dong49b6fba2010-06-17 16:03:22 -070054 GsmAmrEncoderState* gae = new GsmAmrEncoderState();
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080055 if (gae == NULL) {
Elliott Hughes15dd15f2011-04-08 17:42:34 -070056 jniThrowRuntimeException(env, "Out of memory");
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080057 }
Ashok Bhatb348c3f2013-12-17 14:10:25 +000058 return (jlong)gae;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080059}
60
61static void android_media_AmrInputStream_GsmAmrEncoderInitialize
Ashok Bhatb348c3f2013-12-17 14:10:25 +000062 (JNIEnv *env, jclass clazz, jlong gae) {
James Dong49b6fba2010-06-17 16:03:22 -070063 GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
64 int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false);
65 if (nResult != OK) {
Elliott Hughes15dd15f2011-04-08 17:42:34 -070066 jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
James Dong49b6fba2010-06-17 16:03:22 -070067 "GsmAmrEncoder initialization failed %d", nResult);
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080068 }
69}
70
71static jint android_media_AmrInputStream_GsmAmrEncoderEncode
72 (JNIEnv *env, jclass clazz,
Ashok Bhatb348c3f2013-12-17 14:10:25 +000073 jlong gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) {
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080074
James Dong49b6fba2010-06-17 16:03:22 -070075 jbyte inBuf[BYTES_PER_FRAME];
76 jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE];
77
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080078 env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf);
James Dong49b6fba2010-06-17 16:03:22 -070079 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) {
Elliott Hughes15dd15f2011-04-08 17:42:34 -070087 jniThrowExceptionFmt(env, "java/io/IOException",
James Dong49b6fba2010-06-17 16:03:22 -070088 "Failed to encode a frame with error code: %d", length);
Ashok Bhatb348c3f2013-12-17 14:10:25 +000089 return (jint)-1;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080090 }
91
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080092 // 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
Ashok Bhatb348c3f2013-12-17 14:10:25 +0000104 return (jint)length;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800105}
106
107static void android_media_AmrInputStream_GsmAmrEncoderCleanup
Ashok Bhatb348c3f2013-12-17 14:10:25 +0000108 (JNIEnv *env, jclass clazz, jlong gae) {
109 GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
James Dong49b6fba2010-06-17 16:03:22 -0700110 AMREncodeExit(&state->mEncState, &state->mSidState);
111 state->mEncState = NULL;
112 state->mSidState = NULL;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800113}
114
115static void android_media_AmrInputStream_GsmAmrEncoderDelete
Ashok Bhatb348c3f2013-12-17 14:10:25 +0000116 (JNIEnv *env, jclass clazz, jlong gae) {
James Dong49b6fba2010-06-17 16:03:22 -0700117 delete (GsmAmrEncoderState*)gae;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800118}
119
120// ----------------------------------------------------------------------------
121
122static JNINativeMethod gMethods[] = {
Ashok Bhatb348c3f2013-12-17 14:10:25 +0000123 {"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},
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800128};
129
130
131int register_android_media_AmrInputStream(JNIEnv *env)
132{
133 const char* const kClassPathName = "android/media/AmrInputStream";
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800134
135 return AndroidRuntime::registerNativeMethods(env,
136 kClassPathName, gMethods, NELEM(gMethods));
137}