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

Commit 9f57d3bd authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "PatchPanel: store "downstream" patches for a device"

parents 5681175a adca70f3
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -1817,6 +1817,10 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)

        mHardwareStatus = AUDIO_HW_IDLE;
    }
    if (strcmp(name, AUDIO_HAL_SERVICE_NAME_MSD) == 0) {
        // An MSD module is inserted before hardware modules in order to mix encoded streams.
        flags = static_cast<AudioHwDevice::Flags>(flags | AudioHwDevice::AHWD_IS_INSERT);
    }

    audio_module_handle_t handle = (audio_module_handle_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_MODULE);
    mAudioHwDevs.add(handle, new AudioHwDevice(handle, name, dev, flags));
@@ -2096,6 +2100,7 @@ sp<AudioFlinger::ThreadBase> AudioFlinger::openOutput_l(audio_module_handle_t mo
                      *output, thread.get());
            }
            mPlaybackThreads.add(*output, thread);
            mPatchPanel.notifyStreamOpened(outHwDev, *output);
            return thread;
        }
    }
@@ -2231,6 +2236,7 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)
        const sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
        ioDesc->mIoHandle = output;
        ioConfigChanged(AUDIO_OUTPUT_CLOSED, ioDesc);
        mPatchPanel.notifyStreamClosed(output);
    }
    // The thread entity (active unit of execution) is no longer running here,
    // but the ThreadBase container still exists.
+7 −0
Original line number Diff line number Diff line
@@ -35,6 +35,9 @@ public:
    enum Flags {
        AHWD_CAN_SET_MASTER_VOLUME  = 0x1,
        AHWD_CAN_SET_MASTER_MUTE    = 0x2,
        // Means that this isn't a terminal module, and software patches
        // are used to transport audio data further.
        AHWD_IS_INSERT              = 0x4,
    };

    AudioHwDevice(audio_module_handle_t handle,
@@ -55,6 +58,10 @@ public:
        return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE));
    }

    bool isInsert() const {
        return (0 != (mFlags & AHWD_IS_INSERT));
    }

    audio_module_handle_t handle() const { return mHandle; }
    const char *moduleName() const { return mModuleName; }
    sp<DeviceHalInterface> hwDevice() const { return mHwDevice; }
+115 −23
Original line number Diff line number Diff line
@@ -83,6 +83,16 @@ status_t AudioFlinger::listAudioPatches(unsigned int *num_patches,
    return mPatchPanel.listAudioPatches(num_patches, patches);
}

status_t AudioFlinger::PatchPanel::SoftwarePatch::getLatencyMs_l(double *latencyMs) const
{
    const auto& iter = mPatchPanel.mPatches.find(mPatchHandle);
    if (iter != mPatchPanel.mPatches.end()) {
        return iter->second.getLatencyMs(latencyMs);
    } else {
        return BAD_VALUE;
    }
}

/* List connected audio ports and their attributes */
status_t AudioFlinger::PatchPanel::listAudioPorts(unsigned int *num_ports __unused,
                                struct audio_port *ports __unused)
