1 /*
2 * Copyright 2018 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 #include <sys/types.h>
18
19 #include <cstring>
20
21 #include <media/NdkMediaExtractor.h>
22 #include <utils/logging.h>
23 #include <cinttypes>
24
25 #include "NDKExtractor.h"
26
decode(AAsset * asset,uint8_t * targetData,AudioProperties targetProperties)27 int32_t NDKExtractor::decode(AAsset *asset, uint8_t *targetData, AudioProperties targetProperties) {
28
29 LOGD("Using NDK decoder");
30
31 // open asset as file descriptor
32 off_t start, length;
33 int fd = AAsset_openFileDescriptor(asset, &start, &length);
34
35 // Extract the audio frames
36 AMediaExtractor *extractor = AMediaExtractor_new();
37 media_status_t amresult = AMediaExtractor_setDataSourceFd(extractor, fd,
38 static_cast<off64_t>(start),
39 static_cast<off64_t>(length));
40 if (amresult != AMEDIA_OK){
41 LOGE("Error setting extractor data source, err %d", amresult);
42 return 0;
43 }
44
45 // Specify our desired output format by creating it from our source
46 AMediaFormat *format = AMediaExtractor_getTrackFormat(extractor, 0);
47
48 int32_t sampleRate;
49 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &sampleRate)){
50 LOGD("Source sample rate %d", sampleRate);
51 if (sampleRate != targetProperties.sampleRate){
52 LOGE("Input (%d) and output (%d) sample rates do not match. "
53 "NDK decoder does not support resampling.",
54 sampleRate,
55 targetProperties.sampleRate);
56 return 0;
57 }
58 } else {
59 LOGE("Failed to get sample rate");
60 return 0;
61 };
62
63 int32_t channelCount;
64 if (AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &channelCount)){
65 LOGD("Got channel count %d", channelCount);
66 if (channelCount != targetProperties.channelCount){
67 LOGE("NDK decoder does not support different "
68 "input (%d) and output (%d) channel counts",
69 channelCount,
70 targetProperties.channelCount);
71 }
72 } else {
73 LOGE("Failed to get channel count");
74 return 0;
75 }
76
77 const char *formatStr = AMediaFormat_toString(format);
78 LOGD("Output format %s", formatStr);
79
80 const char *mimeType;
81 if (AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mimeType)) {
82 LOGD("Got mime type %s", mimeType);
83 } else {
84 LOGE("Failed to get mime type");
85 return 0;
86 }
87
88 // Obtain the correct decoder
89 AMediaCodec *codec = nullptr;
90 AMediaExtractor_selectTrack(extractor, 0);
91 codec = AMediaCodec_createDecoderByType(mimeType);
92 AMediaCodec_configure(codec, format, nullptr, nullptr, 0);
93 AMediaCodec_start(codec);
94
95 // DECODE
96
97 bool isExtracting = true;
98 bool isDecoding = true;
99 int32_t bytesWritten = 0;
100
101 while(isExtracting || isDecoding){
102
103 if (isExtracting){
104
105 // Obtain the index of the next available input buffer
106 ssize_t inputIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
107 //LOGV("Got input buffer %d", inputIndex);
108
109 // The input index acts as a status if its negative
110 if (inputIndex < 0){
111 if (inputIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER){
112 // LOGV("Codec.dequeueInputBuffer try again later");
113 } else {
114 LOGE("Codec.dequeueInputBuffer unknown error status");
115 }
116 } else {
117
118 // Obtain the actual buffer and read the encoded data into it
119 size_t inputSize;
120 uint8_t *inputBuffer = AMediaCodec_getInputBuffer(codec, inputIndex, &inputSize);
121 //LOGV("Sample size is: %d", inputSize);
122
123 ssize_t sampleSize = AMediaExtractor_readSampleData(extractor, inputBuffer, inputSize);
124 auto presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
125
126 if (sampleSize > 0){
127
128 // Enqueue the encoded data
129 AMediaCodec_queueInputBuffer(codec, inputIndex, 0, sampleSize,
130 presentationTimeUs,
131 0);
132 AMediaExtractor_advance(extractor);
133
134 } else {
135 LOGD("End of extractor data stream");
136 isExtracting = false;
137
138 // We need to tell the codec that we've reached the end of the stream
139 AMediaCodec_queueInputBuffer(codec, inputIndex, 0, 0,
140 presentationTimeUs,
141 AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
142 }
143 }
144 }
145
146 if (isDecoding){
147 // Dequeue the decoded data
148 AMediaCodecBufferInfo info;
149 ssize_t outputIndex = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
150
151 if (outputIndex >= 0){
152
153 // Check whether this is set earlier
154 if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM){
155 LOGD("Reached end of decoding stream");
156 isDecoding = false;
157 }
158
159 // Valid index, acquire buffer
160 size_t outputSize;
161 uint8_t *outputBuffer = AMediaCodec_getOutputBuffer(codec, outputIndex, &outputSize);
162
163 /*LOGV("Got output buffer index %d, buffer size: %d, info size: %d writing to pcm index %d",
164 outputIndex,
165 outputSize,
166 info.size,
167 m_writeIndex);*/
168
169 // copy the data out of the buffer
170 memcpy(targetData + bytesWritten, outputBuffer, info.size);
171 bytesWritten+=info.size;
172 AMediaCodec_releaseOutputBuffer(codec, outputIndex, false);
173 } else {
174
175 // The outputIndex doubles as a status return if its value is < 0
176 switch(outputIndex){
177 case AMEDIACODEC_INFO_TRY_AGAIN_LATER:
178 LOGD("dequeueOutputBuffer: try again later");
179 break;
180 case AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED:
181 LOGD("dequeueOutputBuffer: output buffers changed");
182 break;
183 case AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED:
184 LOGD("dequeueOutputBuffer: output outputFormat changed");
185 format = AMediaCodec_getOutputFormat(codec);
186 LOGD("outputFormat changed to: %s", AMediaFormat_toString(format));
187 break;
188 }
189 }
190 }
191 }
192
193 // Clean up
194 AMediaFormat_delete(format);
195 AMediaCodec_delete(codec);
196 AMediaExtractor_delete(extractor);
197
198 return bytesWritten;
199 }
200