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