Home | History | Annotate | Download | only in media
      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  * AmrInputStream
     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      * Create a new AmrInputStream, which converts 16 bit PCM to AMR
     55      * @param inputStream InputStream containing 16 bit PCM.
     56      */
     57     public AmrInputStream(InputStream inputStream) {
     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     @Override
     87     public int read() throws IOException {
     88         int rtn = read(mOneByte, 0, 1);
     89         return rtn == 1 ? (0xff & mOneByte[0]) : -1;
     90     }
     91 
     92     @Override
     93     public int read(byte[] b) throws IOException {
     94         return read(b, 0, b.length);
     95     }
     96 
     97     @Override
     98     public int read(byte[] b, int offset, int length) throws IOException {
     99         if (mCodec == null) {
    100             throw new IllegalStateException("not open");
    101         }
    102 
    103         if (mBufOut >= mBufIn && !mSawOutputEOS) {
    104             // no data left in buffer, refill it
    105             mBufOut = 0;
    106             mBufIn = 0;
    107 
    108             // first push as much data into the encoder as possible
    109             while (!mSawInputEOS) {
    110                 int index = mCodec.dequeueInputBuffer(0);
    111                 if (index < 0) {
    112                     // no input buffer currently available
    113                     break;
    114                 } else {
    115                     int numRead;
    116                     for (numRead = 0; numRead < SAMPLES_PER_FRAME * 2; ) {
    117                         int n = mInputStream.read(mBuf, numRead, SAMPLES_PER_FRAME * 2 - numRead);
    118                         if (n == -1) {
    119                             mSawInputEOS = true;
    120                             break;
    121                         }
    122                         numRead += n;
    123                     }
    124                     ByteBuffer buf = mCodec.getInputBuffer(index);
    125                     buf.put(mBuf, 0, numRead);
    126                     mCodec.queueInputBuffer(index,
    127                             0 /* offset */,
    128                             numRead,
    129                             0 /* presentationTimeUs */,
    130                             mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 /* flags */);
    131                 }
    132             }
    133 
    134             // now read encoded data from the encoder (blocking, since we just filled up the
    135             // encoder's input with data it should be able to output at least one buffer)
    136             while (true) {
    137                 int index = mCodec.dequeueOutputBuffer(mInfo, -1);
    138                 if (index >= 0) {
    139                     mBufIn = mInfo.size;
    140                     ByteBuffer out = mCodec.getOutputBuffer(index);
    141                     out.get(mBuf, 0 /* offset */, mBufIn /* length */);
    142                     mCodec.releaseOutputBuffer(index,  false /* render */);
    143                     if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
    144                         mSawOutputEOS = true;
    145                     }
    146                     break;
    147                 }
    148             }
    149         }
    150 
    151         if (mBufOut < mBufIn) {
    152             // there is data in the buffer
    153             if (length > mBufIn - mBufOut) {
    154                 length = mBufIn - mBufOut;
    155             }
    156             System.arraycopy(mBuf, mBufOut, b, offset, length);
    157             mBufOut += length;
    158             return length;
    159         }
    160 
    161         if (mSawInputEOS && mSawOutputEOS) {
    162             // no more data available in buffer, codec or input stream
    163             return -1;
    164         }
    165 
    166         // caller should try again
    167         return 0;
    168     }
    169 
    170     @Override
    171     public void close() throws IOException {
    172         try {
    173             if (mInputStream != null) {
    174                 mInputStream.close();
    175             }
    176         } finally {
    177             mInputStream = null;
    178             try {
    179                 if (mCodec != null) {
    180                     mCodec.release();
    181                 }
    182             } finally {
    183                 mCodec = null;
    184             }
    185         }
    186     }
    187 
    188     @Override
    189     protected void finalize() throws Throwable {
    190         if (mCodec != null) {
    191             Log.w(TAG, "AmrInputStream wasn't closed");
    192             mCodec.release();
    193         }
    194     }
    195 }
    196