| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.media.filterfw.decoder; |
| |
| import android.annotation.TargetApi; |
| import android.media.MediaCodec; |
| import android.media.MediaCodec.BufferInfo; |
| import android.media.MediaFormat; |
| |
| import androidx.media.filterfw.FrameValue; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| |
| /** |
| * {@link TrackDecoder} for decoding audio tracks. |
| * |
| * TODO: find out if we always get 16 bits per channel and document. |
| */ |
| @TargetApi(16) |
| public class AudioTrackDecoder extends TrackDecoder { |
| |
| private final ByteArrayOutputStream mAudioByteStream; // Guarded by mAudioByteStreamLock. |
| private final Object mAudioByteStreamLock; |
| |
| private int mAudioSampleRate; |
| private int mAudioChannelCount; |
| private long mAudioPresentationTimeUs; |
| |
| public AudioTrackDecoder(int trackIndex, MediaFormat format, Listener listener) { |
| super(trackIndex, format, listener); |
| |
| if (!DecoderUtil.isAudioFormat(format)) { |
| throw new IllegalArgumentException( |
| "AudioTrackDecoder can only be used with audio formats"); |
| } |
| |
| mAudioByteStream = new ByteArrayOutputStream(); |
| mAudioByteStreamLock = new Object(); |
| |
| mAudioSampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE); |
| mAudioChannelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT); |
| } |
| |
| @Override |
| protected MediaCodec initMediaCodec(MediaFormat format) { |
| MediaCodec mediaCodec = MediaCodec.createDecoderByType( |
| format.getString(MediaFormat.KEY_MIME)); |
| mediaCodec.configure(format, null, null, 0); |
| return mediaCodec; |
| } |
| |
| @Override |
| protected boolean onDataAvailable( |
| MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info) { |
| ByteBuffer buffer = buffers[bufferIndex]; |
| byte[] data = new byte[info.size]; |
| buffer.position(info.offset); |
| buffer.get(data, 0, info.size); |
| |
| synchronized (mAudioByteStreamLock) { |
| try { |
| if (mAudioByteStream.size() == 0 && data.length > 0) { |
| mAudioPresentationTimeUs = info.presentationTimeUs; |
| } |
| |
| mAudioByteStream.write(data); |
| } catch (IOException e) { |
| // Just drop the audio sample. |
| } |
| } |
| |
| buffer.clear(); |
| codec.releaseOutputBuffer(bufferIndex, false); |
| notifyListener(); |
| return true; |
| } |
| |
| /** |
| * Fills the argument {@link FrameValue} with an audio sample containing the audio that was |
| * decoded since the last call of this method. The decoder's buffer is cleared as a result. |
| */ |
| public void grabSample(FrameValue audioFrame) { |
| synchronized (mAudioByteStreamLock) { |
| if (audioFrame != null) { |
| AudioSample sample = new AudioSample( |
| mAudioSampleRate, mAudioChannelCount, mAudioByteStream.toByteArray()); |
| audioFrame.setValue(sample); |
| audioFrame.setTimestamp(mAudioPresentationTimeUs * 1000); |
| } |
| clearBuffer(); |
| } |
| } |
| |
| /** |
| * Clears the decoder's buffer. |
| */ |
| public void clearBuffer() { |
| synchronized (mAudioByteStreamLock) { |
| mAudioByteStream.reset(); |
| } |
| } |
| |
| } |