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 <media/mediarecorder.h> 22#include <stdio.h> 23#include <assert.h> 24#include <limits.h> 25#include <unistd.h> 26#include <fcntl.h> 27#include <utils/threads.h> 28 29#include "jni.h" 30#include "JNIHelp.h" 31#include "android_runtime/AndroidRuntime.h" 32#include "gsmamr_encoder_wrapper.h" 33 34 35// ---------------------------------------------------------------------------- 36 37using namespace android; 38 39// Corresponds to max bit rate of 12.2 kbps. 40static const int aMaxOutputBufferSize = 32; 41 42static const int SAMPLES_PER_FRAME = 8000 * 20 / 1000; 43 44 45// 46// helper function to throw an exception 47// 48static void throwException(JNIEnv *env, const char* ex, const char* fmt, int data) { 49 if (jclass cls = env->FindClass(ex)) { 50 char msg[1000]; 51 sprintf(msg, fmt, data); 52 env->ThrowNew(cls, msg); 53 env->DeleteLocalRef(cls); 54 } 55} 56 57static jint android_media_AmrInputStream_GsmAmrEncoderNew 58 (JNIEnv *env, jclass clazz) { 59 CPvGsmAmrEncoder* gae = new CPvGsmAmrEncoder(); 60 if (gae == NULL) { 61 throwException(env, "java/lang/IllegalStateException", 62 "new CPvGsmAmrEncoder() failed", 0); 63 } 64 return (jint)gae; 65} 66 67static void android_media_AmrInputStream_GsmAmrEncoderInitialize 68 (JNIEnv *env, jclass clazz, jint gae) { 69 // set input parameters 70 TEncodeProperties encodeProps; 71 encodeProps.iInBitsPerSample = 16; 72 encodeProps.iInSamplingRate = 8000; 73 encodeProps.iInClockRate = 1000; 74 encodeProps.iInNumChannels = 1; 75 encodeProps.iInInterleaveMode = TEncodeProperties::EINTERLEAVE_LR; 76 encodeProps.iMode = CPvGsmAmrEncoder::GSM_AMR_12_2; 77 encodeProps.iBitStreamFormat = false; 78 encodeProps.iAudioObjectType = 0; 79 encodeProps.iOutSamplingRate = encodeProps.iInSamplingRate; 80 encodeProps.iOutNumChannels = encodeProps.iInNumChannels; 81 encodeProps.iOutClockRate = encodeProps.iInClockRate; 82 83 if (int rtn = ((CPvGsmAmrEncoder*)gae)-> 84 InitializeEncoder(aMaxOutputBufferSize, &encodeProps)) { 85 throwException(env, "java/lang/IllegalArgumentException", 86 "CPvGsmAmrEncoder::InitializeEncoder failed %d", rtn); 87 } 88} 89 90static jint android_media_AmrInputStream_GsmAmrEncoderEncode 91 (JNIEnv *env, jclass clazz, 92 jint gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) { 93 94 // set up input stream 95 jbyte inBuf[SAMPLES_PER_FRAME*2]; 96 TInputAudioStream in; 97 in.iSampleBuffer = (uint8*)inBuf; 98 env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf); 99 in.iSampleLength = sizeof(inBuf); 100 in.iMode = CPvGsmAmrEncoder::GSM_AMR_12_2; 101 in.iStartTime = 0; 102 in.iStopTime = 0; 103 104 // set up output stream 105 jbyte outBuf[aMaxOutputBufferSize]; 106 int32 sampleFrameSize[1] = { 0 }; 107 TOutputAudioStream out; 108 out.iBitStreamBuffer = (uint8*)outBuf; 109 out.iNumSampleFrames = 0; 110 out.iSampleFrameSize = sampleFrameSize; 111 out.iStartTime = 0; 112 out.iStopTime = 0; 113 114 // encode 115 if (int rtn = ((CPvGsmAmrEncoder*)gae)->Encode(in, out)) { 116 throwException(env, "java/io/IOException", "CPvGsmAmrEncoder::Encode failed %d", rtn); 117 return -1; 118 } 119 120 // validate one-frame assumption 121 if (out.iNumSampleFrames != 1) { 122 throwException(env, "java/io/IOException", 123 "CPvGsmAmrEncoder::Encode more than one frame returned %d", out.iNumSampleFrames); 124 return 0; 125 } 126 127 // copy result 128 int length = out.iSampleFrameSize[0]; 129 130 // The 1st byte of PV AMR frames are WMF (Wireless Multimedia Forum) 131 // bitpacked, i.e.; 132 // [P(4) + FT(4)]. Q=1 for good frame, P=padding bit, 0 133 // Here we are converting the header to be as specified in Section 5.3 of 134 // RFC 3267 (AMR storage format) i.e. 135 // [P(1) + FT(4) + Q(1) + P(2)]. 136 if (length > 0) { 137 outBuf[0] = (outBuf[0] << 3) | 0x4; 138 } 139 140 env->SetByteArrayRegion(amr, amrOffset, length, outBuf); 141 142 return length; 143} 144 145static void android_media_AmrInputStream_GsmAmrEncoderCleanup 146 (JNIEnv *env, jclass clazz, jint gae) { 147 if (int rtn = ((CPvGsmAmrEncoder*)gae)->CleanupEncoder()) { 148 throwException(env, "java/lang/IllegalStateException", 149 "CPvGsmAmrEncoder::CleanupEncoder failed %d", rtn); 150 } 151} 152 153static void android_media_AmrInputStream_GsmAmrEncoderDelete 154 (JNIEnv *env, jclass clazz, jint gae) { 155 delete (CPvGsmAmrEncoder*)gae; 156} 157 158// ---------------------------------------------------------------------------- 159 160static JNINativeMethod gMethods[] = { 161 {"GsmAmrEncoderNew", "()I", (void*)android_media_AmrInputStream_GsmAmrEncoderNew}, 162 {"GsmAmrEncoderInitialize", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize}, 163 {"GsmAmrEncoderEncode", "(I[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode}, 164 {"GsmAmrEncoderCleanup", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderCleanup}, 165 {"GsmAmrEncoderDelete", "(I)V", (void*)android_media_AmrInputStream_GsmAmrEncoderDelete}, 166}; 167 168 169int register_android_media_AmrInputStream(JNIEnv *env) 170{ 171 const char* const kClassPathName = "android/media/AmrInputStream"; 172 jclass clazz; 173 174 clazz = env->FindClass(kClassPathName); 175 if (clazz == NULL) { 176 LOGE("Can't find %s", kClassPathName); 177 return -1; 178 } 179 180 return AndroidRuntime::registerNativeMethods(env, 181 kClassPathName, gMethods, NELEM(gMethods)); 182} 183 184 185