1 /*
2  * Copyright (C) 2019 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "SoundPool::Sound"
19 #include <utils/Log.h>
20 
21 #include "Sound.h"
22 
23 #include <media/NdkMediaCodec.h>
24 #include <media/NdkMediaExtractor.h>
25 #include <media/NdkMediaFormat.h>
26 
27 namespace android::soundpool {
28 
29 constexpr uint32_t kMaxSampleRate = 192000;
30 constexpr size_t   kDefaultHeapSize = 1024 * 1024; // 1MB (compatible with low mem devices)
31 
Sound(int32_t soundID,int fd,int64_t offset,int64_t length)32 Sound::Sound(int32_t soundID, int fd, int64_t offset, int64_t length)
33     : mSoundID(soundID)
34     , mFd(fcntl(fd, F_DUPFD_CLOEXEC, (int)0 /* arg */)) // dup(fd) + close on exec to prevent leaks.
35     , mOffset(offset)
36     , mLength(length)
37 {
38     ALOGV("%s(soundID=%d, fd=%d, offset=%lld, length=%lld)",
39             __func__, soundID, fd, (long long)offset, (long long)length);
40     ALOGW_IF(mFd == -1, "Unable to dup descriptor %d", fd);
41 }
42 
~Sound()43 Sound::~Sound()
44 {
45     ALOGV("%s(soundID=%d, fd=%d)", __func__, mSoundID, mFd.get());
46 }
47 
decode(int fd,int64_t offset,int64_t length,uint32_t * rate,int32_t * channelCount,audio_format_t * audioFormat,audio_channel_mask_t * channelMask,const sp<MemoryHeapBase> & heap,size_t * sizeInBytes)48 static status_t decode(int fd, int64_t offset, int64_t length,
49         uint32_t *rate, int32_t *channelCount, audio_format_t *audioFormat,
50         audio_channel_mask_t *channelMask, const sp<MemoryHeapBase>& heap,
51         size_t *sizeInBytes) {
52     ALOGV("%s(fd=%d, offset=%lld, length=%lld, ...)",
53             __func__, fd, (long long)offset, (long long)length);
54     std::unique_ptr<AMediaExtractor, decltype(&AMediaExtractor_delete)> ex{
55             AMediaExtractor_new(), &AMediaExtractor_delete};
56     status_t err = AMediaExtractor_setDataSourceFd(ex.get(), fd, offset, length);
57 
58     if (err != AMEDIA_OK) {
59         return err;
60     }
61 
62     *audioFormat = AUDIO_FORMAT_PCM_16_BIT;  // default format for audio codecs.
63     const size_t numTracks = AMediaExtractor_getTrackCount(ex.get());
64     for (size_t i = 0; i < numTracks; i++) {
65         std::unique_ptr<AMediaFormat, decltype(&AMediaFormat_delete)> format{
66                 AMediaExtractor_getTrackFormat(ex.get(), i), &AMediaFormat_delete};
67         const char *mime;
68         if (!AMediaFormat_getString(format.get(),  AMEDIAFORMAT_KEY_MIME, &mime)) {
69             return UNKNOWN_ERROR;
70         }
71         if (strncmp(mime, "audio/", 6) == 0) {
72             std::unique_ptr<AMediaCodec, decltype(&AMediaCodec_delete)> codec{
73                     AMediaCodec_createDecoderByType(mime), &AMediaCodec_delete};
74             if (codec == nullptr
75                     || AMediaCodec_configure(codec.get(), format.get(),
76                             nullptr /* window */, nullptr /* drm */, 0 /* flags */) != AMEDIA_OK
77                     || AMediaCodec_start(codec.get()) != AMEDIA_OK
78                     || AMediaExtractor_selectTrack(ex.get(), i) != AMEDIA_OK) {
79                 return UNKNOWN_ERROR;
80             }
81 
82             bool sawInputEOS = false;
83             bool sawOutputEOS = false;
84             auto writePos = static_cast<uint8_t*>(heap->getBase());
85             size_t available = heap->getSize();
86             size_t written = 0;
87             format.reset(AMediaCodec_getOutputFormat(codec.get())); // update format.
88 
89             while (!sawOutputEOS) {
90                 if (!sawInputEOS) {
91                     ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec.get(), 5000);
92                     ALOGV("%s: input buffer %zd", __func__, bufidx);
93                     if (bufidx >= 0) {
94                         size_t bufsize;
95                         uint8_t * const buf = AMediaCodec_getInputBuffer(
96                                 codec.get(), bufidx, &bufsize);
97                         if (buf == nullptr) {
98                             ALOGE("%s: AMediaCodec_getInputBuffer returned nullptr, short decode",
99                                     __func__);
100                             break;
101                         }
102                         int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
103                         ALOGV("%s: read %d", __func__, sampleSize);
104                         if (sampleSize < 0) {
105                             sampleSize = 0;
106                             sawInputEOS = true;
107                             ALOGV("%s: EOS", __func__);
108                         }
109                         const int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex.get());
110 
111                         const media_status_t mstatus = AMediaCodec_queueInputBuffer(
112                                 codec.get(), bufidx,
113                                 0 /* offset */, sampleSize, presentationTimeUs,
114                                 sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
115                         if (mstatus != AMEDIA_OK) {
116                             // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
117                             ALOGE("%s: AMediaCodec_queueInputBuffer returned status %d,"
118                                     "short decode",
119                                     __func__, (int)mstatus);
120                             break;
121                         }
122                         (void)AMediaExtractor_advance(ex.get());
123                     }
124                 }
125 
126                 AMediaCodecBufferInfo info;
127                 const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
128                 ALOGV("%s: dequeueoutput returned: %d", __func__, status);
129                 if (status >= 0) {
130                     if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
131                         ALOGV("%s: output EOS", __func__);
132                         sawOutputEOS = true;
133                     }
134                     ALOGV("%s: got decoded buffer size %d", __func__, info.size);
135 
136                     const uint8_t * const buf = AMediaCodec_getOutputBuffer(
137                             codec.get(), status, nullptr /* out_size */);
138                     if (buf == nullptr) {
139                         ALOGE("%s: AMediaCodec_getOutputBuffer returned nullptr, short decode",
140                                 __func__);
141                         break;
142                     }
143                     const size_t dataSize = std::min((size_t)info.size, available);
144                     memcpy(writePos, buf + info.offset, dataSize);
145                     writePos += dataSize;
146                     written += dataSize;
147                     available -= dataSize;
148                     const media_status_t mstatus = AMediaCodec_releaseOutputBuffer(
149                             codec.get(), status, false /* render */);
150                     if (mstatus != AMEDIA_OK) {
151                         // AMEDIA_ERROR_UNKNOWN == { -ERANGE -EINVAL -EACCES }
152                         ALOGE("%s: AMediaCodec_releaseOutputBuffer"
153                                 " returned status %d, short decode",
154                                 __func__, (int)mstatus);
155                         break;
156                     }
157                     if (available == 0) {
158                         // there might be more data, but there's no space for it
159                         sawOutputEOS = true;
160                     }
161                 } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
162                     ALOGV("%s: output buffers changed", __func__);
163                 } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
164                     format.reset(AMediaCodec_getOutputFormat(codec.get())); // update format
165                     ALOGV("%s: format changed to: %s",
166                            __func__, AMediaFormat_toString(format.get()));
167                 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
168                     ALOGV("%s: no output buffer right now", __func__);
169                 } else if (status <= AMEDIA_ERROR_BASE) {
170                     ALOGE("%s: decode error: %d", __func__, status);
171                     break;
172                 } else {
173                     ALOGV("%s: unexpected info code: %d", __func__, status);
174                 }
175             }
176 
177             (void)AMediaCodec_stop(codec.get());
178             if (!AMediaFormat_getInt32(
179                     format.get(), AMEDIAFORMAT_KEY_SAMPLE_RATE, (int32_t*) rate) ||
180                 !AMediaFormat_getInt32(
181                     format.get(), AMEDIAFORMAT_KEY_CHANNEL_COUNT, channelCount)) {
182                 return UNKNOWN_ERROR;
183             }
184             if (!AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_CHANNEL_MASK,
185                     (int32_t*) channelMask)) {
186                 *channelMask = AUDIO_CHANNEL_NONE;
187             }
188             *sizeInBytes = written;
189             return OK;
190         }
191     }
192     return UNKNOWN_ERROR;
193 }
194 
doLoad()195 status_t Sound::doLoad()
196 {
197     ALOGV("%s()", __func__);
198     status_t status = NO_INIT;
199     if (mFd.get() != -1) {
200         mHeap = new MemoryHeapBase(kDefaultHeapSize);
201 
202         ALOGV("%s: start decode", __func__);
203         uint32_t sampleRate;
204         int32_t channelCount;
205         audio_format_t format;
206         audio_channel_mask_t channelMask;
207         status = decode(mFd.get(), mOffset, mLength, &sampleRate, &channelCount, &format,
208                         &channelMask, mHeap, &mSizeInBytes);
209         ALOGV("%s: close(%d)", __func__, mFd.get());
210         mFd.reset();  // close
211 
212         if (status != NO_ERROR) {
213             ALOGE("%s: unable to load sound", __func__);
214         } else if (sampleRate > kMaxSampleRate) {
215             ALOGE("%s: sample rate (%u) out of range", __func__, sampleRate);
216             status = BAD_VALUE;
217         } else if (channelCount < 1 || channelCount > FCC_8) {
218             ALOGE("%s: sample channel count (%d) out of range", __func__, channelCount);
219             status = BAD_VALUE;
220         } else {
221             // Correctly loaded, proper parameters
222             ALOGV("%s: pointer = %p, sizeInBytes = %zu, sampleRate = %u, channelCount = %d",
223                   __func__, mHeap->getBase(), mSizeInBytes, sampleRate, channelCount);
224             mData = new MemoryBase(mHeap, 0, mSizeInBytes);
225             mSampleRate = sampleRate;
226             mChannelCount = channelCount;
227             mFormat = format;
228             mChannelMask = channelMask;
229             mState = READY;  // this should be last, as it is an atomic sync point
230             return NO_ERROR;
231         }
232     } else {
233         ALOGE("%s: uninitialized fd, dup failed", __func__);
234     }
235     // ERROR handling
236     mHeap.clear();
237     mState = DECODE_ERROR; // this should be last, as it is an atomic sync point
238     return status;
239 }
240 
241 } // namespace android::soundpool
242