1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media;
18 
19 import java.io.InputStream;
20 import java.io.IOException;
21 
22 
23 /**
24  * AmrInputStream
25  * @hide
26  */
27 public final class AmrInputStream extends InputStream
28 {
29     static {
30         System.loadLibrary("media_jni");
31     }
32 
33     private final static String TAG = "AmrInputStream";
34 
35     // frame is 20 msec at 8.000 khz
36     private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
37 
38     // pcm input stream
39     private InputStream mInputStream;
40 
41     // native handle
42     private long mGae;
43 
44     // result amr stream
45     private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
46     private int mBufIn = 0;
47     private int mBufOut = 0;
48 
49     // helper for bytewise read()
50     private byte[] mOneByte = new byte[1];
51 
52     /**
53      * Create a new AmrInputStream, which converts 16 bit PCM to AMR
54      * @param inputStream InputStream containing 16 bit PCM.
55      */
AmrInputStream(InputStream inputStream)56     public AmrInputStream(InputStream inputStream) {
57         mInputStream = inputStream;
58         mGae = GsmAmrEncoderNew();
59         GsmAmrEncoderInitialize(mGae);
60     }
61 
62     @Override
read()63     public int read() throws IOException {
64         int rtn = read(mOneByte, 0, 1);
65         return rtn == 1 ? (0xff & mOneByte[0]) : -1;
66     }
67 
68     @Override
read(byte[] b)69     public int read(byte[] b) throws IOException {
70         return read(b, 0, b.length);
71     }
72 
73     @Override
read(byte[] b, int offset, int length)74     public int read(byte[] b, int offset, int length) throws IOException {
75         if (mGae == 0) throw new IllegalStateException("not open");
76 
77         // local buffer of amr encoded audio empty
78         if (mBufOut >= mBufIn) {
79             // reset the buffer
80             mBufOut = 0;
81             mBufIn = 0;
82 
83             // fetch a 20 msec frame of pcm
84             for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) {
85                 int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i);
86                 if (n == -1) return -1;
87                 i += n;
88             }
89 
90             // encode it
91             mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0);
92         }
93 
94         // return encoded audio to user
95         if (length > mBufIn - mBufOut) length = mBufIn - mBufOut;
96         System.arraycopy(mBuf, mBufOut, b, offset, length);
97         mBufOut += length;
98 
99         return length;
100     }
101 
102     @Override
close()103     public void close() throws IOException {
104         try {
105             if (mInputStream != null) mInputStream.close();
106         } finally {
107             mInputStream = null;
108             try {
109                 if (mGae != 0) GsmAmrEncoderCleanup(mGae);
110             } finally {
111                 try {
112                     if (mGae != 0) GsmAmrEncoderDelete(mGae);
113                 } finally {
114                     mGae = 0;
115                 }
116             }
117         }
118     }
119 
120     @Override
finalize()121     protected void finalize() throws Throwable {
122         if (mGae != 0) {
123             close();
124             throw new IllegalStateException("someone forgot to close AmrInputStream");
125         }
126     }
127 
128     //
129     // AudioRecord JNI interface
130     //
GsmAmrEncoderNew()131     private static native long GsmAmrEncoderNew();
GsmAmrEncoderInitialize(long gae)132     private static native void GsmAmrEncoderInitialize(long gae);
GsmAmrEncoderEncode(long gae, byte[] pcm, int pcmOffset, byte[] amr, int amrOffset)133     private static native int GsmAmrEncoderEncode(long gae,
134             byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException;
GsmAmrEncoderCleanup(long gae)135     private static native void GsmAmrEncoderCleanup(long gae);
GsmAmrEncoderDelete(long gae)136     private static native void GsmAmrEncoderDelete(long gae);
137 
138 }
139