UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
pipewire.cpp File Reference
#include "config.h"
#include "config_unix.h"
#include "config_win32.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <memory>
#include <atomic>
#include "debug.h"
#include "host.h"
#include "lib_common.h"
#include "utils/color_out.h"
#include "utils/macros.h"
#include "utils/misc.h"
#include "utils/text.h"
#include "video.h"
#include "video_codec.h"
#include "video_display.h"
#include "pipewire_common.hpp"
#include "utils/string_view_utils.hpp"
#include <spa/param/video/format-utils.h>
Include dependency graph for pipewire.cpp:

Go to the source code of this file.

Classes

struct  display_pw_state
 

Macros

#define MOD_NAME   "[PW disp] "
 

Functions

static void display_pw_done (void *state)
 
static bool display_pw_get_property (void *state, int property, void *val, size_t *len)
 
static struct video_frame * display_pw_getf (void *state)
 
static void display_pw_help ()
 
static void * display_pw_init (struct module *, const char *cfg, unsigned int)
 
static void display_pw_probe (struct device_info **available_cards, int *count, void(**deleter)(void *))
 
static bool display_pw_putf (void *state, struct video_frame *frame, long long flags)
 
static bool display_pw_reconfigure (void *state, struct video_desc desc)
 
static void on_add_buffer (void *data, struct pw_buffer *buffer)
 
static void on_param_changed (void *state, uint32_t id, const struct spa_pod *param)
 
static void on_remove_buffer (void *, struct pw_buffer *buffer)
 
static void on_state_changed (void *state, enum pw_stream_state old, enum pw_stream_state new_state, const char *error)
 
 REGISTER_MODULE (pipewire, &display_pw_info, LIBRARY_CLASS_VIDEO_DISPLAY, VIDEO_DISPLAY_ABI_VERSION)
 

Variables

static const struct video_display_info display_pw_info
 
static const pw_stream_events stream_events
 

Macro Definition Documentation

◆ MOD_NAME

#define MOD_NAME   "[PW disp] "

Definition at line 28 of file pipewire.cpp.

Function Documentation

◆ display_pw_done()

static void display_pw_done ( void * state)
static

Definition at line 281 of file pipewire.cpp.

282{
283 auto s = std::unique_ptr<display_pw_state>(static_cast<display_pw_state *>(state));
284
285 {
286 pipewire_thread_loop_lock_guard lock(s->pw.pipewire_loop.get());
287 pw_stream_disconnect(s->stream.get());
288 }
289
290 pw_thread_loop_stop(s->pw.pipewire_loop.get());
291}
void ** state

References state.

◆ display_pw_get_property()

static bool display_pw_get_property ( void * state,
int property,
void * val,
size_t * len )
static

Definition at line 354 of file pipewire.cpp.

355{
356 [[maybe_unused]] auto s = static_cast<display_pw_state *>(state);
357
358 codec_t codecs[] = {UYVY, RGB, RGBA, BGR};
359 int rgb_shift[] = {0, 8, 16};
360
361 switch (property) {
363 if(*len < sizeof(codecs)) {
364 return false;
365 }
366 *len = sizeof(codecs);
367 memcpy(val, codecs, *len);
368 break;
370 if(sizeof(rgb_shift) > *len) {
371 return false;
372 }
373 memcpy(val, rgb_shift, sizeof(rgb_shift));
374 *len = sizeof(rgb_shift);
375 break;
376 default:
377 return false;
378 }
379 return true;
380}
int void * val
int void size_t * len
int property
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
codec_t
Definition types.h:65
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
Definition types.h:71
@ BGR
8-bit BGR
Definition types.h:88
@ RGBA
RGBA 8-bit, big-endian.
Definition types.h:70
static struct @76 codecs[]

References BGR, codecs, DISPLAY_PROPERTY_CODECS, DISPLAY_PROPERTY_RGB_SHIFT, len, property, RGB, RGBA, state, UYVY, and val.

◆ display_pw_getf()

static struct video_frame * display_pw_getf ( void * state)
static

Definition at line 293 of file pipewire.cpp.

