aboutsummaryrefslogtreecommitdiff
path: root/externals/oboe/samples/RhythmGame/src/main/cpp/audio/NDKExtractor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'externals/oboe/samples/RhythmGame/src/main/cpp/audio/NDKExtractor.cpp')
-rw-r--r--externals/oboe/samples/RhythmGame/src/main/cpp/audio/NDKExtractor.cpp199
1 files changed, 199 insertions, 0 deletions
diff --git a/externals/oboe/samples/RhythmGame/src/main/cpp/audio/NDKExtractor.cpp b/externals/oboe/samples/RhythmGame/src/main/cpp/audio/NDKExtractor.cpp
new file mode 100644
index 0000000000..ac50940d91
--- /dev/null
+++ b/externals/oboe/samples/RhythmGame/src/main/cpp/audio/NDKExtractor.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include <sys/types.h>
+
+#include <cstring>
+
+#include <media/NdkMediaExtractor.h>
+#include <utils/logging.h>
+#include <cinttypes>
+
+#include "NDKExtractor.h"
+
+int32_t NDKExtractor::decode(AAsset *asset, uint8_t *targetData, AudioProperties targetProperties) {
+
+ LOGD("Using NDK decoder");
+
+ // open asset as file descriptor
+ off_t start, length;
+ int fd = AAsset_openFileDescriptor(asset, &start, &length);
+
+ // Extract the audio frames
+ AMediaExtractor *extractor = AMediaExtractor_new();
+ media_status_t amresult = AMediaExtractor_setDataSourceFd(extractor, fd,
+ static_cast<off64_t>(start),
+ static_cast<off64_t>(length));
+ if (amresult != AMEDIA_OK){
+ LOGE("Error setting extractor data source, err %d", amresult);
+ return 0;
+ }
+
+ // Specify our desired output format by creating it from our source
+ AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, 0);
+
+ int32_t sampleRate;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate)){
+ LOGD("Source sample rate %d", sampleRate);
+ if (sampleRate != targetProperties.sampleRate){
+ LOGE("Input (%d) and output (%d) sample rates do not match. "
+ "NDK decoder does not support resampling.",
+ sampleRate,
+ targetProperties.sampleRate);
+ return 0;
+ }
+ } else {
+ LOGE("Failed to get sample rate");
+ return 0;
+ };
+
+ int32_t channelCount;
+ if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount)){
+ LOGD("Got channel count %d", channelCount);
+ if (channelCount != targetProperties.channelCount){
+ LOGE("NDK decoder does not support different "
+ "input (%d) and output (%d) channel counts",
+ channelCount,
+ targetProperties.channelCount);
+ }
+ } else {
+ LOGE("Failed to get channel count");
+ return 0;
+ }
+
+ const char *formatStr = AMediaFormat_toString(format);
+ LOGD("Output format %s", formatStr);
+
+ const char *mimeType;
+ if (AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mimeType)) {
+ LOGD("Got mime type %s", mimeType);
+ } else {
+ LOGE("Failed to get mime type");
+ return 0;
+ }
+
+ // Obtain the correct decoder
+ AMediaCodec *codec = nullptr;
+ AMediaExtractor_selectTrack(extractor, 0);
+ codec = AMediaCodec_createDecoderByType(mimeType);
+ AMediaCodec_configure(codec, format, nullptr, nullptr, 0);
+ AMediaCodec_start(codec);
+
+ // DECODE
+
+ bool isExtracting = true;
+ bool isDecoding = true;
+ int32_t bytesWritten = 0;
+
+ while(isExtracting || isDecoding){
+
+ if (isExtracting){
+
+ // Obtain the index of the next available input buffer
+ ssize_t inputIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
+ //LOGV("Got input buffer %d", inputIndex);
+
+ // The input index acts as a status if its negative
+ if (inputIndex < 0){
+ if (inputIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER){
+ // LOGV("Codec.dequeueInputBuffer try again later");
+ } else {
+ LOGE("Codec.dequeueInputBuffer unknown error status");
+ }
+ } else {
+
+ // Obtain the actual buffer and read the encoded data into it
+ size_t inputSize;
+ uint8_t *inputBuffer = AMediaCodec_getInputBuffer(codec, inputIndex, &inputSize);
+ //LOGV("Sample size is: %d", inputSize);
+
+ ssize_t sampleSize = AMediaExtractor_readSampleData(extractor, inputBuffer, inputSize);
+ auto presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+
+ if (sampleSize > 0){
+
+ // Enqueue the encoded data
+ AMediaCodec_queueInputBuffer(codec, inputIndex, 0, sampleSize,
+ presentationTimeUs,
+ 0);
+ AMediaExtractor_advance(extractor);
+
+ } else {
+ LOGD("End of extractor data stream");
+ isExtracting = false;
+
+ // We need to tell the codec that we've reached the end of the stream
+ AMediaCodec_queueInputBuffer(codec, inputIndex, 0, 0,
+ presentationTimeUs,
+ AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
+ }
+ }
+ }
+
+ if (isDecoding){
+ // Dequeue the decoded data
+ AMediaCodecBufferInfo info;
+ ssize_t outputIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
+
+ if (outputIndex >= 0){
+
+ // Check whether this is set earlier
+ if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM){
+ LOGD("Reached end of decoding stream");
+ isDecoding = false;
+ }
+
+ // Valid index, acquire buffer
+ size_t outputSize;
+ uint8_t *outputBuffer = AMediaCodec_getOutputBuffer(codec, outputIndex, &outputSize);
+
+ /*LOGV("Got output buffer index %d, buffer size: %d, info size: %d writing to pcm index %d",
+ outputIndex,
+ outputSize,
+ info.size,
+ m_writeIndex);*/
+
+ // copy the data out of the buffer
+ memcpy(targetData + bytesWritten, outputBuffer, info.size);
+ bytesWritten+=info.size;
+ AMediaCodec_releaseOutputBuffer(codec, outputIndex, false);
+ } else {
+
+ // The outputIndex doubles as a status return if its value is < 0
+ switch(outputIndex){
+ case AMEDIACODEC_INFO_TRY_AGAIN_LATER:
+ LOGD("dequeueOutputBuffer: try again later");
+ break;
+ case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
+ LOGD("dequeueOutputBuffer: output buffers changed");
+ break;
+ case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
+ LOGD("dequeueOutputBuffer: output outputFormat changed");
+ format = AMediaCodec_getOutputFormat(codec);
+ LOGD("outputFormat changed to: %s", AMediaFormat_toString(format));
+ break;
+ }
+ }
+ }
+ }
+
+ // Clean up
+ AMediaFormat_delete(format);
+ AMediaCodec_delete(codec);
+ AMediaExtractor_delete(extractor);
+
+ return bytesWritten;
+}