blob: 2c65f502c2917c23290a51bf1c24eac8d50129f1 [file] [log] [blame]
Michael Olbrichd5d5aa92019-04-23 12:34:05 +02001/*
2 * Copyright © 2019 Pengutronix, Michael Olbrich <m.olbrich@pengutronix.de>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the
13 * next paragraph) shall be included in all copies or substantial
14 * portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
Marius Vladc4076ef2020-08-28 16:59:36 +030026#include <libweston/pipewire-plugin.h>
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020027#include "backend.h"
28#include "libweston-internal.h"
29#include "shared/timespec-util.h"
30#include <libweston/backend-drm.h>
31#include <libweston/weston-log.h>
32
33#include <sys/mman.h>
34#include <errno.h>
35#include <unistd.h>
36
James Hilliard80b585f2020-07-06 00:58:02 -060037#include <pipewire/pipewire.h>
38
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020039#include <spa/param/format-utils.h>
40#include <spa/param/video/format-utils.h>
41#include <spa/utils/defs.h>
42
James Hilliard80b585f2020-07-06 00:58:02 -060043#if PW_CHECK_VERSION(0, 2, 90)
44#include <spa/buffer/meta.h>
45#include <spa/utils/result.h>
46#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020047
48#define PROP_RANGE(min, max) 2, (min), (max)
49
James Hilliard80b585f2020-07-06 00:58:02 -060050#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020051struct type {
52 struct spa_type_media_type media_type;
53 struct spa_type_media_subtype media_subtype;
54 struct spa_type_format_video format_video;
55 struct spa_type_video_format video_format;
56};
James Hilliard80b585f2020-07-06 00:58:02 -060057#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020058
59struct weston_pipewire {
60 struct weston_compositor *compositor;
61 struct wl_list output_list;
62 struct wl_listener destroy_listener;
63 const struct weston_drm_virtual_output_api *virtual_output_api;
64
65 struct weston_log_scope *debug;
66
67 struct pw_loop *loop;
68 struct wl_event_source *loop_source;
69
James Hilliard80b585f2020-07-06 00:58:02 -060070#if PW_CHECK_VERSION(0, 2, 90)
71 struct pw_context *context;
72#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020073 struct pw_core *core;
74 struct pw_type *t;
James Hilliard80b585f2020-07-06 00:58:02 -060075#if PW_CHECK_VERSION(0, 2, 90)
76 struct spa_hook core_listener;
77#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020078 struct type type;
79
80 struct pw_remote *remote;
81 struct spa_hook remote_listener;
James Hilliard80b585f2020-07-06 00:58:02 -060082#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020083};
84
85struct pipewire_output {
86 struct weston_output *output;
87 void (*saved_destroy)(struct weston_output *output);
88 int (*saved_enable)(struct weston_output *output);
89 int (*saved_disable)(struct weston_output *output);
90 int (*saved_start_repaint_loop)(struct weston_output *output);
91
92 struct weston_head *head;
93
94 struct weston_pipewire *pipewire;
95
96 uint32_t seq;
97 struct pw_stream *stream;
98 struct spa_hook stream_listener;
99
100 struct spa_video_info_raw video_format;
101
102 struct wl_event_source *finish_frame_timer;
103 struct wl_list link;
104 bool submitted_frame;
Michael Olbricha24a3262020-08-19 08:43:15 +0200105 enum dpms_enum dpms;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200106};
107
108struct pipewire_frame_data {
109 struct pipewire_output *output;
110 int fd;
111 int stride;
112 struct drm_fb *drm_buffer;
113 int fence_sync_fd;
114 struct wl_event_source *fence_sync_event_source;
115};
116
James Hilliard80b585f2020-07-06 00:58:02 -0600117#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200118static inline void init_type(struct type *type, struct spa_type_map *map)
119{
120 spa_type_media_type_map(map, &type->media_type);
121 spa_type_media_subtype_map(map, &type->media_subtype);
122 spa_type_format_video_map(map, &type->format_video);
123 spa_type_video_format_map(map, &type->video_format);
124}
James Hilliard80b585f2020-07-06 00:58:02 -0600125#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200126
127static void
128pipewire_debug_impl(struct weston_pipewire *pipewire,
129 struct pipewire_output *output,
130 const char *fmt, va_list ap)
131{
132 FILE *fp;
133 char *logstr;
134 size_t logsize;
135 char timestr[128];
136
137 if (!weston_log_scope_is_enabled(pipewire->debug))
138 return;
139
140 fp = open_memstream(&logstr, &logsize);
141 if (!fp)
142 return;
143
144 weston_log_scope_timestamp(pipewire->debug, timestr, sizeof timestr);
145 fprintf(fp, "%s", timestr);
146
147 if (output)
148 fprintf(fp, "[%s]", output->output->name);
149
150 fprintf(fp, " ");
151 vfprintf(fp, fmt, ap);
152 fprintf(fp, "\n");
153
154 if (fclose(fp) == 0)
155 weston_log_scope_write(pipewire->debug, logstr, logsize);
156
157 free(logstr);
158}
159
James Hilliard80b585f2020-07-06 00:58:02 -0600160#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200161static void
162pipewire_debug(struct weston_pipewire *pipewire, const char *fmt, ...)
163{
164 va_list ap;
165
166 va_start(ap, fmt);
167 pipewire_debug_impl(pipewire, NULL, fmt, ap);
168 va_end(ap);
169}
James Hilliard80b585f2020-07-06 00:58:02 -0600170#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200171
172static void
173pipewire_output_debug(struct pipewire_output *output, const char *fmt, ...)
174{
175 va_list ap;
176
177 va_start(ap, fmt);
178 pipewire_debug_impl(output->pipewire, output, fmt, ap);
179 va_end(ap);
180}
181
182static struct weston_pipewire *
183weston_pipewire_get(struct weston_compositor *compositor);
184
185static struct pipewire_output *
186lookup_pipewire_output(struct weston_output *base_output)
187{
188 struct weston_compositor *c = base_output->compositor;
189 struct weston_pipewire *pipewire = weston_pipewire_get(c);
190 struct pipewire_output *output;
191
192 wl_list_for_each(output, &pipewire->output_list, link) {
193 if (output->output == base_output)
194 return output;
195 }
196 return NULL;
197}
198
199static void
200pipewire_output_handle_frame(struct pipewire_output *output, int fd,
201 int stride, struct drm_fb *drm_buffer)
202{
203 const struct weston_drm_virtual_output_api *api =
204 output->pipewire->virtual_output_api;
205 size_t size = output->output->height * stride;
James Hilliard80b585f2020-07-06 00:58:02 -0600206#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200207 struct pw_type *t = output->pipewire->t;
James Hilliard80b585f2020-07-06 00:58:02 -0600208#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200209 struct pw_buffer *buffer;
210 struct spa_buffer *spa_buffer;
211 struct spa_meta_header *h;
212 void *ptr;
213
214 if (pw_stream_get_state(output->stream, NULL) !=
215 PW_STREAM_STATE_STREAMING)
216 goto out;
217
218 buffer = pw_stream_dequeue_buffer(output->stream);
219 if (!buffer) {
220 weston_log("Failed to dequeue a pipewire buffer\n");
221 goto out;
222 }
223
224 spa_buffer = buffer->buffer;
225
James Hilliard80b585f2020-07-06 00:58:02 -0600226#if PW_CHECK_VERSION(0, 2, 90)
227 if ((h = spa_buffer_find_meta_data(spa_buffer, SPA_META_Header,
228 sizeof(struct spa_meta_header)))) {
229#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200230 if ((h = spa_buffer_find_meta(spa_buffer, t->meta.Header))) {
James Hilliard80b585f2020-07-06 00:58:02 -0600231#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200232 h->pts = -1;
233 h->flags = 0;
234 h->seq = output->seq++;
235 h->dts_offset = 0;
236 }
237
238 ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
239 memcpy(spa_buffer->datas[0].data, ptr, size);
240 munmap(ptr, size);
241
242 spa_buffer->datas[0].chunk->offset = 0;
243 spa_buffer->datas[0].chunk->stride = stride;
244 spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
245
246 pipewire_output_debug(output, "push frame");
247 pw_stream_queue_buffer(output->stream, buffer);
248
249out:
250 close(fd);
251 output->submitted_frame = true;
252 api->buffer_released(drm_buffer);
253}
254
255static int
256pipewire_output_fence_sync_handler(int fd, uint32_t mask, void *data)
257{
258 struct pipewire_frame_data *frame_data = data;
259 struct pipewire_output *output = frame_data->output;
260
261 pipewire_output_handle_frame(output, frame_data->fd, frame_data->stride,
262 frame_data->drm_buffer);
263
264 wl_event_source_remove(frame_data->fence_sync_event_source);
265 close(frame_data->fence_sync_fd);
266 free(frame_data);
267
268 return 0;
269}
270
271static int
272pipewire_output_submit_frame(struct weston_output *base_output, int fd,
273 int stride, struct drm_fb *drm_buffer)
274{
275 struct pipewire_output *output = lookup_pipewire_output(base_output);
276 struct weston_pipewire *pipewire = output->pipewire;
277 const struct weston_drm_virtual_output_api *api =
278 pipewire->virtual_output_api;
279 struct wl_event_loop *loop;
280 struct pipewire_frame_data *frame_data;
281 int fence_sync_fd;
282
283 pipewire_output_debug(output, "submit frame: fd = %d drm_fb = %p",
284 fd, drm_buffer);
285
286 fence_sync_fd = api->get_fence_sync_fd(output->output);
287 if (fence_sync_fd == -1) {
288 pipewire_output_handle_frame(output, fd, stride, drm_buffer);
289 return 0;
290 }
291
292 frame_data = zalloc(sizeof *frame_data);
293 if (!frame_data) {
294 close(fence_sync_fd);
295 pipewire_output_handle_frame(output, fd, stride, drm_buffer);
296 return 0;
297 }
298
299 loop = wl_display_get_event_loop(pipewire->compositor->wl_display);
300
301 frame_data->output = output;
302 frame_data->fd = fd;
303 frame_data->stride = stride;
304 frame_data->drm_buffer = drm_buffer;
305 frame_data->fence_sync_fd = fence_sync_fd;
306 frame_data->fence_sync_event_source =
307 wl_event_loop_add_fd(loop, frame_data->fence_sync_fd,
308 WL_EVENT_READABLE,
309 pipewire_output_fence_sync_handler,
310 frame_data);
311
312 return 0;
313}
314
315static void
316pipewire_output_timer_update(struct pipewire_output *output)
317{
318 int64_t msec;
319 int32_t refresh;
320
321 if (pw_stream_get_state(output->stream, NULL) ==
322 PW_STREAM_STATE_STREAMING)
323 refresh = output->output->current_mode->refresh;
324 else
325 refresh = 1000;
326
327 msec = millihz_to_nsec(refresh) / 1000000;
328 wl_event_source_timer_update(output->finish_frame_timer, msec);
329}
330
331static int
332pipewire_output_finish_frame_handler(void *data)
333{
334 struct pipewire_output *output = data;
335 const struct weston_drm_virtual_output_api *api
336 = output->pipewire->virtual_output_api;
337 struct timespec now;
338
339 if (output->submitted_frame) {
340 struct weston_compositor *c = output->pipewire->compositor;
341 output->submitted_frame = false;
342 weston_compositor_read_presentation_clock(c, &now);
343 api->finish_frame(output->output, &now, 0);
344 }
345
Michael Olbricha24a3262020-08-19 08:43:15 +0200346 if (output->dpms == WESTON_DPMS_ON)
347 pipewire_output_timer_update(output);
348 else
349 wl_event_source_timer_update(output->finish_frame_timer, 0);
350
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200351 return 0;
352}
353
354static void
355pipewire_output_destroy(struct weston_output *base_output)
356{
357 struct pipewire_output *output = lookup_pipewire_output(base_output);
358 struct weston_mode *mode, *next;
359
360 wl_list_for_each_safe(mode, next, &base_output->mode_list, link) {
361 wl_list_remove(&mode->link);
362 free(mode);
363 }
364
365 output->saved_destroy(base_output);
366
367 pw_stream_destroy(output->stream);
368
369 wl_list_remove(&output->link);
370 weston_head_release(output->head);
371 free(output->head);
372 free(output);
373}
374
375static int
376pipewire_output_start_repaint_loop(struct weston_output *base_output)
377{
378 struct pipewire_output *output = lookup_pipewire_output(base_output);
379
380 pipewire_output_debug(output, "start repaint loop");
381 output->saved_start_repaint_loop(base_output);
382
383 pipewire_output_timer_update(output);
384
385 return 0;
386}
387
Michael Olbricha24a3262020-08-19 08:43:15 +0200388static void
389pipewire_set_dpms(struct weston_output *base_output, enum dpms_enum level)
390{
391 struct pipewire_output *output = lookup_pipewire_output(base_output);
392
393 if (output->dpms == level)
394 return;
395
396 output->dpms = level;
397 pipewire_output_finish_frame_handler(output);
398}
399
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200400static int
401pipewire_output_connect(struct pipewire_output *output)
402{
James Hilliard80b585f2020-07-06 00:58:02 -0600403#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200404 struct weston_pipewire *pipewire = output->pipewire;
405 struct type *type = &pipewire->type;
James Hilliard80b585f2020-07-06 00:58:02 -0600406#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200407 uint8_t buffer[1024];
408 struct spa_pod_builder builder =
409 SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
410 const struct spa_pod *params[1];
James Hilliard80b585f2020-07-06 00:58:02 -0600411#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200412 struct pw_type *t = pipewire->t;
James Hilliard80b585f2020-07-06 00:58:02 -0600413#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200414 int frame_rate = output->output->current_mode->refresh / 1000;
415 int width = output->output->width;
416 int height = output->output->height;
417 int ret;
418
James Hilliard80b585f2020-07-06 00:58:02 -0600419#if PW_CHECK_VERSION(0, 2, 90)
420 params[0] = spa_pod_builder_add_object(&builder,
421 SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
422 SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
423 SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
424 SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_BGRx),
425 SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&SPA_RECTANGLE(width, height)),
426 SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&SPA_FRACTION (0, 1)),
427 SPA_FORMAT_VIDEO_maxFramerate,
428 SPA_POD_CHOICE_RANGE_Fraction(&SPA_FRACTION(frame_rate, 1),
429 &SPA_FRACTION(1, 1),
430 &SPA_FRACTION(frame_rate, 1)));
431
432 ret = pw_stream_connect(output->stream, PW_DIRECTION_OUTPUT, SPA_ID_INVALID,
433 (PW_STREAM_FLAG_DRIVER |
434 PW_STREAM_FLAG_MAP_BUFFERS),
435 params, 1);
436#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200437 params[0] = spa_pod_builder_object(&builder,
438 t->param.idEnumFormat, t->spa_format,
439 "I", type->media_type.video,
440 "I", type->media_subtype.raw,
441 ":", type->format_video.format,
442 "I", type->video_format.BGRx,
443 ":", type->format_video.size,
444 "R", &SPA_RECTANGLE(width, height),
445 ":", type->format_video.framerate,
446 "F", &SPA_FRACTION(0, 1),
447 ":", type->format_video.max_framerate,
448 "Fru", &SPA_FRACTION(frame_rate, 1),
449 PROP_RANGE(&SPA_FRACTION(1, 1),
450 &SPA_FRACTION(frame_rate, 1)));
451
452 ret = pw_stream_connect(output->stream, PW_DIRECTION_OUTPUT, NULL,
453 (PW_STREAM_FLAG_DRIVER |
454 PW_STREAM_FLAG_MAP_BUFFERS),
455 params, 1);
James Hilliard80b585f2020-07-06 00:58:02 -0600456#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200457 if (ret != 0) {
458 weston_log("Failed to connect pipewire stream: %s",
459 spa_strerror(ret));
460 return -1;
461 }
462
463 return 0;
464}
465
466static int
467pipewire_output_enable(struct weston_output *base_output)
468{
469 struct pipewire_output *output = lookup_pipewire_output(base_output);
470 struct weston_compositor *c = base_output->compositor;
471 const struct weston_drm_virtual_output_api *api
472 = output->pipewire->virtual_output_api;
473 struct wl_event_loop *loop;
474 int ret;
475
476 api->set_submit_frame_cb(base_output, pipewire_output_submit_frame);
477
478 ret = pipewire_output_connect(output);
479 if (ret < 0)
480 return ret;
481
482 ret = output->saved_enable(base_output);
483 if (ret < 0)
484 return ret;
485
486 output->saved_start_repaint_loop = base_output->start_repaint_loop;
487 base_output->start_repaint_loop = pipewire_output_start_repaint_loop;
Michael Olbricha24a3262020-08-19 08:43:15 +0200488 base_output->set_dpms = pipewire_set_dpms;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200489
490 loop = wl_display_get_event_loop(c->wl_display);
491 output->finish_frame_timer =
492 wl_event_loop_add_timer(loop,
493 pipewire_output_finish_frame_handler,
494 output);
Michael Olbricha24a3262020-08-19 08:43:15 +0200495 output->dpms = WESTON_DPMS_ON;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200496
497 return 0;
498}
499
500static int
501pipewire_output_disable(struct weston_output *base_output)
502{
503 struct pipewire_output *output = lookup_pipewire_output(base_output);
504
505 wl_event_source_remove(output->finish_frame_timer);
506
507 pw_stream_disconnect(output->stream);
508
509 return output->saved_disable(base_output);
510}
511
512static void
513pipewire_output_stream_state_changed(void *data, enum pw_stream_state old,
514 enum pw_stream_state state,
515 const char *error_message)
516{
517 struct pipewire_output *output = data;
518
519 pipewire_output_debug(output, "state changed %s -> %s",
520 pw_stream_state_as_string(old),
521 pw_stream_state_as_string(state));
522
523 switch (state) {
524 case PW_STREAM_STATE_STREAMING:
525 weston_output_schedule_repaint(output->output);
526 break;
527 default:
528 break;
529 }
530}
531
532static void
James Hilliard80b585f2020-07-06 00:58:02 -0600533#if PW_CHECK_VERSION(0, 2, 90)
534pipewire_output_stream_param_changed(void *data, uint32_t id, const struct spa_pod *format)
535#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200536pipewire_output_stream_format_changed(void *data, const struct spa_pod *format)
James Hilliard80b585f2020-07-06 00:58:02 -0600537#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200538{
539 struct pipewire_output *output = data;
James Hilliard80b585f2020-07-06 00:58:02 -0600540#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200541 struct weston_pipewire *pipewire = output->pipewire;
James Hilliard80b585f2020-07-06 00:58:02 -0600542#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200543 uint8_t buffer[1024];
544 struct spa_pod_builder builder =
545 SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
546 const struct spa_pod *params[2];
James Hilliard80b585f2020-07-06 00:58:02 -0600547#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200548 struct pw_type *t = pipewire->t;
James Hilliard80b585f2020-07-06 00:58:02 -0600549#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200550 int32_t width, height, stride, size;
551 const int bpp = 4;
552
553 if (!format) {
554 pipewire_output_debug(output, "format = None");
James Hilliard80b585f2020-07-06 00:58:02 -0600555#if PW_CHECK_VERSION(0, 2, 90)
556 pw_stream_update_params(output->stream, NULL, 0);
557#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200558 pw_stream_finish_format(output->stream, 0, NULL, 0);
James Hilliard80b585f2020-07-06 00:58:02 -0600559#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200560 return;
561 }
562
James Hilliard80b585f2020-07-06 00:58:02 -0600563#if PW_CHECK_VERSION(0, 2, 90)
564 spa_format_video_raw_parse(format, &output->video_format);
565#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200566 spa_format_video_raw_parse(format, &output->video_format,
567 &pipewire->type.format_video);
James Hilliard80b585f2020-07-06 00:58:02 -0600568#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200569
570 width = output->video_format.size.width;
571 height = output->video_format.size.height;
572 stride = SPA_ROUND_UP_N(width * bpp, 4);
573 size = height * stride;
574
575 pipewire_output_debug(output, "format = %dx%d", width, height);
576
James Hilliard80b585f2020-07-06 00:58:02 -0600577#if PW_CHECK_VERSION(0, 2, 90)
578 params[0] = spa_pod_builder_add_object(&builder,
579 SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
580 SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
581 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride),
582 SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(4, 2, 8),
583 SPA_PARAM_BUFFERS_align, SPA_POD_Int(16));
584
585 params[1] = spa_pod_builder_add_object(&builder,
586 SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
587 SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
588 SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
589
590 pw_stream_update_params(output->stream, params, 2);
591#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200592 params[0] = spa_pod_builder_object(&builder,
593 t->param.idBuffers, t->param_buffers.Buffers,
594 ":", t->param_buffers.size,
595 "i", size,
596 ":", t->param_buffers.stride,
597 "i", stride,
598 ":", t->param_buffers.buffers,
599 "iru", 4, PROP_RANGE(2, 8),
600 ":", t->param_buffers.align,
601 "i", 16);
602
603 params[1] = spa_pod_builder_object(&builder,
604 t->param.idMeta, t->param_meta.Meta,
605 ":", t->param_meta.type, "I", t->meta.Header,
606 ":", t->param_meta.size, "i", sizeof(struct spa_meta_header));
607
608 pw_stream_finish_format(output->stream, 0, params, 2);
James Hilliard80b585f2020-07-06 00:58:02 -0600609#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200610}
611
612static const struct pw_stream_events stream_events = {
613 PW_VERSION_STREAM_EVENTS,
614 .state_changed = pipewire_output_stream_state_changed,
James Hilliard80b585f2020-07-06 00:58:02 -0600615#if PW_CHECK_VERSION(0, 2, 90)
616 .param_changed = pipewire_output_stream_param_changed,
617#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200618 .format_changed = pipewire_output_stream_format_changed,
James Hilliard80b585f2020-07-06 00:58:02 -0600619#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200620};
621
622static struct weston_output *
623pipewire_output_create(struct weston_compositor *c, char *name)
624{
625 struct weston_pipewire *pipewire = weston_pipewire_get(c);
626 struct pipewire_output *output;
627 struct weston_head *head;
628 const struct weston_drm_virtual_output_api *api;
629 const char *make = "Weston";
630 const char *model = "Virtual Display";
631 const char *serial_number = "unknown";
632 const char *connector_name = "pipewire";
633
634 if (!name || !strlen(name))
635 return NULL;
636
637 api = pipewire->virtual_output_api;
638
639 output = zalloc(sizeof *output);
640 if (!output)
641 return NULL;
642
643 head = zalloc(sizeof *head);
644 if (!head)
645 goto err;
646
James Hilliard80b585f2020-07-06 00:58:02 -0600647#if PW_CHECK_VERSION(0, 2, 90)
648 output->stream = pw_stream_new(pipewire->core, name, NULL);
649#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200650 output->stream = pw_stream_new(pipewire->remote, name, NULL);
James Hilliard80b585f2020-07-06 00:58:02 -0600651#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200652 if (!output->stream) {
653 weston_log("Cannot initialize pipewire stream\n");
654 goto err;
655 }
656
657 pw_stream_add_listener(output->stream, &output->stream_listener,
658 &stream_events, output);
659
660 output->output = api->create_output(c, name);
661 if (!output->output) {
662 weston_log("Cannot create virtual output\n");
663 goto err;
664 }
665
666 output->saved_destroy = output->output->destroy;
667 output->output->destroy = pipewire_output_destroy;
668 output->saved_enable = output->output->enable;
669 output->output->enable = pipewire_output_enable;
670 output->saved_disable = output->output->disable;
671 output->output->disable = pipewire_output_disable;
672 output->pipewire = pipewire;
673 wl_list_insert(pipewire->output_list.prev, &output->link);
674
675 weston_head_init(head, connector_name);
676 weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE);
677 weston_head_set_monitor_strings(head, make, model, serial_number);
678 head->compositor = c;
679 output->head = head;
680
681 weston_output_attach_head(output->output, head);
682
683 pipewire_output_debug(output, "created");
684
685 return output->output;
686err:
687 if (output->stream)
688 pw_stream_destroy(output->stream);
689 if (head)
690 free(head);
691 free(output);
692 return NULL;
693}
694
695static bool
696pipewire_output_is_pipewire(struct weston_output *output)
697{
698 return lookup_pipewire_output(output) != NULL;
699}
700
701static int
702pipewire_output_set_mode(struct weston_output *base_output, const char *modeline)
703{
704 struct pipewire_output *output = lookup_pipewire_output(base_output);
705 const struct weston_drm_virtual_output_api *api =
706 output->pipewire->virtual_output_api;
707 struct weston_mode *mode;
708 int n, width, height, refresh = 0;
709
710 if (output == NULL) {
711 weston_log("Output is not pipewire.\n");
712 return -1;
713 }
714
715 if (!modeline)
716 return -1;
717
718 n = sscanf(modeline, "%dx%d@%d", &width, &height, &refresh);
719 if (n != 2 && n != 3)
720 return -1;
721
722 if (pw_stream_get_state(output->stream, NULL) !=
723 PW_STREAM_STATE_UNCONNECTED) {
724 return -1;
725 }
726
727 mode = zalloc(sizeof *mode);
728 if (!mode)
729 return -1;
730
731 pipewire_output_debug(output, "mode = %dx%d@%d", width, height, refresh);
732
733 mode->flags = WL_OUTPUT_MODE_CURRENT;
734 mode->width = width;
735 mode->height = height;
736 mode->refresh = (refresh ? refresh : 60) * 1000LL;
737
738 wl_list_insert(base_output->mode_list.prev, &mode->link);
739
740 base_output->current_mode = mode;
741
742 api->set_gbm_format(base_output, "XRGB8888");
743
744 return 0;
745}
746
747static void
748pipewire_output_set_seat(struct weston_output *output, const char *seat)
749{
750}
751
752static void
753weston_pipewire_destroy(struct wl_listener *l, void *data)
754{
755 struct weston_pipewire *pipewire =
756 wl_container_of(l, pipewire, destroy_listener);
757
Leandro Ribeirof0149642019-12-18 15:52:18 -0300758 weston_log_scope_destroy(pipewire->debug);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200759 pipewire->debug = NULL;
760
761 wl_event_source_remove(pipewire->loop_source);
762 pw_loop_leave(pipewire->loop);
763 pw_loop_destroy(pipewire->loop);
764}
765
766static struct weston_pipewire *
767weston_pipewire_get(struct weston_compositor *compositor)
768{
769 struct wl_listener *listener;
770 struct weston_pipewire *pipewire;
771
772 listener = wl_signal_get(&compositor->destroy_signal,
773 weston_pipewire_destroy);
774 if (!listener)
775 return NULL;
776
777 pipewire = wl_container_of(listener, pipewire, destroy_listener);
778 return pipewire;
779}
780
781static int
782weston_pipewire_loop_handler(int fd, uint32_t mask, void *data)
783{
784 struct weston_pipewire *pipewire = data;
785 int ret;
786
787 ret = pw_loop_iterate(pipewire->loop, 0);
788 if (ret < 0)
789 weston_log("pipewire_loop_iterate failed: %s",
790 spa_strerror(ret));
791
792 return 0;
793}
794
James Hilliard80b585f2020-07-06 00:58:02 -0600795#if PW_CHECK_VERSION(0, 2, 90)
796static void
797weston_pipewire_error(void *data, uint32_t id, int seq, int res,
798 const char *error)
799{
800 weston_log("pipewire remote error: %s\n", error);
801}
802#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200803static void
804weston_pipewire_state_changed(void *data, enum pw_remote_state old,
805 enum pw_remote_state state, const char *error)
806{
807 struct weston_pipewire *pipewire = data;
808
809 pipewire_debug(pipewire, "[remote] state changed %s -> %s",
810 pw_remote_state_as_string(old),
811 pw_remote_state_as_string(state));
812
813 switch (state) {
814 case PW_REMOTE_STATE_ERROR:
815 weston_log("pipewire remote error: %s\n", error);
816 break;
817 case PW_REMOTE_STATE_CONNECTED:
818 weston_log("connected to pipewire daemon\n");
819 break;
820 default:
821 break;
822 }
823}
James Hilliard80b585f2020-07-06 00:58:02 -0600824#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200825
826
James Hilliard80b585f2020-07-06 00:58:02 -0600827#if PW_CHECK_VERSION(0, 2, 90)
828static const struct pw_core_events core_events = {
829 PW_VERSION_CORE_EVENTS,
830 .error = weston_pipewire_error,
831};
832#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200833static const struct pw_remote_events remote_events = {
834 PW_VERSION_REMOTE_EVENTS,
835 .state_changed = weston_pipewire_state_changed,
836};
James Hilliard80b585f2020-07-06 00:58:02 -0600837#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200838
839static int
840weston_pipewire_init(struct weston_pipewire *pipewire)
841{
842 struct wl_event_loop *loop;
843
844 pw_init(NULL, NULL);
845
846 pipewire->loop = pw_loop_new(NULL);
847 if (!pipewire->loop)
848 return -1;
849
850 pw_loop_enter(pipewire->loop);
851
James Hilliard80b585f2020-07-06 00:58:02 -0600852#if PW_CHECK_VERSION(0, 2, 90)
853 pipewire->context = pw_context_new(pipewire->loop, NULL, 0);
854#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200855 pipewire->core = pw_core_new(pipewire->loop, NULL);
856 pipewire->t = pw_core_get_type(pipewire->core);
857 init_type(&pipewire->type, pipewire->t->map);
James Hilliard80b585f2020-07-06 00:58:02 -0600858#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200859
James Hilliard80b585f2020-07-06 00:58:02 -0600860#if PW_CHECK_VERSION(0, 2, 90)
861 pw_core_add_listener(pipewire->core,
862 &pipewire->core_listener,
863 &core_events, pipewire);
864#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200865 pipewire->remote = pw_remote_new(pipewire->core, NULL, 0);
866 pw_remote_add_listener(pipewire->remote,
867 &pipewire->remote_listener,
868 &remote_events, pipewire);
869
870 pw_remote_connect(pipewire->remote);
871
872 while (true) {
873 enum pw_remote_state state;
874 const char *error = NULL;
875 int ret;
876
877 state = pw_remote_get_state(pipewire->remote, &error);
878 if (state == PW_REMOTE_STATE_CONNECTED)
879 break;
880
881 if (state == PW_REMOTE_STATE_ERROR) {
882 weston_log("pipewire error: %s\n", error);
883 goto err;
884 }
885
886 ret = pw_loop_iterate(pipewire->loop, -1);
887 if (ret < 0) {
888 weston_log("pipewire_loop_iterate failed: %s",
889 spa_strerror(ret));
890 goto err;
891 }
892 }
James Hilliard80b585f2020-07-06 00:58:02 -0600893#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200894
895 loop = wl_display_get_event_loop(pipewire->compositor->wl_display);
896 pipewire->loop_source =
897 wl_event_loop_add_fd(loop, pw_loop_get_fd(pipewire->loop),
898 WL_EVENT_READABLE,
899 weston_pipewire_loop_handler,
900 pipewire);
901
902 return 0;
James Hilliard80b585f2020-07-06 00:58:02 -0600903#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200904err:
905 if (pipewire->remote)
906 pw_remote_destroy(pipewire->remote);
907 pw_loop_leave(pipewire->loop);
908 pw_loop_destroy(pipewire->loop);
909 return -1;
James Hilliard80b585f2020-07-06 00:58:02 -0600910#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200911}
912
913static const struct weston_pipewire_api pipewire_api = {
914 pipewire_output_create,
915 pipewire_output_is_pipewire,
916 pipewire_output_set_mode,
917 pipewire_output_set_seat,
918};
919
920WL_EXPORT int
921weston_module_init(struct weston_compositor *compositor)
922{
923 int ret;
924 struct weston_pipewire *pipewire;
925 const struct weston_drm_virtual_output_api *api =
926 weston_drm_virtual_output_get_api(compositor);
927
928 if (!api)
929 return -1;
930
931 pipewire = zalloc(sizeof *pipewire);
932 if (!pipewire)
933 return -1;
934
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200935 if (!weston_compositor_add_destroy_listener_once(compositor,
936 &pipewire->destroy_listener,
937 weston_pipewire_destroy)) {
938 free(pipewire);
939 return 0;
940 }
941
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200942 pipewire->virtual_output_api = api;
943 pipewire->compositor = compositor;
944 wl_list_init(&pipewire->output_list);
945
946 ret = weston_plugin_api_register(compositor, WESTON_PIPEWIRE_API_NAME,
947 &pipewire_api, sizeof(pipewire_api));
948
949 if (ret < 0) {
950 weston_log("Failed to register pipewire API.\n");
951 goto failed;
952 }
953
954 ret = weston_pipewire_init(pipewire);
955 if (ret < 0) {
956 weston_log("Failed to initialize pipewire.\n");
957 goto failed;
958 }
959
Leandro Ribeiro5976dbb2019-12-18 15:21:03 -0300960 pipewire->debug =
Leandro Ribeiro23289352019-12-26 16:44:56 -0300961 weston_compositor_add_log_scope(compositor, "pipewire",
962 "Debug messages from pipewire plugin\n",
963 NULL, NULL, NULL);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200964
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200965 return 0;
966
967failed:
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200968 wl_list_remove(&pipewire->destroy_listener.link);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200969 free(pipewire);
970 return -1;
971}