Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bfb1b832 authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioFlinger: offload playback, non-blocking write

- Added specialized playback thread class for offload playback,
derived from directoutput thread.
This thread type handles specific state transitions for offloaded
tracks and offloading commands (pause/resume/drain/flush..) to audio HAL.
As opposed to other threads, does not go to standby if the track is paused.

- Added support for asynchronous write and drain operations at audio HAL.
Use a thread to handle async callback events from HAL: this avoids locking
playback thread mutex when executing the callback and cause deadlocks when
calling audio HAL functions with the playback thread mutex locked.

- Better accouting for track activity: call start/stop and release Output
methods in audio policy manager when tracks are actually added and removed
from the active tracks list.
Added a command thread in audio policy service to handle stop/release commands
asynchronously and avoid deadlocks with playback thread.

- Track terminated status is not a state anymore. This condition is othogonal
to state to permitted state transitions while terminated.

Change-Id: Id157f4b3277620568d8eace7535d9186602564de
parent fc5ea083
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -291,6 +291,11 @@ public:
    virtual uint32_t    getUnderrunFrames() const {
        return mCblk->u.mStreaming.mUnderrunFrames;
    }

    bool        clearStreamEndDone();   // and return previous value

    bool        getStreamEndDone() const;

};

class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -405,6 +410,8 @@ public:
    // should avoid doing a state queue poll from within framesReady().
    // FIXME Change AudioFlinger to not call framesReady() from normal mixer thread.
    virtual void        framesReadyIsCalledByMultipleThreads() { }

    bool     setStreamEndDone();    // and return previous value
};

class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
+18 −0
Original line number Diff line number Diff line
@@ -322,6 +322,14 @@ void AudioTrackClientProxy::flush()
    mCblk->u.mStreaming.mFlush++;
}

bool AudioTrackClientProxy::clearStreamEndDone() {
    return android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE;
}

bool AudioTrackClientProxy::getStreamEndDone() const {
    return (mCblk->flags & CBLK_STREAM_END_DONE) != 0;
}

// ---------------------------------------------------------------------------

StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers,
@@ -524,6 +532,16 @@ size_t AudioTrackServerProxy::framesReady()
    return filled;
}

bool  AudioTrackServerProxy::setStreamEndDone() {
    bool old =
            (android_atomic_or(CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0;
    if (!old) {
        (void) __futex_syscall3(&mCblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
                1);
    }
    return old;
}

// ---------------------------------------------------------------------------

StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
+27 −5
Original line number Diff line number Diff line
@@ -1423,13 +1423,15 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
    audio_stream_out_t *outStream = NULL;
    AudioHwDevice *outHwDev;

    ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
    ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x",
              module,
              (pDevices != NULL) ? *pDevices : 0,
              config.sample_rate,
              config.format,
              config.channel_mask,
              flags);
    ALOGV("openOutput(), offloadInfo %p version 0x%04x",
          offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version );

    if (pDevices == NULL || *pDevices == 0) {
        return 0;
@@ -1454,7 +1456,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
                                          &outStream);

    mHardwareStatus = AUDIO_HW_IDLE;
    ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, "
    ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %#08x, "
            "Channels %x, status %d",
            outStream,
            config.sample_rate,
@@ -1463,9 +1465,12 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
            status);

    if (status == NO_ERROR && outStream != NULL) {
        AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
        AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags);

        if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
        if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
            thread = new OffloadThread(this, output, id, *pDevices);
            ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
        } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
            (config.format != AUDIO_FORMAT_PCM_16_BIT) ||
            (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
            thread = new DirectOutputThread(this, output, id, *pDevices);
@@ -1555,11 +1560,28 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)
                    DuplicatingThread *dupThread =
                            (DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
                    dupThread->removeOutputTrack((MixerThread *)thread.get());

                }
            }
        }
        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);


        mPlaybackThreads.removeItem(output);
        // save all effects to the default thread
        if (mPlaybackThreads.size()) {
            PlaybackThread *dstThread = checkPlaybackThread_l(mPlaybackThreads.keyAt(0));
            if (dstThread != NULL) {
                // audioflinger lock is held here so the acquisition order of thread locks does not
                // matter
                Mutex::Autolock _dl(dstThread->mLock);
                Mutex::Autolock _sl(thread->mLock);
                Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
                for (size_t i = 0; i < effectChains.size(); i ++) {
                    moveEffectChain_l(effectChains[i]->sessionId(), thread.get(), dstThread, true);
                }
            }
        }
        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
    }
    thread->exit();
    // The thread entity (active unit of execution) is no longer running here,
