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