am cc34967f: Merge "Fix issue 3439872: video chat and bluetooth SCO" into honeycomb-mr1

* commit 'cc34967fdcbf64826a068901d6fb1bd8bb1fa99f':
  Fix issue 3439872: video chat and bluetooth SCO
This commit is contained in:
Eric Laurent
2011-03-09 10:57:49 -08:00
committed by Android Git Automerger
7 changed files with 364 additions and 90 deletions

View File

@@ -315,7 +315,11 @@ static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz
}
// get the pointer to where we'll record the audio
recordBuff = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
// NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
// a way that it becomes much more efficient. When doing so, we will have to prevent the
// AudioSystem callback to be called while in critical section (in case of media server
// process crash for instance)
recordBuff = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
if (recordBuff == NULL) {
LOGE("Error retrieving destination for recorded audio data, can't record");
@@ -327,7 +331,7 @@ static jint android_media_AudioRecord_readInByteArray(JNIEnv *env, jobject thiz
ssize_t readSize = lpRecorder->read(recordBuff + offsetInBytes,
sizeInBytes > (jint)recorderBuffSize ?
(jint)recorderBuffSize : sizeInBytes );
env->ReleasePrimitiveArrayCritical(javaAudioData, recordBuff, 0);
env->ReleaseByteArrayElements(javaAudioData, recordBuff, 0);
return (jint) readSize;
}

View File

@@ -530,8 +530,12 @@ static jint android_media_AudioTrack_native_write(JNIEnv *env, jobject thiz,
}
// get the pointer for the audio data from the java array
// NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
// a way that it becomes much more efficient. When doing so, we will have to prevent the
// AudioSystem callback to be called while in critical section (in case of media server
// process crash for instance)
if (javaAudioData) {
cAudioData = (jbyte *)env->GetPrimitiveArrayCritical(javaAudioData, NULL);
cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL);
if (cAudioData == NULL) {
LOGE("Error retrieving source of audio data to play, can't play");
return 0; // out of memory or no data to load
@@ -543,7 +547,7 @@ static jint android_media_AudioTrack_native_write(JNIEnv *env, jobject thiz,
jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes);
env->ReleasePrimitiveArrayCritical(javaAudioData, cAudioData, 0);
env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0);
//LOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d",
// (int)written, (int)(sizeInBytes), (int)offsetInBytes);

View File

@@ -346,12 +346,14 @@ private:
};
bool processAudioBuffer(const sp<ClientRecordThread>& thread);
status_t openRecord(uint32_t sampleRate,
status_t openRecord_l(uint32_t sampleRate,
int format,
int channelCount,
int frameCount,
uint32_t flags,
audio_io_handle_t input);
audio_io_handle_t getInput_l();
status_t restoreRecord_l(audio_track_cblk_t*& cblk);
sp<IAudioRecord> mAudioRecord;
sp<IMemory> mCblkMemory;

View File

@@ -437,7 +437,7 @@ private:
};
bool processAudioBuffer(const sp<AudioTrackThread>& thread);
status_t createTrack(int streamType,
status_t createTrack_l(int streamType,
uint32_t sampleRate,
int format,
int channelCount,
@@ -446,6 +446,10 @@ private:
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
bool enforceFrameCount);
void flush_l();
status_t setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
audio_io_handle_t getOutput_l();
status_t restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart);
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;

View File

