pipewire: initialize stream
This commit is contained in:
parent
e075ac065f
commit
bb3d5a4edb
5 changed files with 153 additions and 14 deletions
22
meson.build
22
meson.build
|
@ -35,6 +35,7 @@ xkbcommon = dependency('xkbcommon')
|
|||
thread_dep = dependency('threads')
|
||||
cap_dep = dependency('libcap')
|
||||
sdl_dep = dependency('SDL2')
|
||||
pipewire_dep = dependency('libpipewire-0.3', required: get_option('pipewire'))
|
||||
|
||||
wlroots_dep = dependency(
|
||||
'wlroots',
|
||||
|
@ -67,11 +68,14 @@ liftoff_dep = dependency(
|
|||
default_options: ['default_library=static'],
|
||||
)
|
||||
|
||||
add_project_arguments(
|
||||
'-DHAVE_PIPEWIRE=@0@'.format(pipewire_dep.found().to_int()),
|
||||
language: 'cpp',
|
||||
)
|
||||
|
||||
subdir('protocol')
|
||||
|
||||
executable(
|
||||
'gamescope',
|
||||
[
|
||||
src = [
|
||||
'src/steamcompmgr.cpp',
|
||||
'src/main.cpp',
|
||||
'src/wlserver.cpp',
|
||||
|
@ -83,12 +87,20 @@ executable(
|
|||
'src/log.cpp',
|
||||
spirv_shader,
|
||||
gamescope_xwayland_proto_files,
|
||||
],
|
||||
]
|
||||
|
||||
if pipewire_dep.found()
|
||||
src += 'src/pipewire.cpp'
|
||||
endif
|
||||
|
||||
executable(
|
||||
'gamescope',
|
||||
src,
|
||||
dependencies: [
|
||||
dep_x11, dep_xdamage, dep_xcomposite, dep_xrender, dep_xext, dep_xfixes,
|
||||
dep_xxf86vm, dep_xres, drm_dep, wayland_server, wayland_protos,
|
||||
xkbcommon, thread_dep, sdl_dep, wlroots_dep,
|
||||
vulkan_dep, liftoff_dep, dep_xtst, cap_dep
|
||||
vulkan_dep, liftoff_dep, dep_xtst, cap_dep, pipewire_dep,
|
||||
],
|
||||
install: true,
|
||||
)
|
||||
|
|
1
meson_options.txt
Normal file
1
meson_options.txt
Normal file
|
@ -0,0 +1 @@
|
|||
option('pipewire', type: 'feature', description: 'Screen capture via PipeWire')
|
11
src/main.cpp
11
src/main.cpp
|
@ -19,6 +19,10 @@
|
|||
#include "wlserver.hpp"
|
||||
#include "gpuvis_trace_utils.h"
|
||||
|
||||
#if HAVE_PIPEWIRE
|
||||
#include "pipewire.hpp"
|
||||
#endif
|
||||
|
||||
std::atomic< bool > g_bRun{true};
|
||||
|
||||
int g_nNestedWidth = 0;
|
||||
|
@ -255,6 +259,13 @@ int main(int argc, char **argv)
|
|||
setenv("DISPLAY", wlserver_get_nested_display_name(), 1);
|
||||
setenv("GAMESCOPE_WAYLAND_DISPLAY", wlserver_get_wl_display_name(), 1);
|
||||
|
||||
#if HAVE_PIPEWIRE
|
||||
if ( !init_pipewire() )
|
||||
{
|
||||
fprintf( stderr, "Warning: failed to setup PipeWire, screen capture won't be available\n" );
|
||||
}
|
||||
#endif
|
||||
|
||||
std::thread steamCompMgrThread( steamCompMgrThreadRun, argc, argv );
|
||||
steamCompMgrThread.detach();
|
||||
|
||||
|
|
102
src/pipewire.cpp
Normal file
102
src/pipewire.cpp
Normal file
|
@ -0,0 +1,102 @@
|
|||
#include <stdio.h>
|
||||
#include <spa/param/video/format-utils.h>
|
||||
|
||||
#include "main.hpp"
|
||||
#include "pipewire.hpp"
|
||||
|
||||
static void stream_handle_state_changed(void *data, enum pw_stream_state old_stream_state, enum pw_stream_state stream_state, const char *error)
|
||||
{
|
||||
struct pipewire_state *state = (struct pipewire_state *) data;
|
||||
|
||||
fprintf(stderr, "pipewire: stream state changed: %s\n", pw_stream_state_as_string(stream_state));
|
||||
|
||||
switch (stream_state) {
|
||||
case PW_STREAM_STATE_PAUSED:
|
||||
if (state->stream_node_id == SPA_ID_INVALID) {
|
||||
state->stream_node_id = pw_stream_get_node_id(state->stream);
|
||||
}
|
||||
break;
|
||||
case PW_STREAM_STATE_STREAMING:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct pw_stream_events stream_events = {
|
||||
.version = PW_VERSION_STREAM_EVENTS,
|
||||
.state_changed = stream_handle_state_changed,
|
||||
//.param_changed = stream_handle_param_changed,
|
||||
//.add_buffer = stream_handle_add_buffer,
|
||||
//.remove_buffer = stream_handle_remove_buffer,
|
||||
};
|
||||
|
||||
bool init_pipewire(void)
|
||||
{
|
||||
pw_init(nullptr, nullptr);
|
||||
|
||||
struct pipewire_state state = { .stream_node_id = SPA_ID_INVALID };
|
||||
|
||||
state.loop = pw_loop_new(nullptr);
|
||||
if (!state.loop) {
|
||||
fprintf(stderr, "pipewire: pw_loop_new failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.context = pw_context_new(state.loop, nullptr, 0);
|
||||
if (!state.context) {
|
||||
fprintf(stderr, "pipewire: pw_context_new failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.core = pw_context_connect(state.context, nullptr, 0);
|
||||
if (!state.core) {
|
||||
fprintf(stderr, "pipewire: pw_context_connect failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
state.stream = pw_stream_new(state.core, "gamescope",
|
||||
pw_properties_new(
|
||||
PW_KEY_MEDIA_CLASS, "Video/Source",
|
||||
nullptr));
|
||||
if (!state.stream) {
|
||||
fprintf(stderr, "pipewire: pw_stream_new failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct spa_hook stream_hook;
|
||||
pw_stream_add_listener(state.stream, &stream_hook, &stream_events, &state);
|
||||
|
||||
struct spa_rectangle size = SPA_RECTANGLE(g_nOutputWidth, g_nOutputHeight);
|
||||
struct spa_fraction framerate = SPA_FRACTION(0, 1);
|
||||
|
||||
uint8_t buf[1024];
|
||||
struct spa_pod_builder builder = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
|
||||
const struct spa_pod *param = (const struct spa_pod *)spa_pod_builder_add_object(&builder,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_RGB),
|
||||
SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&size),
|
||||
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&framerate));
|
||||
|
||||
// TODO: PW_STREAM_FLAG_ALLOC_BUFFERS
|
||||
enum pw_stream_flags flags = PW_STREAM_FLAG_DRIVER;
|
||||
int ret = pw_stream_connect(state.stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, flags, ¶m, 1);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "pipewire: pw_stream_connect failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
while (state.stream_node_id == SPA_ID_INVALID) {
|
||||
int ret = pw_loop_iterate(state.loop, -1);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "pipewire: pw_loop_iterate failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "pipewire: stream available on node ID: %u\n", state.stream_node_id);
|
||||
|
||||
return true;
|
||||
}
|
13
src/pipewire.hpp
Normal file
13
src/pipewire.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <pipewire/pipewire.h>
|
||||
|
||||
struct pipewire_state {
|
||||
struct pw_loop *loop;
|
||||
struct pw_context *context;
|
||||
struct pw_core *core;
|
||||
struct pw_stream *stream;
|
||||
uint32_t stream_node_id;
|
||||
};
|
||||
|
||||
bool init_pipewire(void);
|
Loading…
Reference in a new issue