}
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::audioConfigChanged_l(int event, const sp<ThreadBase>& thread, void *param2) {
- int ioHandle = 0;
-
- for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
- if (mPlaybackThreads.valueAt(i) == thread) {
- ioHandle = mPlaybackThreads.keyAt(i);
- break;
- }
- }
- if (ioHandle == 0) {
- for (size_t i = 0; i < mRecordThreads.size(); i++) {
- if (mRecordThreads.valueAt(i) == thread) {
- ioHandle = mRecordThreads.keyAt(i);
- break;
- }
- }
- }
-
- if (ioHandle != 0) {
- size_t size = mNotificationClients.size();
- for (size_t i = 0; i < size; i++) {
- sp<IBinder> binder = mNotificationClients.itemAt(i);
- LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get());
- sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
- client->ioConfigChanged(event, ioHandle, param2);
- }
+void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) {
+ size_t size = mNotificationClients.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<IBinder> binder = mNotificationClients.itemAt(i);
+ LOGV("audioConfigChanged_l() Notifying change to client %p", binder.get());
+ sp<IAudioFlingerClient> client = interface_cast<IAudioFlingerClient> (binder);
+ client->ioConfigChanged(event, ioHandle, param2);
}
}
// ----------------------------------------------------------------------------
-AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger)
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id)
: Thread(false),
mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0),
- mFormat(0), mFrameSize(1), mStandby(false)
+ mFormat(0), mFrameSize(1), mStandby(false), mId(id), mExiting(false)
{
}
LOGV("ThreadBase::exit");
{
AutoMutex lock(&mLock);
+ mExiting = true;
requestExit();
mWaitWorkCV.signal();
}
// ----------------------------------------------------------------------------
-AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
- : ThreadBase(audioFlinger),
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+ : ThreadBase(audioFlinger, id),
mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false)
{
{
status_t status = ALREADY_EXISTS;
- // here the track could be either new, or restarted
- // in both cases "unstop" the track
- if (track->isPaused()) {
- track->mState = TrackBase::RESUMING;
- LOGV("PAUSED => RESUMING (%d) on thread %p", track->name(), this);
- } else {
- track->mState = TrackBase::ACTIVE;
- LOGV("? => ACTIVE (%d) on thread %p", track->name(), this);
- }
// set retry count for buffer fill
track->mRetryCount = kMaxTrackStartupRetries;
if (mActiveTracks.indexOf(track) < 0) {
{
track->mState = TrackBase::TERMINATED;
if (mActiveTracks.indexOf(track) < 0) {
- LOGV("remove track (%d) and delete from mixer", track->name());
mTracks.remove(track);
deleteTrackName_l(track->name());
}
break;
}
Mutex::Autolock _l(mAudioFlinger->mLock);
- mAudioFlinger->audioConfigChanged_l(event, this, param2);
+ mAudioFlinger->audioConfigChanged_l(event, mId, param2);
}
void AudioFlinger::PlaybackThread::readOutputParameters()
mFrameSize = mOutput->frameSize();
mFrameCount = mOutput->bufferSize() / mFrameSize;
- mMinBytesToWrite = (mOutput->latency() * mSampleRate * mFrameSize) / 1000;
// FIXME - Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
if (mMixBuffer != NULL) delete mMixBuffer;
// ----------------------------------------------------------------------------
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
- : PlaybackThread(audioFlinger, output),
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+ : PlaybackThread(audioFlinger, output, id),
mAudioMixer(0)
{
mType = PlaybackThread::MIXER;
bool AudioFlinger::MixerThread::threadLoop()
{
- uint32_t sleepTime = 1000;
- uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
- size_t enabledTracks = 0;
+ uint32_t mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount * mFrameSize;
// FIXME: Relaxed timing because of a certain device that can't meet latency
// Should be reduced to 2x after the vendor fixes the driver issue
nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
nsecs_t lastWarning = 0;
+ bool longStandbyExit = false;
+ uint32_t activeSleepTime = activeSleepTimeUs();
+ uint32_t idleSleepTime = idleSleepTimeUs();
+ uint32_t sleepTime = idleSleepTime;
while (!exitPending())
{
processConfigEvents();
- enabledTracks = 0;
+ mixerStatus = MIXER_IDLE;
{ // scope for mLock
Mutex::Autolock _l(mLock);
// FIXME: Relaxed timing because of a certain device that can't meet latency
// Should be reduced to 2x after the vendor fixes the driver issue
maxPeriod = seconds(mFrameCount) / mSampleRate * 3;
- maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
+ activeSleepTime = activeSleepTimeUs();
+ idleSleepTime = idleSleepTimeUs();
}
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
}
standbyTime = systemTime() + kStandbyTimeInNsecs;
- sleepTime = 1000;
+ sleepTime = idleSleepTime;
continue;
}
}
- enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove);
+ mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
}
- if (LIKELY(enabledTracks)) {
+ if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
mAudioMixer->process(curBuf);
sleepTime = 0;
// If no tracks are ready, sleep once for the duration of an output
// buffer size, then write 0s to the output
if (sleepTime == 0) {
- sleepTime = maxBufferRecoveryInUsecs;
- } else if (mBytesWritten != 0) {
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0 ||
+ (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) {
memset (curBuf, 0, mixBufferSize);
sleepTime = 0;
+ LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start");
}
}
if (mSuspended) {
- sleepTime = maxBufferRecoveryInUsecs;
+ sleepTime = idleSleepTime;
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
if (bytesWritten > 0) mBytesWritten += bytesWritten;
mNumWrites++;
mInWrite = false;
- mStandby = false;
nsecs_t now = systemTime();
nsecs_t delta = now - mLastWriteTime;
if (delta > maxPeriod) {
ns2ms(delta), mNumDelayedWrites, this);
lastWarning = now;
}
+ if (mStandby) {
+ longStandbyExit = true;
+ }
}
+ mStandby = false;
} else {
usleep(sleepTime);
}
}
// prepareTracks_l() must be called with ThreadBase::mLock held
-size_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
+uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove)
{
- size_t enabledTracks = 0;
+ uint32_t mixerStatus = MIXER_IDLE;
// find out which tracks need to be processed
size_t count = activeTracks.size();
for (size_t i=0 ; i<count ; i++) {
// reset retry count
track->mRetryCount = kMaxTrackRetries;
- enabledTracks++;
+ mixerStatus = MIXER_TRACKS_READY;
} else {
//LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
if (track->isStopped()) {
if (--(track->mRetryCount) <= 0) {
LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
tracksToRemove->add(track);
+ } else if (mixerStatus != MIXER_TRACKS_READY) {
+ mixerStatus = MIXER_TRACKS_ENABLED;
}
- // For tracks using static shared memory buffer, make sure that we have
- // written enough data to audio hardware before disabling the track
- // NOTE: this condition with arrive before track->mRetryCount <= 0 so we
- // don't care about code removing track from active list above.
- if ((track->mSharedBuffer == 0) || (mBytesWritten >= mMinBytesToWrite)) {
- mAudioMixer->disable(AudioMixer::MIXING);
- } else {
- enabledTracks++;
- }
+
+ mAudioMixer->disable(AudioMixer::MIXING);
}
}
}
}
}
- return enabledTracks;
+ return mixerStatus;
}
void AudioFlinger::MixerThread::getTracks(
// deleteTrackName_l() must be called with ThreadBase::mLock held
void AudioFlinger::MixerThread::deleteTrackName_l(int name)
{
+ LOGV("remove track (%d) and delete from mixer", name);
mAudioMixer->deleteTrackName(name);
}
return NO_ERROR;
}
-uint32_t AudioFlinger::MixerThread::getMaxBufferRecoveryInUsecs()
+uint32_t AudioFlinger::MixerThread::activeSleepTimeUs()
{
- uint32_t time = ((mFrameCount * 1000) / mSampleRate) * 1000;
- // Add some margin with regard to scheduling precision
- if (time > 10000) {
- time -= 10000;
- }
- return time;
+ return (uint32_t)(mOutput->latency() * 1000) / 2;
+}
+
+uint32_t AudioFlinger::MixerThread::idleSleepTimeUs()
+{
+ return (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
}
// ----------------------------------------------------------------------------
-AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output)
- : PlaybackThread(audioFlinger, output),
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id)
+ : PlaybackThread(audioFlinger, output, id),
mLeftVolume (1.0), mRightVolume(1.0)
{
mType = PlaybackThread::DIRECT;
bool AudioFlinger::DirectOutputThread::threadLoop()
{
- uint32_t sleepTime = 1000;
- uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
+ uint32_t mixerStatus = MIXER_IDLE;
sp<Track> trackToRemove;
sp<Track> activeTrack;
nsecs_t standbyTime = systemTime();
int8_t *curBuf;
size_t mixBufferSize = mFrameCount*mFrameSize;
+ uint32_t activeSleepTime = activeSleepTimeUs();
+ uint32_t idleSleepTime = idleSleepTimeUs();
+ uint32_t sleepTime = idleSleepTime;
+
while (!exitPending())
{
processConfigEvents();
+ mixerStatus = MIXER_IDLE;
+
{ // scope for the mLock
Mutex::Autolock _l(mLock);
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount*mFrameSize;
- maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
+ activeSleepTime = activeSleepTimeUs();
+ idleSleepTime = idleSleepTimeUs();
}
// put audio hardware into standby after short delay
}
standbyTime = systemTime() + kStandbyTimeInNsecs;
- sleepTime = 1000;
+ sleepTime = idleSleepTime;
continue;
}
}
// reset retry count
track->mRetryCount = kMaxTrackRetries;
activeTrack = t;
+ mixerStatus = MIXER_TRACKS_READY;
} else {
//LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
if (track->isStopped()) {
if (--(track->mRetryCount) <= 0) {
LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
trackToRemove = track;
+ } else {
+ mixerStatus = MIXER_TRACKS_ENABLED;
}
-
- // For tracks using static shared memry buffer, make sure that we have
- // written enough data to audio hardware before disabling the track
- // NOTE: this condition with arrive before track->mRetryCount <= 0 so we
- // don't care about code removing track from active list above.
- if ((track->mSharedBuffer != 0) && (mBytesWritten < mMinBytesToWrite)) {
- activeTrack = t;
- }
- }
+ }
}
}
}
}
- if (activeTrack != 0) {
+ if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
AudioBufferProvider::Buffer buffer;
size_t frameCount = mFrameCount;
curBuf = (int8_t *)mMixBuffer;
standbyTime = systemTime() + kStandbyTimeInNsecs;
} else {
if (sleepTime == 0) {
- sleepTime = maxBufferRecoveryInUsecs;
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
} else if (mBytesWritten != 0 && AudioSystem::isLinearPCM(mFormat)) {
memset (mMixBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
if (mSuspended) {
- sleepTime = maxBufferRecoveryInUsecs;
+ sleepTime = idleSleepTime;
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
return reconfig;
}
-uint32_t AudioFlinger::DirectOutputThread::getMaxBufferRecoveryInUsecs()
+uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs()
{
uint32_t time;
if (AudioSystem::isLinearPCM(mFormat)) {
- time = ((mFrameCount * 1000) / mSampleRate) * 1000;
- // Add some margin with regard to scheduling precision
- if (time > 10000) {
- time -= 10000;
- }
+ time = (uint32_t)(mOutput->latency() * 1000) / 2;
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs()
+{
+ uint32_t time;
+ if (AudioSystem::isLinearPCM(mFormat)) {
+ time = (uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000;
} else {
time = 10000;
}
// ----------------------------------------------------------------------------
-AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread)
- : MixerThread(audioFlinger, mainThread->getOutput())
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id)
{
mType = PlaybackThread::DUPLICATING;
addOutputTrack(mainThread);
AudioFlinger::DuplicatingThread::~DuplicatingThread()
{
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ mOutputTracks[i]->destroy();
+ }
mOutputTracks.clear();
}
bool AudioFlinger::DuplicatingThread::threadLoop()
{
- uint32_t sleepTime = 1000;
- uint32_t maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
int16_t* curBuf = mMixBuffer;
Vector< sp<Track> > tracksToRemove;
- size_t enabledTracks = 0;
+ uint32_t mixerStatus = MIXER_IDLE;
nsecs_t standbyTime = systemTime();
size_t mixBufferSize = mFrameCount*mFrameSize;
SortedVector< sp<OutputTrack> > outputTracks;
uint32_t writeFrames = 0;
+ uint32_t activeSleepTime = activeSleepTimeUs();
+ uint32_t idleSleepTime = idleSleepTimeUs();
+ uint32_t sleepTime = idleSleepTime;
while (!exitPending())
{
processConfigEvents();
- enabledTracks = 0;
+ mixerStatus = MIXER_IDLE;
{ // scope for the mLock
Mutex::Autolock _l(mLock);
if (checkForNewParameters_l()) {
mixBufferSize = mFrameCount*mFrameSize;
- maxBufferRecoveryInUsecs = getMaxBufferRecoveryInUsecs();
+ activeSleepTime = activeSleepTimeUs();
+ idleSleepTime = idleSleepTimeUs();
}
const SortedVector< wp<Track> >& activeTracks = mActiveTracks;
}
standbyTime = systemTime() + kStandbyTimeInNsecs;
- sleepTime = 1000;
+ sleepTime = idleSleepTime;
continue;
}
}
- enabledTracks = prepareTracks_l(activeTracks, &tracksToRemove);
+ mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove);
}
- if (LIKELY(enabledTracks)) {
+ if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) {
// mix buffers...
mAudioMixer->process(curBuf);
sleepTime = 0;
writeFrames = mFrameCount;
} else {
if (sleepTime == 0) {
- sleepTime = maxBufferRecoveryInUsecs;
+ if (mixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
} else if (mBytesWritten != 0) {
// flush remaining overflow buffers in output tracks
for (size_t i = 0; i < outputTracks.size(); i++) {
}
if (mSuspended) {
- sleepTime = maxBufferRecoveryInUsecs;
+ sleepTime = idleSleepTime;
}
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
outputTracks.clear();
}
- { // scope for the mLock
-
- Mutex::Autolock _l(mLock);
- if (!mStandby) {
- LOGV("DuplicatingThread() exiting out of standby");
- for (size_t i = 0; i < mOutputTracks.size(); i++) {
- mOutputTracks[i]->destroy();
- }
- }
- }
-
return false;
}
{ // scope for mLock
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
+ if (!isOutputTrack()) {
+ if (mState == ACTIVE || mState == RESUMING) {
+ AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ }
+ AudioSystem::releaseOutput(thread->id());
+ }
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
playbackThread->destroyTrack_l(this);
status_t AudioFlinger::PlaybackThread::Track::start()
{
+ status_t status = NO_ERROR;
LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- playbackThread->addTrack_l(this);
+ int state = mState;
+ // here the track could be either new, or restarted
+ // in both cases "unstop" the track
+ if (mState == PAUSED) {
+ mState = TrackBase::RESUMING;
+ LOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
+ } else {
+ mState = TrackBase::ACTIVE;
+ LOGV("? => ACTIVE (%d) on thread %p", mName, this);
+ }
+
+ if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
+ thread->mLock.unlock();
+ status = AudioSystem::startOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ thread->mLock.lock();
+ }
+ if (status == NO_ERROR) {
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ playbackThread->addTrack_l(this);
+ } else {
+ mState = state;
+ }
+ } else {
+ status = BAD_VALUE;
}
- return NO_ERROR;
+ return status;
}
void AudioFlinger::PlaybackThread::Track::stop()
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
+ int state = mState;
if (mState > STOPPED) {
mState = STOPPED;
// If the track is not active (PAUSED and buffers full), flush buffers
}
LOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread);
}
+ if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
+ thread->mLock.unlock();
+ AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ thread->mLock.lock();
+ }
}
}
if (mState == ACTIVE || mState == RESUMING) {
mState = PAUSING;
LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
+ if (!isOutputTrack()) {
+ thread->mLock.unlock();
+ AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType);
+ thread->mLock.lock();
+ }
}
}
}
AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ AudioSystem::releaseInput(thread->id());
+ }
}
status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer)
if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get();
return recordThread->start(this);
+ } else {
+ return BAD_VALUE;
}
- return NO_INIT;
}
void AudioFlinger::RecordThread::RecordTrack::stop()
// ----------------------------------------------------------------------------
-AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels) :
- ThreadBase(audioFlinger),
+AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger, AudioStreamIn *input, uint32_t sampleRate, uint32_t channels, int id) :
+ ThreadBase(audioFlinger, id),
mInput(input), mResampler(0), mRsmpOutBuffer(0), mRsmpInBuffer(0)
{
mReqChannelCount = AudioSystem::popCount(channels);
run(buffer, PRIORITY_URGENT_AUDIO);
}
+
bool AudioFlinger::RecordThread::threadLoop()
{
AudioBufferProvider::Buffer buffer;
}
if (mActiveTrack != 0) {
if (mActiveTrack->mState == TrackBase::PAUSING) {
+ if (!mStandby) {
+ mInput->standby();
+ mStandby = true;
+ }
mActiveTrack.clear();
mStartStopCond.broadcast();
} else if (mActiveTrack->mState == TrackBase::RESUMING) {
- mRsmpInIndex = mFrameCount;
if (mReqChannelCount != mActiveTrack->channelCount()) {
mActiveTrack.clear();
- } else {
- mActiveTrack->mState = TrackBase::ACTIVE;
+ mStartStopCond.broadcast();
+ } else if (mBytesRead != 0) {
+ // record start succeeds only if first read from audio input
+ // succeeds
+ if (mBytesRead > 0) {
+ mActiveTrack->mState = TrackBase::ACTIVE;
+ } else {
+ mActiveTrack.clear();
+ }
+ mStartStopCond.broadcast();
}
- mStartStopCond.broadcast();
+ mStandby = false;
}
- mStandby = false;
}
}
if (mActiveTrack != 0) {
+ if (mActiveTrack->mState != TrackBase::ACTIVE &&
+ mActiveTrack->mState != TrackBase::RESUMING) {
+ usleep(5000);
+ continue;
+ }
buffer.frameCount = mFrameCount;
if (LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
size_t framesOut = buffer.frameCount;
}
}
if (framesOut && mFrameCount == mRsmpInIndex) {
- ssize_t bytesRead;
if (framesOut == mFrameCount &&
(mChannelCount == mReqChannelCount || mFormat != AudioSystem::PCM_16_BIT)) {
- bytesRead = mInput->read(buffer.raw, mInputBytes);
+ mBytesRead = mInput->read(buffer.raw, mInputBytes);
framesOut = 0;
} else {
- bytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
mRsmpInIndex = 0;
}
- if (bytesRead < 0) {
+ if (mBytesRead < 0) {
LOGE("Error reading audio input");
- sleep(1);
+ if (mActiveTrack->mState == TrackBase::ACTIVE) {
+ sleep(1);
+ }
mRsmpInIndex = mFrameCount;
framesOut = 0;
buffer.frameCount = 0;
}
mActiveTrack.clear();
+ mStartStopCond.broadcast();
+
LOGV("RecordThread %p exiting", this);
return false;
}
status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack)
{
LOGV("RecordThread::start");
- AutoMutex lock(&mLock);
-
- if (mActiveTrack != 0) {
- if (recordTrack != mActiveTrack.get()) return -EBUSY;
-
- if (mActiveTrack->mState == TrackBase::PAUSING) mActiveTrack->mState = TrackBase::RESUMING;
-
- return NO_ERROR;
- }
+ sp <ThreadBase> strongMe = this;
+ status_t status = NO_ERROR;
+ {
+ AutoMutex lock(&mLock);
+ if (mActiveTrack != 0) {
+ if (recordTrack != mActiveTrack.get()) {
+ status = -EBUSY;
+ } else if (mActiveTrack->mState == TrackBase::PAUSING) {
+ mActiveTrack->mState = TrackBase::ACTIVE;
+ }
+ return status;
+ }
- mActiveTrack = recordTrack;
- mActiveTrack->mState = TrackBase::RESUMING;
- // signal thread to start
- LOGV("Signal record thread");
- mWaitWorkCV.signal();
- mStartStopCond.wait(mLock);
- if (mActiveTrack != 0) {
+ recordTrack->mState = TrackBase::IDLE;
+ mActiveTrack = recordTrack;
+ mLock.unlock();
+ status_t status = AudioSystem::startInput(mId);
+ mLock.lock();
+ if (status != NO_ERROR) {
+ mActiveTrack.clear();
+ return status;
+ }
+ mActiveTrack->mState = TrackBase::RESUMING;
+ mRsmpInIndex = mFrameCount;
+ mBytesRead = 0;
+ // signal thread to start
+ LOGV("Signal record thread");
+ mWaitWorkCV.signal();
+ // do not wait for mStartStopCond if exiting
+ if (mExiting) {
+ mActiveTrack.clear();
+ status = INVALID_OPERATION;
+ goto startError;
+ }
+ mStartStopCond.wait(mLock);
+ if (mActiveTrack == 0) {
+ LOGV("Record failed to start");
+ status = BAD_VALUE;
+ goto startError;
+ }
LOGV("Record started OK");
- return NO_ERROR;
- } else {
- LOGV("Record failed to start");
- return BAD_VALUE;
+ return status;
}
+startError:
+ AudioSystem::stopInput(mId);
+ return status;
}
void AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
LOGV("RecordThread::stop");
- AutoMutex lock(&mLock);
- if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) {
- mActiveTrack->mState = TrackBase::PAUSING;
- mStartStopCond.wait(mLock);
+ sp <ThreadBase> strongMe = this;
+ {
+ AutoMutex lock(&mLock);
+ if (mActiveTrack != 0 && recordTrack == mActiveTrack.get()) {
+ mActiveTrack->mState = TrackBase::PAUSING;
+ // do not wait for mStartStopCond if exiting
+ if (mExiting) {
+ return;
+ }
+ mStartStopCond.wait(mLock);
+ // if we have been restarted, recordTrack == mActiveTrack.get() here
+ if (mActiveTrack == 0 || recordTrack != mActiveTrack.get()) {
+ mLock.unlock();
+ AudioSystem::stopInput(mId);
+ mLock.lock();
+ LOGV("Record stopped OK");
+ }
+ }
}
}
int channelCount;
if (framesReady == 0) {
- ssize_t bytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
- if (bytesRead < 0) {
+ mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes);
+ if (mBytesRead < 0) {
LOGE("RecordThread::getNextBuffer() Error reading audio input");
- sleep(1);
+ if (mActiveTrack->mState == TrackBase::ACTIVE) {
+ sleep(1);
+ }
buffer->raw = 0;
buffer->frameCount = 0;
return NOT_ENOUGH_DATA;
break;
}
Mutex::Autolock _l(mAudioFlinger->mLock);
- mAudioFlinger->audioConfigChanged_l(event, this, param2);
+ mAudioFlinger->audioConfigChanged_l(event, mId, param2);
}
void AudioFlinger::RecordThread::readInputParameters()
if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) ||
(format != AudioSystem::PCM_16_BIT) ||
(channels != AudioSystem::CHANNEL_OUT_STEREO)) {
- thread = new DirectOutputThread(this, output);
- LOGV("openOutput() created direct output: ID %d thread %p", (mNextThreadId + 1), thread);
+ thread = new DirectOutputThread(this, output, ++mNextThreadId);
+ LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread);
} else {
- thread = new MixerThread(this, output);
- LOGV("openOutput() created mixer output: ID %d thread %p", (mNextThreadId + 1), thread);
+ thread = new MixerThread(this, output, ++mNextThreadId);
+ LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread);
}
- mPlaybackThreads.add(++mNextThreadId, thread);
+ mPlaybackThreads.add(mNextThreadId, thread);
if (pSamplingRate) *pSamplingRate = samplingRate;
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = channels;
if (pLatencyMs) *pLatencyMs = thread->latency();
+
+ return mNextThreadId;
}
- return mNextThreadId;
+ return 0;
}
int AudioFlinger::openDuplicateOutput(int output1, int output2)
}
- DuplicatingThread *thread = new DuplicatingThread(this, thread1);
+ DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId);
thread->addOutputTrack(thread2);
- mPlaybackThreads.add(++mNextThreadId, thread);
+ mPlaybackThreads.add(mNextThreadId, thread);
return mNextThreadId;
}
}
}
void *param2 = 0;
- audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, thread, param2);
+ audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
mPlaybackThreads.removeItem(output);
}
thread->exit();
if (input != 0) {
// Start record thread
- thread = new RecordThread(this, input, reqSamplingRate, reqChannels);
- mRecordThreads.add(++mNextThreadId, thread);
+ thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId);
+ mRecordThreads.add(mNextThreadId, thread);
LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread);
if (pSamplingRate) *pSamplingRate = reqSamplingRate;
if (pFormat) *pFormat = format;
if (pChannels) *pChannels = reqChannels;
input->standby();
+
+ return mNextThreadId;
}
- return mNextThreadId;
+ return 0;
}
status_t AudioFlinger::closeInput(int input)
LOGV("closeInput() %d", input);
void *param2 = 0;
- audioConfigChanged_l(AudioSystem::INPUT_CLOSED, thread, param2);
+ audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2);
mRecordThreads.removeItem(input);
}
thread->exit();
#include <ui/GraphicBuffer.h>
#include <ui/PixelFormat.h>
#include <ui/FramebufferNativeWindow.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
#include <hardware/copybit.h>
LayerBuffer::LayerBuffer(SurfaceFlinger* flinger, DisplayID display,
const sp<Client>& client, int32_t i)
: LayerBaseClient(flinger, display, client, i),
- mNeedsBlending(false)
+ mNeedsBlending(false), mBlitEngine(0)
{
}
LayerBuffer::~LayerBuffer()
{
+ if (mBlitEngine) {
+ copybit_close(mBlitEngine);
+ }
}
void LayerBuffer::onFirstRef()
sGrallocModule = (gralloc_module_t const *)module;
}
}
+
+ if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) {
+ copybit_open(module, &mBlitEngine);
+ }
}
sp<LayerBaseClient::Surface> LayerBuffer::createSurface() const
sp<Source> source(getSource());
if (source != 0)
source->onTransaction(flags);
- return LayerBase::doTransaction(flags);
+ uint32_t res = LayerBase::doTransaction(flags);
+ // we always want filtering for these surfaces
+ mUseLinearFiltering = !(mFlags & DisplayHardware::SLOW_CONFIG);
+ return res;
}
void LayerBuffer::unlockPageFlip(const Transform& planeTransform,
: mBufferHeap(buffers)
{
NativeBuffer& src(mNativeBuffer);
- src.img.handle = 0;
+ src.crop.l = 0;
+ src.crop.t = 0;
+ src.crop.r = buffers.w;
+ src.crop.b = buffers.h;
+
+ src.img.w = buffers.hor_stride ?: buffers.w;
+ src.img.h = buffers.ver_stride ?: buffers.h;
+ src.img.format = buffers.format;
+ src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset);
+ src.img.handle = 0;
gralloc_module_t const * module = LayerBuffer::getGrallocModule();
if (module && module->perform) {
offset, buffers.heap->base(),
&src.img.handle);
- if (err == NO_ERROR) {
- src.crop.l = 0;
- src.crop.t = 0;
- src.crop.r = buffers.w;
- src.crop.b = buffers.h;
-
- src.img.w = buffers.hor_stride ?: buffers.w;
- src.img.h = buffers.ver_stride ?: buffers.h;
- src.img.format = buffers.format;
- src.img.base = (void*)(intptr_t(buffers.heap->base()) + offset);
- }
+ LOGE_IF(err, "CREATE_HANDLE_FROM_BUFFER (heapId=%d, size=%d, "
+ "offset=%ld, base=%p) failed (%s)",
+ buffers.heap->heapID(), buffers.heap->getSize(),
+ offset, buffers.heap->base(), strerror(-err));
}
-}
+ }
LayerBuffer::Buffer::~Buffer()
{
#if defined(EGL_ANDROID_image_native_buffer)
if (mLayer.mFlags & DisplayHardware::DIRECT_TEXTURE) {
- // NOTE: Assume the buffer is allocated with the proper USAGE flags
- sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
- src.crop.r, src.crop.b, src.img.format,
- GraphicBuffer::USAGE_HW_TEXTURE,
- src.img.w, src.img.handle, false);
-
- graphicBuffer->setVerticalStride(src.img.h);
+ copybit_device_t* copybit = mLayer.mBlitEngine;
+ if (copybit) {
+ // create our EGLImageKHR the first time
+ err = initTempBuffer();
+ if (err == NO_ERROR) {
+ // NOTE: Assume the buffer is allocated with the proper USAGE flags
+ const NativeBuffer& dst(mTempBuffer);
+ region_iterator clip(Region(Rect(dst.crop.r, dst.crop.b)));
+ copybit->set_parameter(copybit, COPYBIT_TRANSFORM, 0);
+ copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, 0xFF);
+ copybit->set_parameter(copybit, COPYBIT_DITHER, COPYBIT_ENABLE);
+ err = copybit->stretch(copybit, &dst.img, &src.img,
+ &dst.crop, &src.crop, &clip);
- err = mLayer.initializeEglImage(graphicBuffer, &mTexture);
+ }
+ } else {
+ err = INVALID_OPERATION;
+ }
}
#endif
else {
mLayer.drawWithOpenGL(clip, mTexture);
}
+status_t LayerBuffer::BufferSource::initTempBuffer() const
+{
+ // figure out the size we need now
+ const ISurface::BufferHeap& buffers(mBufferHeap);
+ uint32_t w = mLayer.mTransformedBounds.width();
+ uint32_t h = mLayer.mTransformedBounds.height();
+ if (buffers.w * h != buffers.h * w) {
+ int t = w; w = h; h = t;
+ }
+
+ if (mTexture.image != EGL_NO_IMAGE_KHR) {
+ // we have an EGLImage, make sure the needed size didn't change
+ if (w!=mTexture.width || h!= mTexture.height) {
+ // delete the EGLImage and texture
+ EGLDisplay dpy(mLayer.mFlinger->graphicPlane(0).getEGLDisplay());
+ glDeleteTextures(1, &mTexture.name);
+ eglDestroyImageKHR(dpy, mTexture.image);
+ Texture defaultTexture;
+ mTexture = defaultTexture;
+ mTempGraphicBuffer.clear();
+ } else {
+ // we're good, we have an EGLImageKHR and it's (still) the
+ // right size
+ return NO_ERROR;
+ }
+ }
+
+ // figure out if we need linear filtering
+ if (buffers.w * h == buffers.h * w) {
+ // same pixel area, don't use filtering
+ mLayer.mUseLinearFiltering = false;
+ }
+
+ // Allocate a temporary buffer and create the corresponding EGLImageKHR
+
+ status_t err;
+ mTempGraphicBuffer.clear();
+ mTempGraphicBuffer = new GraphicBuffer(
+ w, h, HAL_PIXEL_FORMAT_RGB_565,
+ GraphicBuffer::USAGE_HW_TEXTURE |
+ GraphicBuffer::USAGE_HW_2D);
+
+ err = mTempGraphicBuffer->initCheck();
+ if (err == NO_ERROR) {
+ NativeBuffer& dst(mTempBuffer);
+ dst.img.w = mTempGraphicBuffer->getStride();
+ dst.img.h = h;
+ dst.img.format = mTempGraphicBuffer->getPixelFormat();
+ dst.img.handle = (native_handle_t *)mTempGraphicBuffer->handle;
+ dst.img.base = 0;
+ dst.crop.l = 0;
+ dst.crop.t = 0;
+ dst.crop.r = w;
+ dst.crop.b = h;
+
+ err = mLayer.initializeEglImage(
+ mTempGraphicBuffer, &mTexture);
+ // once the EGLImage has been created (whether it fails
+ // or not) we don't need the graphic buffer reference
+ // anymore.
+ mTempGraphicBuffer.clear();
+ }
+
+ return err;
+}
+
// ---------------------------------------------------------------------------
LayerBuffer::OverlaySource::OverlaySource(LayerBuffer& layer,
status_t SurfaceControl::setLayer(int32_t layer) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setLayer(mToken, layer);
}
status_t SurfaceControl::setPosition(int32_t x, int32_t y) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setPosition(mToken, x, y);
}
status_t SurfaceControl::setSize(uint32_t w, uint32_t h) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setSize(mToken, w, h);
}
status_t SurfaceControl::hide() {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->hide(mToken);
}
status_t SurfaceControl::show(int32_t layer) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->show(mToken, layer);
}
status_t SurfaceControl::freeze() {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->freeze(mToken);
}
status_t SurfaceControl::unfreeze() {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->unfreeze(mToken);
}
status_t SurfaceControl::setFlags(uint32_t flags, uint32_t mask) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setFlags(mToken, flags, mask);
}
status_t SurfaceControl::setTransparentRegionHint(const Region& transparent) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setTransparentRegionHint(mToken, transparent);
}
status_t SurfaceControl::setAlpha(float alpha) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setAlpha(mToken, alpha);
}
status_t SurfaceControl::setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setMatrix(mToken, dsdx, dtdx, dsdy, dtdy);
}
status_t SurfaceControl::setFreezeTint(uint32_t tint) {
const sp<SurfaceComposerClient>& client(mClient);
- if (client == 0) return NO_INIT;
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err < 0) return err;
return client->setFreezeTint(mToken, tint);
}
-status_t SurfaceControl::validate(SharedClient const* cblk) const
+status_t SurfaceControl::validate() const
{
if (mToken<0 || mClient==0) {
LOGE("invalid token (%d, identity=%u) or client (%p)",
mToken, mIdentity, mClient.get());
return NO_INIT;
}
+ SharedClient const* cblk = mClient->mControl;
if (cblk == 0) {
LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity);
return NO_INIT;
return mToken>=0 && mClient!=0;
}
-status_t Surface::validate(SharedClient const* cblk) const
+status_t Surface::validate() const
{
sp<SurfaceComposerClient> client(getClient());
if (mToken<0 || mClient==0) {
mToken, mIdentity, client.get());
return NO_INIT;
}
+ SharedClient const* cblk = mClient->mControl;
if (cblk == 0) {
LOGE("cblk is null (surface id=%d, identity=%u)", mToken, mIdentity);
return NO_INIT;
int Surface::dequeueBuffer(android_native_buffer_t** buffer)
{
sp<SurfaceComposerClient> client(getClient());
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err != NO_ERROR)
return err;
int Surface::lockBuffer(android_native_buffer_t* buffer)
{
sp<SurfaceComposerClient> client(getClient());
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err != NO_ERROR)
return err;
int Surface::queueBuffer(android_native_buffer_t* buffer)
{
sp<SurfaceComposerClient> client(getClient());
- status_t err = validate(client->mControl);
+ status_t err = validate();
if (err != NO_ERROR)
return err;