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

Commit d5681bc9 authored by Glenn Kasten's avatar Glenn Kasten Committed by Android (Google) Code Review
Browse files

Merge "Start isolating control block accesses in a proxy"

parents 49d24d3f e3aa659e
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
namespace android {

class audio_track_cblk_t;
class AudioRecordClientProxy;

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

@@ -374,6 +375,7 @@ private:
    uint32_t                mUpdatePeriod;      // in ms

    // constant after constructor or set()
    uint32_t                mSampleRate;
    size_t                  mFrameCount;
    audio_format_t          mFormat;
    uint8_t                 mChannelCount;
@@ -393,6 +395,7 @@ private:

    int                     mPreviousPriority;          // before start()
    SchedPolicy             mPreviousSchedulingGroup;
    AudioRecordClientProxy* mProxy;
};

}; // namespace android
+3 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ namespace android {
// ----------------------------------------------------------------------------

class audio_track_cblk_t;
class AudioTrackClientProxy;

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

@@ -538,6 +539,7 @@ protected:

    float                   mVolume[2];
    float                   mSendLevel;
    uint32_t                mSampleRate;
    size_t                  mFrameCount;            // corresponds to current IAudioTrack
    size_t                  mReqFrameCount;         // frame count to request the next time a new
                                                    // IAudioTrack is needed
@@ -596,6 +598,7 @@ protected:
    bool                    mIsTimed;
    int                     mPreviousPriority;          // before start()
    SchedPolicy             mPreviousSchedulingGroup;
    AudioTrackClientProxy*  mProxy;
};

class TimedAudioTrack : public AudioTrack
+183 −45
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <sys/types.h>

#include <utils/threads.h>
#include <utils/Log.h>

namespace android {

@@ -38,9 +39,26 @@ namespace android {
#define CBLK_INVALID    0x04 // track buffer invalidated by AudioFlinger, need to re-create
#define CBLK_DISABLED   0x08 // track disabled by AudioFlinger due to underrun, need to re-start

struct AudioTrackSharedStreaming {
    // similar to NBAIO MonoPipe
    volatile int32_t mFront;
    volatile int32_t mRear;
};

// future
struct AudioTrackSharedStatic {
    int mReserved;
};

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

// Important: do not add any virtual methods, including ~
struct audio_track_cblk_t
{
                friend class Proxy;
                friend class AudioTrackClientProxy;
                friend class AudioRecordClientProxy;
                friend class ServerProxy;

    // The data members are grouped so that members accessed frequently and in the same context
    // are in the same line of data cache.
@@ -72,12 +90,13 @@ struct audio_track_cblk_t
                // For AudioTrack only, not used by AudioRecord.
private:
                uint32_t    mVolumeLR;
public:

                uint32_t    sampleRate;
                uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                            // or 0 == default. Write-only client, read-only server.

                uint8_t     mPad2;           // unused

public:
                // read-only for client, server writes once at initialization and is then read-only
                uint8_t     mName;           // normal tracks: track name, fast tracks: track index

@@ -94,65 +113,184 @@ public:

                // Cache line boundary (32 bytes)

#if 0
                union {
                    AudioTrackSharedStreaming   mStreaming;
                    AudioTrackSharedStatic      mStatic;
                    int                         mAlign[8];
                } u;

                // Cache line boundary (32 bytes)
#endif

                // Since the control block is always located in shared memory, this constructor
                // is only used for placement new().  It is never used for regular new() or stack.
                            audio_track_cblk_t();

                // called by client only, where client includes regular
                // AudioTrack and AudioFlinger::PlaybackThread::OutputTrack
                uint32_t    stepUserIn(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, false); }
                uint32_t    stepUserOut(size_t stepCount, size_t frameCount) { return stepUser(stepCount, frameCount, true); }

                bool        stepServer(size_t stepCount, size_t frameCount, bool isOut);

private:
                // if there is a shared buffer, "buffers" is the value of pointer() for the shared
                // buffer, otherwise "buffers" points immediately after the control block
                void*       buffer(void *buffers, uint32_t frameSize, uint32_t offset) const;

                uint32_t    framesAvailableIn(size_t frameCount)
                                { return framesAvailable(frameCount, false); }
                uint32_t    framesAvailableOut(size_t frameCount)
                                { return framesAvailable(frameCount, true); }
                uint32_t    framesAvailableIn_l(size_t frameCount)
                                { return framesAvailable_l(frameCount, false); }
                uint32_t    framesAvailableOut_l(size_t frameCount)
                                { return framesAvailable_l(frameCount, true); }
                uint32_t    framesReadyIn() { return framesReady(false); }
                uint32_t    framesReadyOut() { return framesReady(true); }
                void*       buffer(void *buffers, uint32_t frameSize, size_t offset) const;

                bool        tryLock();

                // isOut == true means AudioTrack, isOut == false means AudioRecord
                bool        stepServer(size_t stepCount, size_t frameCount, bool isOut);
                uint32_t    stepUser(size_t stepCount, size_t frameCount, bool isOut);
                uint32_t    framesAvailable(size_t frameCount, bool isOut);
                uint32_t    framesAvailable_l(size_t frameCount, bool isOut);
                uint32_t    framesReady(bool isOut);
};

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

// Proxy for shared memory control block, to isolate callers from needing to know the details.
// There is exactly one ClientProxy and one ServerProxy per shared memory control block.
// The proxies are located in normal memory, and are not multi-thread safe within a given side.
class Proxy {
protected:
    Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize) { }
    virtual ~Proxy() { }

public:
    void*   buffer(size_t offset) const {
        return mCblk->buffer(mBuffers, mFrameSize, offset);
    }

protected:
    // These refer to shared memory, and are virtual addresses with respect to the current process.
    // They may have different virtual addresses within the other process.
    audio_track_cblk_t* const   mCblk;          // the control block
    void* const                 mBuffers;       // starting address of buffers

