Merge "Fixes delay when playing first sound in BootAnimation" into nyc-mr1-dev

This commit is contained in:
Geoffrey Pitsch
2016-07-14 14:14:57 +00:00
committed by Android (Google) Code Review
4 changed files with 111 additions and 68 deletions

View File

@ -36,8 +36,4 @@ ifdef TARGET_32_BIT_SURFACEFLINGER
LOCAL_32_BIT_ONLY := true LOCAL_32_BIT_ONLY := true
endif endif
# get asserts to work
APP_OPTIM := debug
LOCAL_CFLAGS += -UNDEBUG
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)

View File

@ -588,7 +588,7 @@ bool BootAnimation::preloadZip(Animation& animation)
return false; return false;
} }
bool hasAudio = false; Animation::Part* partWithAudio = NULL;
ZipEntryRO entry; ZipEntryRO entry;
char name[ANIM_ENTRY_NAME_MAX]; char name[ANIM_ENTRY_NAME_MAX];
while ((entry = zip->nextEntry(cookie)) != NULL) { while ((entry = zip->nextEntry(cookie)) != NULL) {
@ -612,10 +612,10 @@ bool BootAnimation::preloadZip(Animation& animation)
if (map) { if (map) {
Animation::Part& part(animation.parts.editItemAt(j)); Animation::Part& part(animation.parts.editItemAt(j));
if (leaf == "audio.wav") { if (leaf == "audio.wav") {
hasAudio = true;
// a part may have at most one audio file // a part may have at most one audio file
part.audioData = (uint8_t *)map->getDataPtr(); part.audioData = (uint8_t *)map->getDataPtr();
part.audioLength = map->getDataLength(); part.audioLength = map->getDataLength();
partWithAudio = ∂
} else if (leaf == "trim.txt") { } else if (leaf == "trim.txt") {
part.trimData.setTo((char const*)map->getDataPtr(), part.trimData.setTo((char const*)map->getDataPtr(),
map->getDataLength()); map->getDataLength());
@ -666,9 +666,11 @@ bool BootAnimation::preloadZip(Animation& animation)
} }
// Create and initialize audioplay if there is a wav file in any of the animations. // Create and initialize audioplay if there is a wav file in any of the animations.
if (hasAudio) { if (partWithAudio != NULL) {
ALOGD("found audio.wav, creating playback engine"); ALOGD("found audio.wav, creating playback engine");
audioplay::create(); if (!audioplay::create(partWithAudio->audioData, partWithAudio->audioLength)) {
return false;
}
} }
zip->endIteration(cookie); zip->endIteration(cookie);
@ -904,7 +906,10 @@ BootAnimation::Animation* BootAnimation::loadAnimation(const String8& fn)
mLoadedFiles.add(animation->fileName); mLoadedFiles.add(animation->fileName);
parseAnimationDesc(*animation); parseAnimationDesc(*animation);
preloadZip(*animation); if (!preloadZip(*animation)) {
return NULL;
}
mLoadedFiles.remove(fn); mLoadedFiles.remove(fn);
return animation; return animation;

View File

@ -21,7 +21,6 @@
#define CHATTY ALOGD #define CHATTY ALOGD
#include <assert.h>
#include <string.h> #include <string.h>
#include <utils/Log.h> #include <utils/Log.h>
@ -80,8 +79,6 @@ struct ChunkFormat {
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) { void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
(void)bq; (void)bq;
(void)context; (void)context;
assert(bq == bqPlayerBufferQueue);
assert(NULL == context);
audioplay::setPlaying(false); audioplay::setPlaying(false);
} }
@ -90,39 +87,56 @@ bool hasPlayer() {
} }
// create the engine and output mix objects // create the engine and output mix objects
void createEngine() { bool createEngine() {
SLresult result; SLresult result;
// create engine // create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("slCreateEngine failed with result %d", result);
return false;
}
(void)result; (void)result;
// realize the engine // realize the engine
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl engine Realize failed with result %d", result);
return false;
}
(void)result; (void)result;
// get the engine interface, which is needed in order to create other objects // get the engine interface, which is needed in order to create other objects
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl engine GetInterface failed with result %d", result);
return false;
}
(void)result; (void)result;
// create output mix, with environmental reverb specified as a non-required interface // create output mix, with environmental reverb specified as a non-required interface
const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB}; const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};
const SLboolean req[1] = {SL_BOOLEAN_FALSE}; const SLboolean req[1] = {SL_BOOLEAN_FALSE};
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req); result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl engine CreateOutputMix failed with result %d", result);
return false;
}
(void)result; (void)result;
// realize the output mix // realize the output mix
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl outputMix Realize failed with result %d", result);
return false;
}
(void)result; (void)result;
return true;
} }
// create buffer queue audio player // create buffer queue audio player
void createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) { bool createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
SLresult result; SLresult result;
// configure audio source // configure audio source
@ -148,83 +162,89 @@ void createBufferQueueAudioPlayer(const ChunkFormat* chunkFormat) {
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
2, ids, req); 2, ids, req);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl CreateAudioPlayer failed with result %d", result);
return false;
}
(void)result; (void)result;
// realize the player // realize the player
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl player Realize failed with result %d", result);
return false;
}
(void)result; (void)result;
// get the play interface // get the play interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl player GetInterface failed with result %d", result);
return false;
}
(void)result; (void)result;
// get the buffer queue interface // get the buffer queue interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue); &bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl playberBufferQueue GetInterface failed with result %d", result);
return false;
}
(void)result; (void)result;
// register callback on the buffer queue // register callback on the buffer queue
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL); result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl bqPlayerBufferQueue RegisterCallback failed with result %d", result);
return false;
}
(void)result; (void)result;
#if 0 // mute/solo is not supported for sources that are known to be mono, as this is
// get the mute/solo interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);
assert(SL_RESULT_SUCCESS == result);
(void)result;
#endif
// get the volume interface // get the volume interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
assert(SL_RESULT_SUCCESS == result); if (result != SL_RESULT_SUCCESS) {
ALOGE("sl volume GetInterface failed with result %d", result);
return false;
}
(void)result; (void)result;
// set the player's state to playing // set the player's state to playing
audioplay::setPlaying(true); audioplay::setPlaying(true);
CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue); CHATTY("Created buffer queue player: %p", bqPlayerBufferQueue);
return true;
} }
} // namespace bool parseClipBuf(const uint8_t* clipBuf, int clipBufSize, const ChunkFormat** oChunkFormat,
const uint8_t** oSoundBuf, unsigned* oSoundBufSize) {
void create() { *oSoundBuf = clipBuf;
createEngine(); *oSoundBufSize = clipBufSize;
} *oChunkFormat = NULL;
const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)*oSoundBuf;
bool playClip(const uint8_t* buf, int size) { if (*oSoundBufSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
// Parse the WAV header
nextBuffer = buf;
nextSize = size;
const RiffWaveHeader* wavHeader = (const RiffWaveHeader*)buf;
if (nextSize < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) ||
(wavHeader->wave_id != ID_WAVE)) { (wavHeader->wave_id != ID_WAVE)) {
ALOGE("Error: audio file is not a riff/wave file\n"); ALOGE("Error: audio file is not a riff/wave file\n");
return false; return false;
} }
nextBuffer += sizeof(*wavHeader); *oSoundBuf += sizeof(*wavHeader);
nextSize -= sizeof(*wavHeader); *oSoundBufSize -= sizeof(*wavHeader);
const ChunkFormat* chunkFormat = nullptr;
while (true) { while (true) {
const ChunkHeader* chunkHeader = (const ChunkHeader*)nextBuffer; const ChunkHeader* chunkHeader = (const ChunkHeader*)*oSoundBuf;
if (nextSize < sizeof(*chunkHeader)) { if (*oSoundBufSize < sizeof(*chunkHeader)) {
ALOGE("EOF reading chunk headers"); ALOGE("EOF reading chunk headers");
return false; return false;
} }
nextBuffer += sizeof(*chunkHeader); *oSoundBuf += sizeof(*chunkHeader);
nextSize -= sizeof(*chunkHeader); *oSoundBufSize -= sizeof(*chunkHeader);
bool endLoop = false; bool endLoop = false;
switch (chunkHeader->id) { switch (chunkHeader->id) {
case ID_FMT: case ID_FMT:
chunkFormat = (const ChunkFormat*)nextBuffer; *oChunkFormat = (const ChunkFormat*)*oSoundBuf;
nextBuffer += chunkHeader->sz; *oSoundBuf += chunkHeader->sz;
nextSize -= chunkHeader->sz; *oSoundBufSize -= chunkHeader->sz;
break; break;
case ID_DATA: case ID_DATA:
/* Stop looking for chunks */ /* Stop looking for chunks */
@ -232,27 +252,49 @@ bool playClip(const uint8_t* buf, int size) {
break; break;
default: default:
/* Unknown chunk, skip bytes */ /* Unknown chunk, skip bytes */
nextBuffer += chunkHeader->sz; *oSoundBuf += chunkHeader->sz;
nextSize -= chunkHeader->sz; *oSoundBufSize -= chunkHeader->sz;
} }
if (endLoop) { if (endLoop) {
break; break;
} }
} }
if (!chunkFormat) { if (*oChunkFormat == NULL) {
ALOGE("format not found in WAV file"); ALOGE("format not found in WAV file");
return false; return false;
} }
return true;
}
// If this is the first clip, create the buffer based on this WAV's header. } // namespace
// We assume all future clips with be in the same format.
if (bqPlayerBufferQueue == nullptr) { bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize) {
createBufferQueueAudioPlayer(chunkFormat); if (!createEngine()) {
return false;
} }
assert(bqPlayerBufferQueue != nullptr); // Parse the example clip.
assert(buf != nullptr); const ChunkFormat* chunkFormat;
const uint8_t* soundBuf;
unsigned soundBufSize;
if (!parseClipBuf(exampleClipBuf, exampleClipBufSize, &chunkFormat, &soundBuf, &soundBufSize)) {
return false;
}
// Initialize the BufferQueue based on this clip's format.
if (!createBufferQueueAudioPlayer(chunkFormat)) {
return false;
}
return true;
}
bool playClip(const uint8_t* buf, int size) {
// Parse the WAV header
const ChunkFormat* chunkFormat;
if (!parseClipBuf(buf, size, &chunkFormat, &nextBuffer, &nextSize)) {
return false;
}
if (!hasPlayer()) { if (!hasPlayer()) {
ALOGD("cannot play clip %p without a player", buf); ALOGD("cannot play clip %p without a player", buf);
@ -285,8 +327,6 @@ void setPlaying(bool isPlaying) {
// set the player's state // set the player's state
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay,
isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED); isPlaying ? SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_STOPPED);
assert(SL_RESULT_SUCCESS == result);
(void)result;
} }
} }

View File

@ -22,10 +22,12 @@
namespace audioplay { namespace audioplay {
void create(); // Initializes the engine with an example of the type of WAV clip to play.
// All buffers passed to playClip are assumed to be in the same format.
bool create(const uint8_t* exampleClipBuf, int exampleClipBufSize);
// Play a WAV pointed to by buf. All clips are assumed to be in the same format. // Plays a WAV contained in buf.
// playClip should not be called while a clip is still playing. // Should not be called while a clip is still playing.
bool playClip(const uint8_t* buf, int size); bool playClip(const uint8_t* buf, int size);
void setPlaying(bool isPlaying); void setPlaying(bool isPlaying);
void destroy(); void destroy();