294{
295 auto s = static_cast<display_pw_state *>(state);
296
297 auto get_dummy = [](display_pw_state *s){
298 if (!s->dummy_frame || video_desc_eq(video_desc_from_frame(s->dummy_frame.get()), s->desc))
299 {
300 s->dummy_frame.reset(vf_alloc_desc_data(s->desc));
301 }
302 return s->dummy_frame.get();
303 };
304
305 pipewire_thread_loop_lock_guard lock(s->pw.pipewire_loop.get());
306
307 const char *error = nullptr;
308 auto stream_state = pw_stream_get_state(s->stream.get(), &error);
309 if(stream_state != PW_STREAM_STATE_STREAMING)
310 return get_dummy(s);
311
312 struct pw_buffer *b = pw_stream_dequeue_buffer(s->stream.get());
313 if (!b) {
314 log_msg(LOG_LEVEL_WARNING, "Out of buffers!\n");
315 return get_dummy(s);
316 }
317
318 auto buf = static_cast<memfd_buffer *>(b->user_data);
319 s->in_flight_frame.reset(std::exchange(buf->f, nullptr));
320
321 b->buffer->datas[0].chunk->size = s->in_flight_frame->tiles[0].data_len;
322 b->buffer->datas[0].chunk->offset = 0;
323 b->buffer->datas[0].chunk->stride = vc_get_linesize(s->desc.width, s->desc.color_spec);
324
325 return s->in_flight_frame.get();
326}
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
int vc_get_linesize(unsigned int width, codec_t codec)
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.

References LOG_LEVEL_WARNING, log_msg(), state, vc_get_linesize(), vf_alloc_desc_data(), video_desc_eq(), and video_desc_from_frame().

Here is the call graph for this function:

◆ display_pw_help()

static void display_pw_help ( )
static

Definition at line 243 of file pipewire.cpp.

243 {
244 color_printf("Pipewire video output.\n");
245 color_printf("Usage\n");
246 color_printf(TERM_BOLD TERM_FG_RED "\t-d pipewire" TERM_FG_RESET "[:target=<device>]\n" TERM_RESET);
247 color_printf("\n");
248
249 color_printf("Devices:\n");
250 print_devices("Stream/Input/Video");
251}
int color_printf(const char *format,...)
#define TERM_BOLD
Definition color_out.h:48
#define TERM_FG_RED
Definition color_out.h:50
#define TERM_RESET
Definition color_out.h:47
#define TERM_FG_RESET
Definition color_out.h:59
void print_devices(std::string_view media_class)

References color_printf(), print_devices(), TERM_BOLD, TERM_FG_RED, TERM_FG_RESET, and TERM_RESET.

Referenced by display_pw_init().

Here is the call graph for this function:

◆ display_pw_init()

static void * display_pw_init ( struct module * ,
const char * cfg,
unsigned int  )
static

Definition at line 253 of file pipewire.cpp.

254{
255 auto s = std::make_unique<display_pw_state>();
256
257 std::string_view cfg_sv(cfg);
258 while(!cfg_sv.empty()){
259 auto tok = tokenize(cfg_sv, ':', '"');
260
261 auto key = tokenize(tok, '=');
262 auto val = tokenize(tok, '=');
263
264 if(key == "help"){
266 return INIT_NOERR;
267 } else if(key == "target"){
268 s->target = val;
269 }
270 }
271
273
274 log_msg(LOG_LEVEL_INFO, MOD_NAME "Compiled with libpipewire %s\n", pw_get_headers_version());
275 log_msg(LOG_LEVEL_INFO, MOD_NAME "Linked with libpipewire %s\n", pw_get_library_version());
276
277
278 return s.release();
279}
#define MOD_NAME
Definition pipewire.cpp:57
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define INIT_NOERR
Definition host.h:73
bool initialize_pw_common(pipewire_state_common &s, int fd)
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
const char * key
Definition cineform.cpp:125
static void display_pw_help()
Definition pipewire.cpp:243

References display_pw_help(), INIT_NOERR, initialize_pw_common(), key, LOG_LEVEL_INFO, log_msg(), MOD_NAME, tokenize(), and val.