    const size_t                mFrameCount;    // not necessarily a power of 2
    const size_t                mFrameSize;     // in bytes
#if 0
    const size_t                mFrameCountP2;  // mFrameCount rounded to power of 2, streaming mode
#endif

};

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

// Proxy seen by AudioTrack client and AudioRecord client
class ClientProxy : public Proxy {
protected:
    ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : Proxy(cblk, buffers, frameCount, frameSize) { }
    virtual ~ClientProxy() { }
};

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

// Proxy used by AudioTrack client, which also includes AudioFlinger::PlaybackThread::OutputTrack
class AudioTrackClientProxy : public ClientProxy {
public:
    AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : ClientProxy(cblk, buffers, frameCount, frameSize) { }
    virtual ~AudioTrackClientProxy() { }

    // No barriers on the following operations, so the ordering of loads/stores
    // with respect to other parameters is UNPREDICTABLE. That's considered safe.

                // for AudioTrack client only, caller must limit to 0.0 <= sendLevel <= 1.0
    // caller must limit to 0.0 <= sendLevel <= 1.0
    void        setSendLevel(float sendLevel) {
                    mSendLevel = uint16_t(sendLevel * 0x1000);
        mCblk->mSendLevel = uint16_t(sendLevel * 0x1000);
    }

                // for AudioFlinger only; the return value must be validated by the caller
                uint16_t    getSendLevel_U4_12() const {
                    return mSendLevel;
    // caller must limit to 0 <= volumeLR <= 0x10001000
    void        setVolumeLR(uint32_t volumeLR) {
        mCblk->mVolumeLR = volumeLR;
    }

                // for AudioTrack client only, caller must limit to 0 <= volumeLR <= 0x10001000
                void        setVolumeLR(uint32_t volumeLR) {
                    mVolumeLR = volumeLR;
    void        setSampleRate(uint32_t sampleRate) {
        mCblk->mSampleRate = sampleRate;
    }

                // for AudioFlinger only; the return value must be validated by the caller
                uint32_t    getVolumeLR() const {
                    return mVolumeLR;
    // called by:
    //   PlaybackThread::OutputTrack::write
    //   AudioTrack::createTrack_l
    //   AudioTrack::releaseBuffer
    //   AudioTrack::reload
    //   AudioTrack::restoreTrack_l (2 places)
    size_t      stepUser(size_t stepCount) {
        return mCblk->stepUser(stepCount, mFrameCount, true /*isOut*/);
    }

    // called by AudioTrack::obtainBuffer and AudioTrack::processBuffer
    size_t      framesAvailable() {
        return mCblk->framesAvailable(mFrameCount, true /*isOut*/);
    }

    // called by AudioTrack::obtainBuffer and PlaybackThread::OutputTrack::obtainBuffer
    // FIXME remove this API since it assumes a lock that should be invisible to caller
    size_t      framesAvailable_l() {
        return mCblk->framesAvailable_l(mFrameCount, true /*isOut*/);
    }

private:
                // isOut == true means AudioTrack, isOut == false means AudioRecord
                uint32_t    stepUser(size_t stepCount, size_t frameCount, bool isOut);
                uint32_t    framesAvailable(size_t frameCount, bool isOut);
                uint32_t    framesAvailable_l(size_t frameCount, bool isOut);
                uint32_t    framesReady(bool isOut);
};

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

// Proxy used by AudioRecord client
class AudioRecordClientProxy : public ClientProxy {
public:
    AudioRecordClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
        : ClientProxy(cblk, buffers, frameCount, frameSize) { }
    ~AudioRecordClientProxy() { }

    // called by AudioRecord::releaseBuffer
    size_t      stepUser(size_t stepCount) {
        return mCblk->stepUser(stepCount, mFrameCount, false /*isOut*/);
    }

    // called by AudioRecord::processBuffer
    size_t      framesAvailable() {
        return mCblk->framesAvailable(mFrameCount, false /*isOut*/);
    }

    // called by AudioRecord::obtainBuffer
    size_t      framesReady() {
        return mCblk->framesReady(false /*isOut*/);
    }

};

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

// Proxy used by AudioFlinger server
class ServerProxy : public Proxy {
public:
    ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut)
        : Proxy(cblk, buffers, frameCount, frameSize), mIsOut(isOut) { }
    virtual ~ServerProxy() { }

    // for AudioTrack and AudioRecord
    bool        step(size_t stepCount) { return mCblk->stepServer(stepCount, mFrameCount, mIsOut); }

    // return value of these methods must be validated by the caller
    uint32_t    getSampleRate() const { return mCblk->mSampleRate; }
    uint16_t    getSendLevel_U4_12() const { return mCblk->mSendLevel; }
    uint32_t    getVolumeLR() const { return mCblk->mVolumeLR; }

    // for AudioTrack only
    size_t      framesReady() {
        ALOG_ASSERT(mIsOut);
        return mCblk->framesReady(true);
    }

    // for AudioRecord only, called by RecordThread::RecordTrack::getNextBuffer
    // FIXME remove this API since it assumes a lock that should be invisible to caller
    size_t      framesAvailableIn_l() {
        ALOG_ASSERT(!mIsOut);
        return mCblk->framesAvailable_l(mFrameCount, false);
    }

private:
    const bool  mIsOut;     // true for AudioTrack, false for AudioRecord

};

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

+31 −16
Original line number Diff line number Diff line
@@ -75,7 +75,8 @@ status_t AudioRecord::getMinFrameCount(

AudioRecord::AudioRecord()
    : mStatus(NO_INIT), mSessionId(0),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
      mProxy(NULL)
{
}

@@ -90,7 +91,9 @@ AudioRecord::AudioRecord(
        int notificationFrames,
        int sessionId)
    : mStatus(NO_INIT), mSessionId(0),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mProxy(NULL)
{
    mStatus = set(inputSource, sampleRate, format, channelMask,
            frameCount, cbf, user, notificationFrames, sessionId);
@@ -112,6 +115,7 @@ AudioRecord::~AudioRecord()
        IPCThreadState::self()->flushCommands();
        AudioSystem::releaseAudioSessionId(mSessionId);
    }
    delete mProxy;
}

status_t AudioRecord::set(
@@ -149,6 +153,8 @@ status_t AudioRecord::set(
    if (sampleRate == 0) {
        sampleRate = DEFAULT_SAMPLE_RATE;
    }
    mSampleRate = sampleRate;

    // these below should probably come from the audioFlinger too...
    if (format == AUDIO_FORMAT_DEFAULT) {
        format = AUDIO_FORMAT_PCM_16_BIT;
@@ -166,6 +172,12 @@ status_t AudioRecord::set(
    uint32_t channelCount = popcount(channelMask);
    mChannelCount = channelCount;

    if (audio_is_linear_pcm(mFormat)) {
        mFrameSize = channelCount * audio_bytes_per_sample(format);
    } else {
        mFrameSize = sizeof(uint8_t);
    }

    if (sessionId == 0 ) {
        mSessionId = AudioSystem::newAudioSessionId();
    } else {
@@ -218,12 +230,6 @@ status_t AudioRecord::set(
    // Update buffer size in case it has been limited by AudioFlinger during track creation
    mFrameCount = mCblk->frameCount_;

    if (audio_is_linear_pcm(mFormat)) {
        mFrameSize = channelCount * audio_bytes_per_sample(format);
    } else {
        mFrameSize = sizeof(uint8_t);
    }

    mActive = false;
    mCbf = cbf;
    mNotificationFrames = notificationFrames;
@@ -360,7 +366,7 @@ bool AudioRecord::stopped() const

uint32_t AudioRecord::getSampleRate() const
{
    return mCblk->sampleRate;
    return mSampleRate;
}

status_t AudioRecord::setMarkerPosition(uint32_t marker)
@@ -473,11 +479,18 @@ status_t AudioRecord::openRecord_l(
    mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
    cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
    cblk->waitTimeMs = 0;

    // update proxy
    delete mProxy;
    mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize);

    return NO_ERROR;
}

status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
{
    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);

    AutoMutex lock(mLock);
    bool active;
    status_t result = NO_ERROR;
@@ -488,7 +501,7 @@ status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
    audioBuffer->frameCount  = 0;
    audioBuffer->size        = 0;

    uint32_t framesReady = cblk->framesReadyIn();
    size_t framesReady = mProxy->framesReady();

    if (framesReady == 0) {
        cblk->lock.lock();
@@ -551,7 +564,7 @@ create_new_record:
            }
            // read the server count again
        start_loop_here:
            framesReady = cblk->framesReadyIn();
            framesReady = mProxy->framesReady();
        }
        cblk->lock.unlock();
    }
@@ -573,15 +586,17 @@ create_new_record:

    audioBuffer->frameCount  = framesReq;
    audioBuffer->size        = framesReq * mFrameSize;
    audioBuffer->raw         = cblk->buffer(mBuffers, mFrameSize, u);
    audioBuffer->raw         = mProxy->buffer(u);
    active = mActive;
    return active ? status_t(NO_ERROR) : status_t(STOPPED);
}

void AudioRecord::releaseBuffer(Buffer* audioBuffer)
{
    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);

    AutoMutex lock(mLock);
    mCblk->stepUserIn(audioBuffer->frameCount, mFrameCount);
    (void) mProxy->stepUser(audioBuffer->frameCount);
}

audio_io_handle_t AudioRecord::getInput() const
@@ -594,7 +609,7 @@ audio_io_handle_t AudioRecord::getInput() const
audio_io_handle_t AudioRecord::getInput_l()
{
    mInput = AudioSystem::getInput(mInputSource,
                                mCblk->sampleRate,
                                mSampleRate,
                                mFormat,
                                mChannelMask,
                                mSessionId);
@@ -746,7 +761,7 @@ bool AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread)


    // Manage overrun callback
    if (active && (cblk->framesAvailableIn(mFrameCount) == 0)) {
    if (active && (mProxy->framesAvailable() == 0)) {
        // The value of active is stale, but we are almost sure to be active here because
        // otherwise we would have exited when obtainBuffer returned STOPPED earlier.
        ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
@@ -782,7 +797,7 @@ status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk)
    // 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, mFrameCount, getInput_l());
    result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l());
    if (result == NO_ERROR) {
        newCblk = mCblk;
        // callback thread or sync event hasn't changed
+71 −38

File changed.

Preview size limit exceeded, changes collapsed.

Loading