+6 −2
Original line number Diff line number Diff line
@@ -365,7 +365,9 @@ private:
    class PlaybackThread;
    class MixerThread;
    class DirectOutputThread;
    class OffloadThread;
    class DuplicatingThread;
    class AsyncCallbackThread;
    class Track;
    class RecordTrack;
    class EffectModule;
@@ -432,6 +434,7 @@ private:
        void                stop_nonvirtual();
    };


              PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const;
              MixerThread *checkMixerThread_l(audio_io_handle_t output) const;
              RecordThread *checkRecordThread_l(audio_io_handle_t input) const;
@@ -498,11 +501,12 @@ private:
    struct AudioStreamOut {
        AudioHwDevice* const audioHwDev;
        audio_stream_out_t* const stream;
        audio_output_flags_t flags;

        audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }

        AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out) :
            audioHwDev(dev), stream(out) {}
        AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out, audio_output_flags_t flags) :
            audioHwDev(dev), stream(out), flags(flags) {}
    };

    struct AudioStreamIn {
+94 −16
Original line number Diff line number Diff line
@@ -70,10 +70,11 @@ AudioPolicyService::AudioPolicyService()
    Mutex::Autolock _l(mLock);

    // start tone playback thread
    mTonePlaybackThread = new AudioCommandThread(String8(""));
    mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
    // start audio commands thread
    mAudioCommandThread = new AudioCommandThread(String8("ApmCommand"));

    mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
    // start output activity command thread
    mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
    /* instantiate the audio policy manager */
    rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
    if (rc)
@@ -256,6 +257,15 @@ status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
        return NO_INIT;
    }
    ALOGV("stopOutput()");
    mOutputCommandThread->stopOutputCommand(output, stream, session);
    return NO_ERROR;
}

status_t  AudioPolicyService::doStopOutput(audio_io_handle_t output,
                                      audio_stream_type_t stream,
                                      int session)
{
    ALOGV("doStopOutput from tid %d", gettid());
    Mutex::Autolock _l(mLock);
    return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
}
@@ -266,6 +276,12 @@ void AudioPolicyService::releaseOutput(audio_io_handle_t output)
        return;
    }
    ALOGV("releaseOutput()");
    mOutputCommandThread->releaseOutputCommand(output);
}

void AudioPolicyService::doReleaseOutput(audio_io_handle_t output)
{
    ALOGV("doReleaseOutput from tid %d", gettid());
    Mutex::Autolock _l(mLock);
    mpAudioPolicy->release_output(mpAudioPolicy, output);
}
@@ -641,8 +657,9 @@ status_t AudioPolicyService::onTransact(

// -----------  AudioPolicyService::AudioCommandThread implementation ----------

AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
    : Thread(false), mName(name)
AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name,
                                                           const wp<AudioPolicyService>& service)
    : Thread(false), mName(name), mService(service)
{
    mpToneGenerator = NULL;
}
@@ -650,7 +667,7 @@ AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)

