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