Here is the call graph for this function:

◆ display_pw_probe()

static void display_pw_probe ( struct device_info ** available_cards,
int * count,
void(** deleter )(void *) )
static

Definition at line 449 of file pipewire.cpp.

449 {
451 *available_cards = NULL;
452 *count = 0;
453}
#define UNUSED(x)
int void(** deleter)(void *))
int count
Definition hd-rum.c:44

References count, deleter, and UNUSED.

◆ display_pw_putf()

static bool display_pw_putf ( void * state,
struct video_frame * frame,
long long flags )
static

Definition at line 329 of file pipewire.cpp.

330{
331 auto s = static_cast<display_pw_state *>(state);
332
333 if (flags == PUTF_DISCARD || frame == NULL || frame == s->dummy_frame.get()) {
334 return true;
335 }
336
337 pipewire_thread_loop_lock_guard lock(s->pw.pipewire_loop.get());
338
339 auto buf = static_cast<memfd_buffer *>(frame->callbacks.dispose_udata);
340
341 if(!buf->b){
342 //Frame is invalid - buffer got removed
343 return true;
344 }
345
346 assert(frame == s->in_flight_frame.get());
347 buf->f = s->in_flight_frame.release();
348
349 pw_stream_queue_buffer(s->stream.get(), buf->b);
350
351 return true;
352}
struct video_frame * frame
const char unsigned int flags
#define PUTF_DISCARD
struct video_frame_callbacks callbacks
Definition types.h:324

References video_frame::callbacks, video_frame_callbacks::dispose_udata, flags, frame, PUTF_DISCARD, and state.

◆ display_pw_reconfigure()

static bool display_pw_reconfigure ( void * state,
struct video_desc desc )
static

Definition at line 382 of file pipewire.cpp.

383{
384 auto s = static_cast<display_pw_state *>(state);
385
386 s->desc = desc;
387
388 std::string node_name = "ultragrid_out_";
389 {
390 char buf[32];
391 snprintf(buf, sizeof(buf), "%ld", (long) getpid());
392 node_name += buf;
393 }
394
395 auto props = pw_properties_new(
396 PW_KEY_MEDIA_TYPE, "Video",
397 PW_KEY_MEDIA_CATEGORY, "Source",
398 PW_KEY_MEDIA_ROLE, "Communication",
399 PW_KEY_APP_NAME, "UltraGrid",
400 PW_KEY_APP_ICON_NAME, "ultragrid",
401 PW_KEY_NODE_NAME, node_name.c_str(),
402 PW_KEY_NODE_DESCRIPTION, "UltraGrid playback",
403 STREAM_TARGET_PROPERTY_KEY, s->target.c_str(),
404 nullptr);
405
406 pipewire_thread_loop_lock_guard lock(s->pw.pipewire_loop.get());
407
408 s->stream.reset(pw_stream_new(
409 s->pw.pipewire_core.get(),
410 "UltraGrid video out",
411 props));
412
413 pw_stream_add_listener(
414 s->stream.get(),
415 &s->stream_listener.get(),
417 s);
418
419 const struct spa_pod *params[1];
420
421 auto framerate = SPA_FRACTION(
422 static_cast<unsigned>(get_framerate_n(desc.fps)),
423 static_cast<unsigned>(get_framerate_d(desc.fps)));
424 auto size = SPA_RECTANGLE(desc.width, desc.height);
425
426 std::byte buffer[1024];
427 auto pod_builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
428
429 params[0] = static_cast<spa_pod *> (spa_pod_builder_add_object( &pod_builder,
430 SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
431 SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
432 SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
433 SPA_FORMAT_VIDEO_format, SPA_POD_Id(pw_fmt_from_uv_codec(desc.color_spec)),
434 SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&size),
435 SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&framerate)));
436
437 pw_stream_connect(s->stream.get(),
438 PW_DIRECTION_OUTPUT,
439 PW_ID_ANY,
440 static_cast<pw_stream_flags>(
441 PW_STREAM_FLAG_AUTOCONNECT |
442 PW_STREAM_FLAG_DRIVER |
443 PW_STREAM_FLAG_ALLOC_BUFFERS),
444 &params[0], 1);
445
446 return true;
447}
struct video_desc desc
static const pw_stream_events stream_events
Definition pipewire.cpp:177
int get_framerate_d(double fps)
Definition misc.cpp:190
int get_framerate_n(double fps)
Definition misc.cpp:184
spa_video_format pw_fmt_from_uv_codec(codec_t codec)
#define STREAM_TARGET_PROPERTY_KEY
video_desc desc
Definition pipewire.cpp:136
codec_t color_spec
Definition types.h:182
double fps
Definition types.h:183
unsigned int width
width of every tile (!)
Definition types.h:179
unsigned int height
height of every tile (!)
Definition types.h:180