@@ -159,21 +169,21 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
                }
            }
            mPatches.erase(iter);
            removeSoftwarePatchFromInsertedModules(*handle);
        }
    }

    Patch newPatch{*patch};
    audio_module_handle_t insertedModule = AUDIO_MODULE_HANDLE_NONE;

    switch (patch->sources[0].type) {
        case AUDIO_PORT_TYPE_DEVICE: {
            audio_module_handle_t srcModule = patch->sources[0].ext.device.hw_module;
            ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(srcModule);
            if (index < 0) {
                ALOGW("%s() bad src hw module %d", __func__, srcModule);
            AudioHwDevice *audioHwDevice = findAudioHwDeviceByModule(srcModule);
            if (!audioHwDevice) {
                status = BAD_VALUE;
                goto exit;
            }
            AudioHwDevice *audioHwDevice = mAudioFlinger.mAudioHwDevs.valueAt(index);
            for (unsigned int i = 0; i < patch->num_sinks; i++) {
                // support only one sink if connection to a mix or across HW modules
                if ((patch->sinks[i].type == AUDIO_PORT_TYPE_MIX ||
@@ -285,6 +295,9 @@ status_t AudioFlinger::PatchPanel::createAudioPatch(const struct audio_patch *pa
                if (status != NO_ERROR) {
                    goto exit;
                }
                if (audioHwDevice->isInsert()) {
                    insertedModule = audioHwDevice->handle();
                }
            } else {
                if (patch->sinks[0].type == AUDIO_PORT_TYPE_MIX) {
                    sp<ThreadBase> thread = mAudioFlinger.checkRecordThread_l(
@@ -364,6 +377,9 @@ exit:
        *handle = (audio_patch_handle_t) mAudioFlinger.nextUniqueId(AUDIO_UNIQUE_ID_USE_PATCH);
        newPatch.mHalHandle = halHandle;
        mPatches.insert(std::make_pair(*handle, std::move(newPatch)));
        if (insertedModule != AUDIO_MODULE_HANDLE_NONE) {
            addSoftwarePatchToInsertedModules(insertedModule, *handle);
        }
        ALOGV("%s() added new patch handle %d halHandle %d", __func__, *handle, halHandle);
    } else {
        newPatch.clearConnections(this);
@@ -511,21 +527,17 @@ status_t AudioFlinger::PatchPanel::Patch::getLatencyMs(double *latencyMs) const
    return OK;
}

String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle)
String8 AudioFlinger::PatchPanel::Patch::dump(audio_patch_handle_t myHandle) const
{
    String8 result;

    // TODO: Consider table dump form for patches, just like tracks.
    result.appendFormat("Patch %d: thread %p => thread %p",
            myHandle, mRecord.thread().get(), mPlayback.thread().get());
    String8 result = String8::format("Patch %d: thread %p => thread %p",
            myHandle, mRecord.const_thread().get(), mPlayback.const_thread().get());

    // add latency if it exists
    double latencyMs;
    if (getLatencyMs(&latencyMs) == OK) {
        result.appendFormat("  latency: %.2lf", latencyMs);
    }

    result.append("\n");
    return result;
}

@@ -596,6 +608,7 @@ status_t AudioFlinger::PatchPanel::releaseAudioPatch(audio_patch_handle_t handle
    }

    mPatches.erase(iter);
    removeSoftwarePatchFromInsertedModules(handle);
    return status;
}

@@ -607,35 +620,114 @@ status_t AudioFlinger::PatchPanel::listAudioPatches(unsigned int *num_patches __
    return NO_ERROR;
}

sp<DeviceHalInterface> AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module)
status_t AudioFlinger::PatchPanel::getDownstreamSoftwarePatches(
        audio_io_handle_t stream,
        std::vector<AudioFlinger::PatchPanel::SoftwarePatch> *patches) const
{
    for (const auto& module : mInsertedModules) {
        if (module.second.streams.count(stream)) {
            for (const auto& patchHandle : module.second.sw_patches) {
                const auto& patch_iter = mPatches.find(patchHandle);
                if (patch_iter != mPatches.end()) {
                    const Patch &patch = patch_iter->second;
                    patches->emplace_back(*this, patchHandle,
                            patch.mPlayback.const_thread()->id(),
                            patch.mRecord.const_thread()->id());
                } else {
                    ALOGE("Stale patch handle in the cache: %d", patchHandle);
                }
            }
            return OK;
        }
    }
    // The stream is not associated with any of inserted modules.
    return BAD_VALUE;
}

void AudioFlinger::PatchPanel::notifyStreamOpened(
        AudioHwDevice *audioHwDevice, audio_io_handle_t stream)
{
    if (audioHwDevice->isInsert()) {
        mInsertedModules[audioHwDevice->handle()].streams.insert(stream);
    }
}

void AudioFlinger::PatchPanel::notifyStreamClosed(audio_io_handle_t stream)
{
    for (auto& module : mInsertedModules) {
        module.second.streams.erase(stream);
    }
}

AudioHwDevice* AudioFlinger::PatchPanel::findAudioHwDeviceByModule(audio_module_handle_t module)
{
    if (module == AUDIO_MODULE_HANDLE_NONE) return nullptr;
    ssize_t index = mAudioFlinger.mAudioHwDevs.indexOfKey(module);
    if (index < 0) {
        ALOGW("%s() bad hw module %d", __func__, module);
        return nullptr;
    }
    return mAudioFlinger.mAudioHwDevs.valueAt(index)->hwDevice();
    return mAudioFlinger.mAudioHwDevs.valueAt(index);
}

sp<DeviceHalInterface> AudioFlinger::PatchPanel::findHwDeviceByModule(audio_module_handle_t module)
{
    AudioHwDevice *audioHwDevice = findAudioHwDeviceByModule(module);
    return audioHwDevice ? audioHwDevice->hwDevice() : nullptr;
}

void AudioFlinger::PatchPanel::addSoftwarePatchToInsertedModules(
        audio_module_handle_t module, audio_patch_handle_t handle)
{
    mInsertedModules[module].sw_patches.insert(handle);
}

void AudioFlinger::PatchPanel::removeSoftwarePatchFromInsertedModules(
        audio_patch_handle_t handle)
{
    for (auto& module : mInsertedModules) {
        module.second.sw_patches.erase(handle);
    }
}

void AudioFlinger::PatchPanel::dump(int fd)
void AudioFlinger::PatchPanel::dump(int fd) const
{
    String8 patchPanelDump;
    const char *indent = "  ";

    // Only dump software patches.
    bool headerPrinted = false;
    for (auto& iter : mPatches) {
    for (const auto& iter : mPatches) {
        if (iter.second.isSoftware()) {
            if (!headerPrinted) {
                String8 header("\nSoftware patches:\n");
                write(fd, header.string(), header.size());
                patchPanelDump += "\nSoftware patches:\n";
                headerPrinted = true;
            }
            String8 patchDump("  ");
            patchDump.append(iter.second.dump(iter.first));
            write(fd, patchDump.string(), patchDump.size());
            patchPanelDump.appendFormat("%s%s\n", indent, iter.second.dump(iter.first).string());
        }
    }
    if (headerPrinted) {
        String8 trailing("\n");
        write(fd, trailing.string(), trailing.size());

    headerPrinted = false;
    for (const auto& module : mInsertedModules) {
        if (!module.second.streams.empty() || !module.second.sw_patches.empty()) {
            if (!headerPrinted) {
                patchPanelDump += "\nTracked inserted modules:\n";
                headerPrinted = true;
            }
            String8 moduleDump = String8::format("Module %d: I/O handles: ", module.first);
            for (const auto& stream : module.second.streams) {
                moduleDump.appendFormat("%d ", stream);
            }
            moduleDump.append("; SW Patches: ");
            for (const auto& patch : module.second.sw_patches) {
                moduleDump.appendFormat("%d ", patch);
            }
            patchPanelDump.appendFormat("%s%s\n", indent, moduleDump.string());
        }
    }

    if (!patchPanelDump.isEmpty()) {
        write(fd, patchPanelDump.string(), patchPanelDump.size());
    }
}

+64 −2
Original line number Diff line number Diff line
@@ -19,9 +19,31 @@
    #error This header file should only be included from AudioFlinger.h
#endif


// PatchPanel is concealed within AudioFlinger, their lifetimes are the same.
class PatchPanel {
public:
    class SoftwarePatch {
      public:
        SoftwarePatch(const PatchPanel &patchPanel, audio_patch_handle_t patchHandle,
                audio_io_handle_t playbackThreadHandle, audio_io_handle_t recordThreadHandle)
                : mPatchPanel(patchPanel), mPatchHandle(patchHandle),
                  mPlaybackThreadHandle(playbackThreadHandle),
                  mRecordThreadHandle(recordThreadHandle) {}
        SoftwarePatch(const SoftwarePatch&) = default;
        SoftwarePatch& operator=(const SoftwarePatch&) = default;

        // Must be called under AudioFlinger::mLock
        status_t getLatencyMs_l(double *latencyMs) const;
        audio_io_handle_t getPlaybackThreadHandle() const { return mPlaybackThreadHandle; };
        audio_io_handle_t getRecordThreadHandle() const { return mRecordThreadHandle; };
      private:
        const PatchPanel &mPatchPanel;
        const audio_patch_handle_t mPatchHandle;
        const audio_io_handle_t mPlaybackThreadHandle;
        const audio_io_handle_t mRecordThreadHandle;
    };

    explicit PatchPanel(AudioFlinger* audioFlinger) : mAudioFlinger(*audioFlinger) {}

    /* List connected audio ports and their attributes */
@@ -42,7 +64,16 @@ public:
    status_t listAudioPatches(unsigned int *num_patches,
                                      struct audio_patch *patches);

    void dump(int fd);
    // Retrieves all currently estrablished software patches for a stream
    // opened on an intermediate module.
    status_t getDownstreamSoftwarePatches(audio_io_handle_t stream,
            std::vector<SoftwarePatch> *patches) const;

    // Notifies patch panel about all opened and closed streams.
    void notifyStreamOpened(AudioHwDevice *audioHwDevice, audio_io_handle_t stream);
    void notifyStreamClosed(audio_io_handle_t stream);

    void dump(int fd) const;

private:
    template<typename ThreadType, typename TrackType>
@@ -65,6 +96,7 @@ private:
        audio_patch_handle_t handle() const { return mHandle; }
        sp<ThreadType> thread() { return mThread; }
        sp<TrackType> track() { return mTrack; }
        sp<const ThreadType> const_thread() const { return mThread; }
        sp<const TrackType> const_track() const { return mTrack; }

        void closeConnections(PatchPanel *panel) {
@@ -122,7 +154,7 @@ private:
        // returns the latency of the patch (from record to playback).
        status_t getLatencyMs(double *latencyMs) const;

        String8 dump(audio_patch_handle_t myHandle);
        String8 dump(audio_patch_handle_t myHandle) const;

        // Note that audio_patch::id is only unique within a HAL module
        struct audio_patch              mAudioPatch;
@@ -138,8 +170,38 @@ private:
        Endpoint<RecordThread, RecordThread::PatchRecord> mRecord;
    };

    AudioHwDevice* findAudioHwDeviceByModule(audio_module_handle_t module);
    sp<DeviceHalInterface> findHwDeviceByModule(audio_module_handle_t module);
    void addSoftwarePatchToInsertedModules(
            audio_module_handle_t module, audio_patch_handle_t handle);
    void removeSoftwarePatchFromInsertedModules(audio_patch_handle_t handle);

    AudioFlinger &mAudioFlinger;
    std::map<audio_patch_handle_t, Patch> mPatches;

    // This map allows going from a thread to "downstream" software patches
    // when a processing module inserted in between. Example:
    //
    //  from map value.streams                               map key
    //  [Mixer thread] --> [Virtual output device] --> [Processing module] ---\
    //       [Harware module] <-- [Physical output device] <-- [S/W Patch] <--/
    //                                                 from map value.sw_patches
    //
    // This allows the mixer thread to look up the threads of the software patch
    // for propagating timing info, parameters, etc.
    //
    // The current assumptions are:
    //   1) The processing module acts as a mixer with several outputs which
    //      represent differently downmixed and / or encoded versions of the same
    //      mixed stream. There is no 1:1 correspondence between the input streams
    //      and the software patches, but rather a N:N correspondence between
    //      a group of streams and a group of patches.
    //   2) There are only a couple of inserted processing modules in the system,
    //      so when looking for a stream or patch handle we can iterate over
    //      all modules.
    struct ModuleConnections {
        std::set<audio_io_handle_t> streams;
        std::set<audio_patch_handle_t> sw_patches;
    };
    std::map<audio_module_handle_t, ModuleConnections> mInsertedModules;
};