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