• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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