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

Commit 83b8808f authored by Eric Laurent's avatar Eric Laurent
Browse files

audio flinger: add patch connection between hw modules

Add support for audio device connections between different audio
hw modules.
The patch is performed by creating a bridge between the playback
thread connected to the sink device and the record thread connected
to the source device using a pair of specialized PlaybackTrack and
RecordTrack.
- Added PatchTrack and PatchRecord classes.
- Added TrackBase type to indicate more clearly the track behavior.
- A TrackBase can allocate the buffer or reuse an existing one.
- Factored some code in openOutput() and openInput() for internal use
by PatchPanel.

Bug: 14815883.

Change-Id: Ib9515fcda864610458a4bc81fa8f59096ff4d7db
parent c15c2656
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -175,12 +175,11 @@ protected:

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

public:
    static const struct timespec kForever;
    static const struct timespec kNonBlocking;

@@ -394,8 +393,10 @@ protected:
class AudioTrackServerProxy : public ServerProxy {
public:
    AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
            size_t frameSize, bool clientInServer = false)
        : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) { }
            size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
        : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) {
        mCblk->mSampleRate = sampleRate;
    }
protected:
    virtual ~AudioTrackServerProxy() { }

@@ -458,9 +459,8 @@ private:
class AudioRecordServerProxy : public ServerProxy {
public:
    AudioRecordServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
            size_t frameSize)
        : ServerProxy(cblk, buffers, frameCount, frameSize, false /*isOut*/,
            false /*clientInServer*/) { }
            size_t frameSize, bool clientInServer)
        : ServerProxy(cblk, buffers, frameCount, frameSize, false /*isOut*/, clientInServer) { }
protected:
    virtual ~AudioRecordServerProxy() { }
};
+140 −87
Original line number Diff line number Diff line
@@ -1531,7 +1531,7 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
    }

    audio_module_handle_t handle = nextUniqueId();
    mAudioHwDevs.add(handle, new AudioHwDevice(name, dev, flags));
    mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));

    ALOGI("loadHwModule() Loaded %s audio interface from %s (%s) handle %d",
          name, dev->common.module->name, dev->common.module->id, handle);
@@ -1575,41 +1575,13 @@ status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice)

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

audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
                                           audio_devices_t *pDevices,
                                           uint32_t *pSamplingRate,
                                           audio_format_t *pFormat,
                                           audio_channel_mask_t *pChannelMask,
                                           uint32_t *pLatencyMs,
                                           audio_output_flags_t flags,
                                           const audio_offload_info_t *offloadInfo)
{
    struct audio_config config;
    memset(&config, 0, sizeof(config));
    config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
    config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0;
    config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT;
    if (offloadInfo != NULL) {
        config.offload_info = *offloadInfo;
    }

    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 == AUDIO_DEVICE_NONE) {
        return AUDIO_IO_HANDLE_NONE;
    }

    Mutex::Autolock _l(mLock);

    AudioHwDevice *outHwDev = findSuitableHwDev_l(module, *pDevices);
sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_t module,
                                                            audio_devices_t device,
                                                            struct audio_config *config,
                                                            audio_output_flags_t flags)
{
    AudioHwDevice *outHwDev = findSuitableHwDev_l(module, device);
    if (outHwDev == NULL) {
        return AUDIO_IO_HANDLE_NONE;
    }
@@ -1635,18 +1607,18 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,

    status_t status = hwDevHal->open_output_stream(hwDevHal,
                                          id,
                                          *pDevices,
                                          (audio_output_flags_t)flags,
                                          &config,
                                          device,
                                          flags,
                                          config,
                                          &outStream);

    mHardwareStatus = AUDIO_HW_IDLE;
    ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %#08x, "
    ALOGV("openOutput_l() openOutputStream returned output %p, SamplingRate %d, Format %#08x, "
            "Channels %x, status %d",
            outStream,
            config.sample_rate,
            config.format,
            config.channel_mask,
            config->sample_rate,
            config->format,
            config->channel_mask,
            status);

    if (status == NO_ERROR && outStream != NULL) {
@@ -1654,19 +1626,60 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,

        PlaybackThread *thread;
        if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
            thread = new OffloadThread(this, output, id, *pDevices);
            thread = new OffloadThread(this, output, id, device);
            ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
        } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
                || !isValidPcmSinkFormat(config.format)
                || (config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
            thread = new DirectOutputThread(this, output, id, *pDevices);
                || !isValidPcmSinkFormat(config->format)
                || (config->channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
            thread = new DirectOutputThread(this, output, id, device);
            ALOGV("openOutput() created direct output: ID %d thread %p", id, thread);
        } else {
            thread = new MixerThread(this, output, id, *pDevices);
            thread = new MixerThread(this, output, id, device);
            ALOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
        }
        mPlaybackThreads.add(id, thread);
        return thread;
    }

    return 0;
}

audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
                                           audio_devices_t *pDevices,
                                           uint32_t *pSamplingRate,
                                           audio_format_t *pFormat,
                                           audio_channel_mask_t *pChannelMask,
                                           uint32_t *pLatencyMs,
                                           audio_output_flags_t flags,
                                           const audio_offload_info_t *offloadInfo)
{
    struct audio_config config;
    memset(&config, 0, sizeof(config));
    config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
    config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0;
    config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT;
    if (offloadInfo != NULL) {
        config.offload_info = *offloadInfo;
    }

    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 == AUDIO_DEVICE_NONE) {
        return AUDIO_IO_HANDLE_NONE;
    }

    Mutex::Autolock _l(mLock);

    sp<PlaybackThread> thread = openOutput_l(module, *pDevices, &config, flags);
    if (thread != 0) {
        if (pSamplingRate != NULL) {
            *pSamplingRate = config.sample_rate;
        }
@@ -1686,16 +1699,16 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
        // the first primary output opened designates the primary hw device
        if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
            ALOGI("Using module %d has the primary audio interface", module);
            mPrimaryHardwareDev = outHwDev;
            mPrimaryHardwareDev = thread->getOutput()->audioHwDev;

            AutoMutex lock(mHardwareLock);
            mHardwareStatus = AUDIO_HW_SET_MODE;
            hwDevHal->set_mode(hwDevHal, mMode);
            mPrimaryHardwareDev->hwDevice()->set_mode(mPrimaryHardwareDev->hwDevice(), mMode);
            mHardwareStatus = AUDIO_HW_IDLE;

            mPrimaryOutputSampleRate = config.sample_rate;
        }
        return id;
        return thread->id();
    }

    return AUDIO_IO_HANDLE_NONE;
@@ -1776,13 +1789,27 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)
    // but the ThreadBase container still exists.

    if (thread->type() != ThreadBase::DUPLICATING) {
        closeOutputFinish(thread);
    }

    thread.clear();
    return NO_ERROR;
}

void AudioFlinger::closeOutputFinish(sp<PlaybackThread> thread)
{
    AudioStreamOut *out = thread->clearOutput();
    ALOG_ASSERT(out != NULL, "out shouldn't be NULL");
    // from now on thread->mOutput is NULL
    out->hwDev()->close_output_stream(out->hwDev(), out->stream);
    delete out;
}
    return NO_ERROR;

void AudioFlinger::closeOutputInternal_l(sp<PlaybackThread> thread)
{
    mPlaybackThreads.removeItem(thread->mId);
    thread->exit();
    closeOutputFinish(thread);
}

status_t AudioFlinger::suspendOutput(audio_io_handle_t output)
@@ -1823,6 +1850,12 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
                                          audio_channel_mask_t *pChannelMask,
                                          audio_input_flags_t flags)
{
    Mutex::Autolock _l(mLock);

    if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) {
        return AUDIO_IO_HANDLE_NONE;
    }

    struct audio_config config;
    memset(&config, 0, sizeof(config));
    config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
@@ -1833,13 +1866,36 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
    audio_format_t reqFormat = config.format;
    audio_channel_mask_t reqChannelMask = config.channel_mask;

    if (pDevices == NULL || *pDevices == AUDIO_DEVICE_NONE) {
        return 0;
    sp<RecordThread> thread = openInput_l(module, *pDevices, &config, flags);

    if (thread != 0) {
        if (pSamplingRate != NULL) {
            *pSamplingRate = reqSamplingRate;
        }
        if (pFormat != NULL) {
            *pFormat = config.format;
        }
        if (pChannelMask != NULL) {
            *pChannelMask = reqChannelMask;
        }

    Mutex::Autolock _l(mLock);
        // notify client processes of the new input creation
        thread->audioConfigChanged(AudioSystem::INPUT_OPENED);
        return thread->id();
    }
    return AUDIO_IO_HANDLE_NONE;
}

    AudioHwDevice *inHwDev = findSuitableHwDev_l(module, *pDevices);