AudioPolicyService::AudioCommandThread::~AudioCommandThread()
{
    if (mName != "" && !mAudioCommands.isEmpty()) {
    if (!mAudioCommands.isEmpty()) {
        release_wake_lock(mName.string());
    }
    mAudioCommands.clear();
@@ -659,11 +676,7 @@ AudioPolicyService::AudioCommandThread::~AudioCommandThread()

void AudioPolicyService::AudioCommandThread::onFirstRef()
{
    if (mName != "") {
    run(mName.string(), ANDROID_PRIORITY_AUDIO);
    } else {
        run("AudioCommand", ANDROID_PRIORITY_AUDIO);
    }
}

bool AudioPolicyService::AudioCommandThread::threadLoop()
@@ -738,6 +751,32 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
                    }
                    delete data;
                    }break;
                case STOP_OUTPUT: {
                    StopOutputData *data = (StopOutputData *)command->mParam;
                    ALOGV("AudioCommandThread() processing stop output %d",
                            data->mIO);
                    sp<AudioPolicyService> svc = mService.promote();
                    if (svc == 0) {
                        break;
                    }
                    mLock.unlock();
                    svc->doStopOutput(data->mIO, data->mStream, data->mSession);
                    mLock.lock();
                    delete data;
                    }break;
                case RELEASE_OUTPUT: {
                    ReleaseOutputData *data = (ReleaseOutputData *)command->mParam;
                    ALOGV("AudioCommandThread() processing release output %d",
                            data->mIO);
                    sp<AudioPolicyService> svc = mService.promote();
                    if (svc == 0) {
                        break;
                    }
                    mLock.unlock();
                    svc->doReleaseOutput(data->mIO);
                    mLock.lock();
                    delete data;
                    }break;
                default:
                    ALOGW("AudioCommandThread() unknown command %d", command->mCommand);
                }
@@ -749,7 +788,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
            }
        }
        // release delayed commands wake lock
        if (mName != "" && mAudioCommands.isEmpty()) {
        if (mAudioCommands.isEmpty()) {
            release_wake_lock(mName.string());
        }
        ALOGV("AudioCommandThread() going to sleep");
@@ -893,17 +932,46 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume
    return status;
}

void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output,
                                                               audio_stream_type_t stream,
                                                               int session)
{
    AudioCommand *command = new AudioCommand();
    command->mCommand = STOP_OUTPUT;
    StopOutputData *data = new StopOutputData();
    data->mIO = output;
    data->mStream = stream;
    data->mSession = session;
    command->mParam = (void *)data;
    Mutex::Autolock _l(mLock);
    insertCommand_l(command);
    ALOGV("AudioCommandThread() adding stop output %d", output);
    mWaitWorkCV.signal();
}

void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output)
{
    AudioCommand *command = new AudioCommand();
    command->mCommand = RELEASE_OUTPUT;
    ReleaseOutputData *data = new ReleaseOutputData();
    data->mIO = output;
    command->mParam = (void *)data;
    Mutex::Autolock _l(mLock);
    insertCommand_l(command);
    ALOGV("AudioCommandThread() adding release output %d", output);
    mWaitWorkCV.signal();
}

// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
    ssize_t i;  // not size_t because i will count down to -1
    Vector <AudioCommand *> removedCommands;

    nsecs_t time = 0;
    command->mTime = systemTime() + milliseconds(delayMs);

    // acquire wake lock to make sure delayed commands are processed
    if (mName != "" && mAudioCommands.isEmpty()) {
    if (mAudioCommands.isEmpty()) {
        acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
    }

@@ -1060,7 +1128,17 @@ int AudioPolicyService::setVoiceVolume(float volume, int delayMs)

bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
{
    return false;   // stub function
    if (mpAudioPolicy == NULL) {
        ALOGV("mpAudioPolicy == NULL");
        return false;
    }

    if (mpAudioPolicy->is_offload_supported == NULL) {
        ALOGV("HAL does not implement is_offload_supported");
        return false;
    }

    return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info);
}

// ----------------------------------------------------------------------------
@@ -1404,7 +1482,7 @@ static audio_io_handle_t aps_open_output_on_module(void *service,
        return 0;
    }
    return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask,
                          pLatencyMs, flags);
                          pLatencyMs, flags, offloadInfo);
}

static audio_io_handle_t aps_open_dup_output(void *service,
Loading