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  * 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