From bd1d630f931ec1a844ffa2720b8b17dc8bd94301 Mon Sep 17 00:00:00 2001 From: James Chen Date: Wed, 27 Jul 2016 13:54:39 +0800 Subject: [PATCH] fixed #16192: Crash while decoding small MP3 file on Android. --- cocos/audio/android/AudioDecoder.cpp | 246 ++++++++++++-------- cocos/audio/android/AudioDecoder.h | 16 +- cocos/audio/android/AudioPlayerProvider.cpp | 4 +- 3 files changed, 153 insertions(+), 113 deletions(-) diff --git a/cocos/audio/android/AudioDecoder.cpp b/cocos/audio/android/AudioDecoder.cpp index 8afc181a07..d4b1de6f13 100644 --- a/cocos/audio/android/AudioDecoder.cpp +++ b/cocos/audio/android/AudioDecoder.cpp @@ -28,6 +28,9 @@ THE SOFTWARE. #include "audio/android/AudioResampler.h" #include "audio/android/PcmBufferProvider.h" +#include +#include + namespace cocos2d { namespace experimental { /* Explicitly requesting SL_IID_ANDROIDSIMPLEBUFFERQUEUE and SL_IID_PREFETCHSTATUS @@ -94,12 +97,12 @@ public: } }; -AudioDecoder::AudioDecoder(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate) +AudioDecoder::AudioDecoder(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate, const FdGetterCallback &fdGetterCallback) : _engineItf(engineItf), _url(url), _playObj(nullptr), _formatQueried(false), _prefetchError(false), _counter(0), _numChannelsKeyIndex(-1), _sampleRateKeyIndex(-1), _bitsPerSampleKeyIndex(-1), _containerSizeKeyIndex(-1), _channelMaskKeyIndex(-1), _endiannessKeyIndex(-1), _eos(false), _bufferSizeInFrames(bufferSizeInFrames), - _sampleRate(sampleRate), _assetFd(0) + _sampleRate(sampleRate), _assetFd(0), _fdGetterCallback(fdGetterCallback), _isDecodingCallbackInvoked(false) { BUFFER_SIZE_IN_BYTES = toBufferSizeInBytes(bufferSizeInFrames, 2, 2); _pcmData = (char*) malloc(NB_BUFFERS_IN_QUEUE * BUFFER_SIZE_IN_BYTES); @@ -123,9 +126,28 @@ AudioDecoder::~AudioDecoder() free(_pcmData); } -bool AudioDecoder::start(const FdGetterCallback &fdGetterCallback) +bool AudioDecoder::start() { auto oldTime = clockNow(); + + bool ret; + do + { + ret = decodeToPcm(); if (!ret) break; + ret = resample(); if (!ret) break; + ret = interleave(); if (!ret) break; + + auto nowTime = clockNow(); + + ALOGV("Decoding (%s) to pcm data wasted %fms", _url.c_str(), + intervalInMS(oldTime, nowTime)); + + } while(false); + return ret; +} + +bool AudioDecoder::decodeToPcm() +{ SLresult result; /* Objects this application uses: one audio player */ @@ -159,8 +181,6 @@ bool AudioDecoder::start(const FdGetterCallback &fdGetterCallback) iidArray[i] = SL_IID_NULL; } - _formatQueried = false; - /* ------------------------------------------------------ */ /* Configuration of the player */ @@ -192,7 +212,7 @@ bool AudioDecoder::start(const FdGetterCallback &fdGetterCallback) relativePath = _url; } - _assetFd = fdGetterCallback(relativePath, &start, &length); + _assetFd = _fdGetterCallback(relativePath, &start, &length); if (_assetFd <= 0) { @@ -319,7 +339,7 @@ bool AudioDecoder::start(const FdGetterCallback &fdGetterCallback) while ((prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA) && (timeOutIndex > 0) && !_prefetchError) { - usleep(2 * 1000); + std::this_thread::sleep_for(std::chrono::milliseconds(2)); (*prefetchItf)->GetPrefetchStatus(prefetchItf, &prefetchStatus); timeOutIndex--; } @@ -440,25 +460,17 @@ bool AudioDecoder::start(const FdGetterCallback &fdGetterCallback) _result.pcmBuffer->size() / _result.numChannels / (_result.bitsPerSample / 8); std::string info = _result.toString(); - ALOGV("original audio info: %s, total size: %d", info.c_str(), _result.pcmBuffer->size()); - - resample(); - - interleave(); - - auto nowTime = clockNow(); - ALOGV("Decoding (%s) to pcm data wasted %fms", _url.c_str(), intervalInMS(oldTime, nowTime)); - + ALOGI("Original audio info: %s, total size: %d", info.c_str(), _result.pcmBuffer->size()); return true; } -void AudioDecoder::resample() +bool AudioDecoder::resample() { if (_result.sampleRate == _sampleRate) { - ALOGV("No need to resample since the sample rate (%d) of the decoded pcm data is the same as the device output sample rate", + ALOGI("No need to resample since the sample rate (%d) of the decoded pcm data is the same as the device output sample rate", _sampleRate); - return; + return true; } ALOGV("Resample: %d --> %d", _result.sampleRate, _sampleRate); @@ -564,6 +576,7 @@ void AudioDecoder::resample() free(convert); free(outputVAddr); + return true; } //----------------------------------------------------------------- @@ -574,6 +587,70 @@ void AudioDecoder::signalEos() _eosCondition.notify_one(); } +void AudioDecoder::queryAudioInfo() +{ + if (_formatQueried) + { + return; + } + + SLresult result; + /* Get duration in callback where we use the callback context for the SLPlayItf*/ + SLmillisecond durationInMsec = SL_TIME_UNKNOWN; + result = (*_decContext.playItf)->GetDuration(_decContext.playItf, &durationInMsec); + SL_RETURN_IF_FAILED(result, "decodeProgressCallback,GetDuration failed"); + + if (durationInMsec == SL_TIME_UNKNOWN) + { + ALOGV("Content duration is unknown (in dec callback)"); + } else + { + ALOGV("Content duration is %ums (in dec callback)", durationInMsec); + _result.duration = durationInMsec / 1000.0f; + } + + /* used to query metadata values */ + SLMetadataInfo pcmMetaData; + + result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _sampleRateKeyIndex, + PCM_METADATA_VALUE_SIZE, &pcmMetaData); + + SL_RETURN_IF_FAILED(result, "%s GetValue _sampleRateKeyIndex failed", __FUNCTION__); + // Note: here we could verify the following: + // pcmMetaData->encoding == SL_CHARACTERENCODING_BINARY + // pcmMetaData->size == sizeof(SLuint32) + // but the call was successful for the PCM format keys, so those conditions are implied + + _result.sampleRate = *((SLuint32 *) pcmMetaData.data); + result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _numChannelsKeyIndex, + PCM_METADATA_VALUE_SIZE, &pcmMetaData); + SL_RETURN_IF_FAILED(result, "%s GetValue _numChannelsKeyIndex failed", __FUNCTION__); + + _result.numChannels = *((SLuint32 *) pcmMetaData.data); + + result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _bitsPerSampleKeyIndex, + PCM_METADATA_VALUE_SIZE, &pcmMetaData); + SL_RETURN_IF_FAILED(result, "%s GetValue _bitsPerSampleKeyIndex failed", __FUNCTION__) + _result.bitsPerSample = *((SLuint32 *) pcmMetaData.data); + + result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _containerSizeKeyIndex, + PCM_METADATA_VALUE_SIZE, &pcmMetaData); + SL_RETURN_IF_FAILED(result, "%s GetValue _containerSizeKeyIndex failed", __FUNCTION__) + _result.containerSize = *((SLuint32 *) pcmMetaData.data); + + result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _channelMaskKeyIndex, + PCM_METADATA_VALUE_SIZE, &pcmMetaData); + SL_RETURN_IF_FAILED(result, "%s GetValue _channelMaskKeyIndex failed", __FUNCTION__) + _result.channelMask = *((SLuint32 *) pcmMetaData.data); + + result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _endiannessKeyIndex, + PCM_METADATA_VALUE_SIZE, &pcmMetaData); + SL_RETURN_IF_FAILED(result, "%s GetValue _endiannessKeyIndex failed", __FUNCTION__) + _result.endianness = *((SLuint32 *) pcmMetaData.data); + + _formatQueried = true; +} + void AudioDecoder::prefetchCallback(SLPrefetchStatusItf caller, SLuint32 event) { SLpermille level = 0; @@ -602,6 +679,19 @@ void AudioDecoder::decodeProgressCallback(SLPlayItf caller, SLuint32 event) if (SL_PLAYEVENT_HEADATEND & event) { ALOGV("SL_PLAYEVENT_HEADATEND"); + if (!_isDecodingCallbackInvoked) + { + queryAudioInfo(); + + for (int i = 0; i < NB_BUFFERS_IN_QUEUE; ++i) + { + _result.pcmBuffer->insert(_result.pcmBuffer->end(), _decContext.pData, + _decContext.pData + BUFFER_SIZE_IN_BYTES); + + /* Increase data pointer by buffer size */ + _decContext.pData += BUFFER_SIZE_IN_BYTES; + } + } signalEos(); } } @@ -610,6 +700,8 @@ void AudioDecoder::decodeProgressCallback(SLPlayItf caller, SLuint32 event) /* Callback for decoding buffer queue events */ void AudioDecoder::decodeToPcmCallback(SLAndroidSimpleBufferQueueItf queueItf) { + _isDecodingCallbackInvoked = true; + ALOGV("%s ...", __FUNCTION__); _counter++; SLresult result; // FIXME: ?? @@ -617,15 +709,15 @@ void AudioDecoder::decodeToPcmCallback(SLAndroidSimpleBufferQueueItf queueItf) { SLmillisecond msec; result = (*_decContext.playItf)->GetPosition(_decContext.playItf, &msec); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback,GetPosition failed"); - ALOGV("DecPlayCallback called (iteration %d): current position=%u ms", _counter, msec); + SL_RETURN_IF_FAILED(result, "%s, GetPosition failed", __FUNCTION__); + ALOGV("%s called (iteration %d): current position=%u ms", __FUNCTION__, _counter, msec); } _result.pcmBuffer->insert(_result.pcmBuffer->end(), _decContext.pData, _decContext.pData + BUFFER_SIZE_IN_BYTES); result = (*queueItf)->Enqueue(queueItf, _decContext.pData, BUFFER_SIZE_IN_BYTES); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback,Enqueue failed"); + SL_RETURN_IF_FAILED(result, "%s, Enqueue failed", __FUNCTION__); /* Increase data pointer by buffer size */ _decContext.pData += BUFFER_SIZE_IN_BYTES; @@ -664,97 +756,45 @@ void AudioDecoder::decodeToPcmCallback(SLAndroidSimpleBufferQueueItf queueItf) } #endif - /* Example: query of the decoded PCM format */ - if (_formatQueried) - { - return; - } - - /* Get duration in callback where we use the callback context for the SLPlayItf*/ - SLmillisecond durationInMsec = SL_TIME_UNKNOWN; - result = (*_decContext.playItf)->GetDuration(_decContext.playItf, &durationInMsec); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback,GetDuration failed"); - - if (durationInMsec == SL_TIME_UNKNOWN) - { - ALOGV("Content duration is unknown (in dec callback)"); - } else - { - ALOGV("Content duration is %ums (in dec callback)", durationInMsec); - _result.duration = durationInMsec / 1000.0f; - } - - /* used to query metadata values */ - SLMetadataInfo pcmMetaData; - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _sampleRateKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback.GetValue _sampleRateKeyIndex failed") - // Note: here we could verify the following: - // pcmMetaData->encoding == SL_CHARACTERENCODING_BINARY - // pcmMetaData->size == sizeof(SLuint32) - // but the call was successful for the PCM format keys, so those conditions are implied - - _result.sampleRate = *((SLuint32 *) pcmMetaData.data); - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _numChannelsKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback.GetValue _numChannelsKeyIndex failed") - - _result.numChannels = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _bitsPerSampleKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback.GetValue _bitsPerSampleKeyIndex failed") - _result.bitsPerSample = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _containerSizeKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback.GetValue _containerSizeKeyIndex failed") - _result.containerSize = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _channelMaskKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback.GetValue _channelMaskKeyIndex failed") - _result.channelMask = *((SLuint32 *) pcmMetaData.data); - - result = (*_decContext.metaItf)->GetValue(_decContext.metaItf, _endiannessKeyIndex, - PCM_METADATA_VALUE_SIZE, &pcmMetaData); - SL_RETURN_IF_FAILED(result, "decodeToPcmCallback.GetValue _endiannessKeyIndex failed") - _result.endianness = *((SLuint32 *) pcmMetaData.data); - - _formatQueried = true; + queryAudioInfo(); } -void AudioDecoder::interleave() +bool AudioDecoder::interleave() { - if (_result.numChannels > 1) + if (_result.numChannels == 2) { - return; + ALOGI("Audio channel count is 2, no need to interleave"); + return true; } - - // If it's a mono audio, try to compose a fake stereo buffer - size_t newBufferSize = _result.pcmBuffer->size() * 2; - auto newBuffer = std::make_shared>(); - newBuffer->reserve(newBufferSize); - size_t totalFrameSizeInBytes = (size_t) (_result.numFrames * _result.bitsPerSample / 8); - - for (size_t i = 0; i < totalFrameSizeInBytes; i += 2) + else if (_result.numChannels == 1) { - // get one short value - char byte1 = _result.pcmBuffer->at(i); - char byte2 = _result.pcmBuffer->at(i + 1); + // If it's a mono audio, try to compose a fake stereo buffer + size_t newBufferSize = _result.pcmBuffer->size() * 2; + auto newBuffer = std::make_shared>(); + newBuffer->reserve(newBufferSize); + size_t totalFrameSizeInBytes = (size_t) (_result.numFrames * _result.bitsPerSample / 8); - // push two short value - for (int j = 0; j < 2; ++j) + for (size_t i = 0; i < totalFrameSizeInBytes; i += 2) { - newBuffer->push_back(byte1); - newBuffer->push_back(byte2); + // get one short value + char byte1 = _result.pcmBuffer->at(i); + char byte2 = _result.pcmBuffer->at(i + 1); + + // push two short value + for (int j = 0; j < 2; ++j) + { + newBuffer->push_back(byte1); + newBuffer->push_back(byte2); + } } + _result.numChannels = 2; + _result.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + _result.pcmBuffer = newBuffer; + return true; } - _result.numChannels = 2; - _result.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; - _result.pcmBuffer = newBuffer; + + ALOGE("Audio channel count (%d) is wrong, interleave only supports converting mono to stereo!", _result.numChannels); + return false; } }} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioDecoder.h b/cocos/audio/android/AudioDecoder.h index c65b2a2ed6..72eb0b5a04 100644 --- a/cocos/audio/android/AudioDecoder.h +++ b/cocos/audio/android/AudioDecoder.h @@ -35,24 +35,24 @@ namespace cocos2d { namespace experimental { class AudioDecoder { public: - AudioDecoder(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate); + AudioDecoder(SLEngineItf engineItf, const std::string &url, int bufferSizeInFrames, int sampleRate, const FdGetterCallback &fdGetterCallback); virtual ~AudioDecoder(); - bool start(const FdGetterCallback &fdGetterCallback); + bool start(); inline PcmData getResult() { return _result; }; private: - void resample(); + bool decodeToPcm(); + bool resample(); + bool interleave(); + void queryAudioInfo(); void signalEos(); - void decodeToPcmCallback(SLAndroidSimpleBufferQueueItf queueItf); - void prefetchCallback(SLPrefetchStatusItf caller, SLuint32 event); - void decodeProgressCallback(SLPlayItf caller, SLuint32 event); private: @@ -99,10 +99,10 @@ private: int _bufferSizeInFrames; int _sampleRate; int _assetFd; + FdGetterCallback _fdGetterCallback; + bool _isDecodingCallbackInvoked; friend class SLAudioDecoderCallbackProxy; - - void interleave(); }; }} // namespace cocos2d { namespace experimental { diff --git a/cocos/audio/android/AudioPlayerProvider.cpp b/cocos/audio/android/AudioPlayerProvider.cpp index 639a88bf8e..e2883df98a 100644 --- a/cocos/audio/android/AudioPlayerProvider.cpp +++ b/cocos/audio/android/AudioPlayerProvider.cpp @@ -279,8 +279,8 @@ void AudioPlayerProvider::preloadEffect(const AudioFileInfo &info, const Preload _threadPool->pushTask([this, audioFilePath](int tid) { ALOGV("AudioPlayerProvider::preloadEffect: (%s)", audioFilePath.c_str()); PcmData d; - AudioDecoder decoder(_engineItf, audioFilePath, _bufferSizeInFrames, _deviceSampleRate); - bool ret = decoder.start(_fdGetterCallback); + AudioDecoder decoder(_engineItf, audioFilePath, _bufferSizeInFrames, _deviceSampleRate, _fdGetterCallback); + bool ret = decoder.start(); if (ret) { d = decoder.getResult();