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 17package androidx.media.filterfw.decoder; 18 19import android.annotation.TargetApi; 20import android.media.MediaCodec; 21import android.media.MediaCodec.BufferInfo; 22import android.media.MediaFormat; 23 24import androidx.media.filterfw.FrameValue; 25 26import java.io.ByteArrayOutputStream; 27import java.io.IOException; 28import 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) 36public 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