fixed #16192: Crash while decoding small MP3 file on Android.

This commit is contained in:
James Chen 2016-07-27 13:54:39 +08:00
parent ad25873821
commit bd1d630f93
3 changed files with 153 additions and 113 deletions

View File

@ -28,6 +28,9 @@ THE SOFTWARE.
#include "audio/android/AudioResampler.h"
#include "audio/android/PcmBufferProvider.h"
#include <thread>
#include <chrono>
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,75 +756,18 @@ void AudioDecoder::decodeToPcmCallback(SLAndroidSimpleBufferQueueItf queueItf)
}
#endif
/* Example: query of the decoded PCM format */
if (_formatQueried)
{
return;
queryAudioInfo();
}
/* 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)
bool AudioDecoder::interleave()
{
ALOGV("Content duration is unknown (in dec callback)");
} else
if (_result.numChannels == 2)
{
ALOGV("Content duration is %ums (in dec callback)", durationInMsec);
_result.duration = durationInMsec / 1000.0f;
ALOGI("Audio channel count is 2, no need to interleave");
return true;
}
/* 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;
}
void AudioDecoder::interleave()
else if (_result.numChannels == 1)
{
if (_result.numChannels > 1)
{
return;
}
// 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<std::vector<char>>();
@ -755,6 +790,11 @@ void AudioDecoder::interleave()
_result.numChannels = 2;
_result.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
_result.pcmBuffer = newBuffer;
return true;
}
ALOGE("Audio channel count (%d) is wrong, interleave only supports converting mono to stereo!", _result.numChannels);
return false;
}
}} // namespace cocos2d { namespace experimental {

View File

@ -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 {

View File

@ -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();