sp<AudioFlinger::RecordThread> AudioFlinger::openInput_l(audio_module_handle_t module,
                                                         audio_devices_t device,
                                                         struct audio_config *config,
                                                         audio_input_flags_t flags)
{
    uint32_t reqSamplingRate = config->sample_rate;
    audio_format_t reqFormat = config->format;
    audio_channel_mask_t reqChannelMask = config->channel_mask;

    AudioHwDevice *inHwDev = findSuitableHwDev_l(module, device);
    if (inHwDev == NULL) {
        return 0;
    }
@@ -1848,14 +1904,14 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
    audio_io_handle_t id = nextUniqueId();

    audio_stream_in_t *inStream = NULL;
    status_t status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config,
    status_t status = inHwHal->open_input_stream(inHwHal, id, device, config,
                                        &inStream, flags);
    ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %#x, Channels %x, "
            "flags %#x, status %d",
            inStream,
            config.sample_rate,
            config.format,
            config.channel_mask,
            config->sample_rate,
            config->format,
            config->channel_mask,
            flags,
            status);

@@ -1863,14 +1919,14 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
    // conversion internally, try to open again with the proposed parameters. The AudioFlinger can
    // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs.
    if (status == BAD_VALUE &&
        reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
        (config.sample_rate <= 2 * reqSamplingRate) &&
        (audio_channel_count_from_in_mask(config.channel_mask) <= FCC_2) &&
        reqFormat == config->format && config->format == AUDIO_FORMAT_PCM_16_BIT &&
        (config->sample_rate <= 2 * reqSamplingRate) &&
        (audio_channel_count_from_in_mask(config->channel_mask) <= FCC_2) &&
        (audio_channel_count_from_in_mask(reqChannelMask) <= FCC_2)) {
        // FIXME describe the change proposed by HAL (save old values so we can log them here)
        ALOGV("openInput() reopening with proposed sampling rate and channel mask");
        inStream = NULL;
        status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config, &inStream, flags);
        status = inHwHal->open_input_stream(inHwHal, id, device, config, &inStream, flags);
        // FIXME log this new status; HAL should not propose any further changes
    }

@@ -1931,30 +1987,18 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
        // Start record thread
        // RecordThread requires both input and output device indication to forward to audio
        // pre processing modules
        RecordThread *thread = new RecordThread(this,
        sp<RecordThread> thread = new RecordThread(this,
                                  input,
                                  id,
                                  primaryOutputDevice_l(),
                                  *pDevices
                                  device
#ifdef TEE_SINK
                                  , teeSink
#endif
                                  );
        mRecordThreads.add(id, thread);
        ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
        if (pSamplingRate != NULL) {
            *pSamplingRate = reqSamplingRate;
        }
        if (pFormat != NULL) {
            *pFormat = config.format;
        }
        if (pChannelMask != NULL) {
            *pChannelMask = reqChannelMask;
        }

        // notify client processes of the new input creation
        thread->audioConfigChanged(AudioSystem::INPUT_OPENED);
        return id;
        ALOGV("openInput() created record thread: ID %d thread %p", id, thread.get());
        return thread;
    }

    return 0;
@@ -1981,17 +2025,26 @@ status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input)
        audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
        mRecordThreads.removeItem(input);
    }
    thread->exit();
    // The thread entity (active unit of execution) is no longer running here,
    // but the ThreadBase container still exists.
    // FIXME: calling thread->exit() without mLock held should not be needed anymore now that
    // we have a different lock for notification client
    closeInputFinish(thread);
    return NO_ERROR;
}

void AudioFlinger::closeInputFinish(sp<RecordThread> thread)
{
    thread->exit();
    AudioStreamIn *in = thread->clearInput();
    ALOG_ASSERT(in != NULL, "in shouldn't be NULL");
    // from now on thread->mInput is NULL
    in->hwDev()->close_input_stream(in->hwDev(), in->stream);
    delete in;
}

    return NO_ERROR;
