Home | History | Annotate | Download | only in decoder
      1 /*
      2  * Copyright (C) 2012 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 androidx.media.filterfw.decoder;
     18 
     19 import android.annotation.TargetApi;
     20 import android.media.MediaCodec;
     21 import android.media.MediaCodec.BufferInfo;
     22 import android.media.MediaFormat;
     23 
     24 import androidx.media.filterfw.FrameValue;
     25 
     26 import java.io.ByteArrayOutputStream;
     27 import java.io.IOException;
     28 import java.nio.ByteBuffer;
     29 
     30 /**
     31  * {@link TrackDecoder} for decoding audio tracks.
     32  *
     33  * TODO: find out if we always get 16 bits per channel and document.
     34  */
     35 @TargetApi(16)
     36 public class AudioTrackDecoder extends TrackDecoder {
     37 
     38     private final ByteArrayOutputStream mAudioByteStream; // Guarded by mAudioByteStreamLock.
     39     private final Object mAudioByteStreamLock;
     40 
     41     private int mAudioSampleRate;
     42     private int mAudioChannelCount;
     43     private long mAudioPresentationTimeUs;
     44 
     45     public AudioTrackDecoder(int trackIndex, MediaFormat format, Listener listener) {
     46         super(trackIndex, format, listener);
     47 
     48         if (!DecoderUtil.isAudioFormat(format)) {
     49             throw new IllegalArgumentException(
     50                     "AudioTrackDecoder can only be used with audio formats");
     51         }
     52 
     53         mAudioByteStream = new ByteArrayOutputStream();
     54         mAudioByteStreamLock = new Object();
     55 
     56         mAudioSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
     57         mAudioChannelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
     58     }
     59 
     60     @Override
     61     protected MediaCodec initMediaCodec(MediaFormat format) {
     62         MediaCodec mediaCodec;
     63         try {
     64             mediaCodec = MediaCodec.createDecoderByType(
     65                     format.getString(MediaFormat.KEY_MIME));
     66         } catch (IOException e) {
     67             throw new RuntimeException(
     68                     "failed to create decoder for "
     69                     + format.getString(MediaFormat.KEY_MIME), e);
     70         }
     71         mediaCodec.configure(format, null, null, 0);
     72         return mediaCodec;
     73     }
     74 
     75     @Override
     76     protected boolean onDataAvailable(
     77             MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info) {
     78         ByteBuffer buffer = buffers[bufferIndex];
     79         byte[] data = new byte[info.size];
     80         buffer.position(info.offset);
     81         buffer.get(data, 0, info.size);
     82 
     83         synchronized (mAudioByteStreamLock) {
     84             try {
     85                 if (mAudioByteStream.size() == 0 && data.length > 0) {
     86                     mAudioPresentationTimeUs = info.presentationTimeUs;
     87                 }
     88 
     89                 mAudioByteStream.write(data);
     90             } catch (IOException e) {
     91                 // Just drop the audio sample.
     92             }
     93         }
     94 
     95         buffer.clear();
     96         codec.releaseOutputBuffer(bufferIndex, false);
     97         notifyListener();
     98         return true;
     99     }
    100 
    101     /**
    102      * Fills the argument {@link FrameValue} with an audio sample containing the audio that was
    103      * decoded since the last call of this method. The decoder's buffer is cleared as a result.
    104      */
    105     public void grabSample(FrameValue audioFrame) {
    106         synchronized (mAudioByteStreamLock) {
    107             if (audioFrame != null) {
    108                 AudioSample sample = new AudioSample(
    109                         mAudioSampleRate, mAudioChannelCount, mAudioByteStream.toByteArray());
    110                 audioFrame.setValue(sample);
    111                 audioFrame.setTimestamp(mAudioPresentationTimeUs * 1000);
    112             }
    113             clearBuffer();
    114         }
    115     }
    116 
    117     /**
    118      * Clears the decoder's buffer.
    119      */
    120     public void clearBuffer() {
    121         synchronized (mAudioByteStreamLock) {
    122             mAudioByteStream.reset();
    123         }
    124     }
    125 
    126 }
    127