References video_desc::color_spec, desc, display_pw_state::desc, video_desc::fps, get_framerate_d(), get_framerate_n(), video_desc::height, pw_fmt_from_uv_codec(), state, stream_events, STREAM_TARGET_PROPERTY_KEY, and video_desc::width.

Here is the call graph for this function:

◆ on_add_buffer()

static void on_add_buffer ( void * data,
struct pw_buffer * buffer )
static

Definition at line 182 of file pipewire.cpp.

182 {
183 auto s = static_cast<display_pw_state *>(data);
184
185 int stride = vc_get_linesize(s->desc.width, s->desc.color_spec);
186 int size = stride * s->desc.height;
187
188 struct spa_data *d = buffer->buffer->datas;
189 if ((d->type & (1<<SPA_DATA_MemFd)) == 0) {
190 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Buffer doesn't support MemFd type\n");
191 return;
192 }
193
194 auto buf = memfd_buf_create(size);
195 buffer->user_data = memfd_buf_ref(buf);
196
197 d->type = SPA_DATA_MemFd;
198 d->flags = SPA_DATA_FLAG_READWRITE;
199 d->fd = buf->fd;
200 d->maxsize = buf->size;
201 d->mapoffset = 0;
202
203 auto ug_frame = vf_alloc_desc(s->desc);
204 buf->f = ug_frame;
205 buf->b = buffer;
206
207 ug_frame->callbacks.data_deleter = memfd_frame_data_deleter;
208 ug_frame->callbacks.dispose_udata = memfd_buf_ref(buf);
209 ug_frame->tiles[0].data = (char *) buf->ptr;
210
211 log_msg(LOG_LEVEL_VERBOSE, "Buffer added\n");
212}
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63

References LOG_LEVEL_ERROR, LOG_LEVEL_VERBOSE, log_msg(), MOD_NAME, vc_get_linesize(), and vf_alloc_desc().

Here is the call graph for this function:

◆ on_param_changed()

static void on_param_changed ( void * state,
uint32_t id,
const struct spa_pod * param )
static

Definition at line 153 of file pipewire.cpp.

153 {
154 auto s = static_cast<display_pw_state *>(state);
155
156 if(!param || id != SPA_PARAM_Format)
157 return;
158
159 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "on param changed\n");
160
161 struct spa_video_info_raw format;
162 spa_format_video_raw_parse(param, &format);
163
164 const int MAX_BUFFERS = 16;
165 int stride = vc_get_linesize(s->desc.width, s->desc.color_spec);
166
167 const struct spa_pod *params[1];
168 std::byte buffer[1024];
169 auto pod_builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
170
171 params[0] = (spa_pod *) spa_pod_builder_add_object(&pod_builder,
172 SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
173 SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 2, MAX_BUFFERS),
174 SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
175 SPA_PARAM_BUFFERS_size, SPA_POD_Int(stride * s->desc.height),
176 SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(1<<SPA_DATA_MemFd),
177 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride));
178
179 pw_stream_update_params(s->stream.get(), params, 1);
180}
const char * param
Definition settings.cpp:195

References LOG_LEVEL_VERBOSE, log_msg(), MOD_NAME, param, state, and vc_get_linesize().

Here is the call graph for this function:

◆ on_remove_buffer()

static void on_remove_buffer ( void * ,
struct pw_buffer * buffer )
static

