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 import java.nio.ByteBuffer; 22 23 import android.media.MediaCodec.BufferInfo; 24 import android.util.Log; 25 26 27 /** 28 * DO NOT USE 29 * @hide 30 */ 31 public final class AmrInputStream extends InputStream { 32 private final static String TAG = "AmrInputStream"; 33 34 // frame is 20 msec at 8.000 khz 35 private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000; 36 37 MediaCodec mCodec; 38 BufferInfo mInfo; 39 boolean mSawOutputEOS; 40 boolean mSawInputEOS; 41 42 // pcm input stream 43 private InputStream mInputStream; 44 45 // result amr stream 46 private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2]; 47 private int mBufIn = 0; 48 private int mBufOut = 0; 49 50 // helper for bytewise read() 51 private byte[] mOneByte = new byte[1]; 52 53 /** 54 * DO NOT USE - use MediaCodec instead 55 */ 56 public AmrInputStream(InputStream inputStream) { 57 Log.w(TAG, "@@@@ AmrInputStream is not a public API @@@@"); 58 mInputStream = inputStream; 59 60 MediaFormat format = new MediaFormat(); 61 format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AMR_NB); 62 format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000); 63 format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); 64 format.setInteger(MediaFormat.KEY_BIT_RATE, 12200); 65 66 MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS); 67 String name = mcl.findEncoderForFormat(format); 68 if (name != null) { 69 try { 70 mCodec = MediaCodec.createByCodecName(name); 71 mCodec.configure(format, 72 null /* surface */, 73 null /* crypto */, 74 MediaCodec.CONFIGURE_FLAG_ENCODE); 75 mCodec.start(); 76 } catch (IOException e) { 77 if (mCodec != null) { 78 mCodec.release(); 79 } 80 mCodec = null; 81 } 82 } 83 mInfo = new BufferInfo(); 84 } 85 86 /** 87 * DO NOT USE 88 */ 89 @Override 90 public int read() throws IOException { 91 int rtn = read(mOneByte, 0, 1); 92 return rtn == 1 ? (0xff & mOneByte[0]) : -1; 93 } 94 95 /** 96 * DO NOT USE 97 */ 98 @Override 99 public int read(byte[] b) throws IOException { 100 return read(b, 0, b.length); 101 } 102 103 /** 104 * DO NOT USE 105 */ 106 @Override 107 public int read(byte[] b, int offset, int length) throws IOException { 108 if (mCodec == null) { 109 throw new IllegalStateException("not open"); 110 } 111 112 if (mBufOut >= mBufIn && !mSawOutputEOS) { 113 // no data left in buffer, refill it 114 mBufOut = 0; 115 mBufIn = 0; 116 117 // first push as much data into the encoder as possible 118 while (!mSawInputEOS) { 119 int index = mCodec.dequeueInputBuffer(0); 120 if (index < 0) { 121 // no input buffer currently available 122 break; 123 } else { 124 int numRead; 125 for (numRead = 0; numRead < SAMPLES_PER_FRAME * 2; ) { 126 int n = mInputStream.read(mBuf, numRead, SAMPLES_PER_FRAME * 2 - numRead); 127 if (n == -1) { 128 mSawInputEOS = true; 129 break; 130 } 131 numRead += n; 132 } 133 ByteBuffer buf = mCodec.getInputBuffer(index); 134 buf.put(mBuf, 0, numRead); 135 mCodec.queueInputBuffer(index, 136 0 /* offset */, 137 numRead, 138 0 /* presentationTimeUs */, 139 mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 /* flags */); 140 } 141 } 142 143 // now read encoded data from the encoder 144 int index = mCodec.dequeueOutputBuffer(mInfo, 0); 145 if (index >= 0) { 146 mBufIn = mInfo.size; 147 ByteBuffer out = mCodec.getOutputBuffer(index); 148 out.get(mBuf, 0 /* offset */, mBufIn /* length */); 149 mCodec.releaseOutputBuffer(index, false /* render */); 150 if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { 151 mSawOutputEOS = true; 152 } 153 } 154 } 155 156 if (mBufOut < mBufIn) { 157 // there is data in the buffer 158 if (length > mBufIn - mBufOut) { 159 length = mBufIn - mBufOut; 160 } 161 System.arraycopy(mBuf, mBufOut, b, offset, length); 162 mBufOut += length; 163 return length; 164 } 165 166 if (mSawInputEOS && mSawOutputEOS) { 167 // no more data available in buffer, codec or input stream 168 return -1; 169 } 170 171 // caller should try again 172 return 0; 173 } 174 175 @Override 176 public void close() throws IOException { 177 try { 178 if (mInputStream != null) { 179 mInputStream.close(); 180 } 181 } finally { 182 mInputStream = null; 183 try { 184 if (mCodec != null) { 185 mCodec.release(); 186 } 187 } finally { 188 mCodec = null; 189 } 190 } 191 } 192 193 @Override 194 protected void finalize() throws Throwable { 195 if (mCodec != null) { 196 Log.w(TAG, "AmrInputStream wasn't closed"); 197 mCodec.release(); 198 } 199 } 200 } 201