diff options
Diffstat (limited to 'tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java')
-rw-r--r-- | tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java new file mode 100644 index 0000000..c81e8b4 --- /dev/null +++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/src/androidx/media/filterfw/decoder/TrackDecoder.java @@ -0,0 +1,178 @@ +/* + * 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.MediaExtractor; +import android.media.MediaFormat; +import android.util.Log; + +import java.nio.ByteBuffer; + +@TargetApi(16) +abstract class TrackDecoder { + + interface Listener { + void onDecodedOutputAvailable(TrackDecoder decoder); + + void onEndOfStream(TrackDecoder decoder); + } + + private static final String LOG_TAG = "TrackDecoder"; + + private static final long TIMEOUT_US = 50; // Timeout for en-queueing and de-queueing buffers. + + private static final int NO_INPUT_BUFFER = -1; + + private final int mTrackIndex; + private final MediaFormat mMediaFormat; + private final Listener mListener; + + private MediaCodec mMediaCodec; + private MediaFormat mOutputFormat; + + private ByteBuffer[] mCodecInputBuffers; + private ByteBuffer[] mCodecOutputBuffers; + + private boolean mShouldEnqueueEndOfStream; + + /** + * @return a configured {@link MediaCodec}. + */ + protected abstract MediaCodec initMediaCodec(MediaFormat format); + + /** + * Called when decoded output is available. The implementer is responsible for releasing the + * assigned buffer. + * + * @return {@code true} if any further decoding should be attempted at the moment. + */ + protected abstract boolean onDataAvailable( + MediaCodec codec, ByteBuffer[] buffers, int bufferIndex, BufferInfo info); + + protected TrackDecoder(int trackIndex, MediaFormat mediaFormat, Listener listener) { + mTrackIndex = trackIndex; + + if (mediaFormat == null) { + throw new NullPointerException("mediaFormat cannot be null"); + } + mMediaFormat = mediaFormat; + + if (listener == null) { + throw new NullPointerException("listener cannot be null"); + } + mListener = listener; + } + + public void init() { + mMediaCodec = initMediaCodec(mMediaFormat); + mMediaCodec.start(); + mCodecInputBuffers = mMediaCodec.getInputBuffers(); + mCodecOutputBuffers = mMediaCodec.getOutputBuffers(); + } + + public void signalEndOfInput() { + mShouldEnqueueEndOfStream = true; + tryEnqueueEndOfStream(); + } + + public void release() { + if (mMediaCodec != null) { + mMediaCodec.stop(); + mMediaCodec.release(); + } + } + + protected MediaCodec getMediaCodec() { + return mMediaCodec; + } + + protected void notifyListener() { + mListener.onDecodedOutputAvailable(this); + } + + public boolean feedInput(MediaExtractor mediaExtractor) { + long presentationTimeUs = 0; + + int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_US); + if (inputBufferIndex != NO_INPUT_BUFFER) { + ByteBuffer destinationBuffer = mCodecInputBuffers[inputBufferIndex]; + int sampleSize = mediaExtractor.readSampleData(destinationBuffer, 0); + // We don't expect to get a sample without any data, so this should never happen. + if (sampleSize < 0) { + Log.w(LOG_TAG, "Media extractor had sample but no data."); + + // Signal the end of the track immediately anyway, using the buffer. + mMediaCodec.queueInputBuffer( + inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + return false; + } + + presentationTimeUs = mediaExtractor.getSampleTime(); + mMediaCodec.queueInputBuffer( + inputBufferIndex, + 0, + sampleSize, + presentationTimeUs, + 0); + + return mediaExtractor.advance() + && mediaExtractor.getSampleTrackIndex() == mTrackIndex; + } + return false; + } + + private void tryEnqueueEndOfStream() { + int inputBufferIndex = mMediaCodec.dequeueInputBuffer(TIMEOUT_US); + // We will always eventually have an input buffer, because we keep trying until the last + // decoded frame is output. + // The EoS does not need to be signaled if the application stops decoding. + if (inputBufferIndex != NO_INPUT_BUFFER) { + mMediaCodec.queueInputBuffer( + inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM); + mShouldEnqueueEndOfStream = false; + } + } + + public boolean drainOutputBuffer() { + BufferInfo outputInfo = new BufferInfo(); + int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(outputInfo, TIMEOUT_US); + + if ((outputInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + mListener.onEndOfStream(this); + return false; + } + if (mShouldEnqueueEndOfStream) { + tryEnqueueEndOfStream(); + } + if (outputBufferIndex >= 0) { + return onDataAvailable( + mMediaCodec, mCodecOutputBuffers, outputBufferIndex, outputInfo); + } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { + mCodecOutputBuffers = mMediaCodec.getOutputBuffers(); + return true; + } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { + mOutputFormat = mMediaCodec.getOutputFormat(); + Log.d(LOG_TAG, "Output format has changed to " + mOutputFormat); + return true; + } + return false; + } + +} |