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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user