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 ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize);
103 ALOGV("%s: read %zd", __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 ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1);
128 ALOGV("%s: dequeueoutput returned: %zd", __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(available, (size_t)std::max(info.size, 0));
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: %zd", __func__, status);
171 break;
172 } else {
173 ALOGV("%s: unexpected info code: %zd", __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 int32_t mediaFormatChannelMask;
185 if (AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_CHANNEL_MASK,
186 &mediaFormatChannelMask)) {
187 *channelMask = audio_channel_mask_from_media_format_mask(mediaFormatChannelMask);
188 } else {
189 *channelMask = AUDIO_CHANNEL_NONE;
190 }
191 *sizeInBytes = written;
192 return OK;
193 }
194 }
195 return UNKNOWN_ERROR;
196 }
197
doLoad()198 status_t Sound::doLoad()
199 {
200 ALOGV("%s()", __func__);
201 status_t status = NO_INIT;
202 if (mFd.get() != -1) {
203 mHeap = new MemoryHeapBase(kDefaultHeapSize);
204
205 ALOGV("%s: start decode", __func__);
206 uint32_t sampleRate;
207 int32_t channelCount;
208 audio_format_t format;
209 audio_channel_mask_t channelMask;
210 status = decode(mFd.get(), mOffset, mLength, &sampleRate, &channelCount, &format,
211 &channelMask, mHeap, &mSizeInBytes);
212 ALOGV("%s: close(%d)", __func__, mFd.get());
213 mFd.reset(); // close
214
215 if (status != NO_ERROR) {
216 ALOGE("%s: unable to load sound", __func__);
217 } else if (sampleRate > kMaxSampleRate) {
218 ALOGE("%s: sample rate (%u) out of range", __func__, sampleRate);
219 status = BAD_VALUE;
220 } else if (channelCount < 1 || channelCount > FCC_LIMIT) {
221 ALOGE("%s: sample channel count (%d) out of range", __func__, channelCount);
222 status = BAD_VALUE;
223 } else {
224 // Correctly loaded, proper parameters
225 ALOGV("%s: pointer = %p, sizeInBytes = %zu, sampleRate = %u, channelCount = %d",
226 __func__, mHeap->getBase(), mSizeInBytes, sampleRate, channelCount);
227 mData = new MemoryBase(mHeap, 0, mSizeInBytes);
228 mSampleRate = sampleRate;
229 mChannelCount = channelCount;
230 mFormat = format;
231 mChannelMask = channelMask;
232 mState = READY; // this should be last, as it is an atomic sync point
233 return NO_ERROR;
234 }
235 } else {
236 ALOGE("%s: uninitialized fd, dup failed", __func__);
237 }
238 // ERROR handling
239 mHeap.clear();
240 mState = DECODE_ERROR; // this should be last, as it is an atomic sync point
241 return status;
242 }
243
244 } // namespace android::soundpool
245