void AudioFlinger::closeInputInternal_l(sp<RecordThread> thread)
{
    mRecordThreads.removeItem(thread->mId);
    closeInputFinish(thread);
}

status_t AudioFlinger::invalidateStream(audio_stream_type_t stream)
+19 −2
Original line number Diff line number Diff line
@@ -489,6 +489,18 @@ private:
              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;
              sp<RecordThread> openInput_l(audio_module_handle_t module,
                                           audio_devices_t device,
                                           struct audio_config *config,
                                           audio_input_flags_t flags);
              sp<PlaybackThread> openOutput_l(audio_module_handle_t module,
                                              audio_devices_t device,
                                              struct audio_config *config,
                                              audio_output_flags_t flags);

              void closeOutputFinish(sp<PlaybackThread> thread);
              void closeInputFinish(sp<RecordThread> thread);

              // no range check, AudioFlinger::mLock held
              bool streamMute_l(audio_stream_type_t stream) const
                                { return mStreamTypes[stream].mute; }
@@ -530,10 +542,11 @@ private:
            AHWD_CAN_SET_MASTER_MUTE    = 0x2,
        };

        AudioHwDevice(const char *moduleName,
        AudioHwDevice(audio_module_handle_t handle,
                      const char *moduleName,
                      audio_hw_device_t *hwDevice,
                      Flags flags)
            : mModuleName(strdup(moduleName))
            : mHandle(handle), mModuleName(strdup(moduleName))
            , mHwDevice(hwDevice)
            , mFlags(flags) { }
        /*virtual*/ ~AudioHwDevice() { free((void *)mModuleName); }
@@ -546,11 +559,13 @@ private:
            return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE));
        }

        audio_module_handle_t handle() const { return mHandle; }
        const char *moduleName() const { return mModuleName; }
        audio_hw_device_t *hwDevice() const { return mHwDevice; }
        uint32_t version() const { return mHwDevice->common.version; }

    private:
        audio_module_handle_t mHandle;
        const char * const mModuleName;
        audio_hw_device_t * const mHwDevice;
        const Flags mFlags;
@@ -669,7 +684,9 @@ private:

    // for use from destructor
    status_t    closeOutput_nonvirtual(audio_io_handle_t output);
    void        closeOutputInternal_l(sp<PlaybackThread> thread);
    status_t    closeInput_nonvirtual(audio_io_handle_t input);
    void        closeInputInternal_l(sp<RecordThread> thread);

#ifdef TEE_SINK
    // all record threads serially share a common tee sink, which is re-created on format change
+288 −64

File changed.

Preview size limit exceeded, changes collapsed.

+24 −6
Original line number Diff line number Diff line
@@ -21,6 +21,9 @@

class PatchPanel : public RefBase {
public:

    class Patch;

    PatchPanel(const sp<AudioFlinger>& audioFlinger);
    virtual ~PatchPanel();

@@ -45,15 +48,30 @@ public:
    /* Set audio port configuration */
    status_t setAudioPortConfig(const struct audio_port_config *config);

    status_t createPatchConnections(Patch *patch,
                                    const struct audio_patch *audioPatch);
    void clearPatchConnections(Patch *patch);

    class Patch {
    public:
        Patch(const struct audio_patch *patch) :
            mAudioPatch(*patch), mHandle(0), mHalHandle(0) {}
            mAudioPatch(*patch), mHandle(AUDIO_PATCH_HANDLE_NONE),
            mHalHandle(AUDIO_PATCH_HANDLE_NONE), mRecordPatchHandle(AUDIO_PATCH_HANDLE_NONE),
            mPlaybackPatchHandle(AUDIO_PATCH_HANDLE_NONE) {}
        ~Patch() {}

        struct audio_patch              mAudioPatch;
        audio_patch_handle_t            mHandle;
        audio_patch_handle_t            mHalHandle;
        sp<PlaybackThread>              mPlaybackThread;
        sp<PlaybackThread::PatchTrack>  mPatchTrack;
        sp<RecordThread>                mRecordThread;
        sp<RecordThread::PatchRecord>   mPatchRecord;
        audio_patch_handle_t            mRecordPatchHandle;
        audio_patch_handle_t            mPlaybackPatchHandle;

    };

private:
    const wp<AudioFlinger>      mAudioFlinger;
    SortedVector <Patch *>      mPatches;
Loading