/*
**
** Copyright 2014, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef INCLUDING_FROM_AUDIOFLINGER_H
#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_patch_handle_t getPatchHandle() const { return mPatchHandle; };
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 */
status_t listAudioPorts(unsigned int *num_ports,
struct audio_port *ports);
/* Get supported attributes for a given audio port */
status_t getAudioPort(struct audio_port *port);
/* Create a patch between several source and sink ports */
status_t createAudioPatch(const struct audio_patch *patch,
audio_patch_handle_t *handle);
/* Release a patch */
status_t releaseAudioPatch(audio_patch_handle_t handle);
/* List connected audio devices and they attributes */
status_t listAudioPatches(unsigned int *num_patches,
struct audio_patch *patches);
// 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>
class Endpoint {
public:
Endpoint() = default;
Endpoint(const Endpoint&) = delete;
Endpoint& operator=(const Endpoint&) = delete;
Endpoint(Endpoint&& other) noexcept { swap(other); }
Endpoint& operator=(Endpoint&& other) noexcept {
swap(other);
return *this;
}
~Endpoint() {
ALOGE_IF(mHandle != AUDIO_PATCH_HANDLE_NONE,
"A non empty Patch Endpoint leaked, handle %d", mHandle);
}
status_t checkTrack(TrackType *trackOrNull) const {
if (trackOrNull == nullptr) return NO_MEMORY;
return trackOrNull->initCheck();
}
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) {
if (mHandle != AUDIO_PATCH_HANDLE_NONE) {
panel->releaseAudioPatch(mHandle);
mHandle = AUDIO_PATCH_HANDLE_NONE;
}
if (mThread != 0) {
if (mTrack != 0) {
mThread->deletePatchTrack(mTrack);
}
if (mCloseThread) {
panel->mAudioFlinger.closeThreadInternal_l(mThread);
}
}
}
audio_patch_handle_t* handlePtr() { return &mHandle; }
void setThread(const sp<ThreadType>& thread, bool closeThread = true) {
mThread = thread;
mCloseThread = closeThread;
}
template <typename T>
void setTrackAndPeer(const sp<TrackType>& track, const sp<T> &peer) {
mTrack = track;
mThread->addPatchTrack(mTrack);
mTrack->setPeerProxy(peer, true /* holdReference */);
}
void clearTrackPeer() { if (mTrack) mTrack->clearPeerProxy(); }
void stopTrack() { if (mTrack) mTrack->stop(); }
void swap(Endpoint &other) noexcept {
using std::swap;
swap(mThread, other.mThread);
swap(mCloseThread, other.mCloseThread);
swap(mHandle, other.mHandle);
swap(mTrack, other.mTrack);
}
friend void swap(Endpoint &a, Endpoint &b) noexcept {
a.swap(b);
}
private:
sp<ThreadType> mThread;
bool mCloseThread = true;
audio_patch_handle_t mHandle = AUDIO_PATCH_HANDLE_NONE;
sp<TrackType> mTrack;
};
class Patch {
public:
explicit Patch(const struct audio_patch &patch) : mAudioPatch(patch) {}
~Patch();
Patch(const Patch&) = delete;
Patch(Patch&&) = default;
Patch& operator=(const Patch&) = delete;
Patch& operator=(Patch&&) = default;
status_t createConnections(PatchPanel *panel);
void clearConnections(PatchPanel *panel);
bool isSoftware() const {
return mRecord.handle() != AUDIO_PATCH_HANDLE_NONE ||
mPlayback.handle() != AUDIO_PATCH_HANDLE_NONE; }
// returns the latency of the patch (from record to playback).
status_t getLatencyMs(double *latencyMs) const;
String8 dump(audio_patch_handle_t myHandle) const;
// Note that audio_patch::id is only unique within a HAL module
struct audio_patch mAudioPatch;
// handle for audio HAL patch handle present only when the audio HAL version is >= 3.0
audio_patch_handle_t mHalHandle = AUDIO_PATCH_HANDLE_NONE;
// below members are used by a software audio patch connecting a source device from a
// given audio HW module to a sink device on an other audio HW module.
// the objects are created by createConnections() and released by clearConnections()
// playback thread is created if no existing playback thread can be used
// connects playback thread output to sink device
Endpoint<PlaybackThread, PlaybackThread::PatchTrack> mPlayback;
// connects source device to record thread input
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;
};