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