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 17package android.media; 18 19import android.util.Log; 20 21import java.io.InputStream; 22import java.io.IOException; 23 24 25/** 26 * AmrInputStream 27 * @hide 28 */ 29public final class AmrInputStream extends InputStream 30{ 31 static { 32 System.loadLibrary("media_jni"); 33 } 34 35 private final static String TAG = "AmrInputStream"; 36 37 // frame is 20 msec at 8.000 khz 38 private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000; 39 40 // pcm input stream 41 private InputStream mInputStream; 42 43 // native handle 44 private int mGae; 45 46 // result amr stream 47 private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2]; 48 private int mBufIn = 0; 49 private int mBufOut = 0; 50 51 // helper for bytewise read() 52 private byte[] mOneByte = new byte[1]; 53 54 /** 55 * Create a new AmrInputStream, which converts 16 bit PCM to AMR 56 * @param inputStream InputStream containing 16 bit PCM. 57 */ 58 public AmrInputStream(InputStream inputStream) { 59 mInputStream = inputStream; 60 mGae = GsmAmrEncoderNew(); 61 GsmAmrEncoderInitialize(mGae); 62 } 63 64 @Override 65 public int read() throws IOException { 66 int rtn = read(mOneByte, 0, 1); 67 return rtn == 1 ? (0xff & mOneByte[0]) : -1; 68 } 69 70 @Override 71 public int read(byte[] b) throws IOException { 72 return read(b, 0, b.length); 73 } 74 75 @Override 76 public int read(byte[] b, int offset, int length) throws IOException { 77 if (mGae == 0) throw new IllegalStateException("not open"); 78 79 // local buffer of amr encoded audio empty 80 if (mBufOut >= mBufIn) { 81 // reset the buffer 82 mBufOut = 0; 83 mBufIn = 0; 84 85 // fetch a 20 msec frame of pcm 86 for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) { 87 int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i); 88 if (n == -1) return -1; 89 i += n; 90 } 91 92 // encode it 93 mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0); 94 } 95 96 // return encoded audio to user 97 if (length > mBufIn - mBufOut) length = mBufIn - mBufOut; 98 System.arraycopy(mBuf, mBufOut, b, offset, length); 99 mBufOut += length; 100 101 return length; 102 } 103 104 @Override 105 public void close() throws IOException { 106 try { 107 if (mInputStream != null) mInputStream.close(); 108 } finally { 109 mInputStream = null; 110 try { 111 if (mGae != 0) GsmAmrEncoderCleanup(mGae); 112 } finally { 113 try { 114 if (mGae != 0) GsmAmrEncoderDelete(mGae); 115 } finally { 116 mGae = 0; 117 } 118 } 119 } 120 } 121 122 @Override 123 protected void finalize() throws Throwable { 124 if (mGae != 0) { 125 close(); 126 throw new IllegalStateException("someone forgot to close AmrInputStream"); 127 } 128 } 129 130 // 131 // AudioRecord JNI interface 132 // 133 private static native int GsmAmrEncoderNew(); 134 private static native void GsmAmrEncoderInitialize(int gae); 135 private static native int GsmAmrEncoderEncode(int gae, 136 byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException; 137 private static native void GsmAmrEncoderCleanup(int gae); 138 private static native void GsmAmrEncoderDelete(int gae); 139 140} 141