blob: c4dd07ee9e496bb4ba31eec4352a2582fd7dacb2 [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
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;
Jianhong Jiang3ca47d12009-04-13 19:01:51 -070077 encodeProps.iBitStreamFormat = false;
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -080078 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";
The Android Open Source Projectf013e1a2008-12-17 18:05:43 -0800172
173 return AndroidRuntime::registerNativeMethods(env,
174 kClassPathName, gMethods, NELEM(gMethods));
175}
176
177