@@ -31,6 +31,7 @@ namespace android {
#define MAX_STARTUP_TIMEOUT_MS 3000 // Longer timeout period at startup to cope with A2DP init time
#define MAX_RUN_TIMEOUT_MS 1000
#define WAIT_PERIOD_MS 10
#define RESTORE_TIMEOUT_MS 5000 // Maximum waiting time for a track to be restored
#define CBLK_UNDERRUN_MSK 0x0001
#define CBLK_UNDERRUN_ON 0x0001 // underrun (out) or overrrun (in) indication
@@ -47,6 +48,12 @@ namespace android {
#define CBLK_DISABLED_MSK 0x0010
#define CBLK_DISABLED_ON 0x0010 // track disabled by AudioFlinger due to underrun:
#define CBLK_DISABLED_OFF 0x0000 // must be re-started
#define CBLK_RESTORING_MSK 0x0020
#define CBLK_RESTORING_ON 0x0020 // track is being restored after invalidation
#define CBLK_RESTORING_OFF 0x0000 // by AudioFlinger
#define CBLK_RESTORED_MSK 0x0040
#define CBLK_RESTORED_ON 0x0040 // track has been restored after invalidation
#define CBLK_RESTORED_OFF 0x0040 // by AudioFlinger
struct audio_track_cblk_t
{

View File

@@ -128,6 +128,9 @@ status_t AudioRecord::set(
{
LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
AutoMutex lock(mLock);
if (mAudioRecord != 0) {
return INVALID_OPERATION;
}
@@ -183,7 +186,7 @@ status_t AudioRecord::set(
mSessionId = sessionId;
// create the IAudioRecord
status = openRecord(sampleRate, format, channelCount,
status = openRecord_l(sampleRate, format, channelCount,
frameCount, flags, input);
if (status != NO_ERROR) {
return status;
@@ -282,21 +285,31 @@ status_t AudioRecord::start()
}
AutoMutex lock(mLock);
// acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
// while we are accessing the cblk
sp <IAudioRecord> audioRecord = mAudioRecord;
sp <IMemory> iMem = mCblkMemory;
audio_track_cblk_t* cblk = mCblk;
if (mActive == 0) {
mActive = 1;
ret = mAudioRecord->start();
if (ret == DEAD_OBJECT) {
LOGV("start() dead IAudioRecord: creating a new one");
ret = openRecord(mCblk->sampleRate, mFormat, mChannelCount,
mFrameCount, mFlags, getInput());
if (ret == NO_ERROR) {
ret = mAudioRecord->start();
cblk->lock.lock();
if (!(cblk->flags & CBLK_INVALID_MSK)) {
cblk->lock.unlock();
ret = mAudioRecord->start();
cblk->lock.lock();
if (ret == DEAD_OBJECT) {
cblk->flags |= CBLK_INVALID_MSK;
}
}
if (cblk->flags & CBLK_INVALID_MSK) {
ret = restoreRecord_l(cblk);
}
cblk->lock.unlock();
if (ret == NO_ERROR) {
mNewPosition = mCblk->user + mUpdatePeriod;
mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
mNewPosition = cblk->user + mUpdatePeriod;
cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
cblk->waitTimeMs = 0;
if (t != 0) {
t->run("ClientRecordThread", THREAD_PRIORITY_AUDIO_CLIENT);
} else {
@@ -353,6 +366,7 @@ bool AudioRecord::stopped() const
uint32_t AudioRecord::getSampleRate()
{
AutoMutex lock(mLock);
return mCblk->sampleRate;
}
@@ -400,6 +414,7 @@ status_t AudioRecord::getPosition(uint32_t *position)
{
if (position == 0) return BAD_VALUE;
AutoMutex lock(mLock);
*position = mCblk->user;
return NO_ERROR;
@@ -415,7 +430,8 @@ unsigned int AudioRecord::getInputFramesLost()
// -------------------------------------------------------------------------
status_t AudioRecord::openRecord(
// must be called with mLock held
status_t AudioRecord::openRecord_l(
uint32_t sampleRate,
int format,
int channelCount,
@@ -459,6 +475,7 @@ status_t AudioRecord::openRecord(
status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
AutoMutex lock(mLock);
int active;
status_t result;
audio_track_cblk_t* cblk = mCblk;
@@ -483,7 +500,19 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
cblk->lock.unlock();
return WOULD_BLOCK;
}
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
if (!(cblk->flags & CBLK_INVALID_MSK)) {
mLock.unlock();
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
cblk->lock.unlock();
mLock.lock();
if (mActive == 0) {
return status_t(STOPPED);
}
cblk->lock.lock();
}
if (cblk->flags & CBLK_INVALID_MSK) {
goto create_new_record;
}
if (__builtin_expect(result!=NO_ERROR, false)) {
cblk->waitTimeMs += waitTimeMs;
if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
@@ -491,16 +520,17 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
"user=%08x, server=%08x", cblk->user, cblk->server);
cblk->lock.unlock();
result = mAudioRecord->start();
if (result == DEAD_OBJECT) {
LOGW("obtainBuffer() dead IAudioRecord: creating a new one");
result = openRecord(cblk->sampleRate, mFormat, mChannelCount,
mFrameCount, mFlags, getInput());
if (result == NO_ERROR) {
cblk = mCblk;
mAudioRecord->start();
}
}
cblk->lock.lock();
if (result == DEAD_OBJECT) {
cblk->flags |= CBLK_INVALID_MSK;
create_new_record:
result = AudioRecord::restoreRecord_l(cblk);
}
if (result != NO_ERROR) {
LOGW("obtainBuffer create Track error %d", result);
cblk->lock.unlock();
return result;
}
cblk->waitTimeMs = 0;
}
if (--waitCount == 0) {
@@ -540,11 +570,18 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
void AudioRecord::releaseBuffer(Buffer* audioBuffer)
{
audio_track_cblk_t* cblk = mCblk;
cblk->stepUser(audioBuffer->frameCount);
AutoMutex lock(mLock);
mCblk->stepUser(audioBuffer->frameCount);
}
audio_io_handle_t AudioRecord::getInput()
{
AutoMutex lock(mLock);
return getInput_l();
}
// must be called with mLock held
audio_io_handle_t AudioRecord::getInput_l()
{
mInput = AudioSystem::getInput(mInputSource,
mCblk->sampleRate,
@@ -573,6 +610,12 @@ ssize_t AudioRecord::read(void* buffer, size_t userSize)
return BAD_VALUE;
}
mLock.lock();
// acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
// while we are accessing the cblk
sp <IAudioRecord> audioRecord = mAudioRecord;
sp <IMemory> iMem = mCblkMemory;
mLock.unlock();
do {
@@ -613,9 +656,17 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
uint32_t frames = mRemainingFrames;
size_t readSize;
mLock.lock();
// acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
// while we are accessing the cblk
sp <IAudioRecord> audioRecord = mAudioRecord;
sp <IMemory> iMem = mCblkMemory;
audio_track_cblk_t* cblk = mCblk;
mLock.unlock();
// Manage marker callback
if (!mMarkerReached && (mMarkerPosition > 0)) {
if (mCblk->user >= mMarkerPosition) {
if (cblk->user >= mMarkerPosition) {
mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
mMarkerReached = true;
}
@@ -623,7 +674,7 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
// Manage new position callback
if (mUpdatePeriod > 0) {
while (mCblk->user >= mNewPosition) {
while (cblk->user >= mNewPosition) {
mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
mNewPosition += mUpdatePeriod;
}
@@ -669,11 +720,11 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
// Manage overrun callback
if (mActive && (mCblk->framesAvailable_l() == 0)) {
LOGV("Overrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags);
if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
if (mActive && (cblk->framesAvailable() == 0)) {
LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
mCbf(EVENT_OVERRUN, mUserData, 0);
mCblk->flags |= CBLK_UNDERRUN_ON;
cblk->flags |= CBLK_UNDERRUN_ON;
}
}
@@ -685,6 +736,69 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
return true;
}
// must be called with mLock and cblk.lock held. Callers must also hold strong references on
// the IAudioRecord and IMemory in case they are recreated here.
// If the IAudioRecord is successfully restored, the cblk pointer is updated
status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& cblk)
{
status_t result;
if (!(cblk->flags & CBLK_RESTORING_MSK)) {
LOGW("dead IAudioRecord, creating a new one");
cblk->flags |= CBLK_RESTORING_ON;
// signal old cblk condition so that other threads waiting for available buffers stop
// waiting now
cblk->cv.broadcast();
cblk->lock.unlock();
// if the new IAudioRecord is created, openRecord_l() will modify the
// following member variables: mAudioRecord, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioRecord and IMemory
result = openRecord_l(cblk->sampleRate, mFormat, mChannelCount,
mFrameCount, mFlags, getInput_l());
if (result == NO_ERROR) {
result = mAudioRecord->start();
}
if (result != NO_ERROR) {
mActive = false;
}
// signal old cblk condition for other threads waiting for restore completion
cblk->lock.lock();
cblk->flags |= CBLK_RESTORED_MSK;
cblk->cv.broadcast();
cblk->lock.unlock();
} else {
if (!(cblk->flags & CBLK_RESTORED_MSK)) {
LOGW("dead IAudioRecord, waiting for a new one to be created");
mLock.unlock();
result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS));
cblk->lock.unlock();
mLock.lock();
} else {
LOGW("dead IAudioRecord, already restored");
result = NO_ERROR;
cblk->lock.unlock();
}
if (result != NO_ERROR || mActive == 0) {
result = status_t(STOPPED);
}
}
LOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
result, mActive, mCblk, cblk, mCblk->flags, cblk->flags);
if (result == NO_ERROR) {
// from now on we switch to the newly created cblk
cblk = mCblk;
}
cblk->lock.lock();
LOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result);
return result;
}
// =========================================================================
AudioRecord::ClientRecordThread::ClientRecordThread(AudioRecord& receiver, bool bCanCallJava)

View File

@@ -148,6 +148,7 @@ status_t AudioTrack::set(
LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
AutoMutex lock(mLock);
if (mAudioTrack != 0) {
LOGE("Track already in use");
return INVALID_OPERATION;
@@ -211,8 +212,15 @@ status_t AudioTrack::set(
mAuxEffectId = 0;
// create the IAudioTrack
status_t status = createTrack(streamType, sampleRate, format, channelCount,
frameCount, flags, sharedBuffer, output, true);
status_t status = createTrack_l(streamType,
sampleRate,
format,
channelCount,
frameCount,
flags,
sharedBuffer,
output,
true);
if (status != NO_ERROR) {
return status;
@@ -312,37 +320,38 @@ void AudioTrack::start()
}
AutoMutex lock(mLock);
// acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
// while we are accessing the cblk
sp <IAudioTrack> audioTrack = mAudioTrack;
sp <IMemory> iMem = mCblkMemory;
audio_track_cblk_t* cblk = mCblk;
if (mActive == 0) {
mActive = 1;
mNewPosition = mCblk->server + mUpdatePeriod;
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
mCblk->waitTimeMs = 0;
mCblk->flags &= ~CBLK_DISABLED_ON;
mNewPosition = cblk->server + mUpdatePeriod;
cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
cblk->waitTimeMs = 0;
cblk->flags &= ~CBLK_DISABLED_ON;
if (t != 0) {
t->run("AudioTrackThread", THREAD_PRIORITY_AUDIO_CLIENT);
} else {
setpriority(PRIO_PROCESS, 0, THREAD_PRIORITY_AUDIO_CLIENT);
}
if (mCblk->flags & CBLK_INVALID_MSK) {
LOGW("start() track %p invalidated, creating a new one", this);
// no need to clear the invalid flag as this cblk will not be used anymore
// force new track creation
status = DEAD_OBJECT;
} else {
LOGV("start %p before lock cblk %p", this, mCblk);
cblk->lock.lock();
if (!(cblk->flags & CBLK_INVALID_MSK)) {
cblk->lock.unlock();
status = mAudioTrack->start();
}
if (status == DEAD_OBJECT) {
LOGV("start() dead IAudioTrack: creating a new one");
status = createTrack(mStreamType, mCblk->sampleRate, mFormat, mChannelCount,
mFrameCount, mFlags, mSharedBuffer, getOutput(), false);
if (status == NO_ERROR) {
status = mAudioTrack->start();
if (status == NO_ERROR) {
mNewPosition = mCblk->server + mUpdatePeriod;
}
cblk->lock.lock();
if (status == DEAD_OBJECT) {
cblk->flags |= CBLK_INVALID_MSK;
}
}
if (cblk->flags & CBLK_INVALID_MSK) {
status = restoreTrack_l(cblk, true);
}
cblk->lock.unlock();
if (status != NO_ERROR) {
LOGV("start() failed");
mActive = 0;
@@ -375,14 +384,14 @@ void AudioTrack::stop()
mAudioTrack->stop();
// Cancel loops (If we are in the middle of a loop, playback
// would not stop until loopCount reaches 0).
setLoop(0, 0, 0);
setLoop_l(0, 0, 0);
// the playback head position will reset to 0, so if a marker is set, we need
// to activate it again
mMarkerReached = false;
// Force flush if a shared buffer is used otherwise audioflinger
// will not stop before end of buffer is reached.
if (mSharedBuffer != 0) {
flush();
flush_l();
}
if (t != 0) {
t->requestExit();
@@ -402,6 +411,13 @@ bool AudioTrack::stopped() const
}
void AudioTrack::flush()
{
AutoMutex lock(mLock);
flush_l();
}
// must be called with mLock held
void AudioTrack::flush_l()
{
LOGV("flush");
@@ -445,6 +461,7 @@ status_t AudioTrack::setVolume(float left, float right)
return BAD_VALUE;
}
AutoMutex lock(mLock);
mVolume[LEFT] = left;
mVolume[RIGHT] = right;
@@ -470,6 +487,7 @@ status_t AudioTrack::setAuxEffectSendLevel(float level)
if (level > 1.0f) {
return BAD_VALUE;
}
AutoMutex lock(mLock);
mSendLevel = level;
@@ -495,16 +513,25 @@ status_t AudioTrack::setSampleRate(int rate)
// Resampler implementation limits input sampling rate to 2 x output sampling rate.
if (rate <= 0 || rate > afSamplingRate*2 ) return BAD_VALUE;
AutoMutex lock(mLock);
mCblk->sampleRate = rate;
return NO_ERROR;
}
uint32_t AudioTrack::getSampleRate()
{
AutoMutex lock(mLock);
return mCblk->sampleRate;
}
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
AutoMutex lock(mLock);
return setLoop_l(loopStart, loopEnd, loopCount);
}
// must be called with mLock held
status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
audio_track_cblk_t* cblk = mCblk;
@@ -540,6 +567,7 @@ status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount
status_t AudioTrack::getLoop(uint32_t *loopStart, uint32_t *loopEnd, int *loopCount)
{
AutoMutex lock(mLock);
if (loopStart != 0) {
*loopStart = mCblk->loopStart;
}
@@ -599,6 +627,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod)
status_t AudioTrack::setPosition(uint32_t position)
{
AutoMutex lock(mLock);
Mutex::Autolock _l(mCblk->lock);
if (!stopped()) return INVALID_OPERATION;
@@ -614,7 +643,7 @@ status_t AudioTrack::setPosition(uint32_t position)
status_t AudioTrack::getPosition(uint32_t *position)
{
if (position == 0) return BAD_VALUE;
AutoMutex lock(mLock);
*position = mCblk->server;
return NO_ERROR;
@@ -622,9 +651,11 @@ status_t AudioTrack::getPosition(uint32_t *position)
status_t AudioTrack::reload()
{
AutoMutex lock(mLock);
if (!stopped()) return INVALID_OPERATION;
flush();
flush_l();
mCblk->stepUser(mCblk->frameCount);
@@ -632,6 +663,13 @@ status_t AudioTrack::reload()
}
audio_io_handle_t AudioTrack::getOutput()
{
AutoMutex lock(mLock);
return getOutput_l();
}
// must be called with mLock held
audio_io_handle_t AudioTrack::getOutput_l()
{
return AudioSystem::getOutput((AudioSystem::stream_type)mStreamType,
mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags);
@@ -654,7 +692,8 @@ status_t AudioTrack::attachAuxEffect(int effectId)
// -------------------------------------------------------------------------
status_t AudioTrack::createTrack(
// must be called with mLock held
status_t AudioTrack::createTrack_l(
int streamType,
uint32_t sampleRate,
int format,
@@ -774,6 +813,7 @@ status_t AudioTrack::createTrack(
status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
AutoMutex lock(mLock);
int active;
status_t result;
audio_track_cblk_t* cblk = mCblk;
@@ -800,12 +840,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
return WOULD_BLOCK;
}
if (!(cblk->flags & CBLK_INVALID_MSK)) {
mLock.unlock();
result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
}
if (cblk->flags & CBLK_INVALID_MSK) {
LOGW("obtainBuffer() track %p invalidated, creating a new one", this);
// no need to clear the invalid flag as this cblk will not be used anymore
cblk->lock.unlock();
mLock.lock();
if (mActive == 0) {
return status_t(STOPPED);
}
cblk->lock.lock();
}
if (cblk->flags & CBLK_INVALID_MSK) {
goto create_new_track;
}
if (__builtin_expect(result!=NO_ERROR, false)) {
@@ -819,18 +864,17 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
//unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
cblk->lock.unlock();
result = mAudioTrack->start();
if (result == DEAD_OBJECT) {
LOGW("obtainBuffer() dead IAudioTrack: creating a new one");
create_new_track:
result = createTrack(mStreamType, cblk->sampleRate, mFormat, mChannelCount,
mFrameCount, mFlags, mSharedBuffer, getOutput(), false);
if (result == NO_ERROR) {
cblk = mCblk;
cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
mAudioTrack->start();
}
}
cblk->lock.lock();
if (result == DEAD_OBJECT) {
cblk->flags |= CBLK_INVALID_MSK;
create_new_track:
result = restoreTrack_l(cblk, false);
}
if (result != NO_ERROR) {
LOGW("obtainBuffer create Track error %d", result);
cblk->lock.unlock();
return result;
}
}
cblk->waitTimeMs = 0;
}
@@ -848,7 +892,7 @@ create_new_track:
}
// restart track if it was disabled by audioflinger due to previous underrun
if (cblk->flags & CBLK_DISABLED_MSK) {
if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
cblk->flags &= ~CBLK_DISABLED_ON;
LOGW("obtainBuffer() track %p disabled, restarting", this);
mAudioTrack->start();
@@ -883,8 +927,8 @@ create_new_track:
void AudioTrack::releaseBuffer(Buffer* audioBuffer)
{
audio_track_cblk_t* cblk = mCblk;
cblk->stepUser(audioBuffer->frameCount);
AutoMutex lock(mLock);
mCblk->stepUser(audioBuffer->frameCount);
}
// -------------------------------------------------------------------------
@@ -903,6 +947,13 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
LOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive);
// acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
// while we are accessing the cblk
mLock.lock();
sp <IAudioTrack> audioTrack = mAudioTrack;
sp <IMemory> iMem = mCblkMemory;
mLock.unlock();
ssize_t written = 0;
const int8_t *src = (const int8_t *)buffer;
Buffer audioBuffer;
@@ -953,21 +1004,29 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
uint32_t frames;
size_t writtenSize;
mLock.lock();
// acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
// while we are accessing the cblk
sp <IAudioTrack> audioTrack = mAudioTrack;
sp <IMemory> iMem = mCblkMemory;
audio_track_cblk_t* cblk = mCblk;
mLock.unlock();
// Manage underrun callback
if (mActive && (mCblk->framesReady() == 0)) {
LOGV("Underrun user: %x, server: %x, flags %04x", mCblk->user, mCblk->server, mCblk->flags);
if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
if (mActive && (cblk->framesReady() == 0)) {
LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
mCbf(EVENT_UNDERRUN, mUserData, 0);
if (mCblk->server == mCblk->frameCount) {
if (cblk->server == cblk->frameCount) {
mCbf(EVENT_BUFFER_END, mUserData, 0);
}
mCblk->flags |= CBLK_UNDERRUN_ON;
cblk->flags |= CBLK_UNDERRUN_ON;
if (mSharedBuffer != 0) return false;
}
}
// Manage loop end callback
while (mLoopCount > mCblk->loopCount) {
while (mLoopCount > cblk->loopCount) {
int loopCount = -1;
mLoopCount--;
if (mLoopCount >= 0) loopCount = mLoopCount;
@@ -977,7 +1036,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
// Manage marker callback
if (!mMarkerReached && (mMarkerPosition > 0)) {
if (mCblk->server >= mMarkerPosition) {
if (cblk->server >= mMarkerPosition) {
mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
mMarkerReached = true;
}
@@ -985,7 +1044,7 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
// Manage new position callback
if (mUpdatePeriod > 0) {
while (mCblk->server >= mNewPosition) {
while (cblk->server >= mNewPosition) {
mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
mNewPosition += mUpdatePeriod;
}
@@ -1068,6 +1127,84 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
return true;
}
// must be called with mLock and cblk.lock held. Callers must also hold strong references on
// the IAudioTrack and IMemory in case they are recreated here.
// If the IAudioTrack is successfully restored, the cblk pointer is updated
status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart)
{
status_t result;
if (!(cblk->flags & CBLK_RESTORING_MSK)) {
LOGW("dead IAudioTrack, creating a new one from %s",
fromStart ? "start()" : "obtainBuffer()");
cblk->flags |= CBLK_RESTORING_ON;
// signal old cblk condition so that other threads waiting for available buffers stop
// waiting now
cblk->cv.broadcast();
cblk->lock.unlock();
// if the new IAudioTrack is created, createTrack_l() will modify the
// following member variables: mAudioTrack, mCblkMemory and mCblk.
// It will also delete the strong references on previous IAudioTrack and IMemory
result = createTrack_l(mStreamType,
cblk->sampleRate,
mFormat,
mChannelCount,
mFrameCount,
mFlags,
mSharedBuffer,
getOutput_l(),
false);
if (result == NO_ERROR) {
if (!fromStart) {
mCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
}
result = mAudioTrack->start();
if (fromStart && result == NO_ERROR) {
mNewPosition = mCblk->server + mUpdatePeriod;
}
}
if (result != NO_ERROR) {
mActive = false;
}
// signal old cblk condition for other threads waiting for restore completion
cblk->lock.lock();
cblk->flags |= CBLK_RESTORED_MSK;
cblk->cv.broadcast();
cblk->lock.unlock();
} else {
if (!(cblk->flags & CBLK_RESTORED_MSK)) {
LOGW("dead IAudioTrack, waiting for a new one");
mLock.unlock();
result = cblk->cv.waitRelative(cblk->lock, milliseconds(RESTORE_TIMEOUT_MS));
cblk->lock.unlock();
mLock.lock();
} else {
LOGW("dead IAudioTrack, already restored");
result = NO_ERROR;
cblk->lock.unlock();
}
if (result != NO_ERROR || mActive == 0) {
result = status_t(STOPPED);
}
}
LOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
result, mActive, mCblk, cblk, mCblk->flags, cblk->flags);
if (result == NO_ERROR) {
// from now on we switch to the newly created cblk
cblk = mCblk;
}
cblk->lock.lock();
LOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result);
return result;
}
status_t AudioTrack::dump(int fd, const Vector<String16>& args) const
{
@@ -1197,7 +1334,9 @@ bool audio_track_cblk_t::stepServer(uint32_t frameCount)
this->server = s;
cv.signal();
if (!(flags & CBLK_INVALID_MSK)) {
cv.signal();
}
lock.unlock();
return true;
}