mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-11-25 14:57:20 +00:00
[client] audio: initial addition of PipeWire audio support via SPICE
This commit is contained in:
parent
8ba4b56dba
commit
e810577317
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@ -43,7 +43,8 @@ jobs:
|
|||||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
||||||
-DCMAKE_LINKER:FILEPATH=/usr/bin/ld \
|
-DCMAKE_LINKER:FILEPATH=/usr/bin/ld \
|
||||||
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||||
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
|
-DENABLE_LIBDECOR=${{ matrix.wayland_shell == 'libdecor' }} \
|
||||||
|
-DENABLE_PIPEWIRE=0 \
|
||||||
..
|
..
|
||||||
- name: Build client
|
- name: Build client
|
||||||
run: |
|
run: |
|
||||||
|
@ -51,6 +51,9 @@ add_feature_info(ENABLE_WAYLAND ENABLE_WAYLAND "Wayland support.")
|
|||||||
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
option(ENABLE_LIBDECOR "Build with libdecor support" OFF)
|
||||||
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
add_feature_info(ENABLE_LIBDECOR ENABLE_LIBDECOR "libdecor support.")
|
||||||
|
|
||||||
|
option(ENABLE_PIPEWIRE "Build with PipeWire audio output support" ON)
|
||||||
|
add_feature_info(ENABLE_PIPEWIRE ENABLE_PIPEWIRE "PipeWire audio support.")
|
||||||
|
|
||||||
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
|
if (NOT ENABLE_X11 AND NOT ENABLE_WAYLAND)
|
||||||
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
|
message(FATAL_ERROR "Either ENABLE_X11 or ENABLE_WAYLAND must be on")
|
||||||
endif()
|
endif()
|
||||||
@ -144,6 +147,7 @@ add_subdirectory("${PROJECT_TOP}/repos/cimgui" "${CMAKE_BINARY_DIR}/cimgui" E
|
|||||||
|
|
||||||
add_subdirectory(displayservers)
|
add_subdirectory(displayservers)
|
||||||
add_subdirectory(renderers)
|
add_subdirectory(renderers)
|
||||||
|
add_subdirectory(audiodevs)
|
||||||
|
|
||||||
add_executable(looking-glass-client ${SOURCES})
|
add_executable(looking-glass-client ${SOURCES})
|
||||||
|
|
||||||
@ -158,6 +162,7 @@ target_link_libraries(looking-glass-client
|
|||||||
purespice
|
purespice
|
||||||
renderers
|
renderers
|
||||||
cimgui
|
cimgui
|
||||||
|
audiodevs
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS looking-glass-client
|
install(TARGETS looking-glass-client
|
||||||
|
43
client/audiodevs/CMakeLists.txt
Normal file
43
client/audiodevs/CMakeLists.txt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(audiodevs LANGUAGES C)
|
||||||
|
|
||||||
|
set(AUDIODEV_H "${CMAKE_BINARY_DIR}/include/dynamic/audiodev.h")
|
||||||
|
set(AUDIODEV_C "${CMAKE_BINARY_DIR}/src/audiodev.c")
|
||||||
|
|
||||||
|
file(WRITE ${AUDIODEV_H} "#include \"interface/audiodev.h\"\n\n")
|
||||||
|
file(APPEND ${AUDIODEV_H} "extern struct LG_AudioDevOps * LG_AudioDevs[];\n\n")
|
||||||
|
|
||||||
|
file(WRITE ${AUDIODEV_C} "#include \"interface/audiodev.h\"\n\n")
|
||||||
|
file(APPEND ${AUDIODEV_C} "#include <stddef.h>\n\n")
|
||||||
|
|
||||||
|
set(AUDIODEVS "_")
|
||||||
|
set(AUDIODEVS_LINK "_")
|
||||||
|
function(add_audiodev name)
|
||||||
|
set(AUDIODEVS "${AUDIODEVS};${name}" PARENT_SCOPE)
|
||||||
|
set(AUDIODEVS_LINK "${AUDIODEVS_LINK};audiodev_${name}" PARENT_SCOPE)
|
||||||
|
add_subdirectory(${name})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Add/remove audiodevs here!
|
||||||
|
if(ENABLE_PIPEWIRE)
|
||||||
|
add_audiodev(PipeWire)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(REMOVE_AT AUDIODEVS 0)
|
||||||
|
list(REMOVE_AT AUDIODEVS_LINK 0)
|
||||||
|
|
||||||
|
list(LENGTH AUDIODEVS AUDIODEV_COUNT)
|
||||||
|
file(APPEND ${AUDIODEV_H} "#define LG_AUDIODEV_COUNT ${AUDIODEV_COUNT}\n")
|
||||||
|
|
||||||
|
foreach(audiodev ${AUDIODEVS})
|
||||||
|
file(APPEND ${AUDIODEV_C} "extern struct LG_AudioDevOps LGAD_${audiodev};\n")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
file(APPEND ${AUDIODEV_C} "\nconst struct LG_AudioDevOps * LG_AudioDevs[] =\n{\n")
|
||||||
|
foreach(audiodev ${AUDIODEVS})
|
||||||
|
file(APPEND ${AUDIODEV_C} " &LGAD_${audiodev},\n")
|
||||||
|
endforeach()
|
||||||
|
file(APPEND ${AUDIODEV_C} " NULL\n};")
|
||||||
|
|
||||||
|
add_library(audiodevs STATIC ${AUDIODEV_C})
|
||||||
|
target_link_libraries(audiodevs ${AUDIODEVS_LINK})
|
21
client/audiodevs/PipeWire/CMakeLists.txt
Normal file
21
client/audiodevs/PipeWire/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
project(audiodev_PipeWire LANGUAGES C)
|
||||||
|
|
||||||
|
find_package(PkgConfig)
|
||||||
|
pkg_check_modules(AUDIODEV_PipeWire REQUIRED IMPORTED_TARGET
|
||||||
|
libpipewire-0.3
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(audiodev_PipeWire STATIC
|
||||||
|
pipewire.c
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(audiodev_PipeWire
|
||||||
|
PkgConfig::AUDIODEV_PipeWire
|
||||||
|
lg_common
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(audiodev_PipeWire
|
||||||
|
PRIVATE
|
||||||
|
src
|
||||||
|
)
|
220
client/audiodevs/PipeWire/pipewire.c
Normal file
220
client/audiodevs/PipeWire/pipewire.c
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2021 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "interface/audiodev.h"
|
||||||
|
|
||||||
|
#include <spa/param/audio/format-utils.h>
|
||||||
|
#include <pipewire/pipewire.h>
|
||||||
|
|
||||||
|
#include "common/debug.h"
|
||||||
|
#include "common/ringbuffer.h"
|
||||||
|
|
||||||
|
struct PipeWire
|
||||||
|
{
|
||||||
|
struct pw_loop * loop;
|
||||||
|
struct pw_thread_loop * thread;
|
||||||
|
struct pw_stream * stream;
|
||||||
|
int channels;
|
||||||
|
int stride;
|
||||||
|
|
||||||
|
RingBuffer buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct PipeWire pw = {0};
|
||||||
|
|
||||||
|
static void pipewire_on_process(void * userdata)
|
||||||
|
{
|
||||||
|
struct pw_buffer * pbuf;
|
||||||
|
|
||||||
|
const int avail = ringbuffer_getCount(pw.buffer);
|
||||||
|
if (!avail)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!(pbuf = pw_stream_dequeue_buffer(pw.stream))) {
|
||||||
|
DEBUG_WARN("out of buffers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct spa_buffer * sbuf = pbuf->buffer;
|
||||||
|
uint8_t * dst;
|
||||||
|
|
||||||
|
if (!(dst = sbuf->datas[0].data))
|
||||||
|
return;
|
||||||
|
|
||||||
|
int frames = sbuf->datas[0].maxsize / pw.stride;
|
||||||
|
if (frames > avail)
|
||||||
|
frames = avail;
|
||||||
|
|
||||||
|
for(int i = 0; i < frames; ++i)
|
||||||
|
{
|
||||||
|
ringbuffer_shift(pw.buffer, dst);
|
||||||
|
dst += pw.stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
sbuf->datas[0].chunk->offset = 0;
|
||||||
|
sbuf->datas[0].chunk->stride = pw.stride;
|
||||||
|
sbuf->datas[0].chunk->size = frames * pw.stride;
|
||||||
|
|
||||||
|
pw_stream_queue_buffer(pw.stream, pbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pipewire_init(void)
|
||||||
|
{
|
||||||
|
pw_init(NULL, NULL);
|
||||||
|
|
||||||
|
pw.loop = pw_loop_new(NULL);
|
||||||
|
struct pw_context * context = pw_context_new(pw.loop, NULL, 0);
|
||||||
|
if (!context)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to create a context");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this is just to test for PipeWire availabillity */
|
||||||
|
struct pw_core * core = pw_context_connect(context, NULL, 0);
|
||||||
|
if (!core)
|
||||||
|
goto err_context;
|
||||||
|
|
||||||
|
pw_context_destroy(context);
|
||||||
|
|
||||||
|
/* PipeWire is available so create the loop thread and start it */
|
||||||
|
pw.thread = pw_thread_loop_new_full(pw.loop, "Playback", NULL);
|
||||||
|
if (!pw.thread)
|
||||||
|
{
|
||||||
|
DEBUG_ERROR("Failed to create the thread loop");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_thread_loop_start(pw.thread);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
err_context:
|
||||||
|
pw_context_destroy(context);
|
||||||
|
|
||||||
|
err:
|
||||||
|
pw_deinit();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pipewire_free(void)
|
||||||
|
{
|
||||||
|
if (pw.thread)
|
||||||
|
{
|
||||||
|
pw_thread_loop_lock(pw.thread);
|
||||||
|
if (pw.stream)
|
||||||
|
{
|
||||||
|
pw_stream_destroy(pw.stream);
|
||||||
|
pw.stream = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pw_thread_loop_signal(pw.thread, true);
|
||||||
|
pw_thread_loop_destroy(pw.thread);
|
||||||
|
pw.loop = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ringbuffer_free(&pw.buffer);
|
||||||
|
pw_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pipewire_start(int channels, int sampleRate)
|
||||||
|
{
|
||||||
|
const struct spa_pod * params[1];
|
||||||
|
uint8_t buffer[1024];
|
||||||
|
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||||
|
static const struct pw_stream_events events =
|
||||||
|
{
|
||||||
|
.version = PW_VERSION_STREAM_EVENTS,
|
||||||
|
.process = pipewire_on_process
|
||||||
|
};
|
||||||
|
|
||||||
|
pw.channels = channels;
|
||||||
|
pw.stride = sizeof(uint16_t) * channels;
|
||||||
|
pw.buffer = ringbuffer_new(sampleRate, channels * sizeof(uint16_t));
|
||||||
|
|
||||||
|
pw_thread_loop_lock(pw.thread);
|
||||||
|
pw.stream = pw_stream_new_simple(
|
||||||
|
pw.loop,
|
||||||
|
"LookingGlass",
|
||||||
|
pw_properties_new(
|
||||||
|
PW_KEY_MEDIA_TYPE , "Audio",
|
||||||
|
PW_KEY_MEDIA_CATEGORY, "Playback",
|
||||||
|
PW_KEY_MEDIA_ROLE , "Music",
|
||||||
|
NULL
|
||||||
|
),
|
||||||
|
&events,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!pw.stream)
|
||||||
|
{
|
||||||
|
pw_thread_loop_unlock(pw.thread);
|
||||||
|
DEBUG_ERROR("Failed to create the stream");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
|
||||||
|
&SPA_AUDIO_INFO_RAW_INIT(
|
||||||
|
.format = SPA_AUDIO_FORMAT_S16,
|
||||||
|
.channels = channels,
|
||||||
|
.rate = sampleRate
|
||||||
|
));
|
||||||
|
|
||||||
|
pw_stream_connect(
|
||||||
|
pw.stream,
|
||||||
|
PW_DIRECTION_OUTPUT,
|
||||||
|
PW_ID_ANY,
|
||||||
|
PW_STREAM_FLAG_AUTOCONNECT |
|
||||||
|
PW_STREAM_FLAG_MAP_BUFFERS |
|
||||||
|
PW_STREAM_FLAG_RT_PROCESS,
|
||||||
|
params, 1);
|
||||||
|
|
||||||
|
pw_thread_loop_unlock(pw.thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pipewire_play(uint8_t * data, int size)
|
||||||
|
{
|
||||||
|
if (!pw.stream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(int i = 0; i < size; i += pw.stride)
|
||||||
|
ringbuffer_push(pw.buffer, data + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pipewire_stop(void)
|
||||||
|
{
|
||||||
|
if (!pw.stream)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pw_thread_loop_lock(pw.thread);
|
||||||
|
pw_stream_flush(pw.stream, true);
|
||||||
|
pw_stream_destroy(pw.stream);
|
||||||
|
pw.stream = NULL;
|
||||||
|
pw_thread_loop_unlock(pw.thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LG_AudioDevOps LGAD_PipeWire =
|
||||||
|
{
|
||||||
|
.name = "PipeWire",
|
||||||
|
.init = pipewire_init,
|
||||||
|
.free = pipewire_free,
|
||||||
|
.start = pipewire_start,
|
||||||
|
.play = pipewire_play,
|
||||||
|
.stop = pipewire_stop
|
||||||
|
};
|
63
client/include/interface/audiodev.h
Normal file
63
client/include/interface/audiodev.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* Looking Glass
|
||||||
|
* Copyright © 2017-2021 The Looking Glass Authors
|
||||||
|
* https://looking-glass.io
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the Free
|
||||||
|
* Software Foundation; either version 2 of the License, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||||
|
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _H_I_AUDIODEV_
|
||||||
|
#define _H_I_AUDIODEV_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct LG_AudioDevOps
|
||||||
|
{
|
||||||
|
/* internal name of the audio for debugging */
|
||||||
|
const char * name;
|
||||||
|
|
||||||
|
/* called very early to allow for option registration, optional */
|
||||||
|
void (*earlyInit)(void);
|
||||||
|
|
||||||
|
/* called to initialize the audio backend */
|
||||||
|
bool (*init)(void);
|
||||||
|
|
||||||
|
/* final free */
|
||||||
|
void (*free)(void);
|
||||||
|
|
||||||
|
/* setup the playback audio stream
|
||||||
|
* Note: currently SPICE only supports S16 samples so always assume so
|
||||||
|
*/
|
||||||
|
void (*start)(int channels, int sampleRate);
|
||||||
|
|
||||||
|
/* called for each packet of output audio to play
|
||||||
|
* Note: size is the size of data in bytes, not frames/samples
|
||||||
|
*/
|
||||||
|
void (*play)(uint8_t * data, int size);
|
||||||
|
|
||||||
|
/* called when SPICE reports the audio stream has stopped */
|
||||||
|
void (*stop)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ASSERT_LG_AUDIODEV_VALID(x) \
|
||||||
|
DEBUG_ASSERT((x)->name ); \
|
||||||
|
DEBUG_ASSERT((x)->init ); \
|
||||||
|
DEBUG_ASSERT((x)->free ); \
|
||||||
|
DEBUG_ASSERT((x)->start ); \
|
||||||
|
DEBUG_ASSERT((x)->play ); \
|
||||||
|
DEBUG_ASSERT((x)->stop );
|
||||||
|
|
||||||
|
#endif
|
@ -428,6 +428,13 @@ static struct Option options[] =
|
|||||||
.type = OPTION_TYPE_BOOL,
|
.type = OPTION_TYPE_BOOL,
|
||||||
.value.x_bool = true
|
.value.x_bool = true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.module = "spice",
|
||||||
|
.name = "audio",
|
||||||
|
.description = "Enable SPICE audio support",
|
||||||
|
.type = OPTION_TYPE_BOOL,
|
||||||
|
.value.x_bool = true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.module = "spice",
|
.module = "spice",
|
||||||
.name = "scaleCursor",
|
.name = "scaleCursor",
|
||||||
@ -609,6 +616,7 @@ bool config_load(int argc, char * argv[])
|
|||||||
|
|
||||||
g_params.useSpiceInput = option_get_bool("spice", "input" );
|
g_params.useSpiceInput = option_get_bool("spice", "input" );
|
||||||
g_params.useSpiceClipboard = option_get_bool("spice", "clipboard");
|
g_params.useSpiceClipboard = option_get_bool("spice", "clipboard");
|
||||||
|
g_params.useSpiceAudio = option_get_bool("spice", "audio" );
|
||||||
|
|
||||||
if (g_params.useSpiceClipboard)
|
if (g_params.useSpiceClipboard)
|
||||||
{
|
{
|
||||||
|
@ -846,6 +846,66 @@ static void reportBadVersion()
|
|||||||
DEBUG_ERROR("Please install the matching host application for this client");
|
DEBUG_ERROR("Please install the matching host application for this client");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void audioStart(int channels, int sampleRate, PSAudioFormat format,
|
||||||
|
uint32_t time)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* we probe here so that the audiodev is operating in the context of the SPICE
|
||||||
|
* thread/loop to avoid any audio API threading issues
|
||||||
|
*/
|
||||||
|
static int probed = false;
|
||||||
|
if (!probed)
|
||||||
|
{
|
||||||
|
probed = true;
|
||||||
|
|
||||||
|
// search for the best audiodev to use
|
||||||
|
for(int i = 0; i < LG_AUDIODEV_COUNT; ++i)
|
||||||
|
if (LG_AudioDevs[i]->init())
|
||||||
|
{
|
||||||
|
g_state.audioDev = LG_AudioDevs[i];
|
||||||
|
DEBUG_INFO("Using AudioDev: %s", g_state.audioDev->name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_state.audioDev)
|
||||||
|
DEBUG_WARN("Failed to initialize an audio backend");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_state.audioDev)
|
||||||
|
{
|
||||||
|
static int lastChannels = 0;
|
||||||
|
static int lastSampleRate = 0;
|
||||||
|
|
||||||
|
if (g_state.audioStarted)
|
||||||
|
{
|
||||||
|
if (channels != lastChannels || sampleRate != lastSampleRate)
|
||||||
|
g_state.audioDev->stop();
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastChannels = channels;
|
||||||
|
lastSampleRate = sampleRate;
|
||||||
|
g_state.audioStarted = true;
|
||||||
|
|
||||||
|
DEBUG_INFO("%d channels @ %dHz", channels, sampleRate);
|
||||||
|
g_state.audioDev->start(channels, sampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void audioStop(void)
|
||||||
|
{
|
||||||
|
if (g_state.audioDev)
|
||||||
|
g_state.audioDev->stop();
|
||||||
|
g_state.audioStarted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audioData(uint8_t * data, size_t size)
|
||||||
|
{
|
||||||
|
if (g_state.audioDev)
|
||||||
|
g_state.audioDev->play(data, size);
|
||||||
|
}
|
||||||
|
|
||||||
static int lg_run(void)
|
static int lg_run(void)
|
||||||
{
|
{
|
||||||
g_cursor.sens = g_params.mouseSens;
|
g_cursor.sens = g_params.mouseSens;
|
||||||
@ -921,7 +981,9 @@ static int lg_run(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// try to connect to the spice server
|
// try to connect to the spice server
|
||||||
if (g_params.useSpiceInput || g_params.useSpiceClipboard)
|
if (g_params.useSpiceInput ||
|
||||||
|
g_params.useSpiceClipboard ||
|
||||||
|
g_params.useSpiceAudio)
|
||||||
{
|
{
|
||||||
if (g_params.useSpiceClipboard)
|
if (g_params.useSpiceClipboard)
|
||||||
spice_set_clipboard_cb(
|
spice_set_clipboard_cb(
|
||||||
@ -930,7 +992,14 @@ static int lg_run(void)
|
|||||||
cb_spiceRelease,
|
cb_spiceRelease,
|
||||||
cb_spiceRequest);
|
cb_spiceRequest);
|
||||||
|
|
||||||
if (!spice_connect(g_params.spiceHost, g_params.spicePort, ""))
|
if (g_params.useSpiceAudio)
|
||||||
|
spice_set_audio_cb(
|
||||||
|
audioStart,
|
||||||
|
audioStop,
|
||||||
|
audioData);
|
||||||
|
|
||||||
|
if (!spice_connect(g_params.spiceHost, g_params.spicePort, "",
|
||||||
|
g_params.useSpiceAudio))
|
||||||
{
|
{
|
||||||
DEBUG_ERROR("Failed to connect to spice server");
|
DEBUG_ERROR("Failed to connect to spice server");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "dynamic/displayservers.h"
|
#include "dynamic/displayservers.h"
|
||||||
#include "dynamic/renderers.h"
|
#include "dynamic/renderers.h"
|
||||||
|
#include "dynamic/audiodev.h"
|
||||||
|
|
||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
@ -133,6 +134,9 @@ struct AppState
|
|||||||
bool resizeDone;
|
bool resizeDone;
|
||||||
|
|
||||||
bool autoIdleInhibitState;
|
bool autoIdleInhibitState;
|
||||||
|
|
||||||
|
struct LG_AudioDevOps * audioDev;
|
||||||
|
bool audioStarted;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AppParams
|
struct AppParams
|
||||||
@ -154,6 +158,7 @@ struct AppParams
|
|||||||
LG_RendererRotate winRotate;
|
LG_RendererRotate winRotate;
|
||||||
bool useSpiceInput;
|
bool useSpiceInput;
|
||||||
bool useSpiceClipboard;
|
bool useSpiceClipboard;
|
||||||
|
bool useSpiceAudio;
|
||||||
const char * spiceHost;
|
const char * spiceHost;
|
||||||
unsigned int spicePort;
|
unsigned int spicePort;
|
||||||
bool clipboardToVM;
|
bool clipboardToVM;
|
||||||
|
@ -93,6 +93,9 @@ feature is disabled when running :ref:`cmake <client_building>`.
|
|||||||
- libwayland-dev
|
- libwayland-dev
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
|
|
||||||
|
- Disable with ``cmake -DENABLE_PIPEWIRE=no ..``
|
||||||
|
|
||||||
|
- libpipewire-0.3-dev
|
||||||
|
|
||||||
.. _client_deps_recommended:
|
.. _client_deps_recommended:
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ cmake
|
|||||||
config
|
config
|
||||||
dejavu
|
dejavu
|
||||||
deuteranope
|
deuteranope
|
||||||
|
dev
|
||||||
dir
|
dir
|
||||||
distros
|
distros
|
||||||
dmabuf
|
dmabuf
|
||||||
@ -19,6 +20,7 @@ ini
|
|||||||
kvmfr
|
kvmfr
|
||||||
laggy
|
laggy
|
||||||
libdecor
|
libdecor
|
||||||
|
libpipewire
|
||||||
libvirt
|
libvirt
|
||||||
linux
|
linux
|
||||||
LookingGlass
|
LookingGlass
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 8d8b47454e29586a1a3e3668aadd942cb5b0cfc0
|
Subproject commit 3ea156974b65d87ea9c2e21d031af2b60d79074a
|
Loading…
Reference in New Issue
Block a user