mumble-voip_mumble/src/mumble/PulseAudio.h

230 lines
8.6 KiB
C++

// Copyright 2007-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#ifndef MUMBLE_MUMBLE_PULSEAUDIO_H_
#define MUMBLE_MUMBLE_PULSEAUDIO_H_
#include "AudioInput.h"
#include "AudioOutput.h"
#include <QtCore/QLibrary>
#include <QtCore/QWaitCondition>
#include <pulse/channelmap.h>
#include <pulse/context.h>
#include <pulse/def.h>
#include <pulse/ext-stream-restore.h>
#include <pulse/introspect.h>
#include <pulse/mainloop-api.h>
#include <pulse/sample.h>
#include <pulse/stream.h>
#include <pulse/subscribe.h>
#include <pulse/thread-mainloop.h>
#include <pulse/volume.h>
#include <condition_variable>
#include <mutex>
struct PulseAttenuation {
uint32_t index;
QString name;
QString stream_restore_id;
pa_cvolume normal_volume;
pa_cvolume attenuated_volume;
};
class PulseAudio : public QObject {
private:
Q_OBJECT
Q_DISABLE_COPY(PulseAudio)
protected:
QLibrary m_lib;
public:
bool m_ok;
const char *(*get_library_version)();
const char *(*strerror)(int error);
void (*operation_unref)(pa_operation *o);
int (*cvolume_equal)(const pa_cvolume *a, const pa_cvolume *b);
pa_cvolume *(*sw_cvolume_multiply_scalar)(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b);
int (*sample_spec_equal)(const pa_sample_spec *a, const pa_sample_spec *b);
int (*channel_map_equal)(const pa_channel_map *a, const pa_channel_map *b);
pa_proplist *(*proplist_new)();
void (*proplist_free)(pa_proplist *p);
const char *(*proplist_gets)(const pa_proplist *p, const char *key);
int (*proplist_sets)(pa_proplist *p, const char *key, const char *value);
pa_threaded_mainloop *(*threaded_mainloop_new)();
void (*threaded_mainloop_free)(pa_threaded_mainloop *m);
int (*threaded_mainloop_start)(pa_threaded_mainloop *m);
void (*threaded_mainloop_stop)(pa_threaded_mainloop *m);
void (*threaded_mainloop_lock)(pa_threaded_mainloop *m);
void (*threaded_mainloop_unlock)(pa_threaded_mainloop *m);
pa_mainloop_api *(*threaded_mainloop_get_api)(pa_threaded_mainloop *m);
int (*context_errno)(const pa_context *c);
pa_context *(*context_new_with_proplist)(pa_mainloop_api *mainloop, const char *name, const pa_proplist *proplist);
void (*context_unref)(pa_context *c);
int (*context_connect)(pa_context *c, const char *server, pa_context_flags_t flags, const pa_spawn_api *api);
void (*context_disconnect)(pa_context *c);
pa_operation *(*context_subscribe)(pa_context *c, pa_subscription_mask_t m, pa_context_success_cb_t cb,
void *userdata);
pa_context_state_t (*context_get_state)(const pa_context *c);
pa_operation *(*context_get_server_info)(pa_context *c, pa_server_info_cb_t cb, void *userdata);
pa_operation *(*context_get_sink_info_by_name)(pa_context *c, const char *name, pa_sink_info_cb_t cb,
void *userdata);
pa_operation *(*context_get_sink_info_list)(pa_context *c, pa_sink_info_cb_t cb, void *userdata);
pa_operation *(*context_get_sink_input_info_list)(pa_context *c, pa_sink_input_info_cb_t cb, void *userdata);
pa_operation *(*context_get_source_info_list)(pa_context *c, pa_source_info_cb_t cb, void *userdata);
pa_operation *(*context_set_sink_input_volume)(pa_context *c, uint32_t idx, const pa_cvolume *volume,
pa_context_success_cb_t cb, void *userdata);
void (*context_set_state_callback)(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
void (*context_set_subscribe_callback)(pa_context *c, pa_context_subscribe_cb_t cb, void *userdata);
pa_stream *(*stream_new)(pa_context *c, const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
void (*stream_unref)(pa_stream *s);
int (*stream_connect_playback)(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags,
const pa_cvolume *volume, pa_stream *sync_stream);
int (*stream_connect_record)(pa_stream *s, const char *dev, const pa_buffer_attr *attr, pa_stream_flags_t flags);
int (*stream_disconnect)(pa_stream *s);
int (*stream_peek)(pa_stream *p, const void **data, size_t *nbytes);
int (*stream_write)(pa_stream *p, const void *data, size_t nbytes, pa_free_cb_t free_cb, int64_t offset,
pa_seek_mode_t seek);
int (*stream_drop)(pa_stream *p);
pa_operation *(*stream_cork)(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
pa_stream_state_t (*stream_get_state)(const pa_stream *p);
pa_context *(*stream_get_context)(const pa_stream *p);
const pa_sample_spec *(*stream_get_sample_spec)(pa_stream *s);
const pa_channel_map *(*stream_get_channel_map)(pa_stream *s);
const pa_buffer_attr *(*stream_get_buffer_attr)(pa_stream *s);
void (*stream_set_state_callback)(pa_stream *s, pa_stream_notify_cb_t cb, void *userdata);
void (*stream_set_read_callback)(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
void (*stream_set_write_callback)(pa_stream *p, pa_stream_request_cb_t cb, void *userdata);
pa_operation *(*ext_stream_restore_read)(pa_context *c, pa_ext_stream_restore_read_cb_t cb, void *userdata);
pa_operation *(*ext_stream_restore_write)(pa_context *c, pa_update_mode_t mode,
const pa_ext_stream_restore_info data[], unsigned n,
int apply_immediately, pa_context_success_cb_t cb, void *userdata);
public:
PulseAudio();
};
class PulseAudioSystem : public QObject {
private:
Q_OBJECT
Q_DISABLE_COPY(PulseAudioSystem)
protected:
bool m_initialized = false;
std::mutex m_initLock;
std::condition_variable m_initWaiter;
void wakeup();
PulseAudio m_pulseAudio;
pa_context *pacContext;
pa_stream *pasInput, *pasOutput, *pasSpeaker;
pa_threaded_mainloop *pam;
pa_defer_event *pade;
bool bSourceDone, bSinkDone, bServerDone, bRunning;
QString qsDefaultInput, qsDefaultOutput;
int iDelayCache;
QString qsOutputCache, qsInputCache, qsEchoCache;
bool bEchoMultiCache;
QHash< QString, QString > qhEchoMap;
QHash< QString, pa_sample_spec > qhSpecMap;
QHash< QString, pa_channel_map > qhChanMap;
bool bAttenuating;
int iRemainingOperations;
int iSinkId;
QHash< uint32_t, PulseAttenuation > qhVolumes;
QList< uint32_t > qlMatchedSinks;
QHash< QString, PulseAttenuation > qhUnmatchedSinks;
QHash< QString, PulseAttenuation > qhMissingSinks;
static void defer_event_callback(pa_mainloop_api *a, pa_defer_event *e, void *userdata);
static void context_state_callback(pa_context *c, void *userdata);
static void subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
static void sink_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata);
static void source_callback(pa_context *c, const pa_source_info *i, int eol, void *userdata);
static void server_callback(pa_context *c, const pa_server_info *i, void *userdata);
static void sink_info_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata);
static void write_stream_callback(pa_stream *s, void *userdata);
static void read_stream_callback(pa_stream *s, void *userdata);
static void read_callback(pa_stream *s, size_t bytes, void *userdata);
static void write_callback(pa_stream *s, size_t bytes, void *userdata);
static void volume_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata);
static void restore_sink_input_list_callback(pa_context *c, const pa_sink_input_info *i, int eol, void *userdata);
static void stream_restore_read_callback(pa_context *c, const pa_ext_stream_restore_info *i, int eol,
void *userdata);
static void restore_volume_success_callback(pa_context *c, int success, void *userdata);
void contextCallback(pa_context *c);
void eventCallback(pa_mainloop_api *a, pa_defer_event *e);
void query();
QString outputDevice() const;
QString inputDevice() const;
void setVolumes();
PulseAttenuation *getAttenuation(QString stream_restore_id);
public:
QHash< QString, QString > qhInput;
QHash< QString, QString > qhOutput;
bool bPulseIsGood;
void wakeup_lock();
PulseAudioSystem();
~PulseAudioSystem() Q_DECL_OVERRIDE;
};
class PulseAudioInput : public AudioInput {
friend class PulseAudioSystem;
private:
Q_OBJECT
Q_DISABLE_COPY(PulseAudioInput)
protected:
QMutex qmMutex;
QWaitCondition qwcWait;
pa_sample_spec pssMic, pssEcho;
public:
PulseAudioInput();
~PulseAudioInput() Q_DECL_OVERRIDE;
void run() Q_DECL_OVERRIDE;
};
class PulseAudioOutput : public AudioOutput {
friend class PulseAudioSystem;
private:
Q_OBJECT
Q_DISABLE_COPY(PulseAudioOutput)
protected:
QMutex qmMutex;
QWaitCondition qwcWait;
pa_sample_spec pss;
pa_channel_map pcm;
public:
PulseAudioOutput();
~PulseAudioOutput() Q_DECL_OVERRIDE;
void run() Q_DECL_OVERRIDE;
};
#endif