26#include <spa/param/video/format-utils.h>
28#define MOD_NAME "[PW disp] "
32 using unique_frame = std::unique_ptr<video_frame, frame_deleter>;
35 memfd_buffer() =
default;
36 memfd_buffer(
const memfd_buffer&) =
delete;
39 memfd_buffer& operator=(
const memfd_buffer&) =
delete;
45 std::atomic<int> ref_count = 0;
68 pw_buffer *b =
nullptr;
72 memfd_buffer::~memfd_buffer(){
73 assert(ref_count == 0);
82 memfd_buffer *memfd_buf_create(
size_t size){
83 auto buf = std::make_unique<memfd_buffer>();
85 buf->fd = memfd_create(
"ultragrid-display", MFD_CLOEXEC);
92 if(ftruncate(buf->fd, size) < 0){
97 buf->ptr = mmap(
nullptr, size, PROT_READ|PROT_WRITE,
98 MAP_SHARED, buf->fd, 0);
100 return buf.release();
103 void memfd_buf_unref(memfd_buffer **buffer){
107 int refcount = buf->ref_count.fetch_sub(1) - 1;
112 memfd_buffer *memfd_buf_ref(memfd_buffer *buf){
121 memfd_buf_unref(&buf);
139static void on_state_changed(
void *
state,
enum pw_stream_state old,
enum pw_stream_state new_state,
const char *error)
143 pw_stream_state_as_string(old),
144 pw_stream_state_as_string(new_state));
150 pw_thread_loop_signal(s->pw.pipewire_loop.get(),
false);
156 if(!
param ||
id != SPA_PARAM_Format)
161 struct spa_video_info_raw format;
162 spa_format_video_raw_parse(
param, &format);
164 const int MAX_BUFFERS = 16;
167 const struct spa_pod *
params[1];
168 std::byte buffer[1024];
169 auto pod_builder = SPA_POD_BUILDER_INIT(buffer,
sizeof(buffer));
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));
179 pw_stream_update_params(s->stream.get(),
params, 1);
186 int size = stride * s->desc.height;
188 struct spa_data *d = buffer->buffer->datas;
189 if ((d->type & (1<<SPA_DATA_MemFd)) == 0) {
194 auto buf = memfd_buf_create(size);
195 buffer->user_data = memfd_buf_ref(buf);
197 d->type = SPA_DATA_MemFd;
198 d->flags = SPA_DATA_FLAG_READWRITE;
200 d->maxsize = buf->size;
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;
215 auto buf =
static_cast<memfd_buffer *
>(buffer->user_data);
221 memfd_buf_unref(&buf);
227 .version = PW_VERSION_STREAM_EVENTS,
230 .control_info =
nullptr,
231 .io_changed =
nullptr,
237#if PW_MAJOR > 0 || PW_MINOR > 3 || (PW_MINOR == 3 && PW_MICRO > 39)
239 .trigger_done =
nullptr,
255 auto s = std::make_unique<display_pw_state>();
257 std::string_view cfg_sv(cfg);
258 while(!cfg_sv.empty()){
259 auto tok =
tokenize(cfg_sv,
':',
'"');
267 }
else if(
key ==
"target"){
287 pw_stream_disconnect(s->stream.get());
290 pw_thread_loop_stop(s->pw.pipewire_loop.get());
302 return s->dummy_frame.get();
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)
312 struct pw_buffer *b = pw_stream_dequeue_buffer(s->stream.get());
318 auto buf =
static_cast<memfd_buffer *
>(b->user_data);
319 s->in_flight_frame.reset(std::exchange(buf->f,
nullptr));
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);
325 return s->in_flight_frame.get();
346 assert(
frame == s->in_flight_frame.get());
347 buf->f = s->in_flight_frame.release();
349 pw_stream_queue_buffer(s->stream.get(), buf->b);
359 int rgb_shift[] = {0, 8, 16};
370 if(
sizeof(rgb_shift) > *
len) {
373 memcpy(
val, rgb_shift,
sizeof(rgb_shift));
374 *
len =
sizeof(rgb_shift);
388 std::string node_name =
"ultragrid_out_";
391 snprintf(buf,
sizeof(buf),
"%ld", (
long) getpid());
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",
408 s->stream.reset(pw_stream_new(
409 s->pw.pipewire_core.get(),
410 "UltraGrid video out",
413 pw_stream_add_listener(
415 &s->stream_listener.get(),
419 const struct spa_pod *
params[1];
421 auto framerate = SPA_FRACTION(
426 std::byte buffer[1024];
427 auto pod_builder = SPA_POD_BUILDER_INIT(buffer,
sizeof(buffer));
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),
434 SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&size),
435 SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&framerate)));
437 pw_stream_connect(s->stream.get(),
440 static_cast<pw_stream_flags
>(
441 PW_STREAM_FLAG_AUTOCONNECT |
442 PW_STREAM_FLAG_DRIVER |
443 PW_STREAM_FLAG_ALLOC_BUFFERS),
451 *available_cards = NULL;
struct video_frame * frame
const char unsigned int flags
int void(** deleter)(void *))
void vf_free(struct video_frame *buf)
Frees video_frame structure.
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(struct video_desc desc)
Allocates video frame accordig given desc.
struct video_frame * vf_alloc_desc_data(struct video_desc desc)
static void on_state_changed(void *state, enum pw_stream_state old, enum pw_stream_state new_state, const char *error)
static const pw_stream_events stream_events
static void on_param_changed(void *state, uint32_t id, const struct spa_pod *param)
int color_printf(const char *format,...)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
#define LOG_LEVEL_WARNING
less severe errors
#define VIDEO_DISPLAY_ABI_VERSION
@ DISPLAY_PROPERTY_RGB_SHIFT
red,green,blue shift - int[3] (bits)
@ DISPLAY_PROPERTY_CODECS
list of natively supported codecs - codec_t[]
@ LIBRARY_CLASS_VIDEO_DISPLAY
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
int get_framerate_d(double fps)
int get_framerate_n(double fps)
void print_devices(std::string_view media_class)
bool initialize_pw_common(pipewire_state_common &s, int fd)
raii_uniq_handle< pw_stream, pw_stream_destroy > pw_stream_uniq
spa_video_format pw_fmt_from_uv_codec(codec_t codec)
#define STREAM_TARGET_PROPERTY_KEY
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
spa_hook_uniq stream_listener
unique_frame in_flight_frame
void operator()(video_frame *f)
unsigned int width
width of every tile (!)
unsigned int height
height of every tile (!)
Struct video_frame represents a video frame and contains video description.
struct video_frame_callbacks callbacks
@ RGB
RGB 8-bit (packed into 24-bit word)
@ UYVY
YCbCr 422 8-bit - Cb Y0 Cr Y1.
@ RGBA
RGBA 8-bit, big-endian.
This is an umbrella header for video functions.
static void on_remove_buffer(void *, struct pw_buffer *)
static void on_add_buffer(void *, struct pw_buffer *)
std::unique_ptr< video_frame, frame_deleter > unique_frame
static struct video_frame * display_pw_getf(void *state)
static bool display_pw_get_property(void *state, int property, void *val, size_t *len)
static bool display_pw_putf(void *state, struct video_frame *frame, long long flags)
static void * display_pw_init(struct module *, const char *cfg, unsigned int)
static bool display_pw_reconfigure(void *state, struct video_desc desc)
static void display_pw_probe(struct device_info **available_cards, int *count, void(**deleter)(void *))
static void display_pw_help()
static const struct video_display_info display_pw_info
static void display_pw_done(void *state)
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.