Definition at line 214 of file pipewire.cpp.

214 {
215 auto buf = static_cast<memfd_buffer *>(buffer->user_data);
216
217 if(buf->f)
218 vf_free(buf->f);
219
220 buf->b = nullptr;
221 memfd_buf_unref(&buf);
222
223 log_msg(LOG_LEVEL_VERBOSE, "Buffer removed\n");
224}
void vf_free(struct video_frame *buf)
Frees video_frame structure.

References LOG_LEVEL_VERBOSE, log_msg(), and vf_free().

Here is the call graph for this function:

◆ on_state_changed()

static void on_state_changed ( void * state,
enum pw_stream_state old,
enum pw_stream_state new_state,
const char * error )
static

Definition at line 139 of file pipewire.cpp.

140{
141 auto s = static_cast<display_pw_state *>(state);
142 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Stream state change: %s -> %s\n",
143 pw_stream_state_as_string(old),
144 pw_stream_state_as_string(new_state));
145
146 if(error){
147 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Stream error: %s\n", error);
148 }
149
150 pw_thread_loop_signal(s->pw.pipewire_loop.get(), false);
151}
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61

References LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, log_msg(), MOD_NAME, and state.

Here is the call graph for this function:

◆ REGISTER_MODULE()

REGISTER_MODULE ( pipewire ,
& display_pw_info,
LIBRARY_CLASS_VIDEO_DISPLAY ,
VIDEO_DISPLAY_ABI_VERSION  )

Variable Documentation

◆ display_pw_info

const struct video_display_info display_pw_info
static
Initial value:
= {
NULL,
NULL,
NULL,
}
static struct video_frame * display_pw_getf(void *state)
Definition pipewire.cpp:293
static bool display_pw_get_property(void *state, int property, void *val, size_t *len)
Definition pipewire.cpp:354
static bool display_pw_putf(void *state, struct video_frame *frame, long long flags)
Definition pipewire.cpp:329
static void * display_pw_init(struct module *, const char *cfg, unsigned int)
Definition pipewire.cpp:253
static bool display_pw_reconfigure(void *state, struct video_desc desc)
Definition pipewire.cpp:382
static void display_pw_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
Definition pipewire.cpp:449
static void display_pw_done(void *state)
Definition pipewire.cpp:281

Definition at line 455 of file pipewire.cpp.

455 {
458 NULL, // _run
464 NULL, // _put_audio_frame
465 NULL, // _reconfigure_audio
466 MOD_NAME,
467};

◆ stream_events

const pw_stream_events stream_events
static
Initial value:
= {
.version = PW_VERSION_STREAM_EVENTS,
.destroy = nullptr,
.state_changed = on_state_changed,
.control_info = nullptr,
.io_changed = nullptr,
.param_changed = on_param_changed,
.add_buffer = on_add_buffer,
.remove_buffer = on_remove_buffer,
.process = nullptr,
.drained = nullptr,
}
static void on_state_changed(void *state, enum pw_stream_state old, enum pw_stream_state new_state, const char *error)
Definition pipewire.cpp:162
static void on_param_changed(void *state, uint32_t id, const struct spa_pod *param)
Definition pipewire.cpp:103
static void on_remove_buffer(void *, struct pw_buffer *)
Definition pipewire.cpp:322
static void on_add_buffer(void *, struct pw_buffer *)
Definition pipewire.cpp:317

Definition at line 226 of file pipewire.cpp.

226 {
227 .version = PW_VERSION_STREAM_EVENTS,
228 .destroy = nullptr,
229 .state_changed = on_state_changed,
230 .control_info = nullptr,
231 .io_changed = nullptr,
232 .param_changed = on_param_changed,
233 .add_buffer = on_add_buffer,
234 .remove_buffer = on_remove_buffer,
235 .process = nullptr,
236 .drained = nullptr,
237#if PW_MAJOR > 0 || PW_MINOR > 3 || (PW_MINOR == 3 && PW_MICRO > 39)
238 .command = nullptr,
239 .trigger_done = nullptr,
240#endif
241};