blob: 55705acfc186c9ff99c1eba66a72831faa3c8a7e [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 Vladbb7ed372020-11-05 19:30:51 +020026#include "config.h"
27
Marius Vladc4076ef2020-08-28 16:59:36 +030028#include <libweston/pipewire-plugin.h>
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020029#include "backend.h"
30#include "libweston-internal.h"
31#include "shared/timespec-util.h"
32#include <libweston/backend-drm.h>
33#include <libweston/weston-log.h>
34
35#include <sys/mman.h>
36#include <errno.h>
37#include <unistd.h>
38
James Hilliard80b585f2020-07-06 00:58:02 -060039#include <pipewire/pipewire.h>
40
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020041#include <spa/param/format-utils.h>
42#include <spa/param/video/format-utils.h>
43#include <spa/utils/defs.h>
44
James Hilliard80b585f2020-07-06 00:58:02 -060045#if PW_CHECK_VERSION(0, 2, 90)
46#include <spa/buffer/meta.h>
47#include <spa/utils/result.h>
48#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020049
50#define PROP_RANGE(min, max) 2, (min), (max)
51
James Hilliard80b585f2020-07-06 00:58:02 -060052#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020053struct type {
54 struct spa_type_media_type media_type;
55 struct spa_type_media_subtype media_subtype;
56 struct spa_type_format_video format_video;
57 struct spa_type_video_format video_format;
58};
James Hilliard80b585f2020-07-06 00:58:02 -060059#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020060
61struct weston_pipewire {
62 struct weston_compositor *compositor;
63 struct wl_list output_list;
64 struct wl_listener destroy_listener;
65 const struct weston_drm_virtual_output_api *virtual_output_api;
66
67 struct weston_log_scope *debug;
68
69 struct pw_loop *loop;
70 struct wl_event_source *loop_source;
71
James Hilliard80b585f2020-07-06 00:58:02 -060072#if PW_CHECK_VERSION(0, 2, 90)
73 struct pw_context *context;
74#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020075 struct pw_core *core;
76 struct pw_type *t;
James Hilliard80b585f2020-07-06 00:58:02 -060077#if PW_CHECK_VERSION(0, 2, 90)
78 struct spa_hook core_listener;
79#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020080 struct type type;
81
82 struct pw_remote *remote;
83 struct spa_hook remote_listener;
James Hilliard80b585f2020-07-06 00:58:02 -060084#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +020085};
86
87struct pipewire_output {
88 struct weston_output *output;
89 void (*saved_destroy)(struct weston_output *output);
90 int (*saved_enable)(struct weston_output *output);
91 int (*saved_disable)(struct weston_output *output);
92 int (*saved_start_repaint_loop)(struct weston_output *output);
93
94 struct weston_head *head;
95
96 struct weston_pipewire *pipewire;
97
98 uint32_t seq;
99 struct pw_stream *stream;
100 struct spa_hook stream_listener;
101
102 struct spa_video_info_raw video_format;
103
104 struct wl_event_source *finish_frame_timer;
105 struct wl_list link;
106 bool submitted_frame;
Michael Olbricha24a3262020-08-19 08:43:15 +0200107 enum dpms_enum dpms;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200108};
109
110struct pipewire_frame_data {
111 struct pipewire_output *output;
112 int fd;
113 int stride;
114 struct drm_fb *drm_buffer;
115 int fence_sync_fd;
116 struct wl_event_source *fence_sync_event_source;
117};
118
James Hilliard80b585f2020-07-06 00:58:02 -0600119#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200120static inline void init_type(struct type *type, struct spa_type_map *map)
121{
122 spa_type_media_type_map(map, &type->media_type);
123 spa_type_media_subtype_map(map, &type->media_subtype);
124 spa_type_format_video_map(map, &type->format_video);
125 spa_type_video_format_map(map, &type->video_format);
126}
James Hilliard80b585f2020-07-06 00:58:02 -0600127#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200128
129static void
130pipewire_debug_impl(struct weston_pipewire *pipewire,
131 struct pipewire_output *output,
132 const char *fmt, va_list ap)
133{
134 FILE *fp;
135 char *logstr;
136 size_t logsize;
137 char timestr[128];
138
139 if (!weston_log_scope_is_enabled(pipewire->debug))
140 return;
141
142 fp = open_memstream(&logstr, &logsize);
143 if (!fp)
144 return;
145
146 weston_log_scope_timestamp(pipewire->debug, timestr, sizeof timestr);
147 fprintf(fp, "%s", timestr);
148
149 if (output)
150 fprintf(fp, "[%s]", output->output->name);
151
152 fprintf(fp, " ");
153 vfprintf(fp, fmt, ap);
154 fprintf(fp, "\n");
155
156 if (fclose(fp) == 0)
157 weston_log_scope_write(pipewire->debug, logstr, logsize);
158
159 free(logstr);
160}
161
James Hilliard80b585f2020-07-06 00:58:02 -0600162#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200163static void
164pipewire_debug(struct weston_pipewire *pipewire, const char *fmt, ...)
165{
166 va_list ap;
167
168 va_start(ap, fmt);
169 pipewire_debug_impl(pipewire, NULL, fmt, ap);
170 va_end(ap);
171}
James Hilliard80b585f2020-07-06 00:58:02 -0600172#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200173
174static void
175pipewire_output_debug(struct pipewire_output *output, const char *fmt, ...)
176{
177 va_list ap;
178
179 va_start(ap, fmt);
180 pipewire_debug_impl(output->pipewire, output, fmt, ap);
181 va_end(ap);
182}
183
184static struct weston_pipewire *
185weston_pipewire_get(struct weston_compositor *compositor);
186
187static struct pipewire_output *
188lookup_pipewire_output(struct weston_output *base_output)
189{
190 struct weston_compositor *c = base_output->compositor;
191 struct weston_pipewire *pipewire = weston_pipewire_get(c);
192 struct pipewire_output *output;
193
194 wl_list_for_each(output, &pipewire->output_list, link) {
195 if (output->output == base_output)
196 return output;
197 }
198 return NULL;
199}
200
201static void
202pipewire_output_handle_frame(struct pipewire_output *output, int fd,
203 int stride, struct drm_fb *drm_buffer)
204{
205 const struct weston_drm_virtual_output_api *api =
206 output->pipewire->virtual_output_api;
207 size_t size = output->output->height * stride;
James Hilliard80b585f2020-07-06 00:58:02 -0600208#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200209 struct pw_type *t = output->pipewire->t;
James Hilliard80b585f2020-07-06 00:58:02 -0600210#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200211 struct pw_buffer *buffer;
212 struct spa_buffer *spa_buffer;
213 struct spa_meta_header *h;
214 void *ptr;
215
216 if (pw_stream_get_state(output->stream, NULL) !=
217 PW_STREAM_STATE_STREAMING)
218 goto out;
219
220 buffer = pw_stream_dequeue_buffer(output->stream);
221 if (!buffer) {
222 weston_log("Failed to dequeue a pipewire buffer\n");
223 goto out;
224 }
225
226 spa_buffer = buffer->buffer;
227
James Hilliard80b585f2020-07-06 00:58:02 -0600228#if PW_CHECK_VERSION(0, 2, 90)
229 if ((h = spa_buffer_find_meta_data(spa_buffer, SPA_META_Header,
230 sizeof(struct spa_meta_header)))) {
231#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200232 if ((h = spa_buffer_find_meta(spa_buffer, t->meta.Header))) {
James Hilliard80b585f2020-07-06 00:58:02 -0600233#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200234 h->pts = -1;
235 h->flags = 0;
236 h->seq = output->seq++;
237 h->dts_offset = 0;
238 }
239
240 ptr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
241 memcpy(spa_buffer->datas[0].data, ptr, size);
242 munmap(ptr, size);
243
244 spa_buffer->datas[0].chunk->offset = 0;
245 spa_buffer->datas[0].chunk->stride = stride;
246 spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
247
248 pipewire_output_debug(output, "push frame");
249 pw_stream_queue_buffer(output->stream, buffer);
250
251out:
252 close(fd);
253 output->submitted_frame = true;
254 api->buffer_released(drm_buffer);
255}
256
257static int
258pipewire_output_fence_sync_handler(int fd, uint32_t mask, void *data)
259{
260 struct pipewire_frame_data *frame_data = data;
261 struct pipewire_output *output = frame_data->output;
262
263 pipewire_output_handle_frame(output, frame_data->fd, frame_data->stride,
264 frame_data->drm_buffer);
265
266 wl_event_source_remove(frame_data->fence_sync_event_source);
267 close(frame_data->fence_sync_fd);
268 free(frame_data);
269
270 return 0;
271}
272
273static int
274pipewire_output_submit_frame(struct weston_output *base_output, int fd,
275 int stride, struct drm_fb *drm_buffer)
276{
277 struct pipewire_output *output = lookup_pipewire_output(base_output);
278 struct weston_pipewire *pipewire = output->pipewire;
279 const struct weston_drm_virtual_output_api *api =
280 pipewire->virtual_output_api;
281 struct wl_event_loop *loop;
282 struct pipewire_frame_data *frame_data;
283 int fence_sync_fd;
284
285 pipewire_output_debug(output, "submit frame: fd = %d drm_fb = %p",
286 fd, drm_buffer);
287
288 fence_sync_fd = api->get_fence_sync_fd(output->output);
289 if (fence_sync_fd == -1) {
290 pipewire_output_handle_frame(output, fd, stride, drm_buffer);
291 return 0;
292 }
293
294 frame_data = zalloc(sizeof *frame_data);
295 if (!frame_data) {
296 close(fence_sync_fd);
297 pipewire_output_handle_frame(output, fd, stride, drm_buffer);
298 return 0;
299 }
300
301 loop = wl_display_get_event_loop(pipewire->compositor->wl_display);
302
303 frame_data->output = output;
304 frame_data->fd = fd;
305 frame_data->stride = stride;
306 frame_data->drm_buffer = drm_buffer;
307 frame_data->fence_sync_fd = fence_sync_fd;
308 frame_data->fence_sync_event_source =
309 wl_event_loop_add_fd(loop, frame_data->fence_sync_fd,
310 WL_EVENT_READABLE,
311 pipewire_output_fence_sync_handler,
312 frame_data);
313
314 return 0;
315}
316
317static void
318pipewire_output_timer_update(struct pipewire_output *output)
319{
320 int64_t msec;
321 int32_t refresh;
322
323 if (pw_stream_get_state(output->stream, NULL) ==
324 PW_STREAM_STATE_STREAMING)
325 refresh = output->output->current_mode->refresh;
326 else
327 refresh = 1000;
328
329 msec = millihz_to_nsec(refresh) / 1000000;
330 wl_event_source_timer_update(output->finish_frame_timer, msec);
331}
332
333static int
334pipewire_output_finish_frame_handler(void *data)
335{
336 struct pipewire_output *output = data;
337 const struct weston_drm_virtual_output_api *api
338 = output->pipewire->virtual_output_api;
339 struct timespec now;
340
341 if (output->submitted_frame) {
342 struct weston_compositor *c = output->pipewire->compositor;
343 output->submitted_frame = false;
344 weston_compositor_read_presentation_clock(c, &now);
345 api->finish_frame(output->output, &now, 0);
346 }
347
Michael Olbricha24a3262020-08-19 08:43:15 +0200348 if (output->dpms == WESTON_DPMS_ON)
349 pipewire_output_timer_update(output);
350 else
351 wl_event_source_timer_update(output->finish_frame_timer, 0);
352
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200353 return 0;
354}
355
356static void
357pipewire_output_destroy(struct weston_output *base_output)
358{
359 struct pipewire_output *output = lookup_pipewire_output(base_output);
360 struct weston_mode *mode, *next;
361
362 wl_list_for_each_safe(mode, next, &base_output->mode_list, link) {
363 wl_list_remove(&mode->link);
364 free(mode);
365 }
366
367 output->saved_destroy(base_output);
368
369 pw_stream_destroy(output->stream);
370
371 wl_list_remove(&output->link);
372 weston_head_release(output->head);
373 free(output->head);
374 free(output);
375}
376
377static int
378pipewire_output_start_repaint_loop(struct weston_output *base_output)
379{
380 struct pipewire_output *output = lookup_pipewire_output(base_output);
381
382 pipewire_output_debug(output, "start repaint loop");
383 output->saved_start_repaint_loop(base_output);
384
385 pipewire_output_timer_update(output);
386
387 return 0;
388}
389
Michael Olbricha24a3262020-08-19 08:43:15 +0200390static void
391pipewire_set_dpms(struct weston_output *base_output, enum dpms_enum level)
392{
393 struct pipewire_output *output = lookup_pipewire_output(base_output);
394
395 if (output->dpms == level)
396 return;
397
398 output->dpms = level;
399 pipewire_output_finish_frame_handler(output);
400}
401
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200402static int
403pipewire_output_connect(struct pipewire_output *output)
404{
James Hilliard80b585f2020-07-06 00:58:02 -0600405#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200406 struct weston_pipewire *pipewire = output->pipewire;
407 struct type *type = &pipewire->type;
James Hilliard80b585f2020-07-06 00:58:02 -0600408#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200409 uint8_t buffer[1024];
410 struct spa_pod_builder builder =
411 SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
412 const struct spa_pod *params[1];
James Hilliard80b585f2020-07-06 00:58:02 -0600413#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200414 struct pw_type *t = pipewire->t;
James Hilliard80b585f2020-07-06 00:58:02 -0600415#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200416 int frame_rate = output->output->current_mode->refresh / 1000;
417 int width = output->output->width;
418 int height = output->output->height;
419 int ret;
420
James Hilliard80b585f2020-07-06 00:58:02 -0600421#if PW_CHECK_VERSION(0, 2, 90)
422 params[0] = spa_pod_builder_add_object(&builder,
423 SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
424 SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
425 SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
426 SPA_FORMAT_VIDEO_format, SPA_POD_Id(SPA_VIDEO_FORMAT_BGRx),
427 SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&SPA_RECTANGLE(width, height)),
428 SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&SPA_FRACTION (0, 1)),
429 SPA_FORMAT_VIDEO_maxFramerate,
430 SPA_POD_CHOICE_RANGE_Fraction(&SPA_FRACTION(frame_rate, 1),
431 &SPA_FRACTION(1, 1),
432 &SPA_FRACTION(frame_rate, 1)));
433
434 ret = pw_stream_connect(output->stream, PW_DIRECTION_OUTPUT, SPA_ID_INVALID,
435 (PW_STREAM_FLAG_DRIVER |
436 PW_STREAM_FLAG_MAP_BUFFERS),
437 params, 1);
438#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200439 params[0] = spa_pod_builder_object(&builder,
440 t->param.idEnumFormat, t->spa_format,
441 "I", type->media_type.video,
442 "I", type->media_subtype.raw,
443 ":", type->format_video.format,
444 "I", type->video_format.BGRx,
445 ":", type->format_video.size,
446 "R", &SPA_RECTANGLE(width, height),
447 ":", type->format_video.framerate,
448 "F", &SPA_FRACTION(0, 1),
449 ":", type->format_video.max_framerate,
450 "Fru", &SPA_FRACTION(frame_rate, 1),
451 PROP_RANGE(&SPA_FRACTION(1, 1),
452 &SPA_FRACTION(frame_rate, 1)));
453
454 ret = pw_stream_connect(output->stream, PW_DIRECTION_OUTPUT, NULL,
455 (PW_STREAM_FLAG_DRIVER |
456 PW_STREAM_FLAG_MAP_BUFFERS),
457 params, 1);
James Hilliard80b585f2020-07-06 00:58:02 -0600458#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200459 if (ret != 0) {
460 weston_log("Failed to connect pipewire stream: %s",
461 spa_strerror(ret));
462 return -1;
463 }
464
465 return 0;
466}
467
468static int
469pipewire_output_enable(struct weston_output *base_output)
470{
471 struct pipewire_output *output = lookup_pipewire_output(base_output);
472 struct weston_compositor *c = base_output->compositor;
473 const struct weston_drm_virtual_output_api *api
474 = output->pipewire->virtual_output_api;
475 struct wl_event_loop *loop;
476 int ret;
477
478 api->set_submit_frame_cb(base_output, pipewire_output_submit_frame);
479
480 ret = pipewire_output_connect(output);
481 if (ret < 0)
482 return ret;
483
484 ret = output->saved_enable(base_output);
485 if (ret < 0)
486 return ret;
487
488 output->saved_start_repaint_loop = base_output->start_repaint_loop;
489 base_output->start_repaint_loop = pipewire_output_start_repaint_loop;
Michael Olbricha24a3262020-08-19 08:43:15 +0200490 base_output->set_dpms = pipewire_set_dpms;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200491
492 loop = wl_display_get_event_loop(c->wl_display);
493 output->finish_frame_timer =
494 wl_event_loop_add_timer(loop,
495 pipewire_output_finish_frame_handler,
496 output);
Michael Olbricha24a3262020-08-19 08:43:15 +0200497 output->dpms = WESTON_DPMS_ON;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200498
499 return 0;
500}
501
502static int
503pipewire_output_disable(struct weston_output *base_output)
504{
505 struct pipewire_output *output = lookup_pipewire_output(base_output);
506
507 wl_event_source_remove(output->finish_frame_timer);
508
509 pw_stream_disconnect(output->stream);
510
511 return output->saved_disable(base_output);
512}
513
514static void
515pipewire_output_stream_state_changed(void *data, enum pw_stream_state old,
516 enum pw_stream_state state,
517 const char *error_message)
518{
519 struct pipewire_output *output = data;
520
521 pipewire_output_debug(output, "state changed %s -> %s",
522 pw_stream_state_as_string(old),
523 pw_stream_state_as_string(state));
524
525 switch (state) {
526 case PW_STREAM_STATE_STREAMING:
527 weston_output_schedule_repaint(output->output);
528 break;
529 default:
530 break;
531 }
532}
533
534static void
James Hilliard80b585f2020-07-06 00:58:02 -0600535#if PW_CHECK_VERSION(0, 2, 90)
536pipewire_output_stream_param_changed(void *data, uint32_t id, const struct spa_pod *format)
537#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200538pipewire_output_stream_format_changed(void *data, const struct spa_pod *format)
James Hilliard80b585f2020-07-06 00:58:02 -0600539#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200540{
541 struct pipewire_output *output = data;
James Hilliard80b585f2020-07-06 00:58:02 -0600542#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200543 struct weston_pipewire *pipewire = output->pipewire;
James Hilliard80b585f2020-07-06 00:58:02 -0600544#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200545 uint8_t buffer[1024];
546 struct spa_pod_builder builder =
547 SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
548 const struct spa_pod *params[2];
James Hilliard80b585f2020-07-06 00:58:02 -0600549#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200550 struct pw_type *t = pipewire->t;
James Hilliard80b585f2020-07-06 00:58:02 -0600551#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200552 int32_t width, height, stride, size;
553 const int bpp = 4;
554
555 if (!format) {
556 pipewire_output_debug(output, "format = None");
James Hilliard80b585f2020-07-06 00:58:02 -0600557#if PW_CHECK_VERSION(0, 2, 90)
558 pw_stream_update_params(output->stream, NULL, 0);
559#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200560 pw_stream_finish_format(output->stream, 0, NULL, 0);
James Hilliard80b585f2020-07-06 00:58:02 -0600561#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200562 return;
563 }
564
James Hilliard80b585f2020-07-06 00:58:02 -0600565#if PW_CHECK_VERSION(0, 2, 90)
566 spa_format_video_raw_parse(format, &output->video_format);
567#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200568 spa_format_video_raw_parse(format, &output->video_format,
569 &pipewire->type.format_video);
James Hilliard80b585f2020-07-06 00:58:02 -0600570#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200571
572 width = output->video_format.size.width;
573 height = output->video_format.size.height;
574 stride = SPA_ROUND_UP_N(width * bpp, 4);
575 size = height * stride;
576
577 pipewire_output_debug(output, "format = %dx%d", width, height);
578
James Hilliard80b585f2020-07-06 00:58:02 -0600579#if PW_CHECK_VERSION(0, 2, 90)
580 params[0] = spa_pod_builder_add_object(&builder,
581 SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
582 SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
583 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride),
584 SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(4, 2, 8),
585 SPA_PARAM_BUFFERS_align, SPA_POD_Int(16));
586
587 params[1] = spa_pod_builder_add_object(&builder,
588 SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
589 SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
590 SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
591
592 pw_stream_update_params(output->stream, params, 2);
593#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200594 params[0] = spa_pod_builder_object(&builder,
595 t->param.idBuffers, t->param_buffers.Buffers,
596 ":", t->param_buffers.size,
597 "i", size,
598 ":", t->param_buffers.stride,
599 "i", stride,
600 ":", t->param_buffers.buffers,
601 "iru", 4, PROP_RANGE(2, 8),
602 ":", t->param_buffers.align,
603 "i", 16);
604
605 params[1] = spa_pod_builder_object(&builder,
606 t->param.idMeta, t->param_meta.Meta,
607 ":", t->param_meta.type, "I", t->meta.Header,
608 ":", t->param_meta.size, "i", sizeof(struct spa_meta_header));
609
610 pw_stream_finish_format(output->stream, 0, params, 2);
James Hilliard80b585f2020-07-06 00:58:02 -0600611#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200612}
613
614static const struct pw_stream_events stream_events = {
615 PW_VERSION_STREAM_EVENTS,
616 .state_changed = pipewire_output_stream_state_changed,
James Hilliard80b585f2020-07-06 00:58:02 -0600617#if PW_CHECK_VERSION(0, 2, 90)
618 .param_changed = pipewire_output_stream_param_changed,
619#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200620 .format_changed = pipewire_output_stream_format_changed,
James Hilliard80b585f2020-07-06 00:58:02 -0600621#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200622};
623
624static struct weston_output *
625pipewire_output_create(struct weston_compositor *c, char *name)
626{
627 struct weston_pipewire *pipewire = weston_pipewire_get(c);
628 struct pipewire_output *output;
629 struct weston_head *head;
630 const struct weston_drm_virtual_output_api *api;
631 const char *make = "Weston";
632 const char *model = "Virtual Display";
633 const char *serial_number = "unknown";
634 const char *connector_name = "pipewire";
Marius Vladbb7ed372020-11-05 19:30:51 +0200635 char *remoting_name;
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200636
637 if (!name || !strlen(name))
638 return NULL;
639
640 api = pipewire->virtual_output_api;
641
642 output = zalloc(sizeof *output);
643 if (!output)
644 return NULL;
645
646 head = zalloc(sizeof *head);
647 if (!head)
648 goto err;
649
James Hilliard80b585f2020-07-06 00:58:02 -0600650#if PW_CHECK_VERSION(0, 2, 90)
651 output->stream = pw_stream_new(pipewire->core, name, NULL);
652#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200653 output->stream = pw_stream_new(pipewire->remote, name, NULL);
James Hilliard80b585f2020-07-06 00:58:02 -0600654#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200655 if (!output->stream) {
656 weston_log("Cannot initialize pipewire stream\n");
657 goto err;
658 }
659
660 pw_stream_add_listener(output->stream, &output->stream_listener,
661 &stream_events, output);
662
663 output->output = api->create_output(c, name);
664 if (!output->output) {
665 weston_log("Cannot create virtual output\n");
666 goto err;
667 }
668
669 output->saved_destroy = output->output->destroy;
670 output->output->destroy = pipewire_output_destroy;
671 output->saved_enable = output->output->enable;
672 output->output->enable = pipewire_output_enable;
673 output->saved_disable = output->output->disable;
674 output->output->disable = pipewire_output_disable;
675 output->pipewire = pipewire;
676 wl_list_insert(pipewire->output_list.prev, &output->link);
677
Marius Vladbb7ed372020-11-05 19:30:51 +0200678 asprintf(&remoting_name, "%s-%s", connector_name, name);
679 weston_head_init(head, remoting_name);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200680 weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE);
681 weston_head_set_monitor_strings(head, make, model, serial_number);
682 head->compositor = c;
683 output->head = head;
684
685 weston_output_attach_head(output->output, head);
686
Marius Vladbb7ed372020-11-05 19:30:51 +0200687 free(remoting_name);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200688 pipewire_output_debug(output, "created");
689
690 return output->output;
691err:
692 if (output->stream)
693 pw_stream_destroy(output->stream);
694 if (head)
695 free(head);
696 free(output);
697 return NULL;
698}
699
700static bool
701pipewire_output_is_pipewire(struct weston_output *output)
702{
703 return lookup_pipewire_output(output) != NULL;
704}
705
706static int
707pipewire_output_set_mode(struct weston_output *base_output, const char *modeline)
708{
709 struct pipewire_output *output = lookup_pipewire_output(base_output);
710 const struct weston_drm_virtual_output_api *api =
711 output->pipewire->virtual_output_api;
712 struct weston_mode *mode;
713 int n, width, height, refresh = 0;
714
715 if (output == NULL) {
716 weston_log("Output is not pipewire.\n");
717 return -1;
718 }
719
720 if (!modeline)
721 return -1;
722
723 n = sscanf(modeline, "%dx%d@%d", &width, &height, &refresh);
724 if (n != 2 && n != 3)
725 return -1;
726
727 if (pw_stream_get_state(output->stream, NULL) !=
728 PW_STREAM_STATE_UNCONNECTED) {
729 return -1;
730 }
731
732 mode = zalloc(sizeof *mode);
733 if (!mode)
734 return -1;
735
736 pipewire_output_debug(output, "mode = %dx%d@%d", width, height, refresh);
737
738 mode->flags = WL_OUTPUT_MODE_CURRENT;
739 mode->width = width;
740 mode->height = height;
741 mode->refresh = (refresh ? refresh : 60) * 1000LL;
742
743 wl_list_insert(base_output->mode_list.prev, &mode->link);
744
745 base_output->current_mode = mode;
746
747 api->set_gbm_format(base_output, "XRGB8888");
748
749 return 0;
750}
751
752static void
753pipewire_output_set_seat(struct weston_output *output, const char *seat)
754{
755}
756
757static void
758weston_pipewire_destroy(struct wl_listener *l, void *data)
759{
760 struct weston_pipewire *pipewire =
761 wl_container_of(l, pipewire, destroy_listener);
762
Leandro Ribeirof0149642019-12-18 15:52:18 -0300763 weston_log_scope_destroy(pipewire->debug);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200764 pipewire->debug = NULL;
765
766 wl_event_source_remove(pipewire->loop_source);
767 pw_loop_leave(pipewire->loop);
768 pw_loop_destroy(pipewire->loop);
769}
770
771static struct weston_pipewire *
772weston_pipewire_get(struct weston_compositor *compositor)
773{
774 struct wl_listener *listener;
775 struct weston_pipewire *pipewire;
776
777 listener = wl_signal_get(&compositor->destroy_signal,
778 weston_pipewire_destroy);
779 if (!listener)
780 return NULL;
781
782 pipewire = wl_container_of(listener, pipewire, destroy_listener);
783 return pipewire;
784}
785
786static int
787weston_pipewire_loop_handler(int fd, uint32_t mask, void *data)
788{
789 struct weston_pipewire *pipewire = data;
790 int ret;
791
792 ret = pw_loop_iterate(pipewire->loop, 0);
793 if (ret < 0)
794 weston_log("pipewire_loop_iterate failed: %s",
795 spa_strerror(ret));
796
797 return 0;
798}
799
James Hilliard80b585f2020-07-06 00:58:02 -0600800#if PW_CHECK_VERSION(0, 2, 90)
801static void
802weston_pipewire_error(void *data, uint32_t id, int seq, int res,
803 const char *error)
804{
805 weston_log("pipewire remote error: %s\n", error);
806}
807#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200808static void
809weston_pipewire_state_changed(void *data, enum pw_remote_state old,
810 enum pw_remote_state state, const char *error)
811{
812 struct weston_pipewire *pipewire = data;
813
814 pipewire_debug(pipewire, "[remote] state changed %s -> %s",
815 pw_remote_state_as_string(old),
816 pw_remote_state_as_string(state));
817
818 switch (state) {
819 case PW_REMOTE_STATE_ERROR:
820 weston_log("pipewire remote error: %s\n", error);
821 break;
822 case PW_REMOTE_STATE_CONNECTED:
823 weston_log("connected to pipewire daemon\n");
824 break;
825 default:
826 break;
827 }
828}
James Hilliard80b585f2020-07-06 00:58:02 -0600829#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200830
831
James Hilliard80b585f2020-07-06 00:58:02 -0600832#if PW_CHECK_VERSION(0, 2, 90)
833static const struct pw_core_events core_events = {
834 PW_VERSION_CORE_EVENTS,
835 .error = weston_pipewire_error,
836};
837#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200838static const struct pw_remote_events remote_events = {
839 PW_VERSION_REMOTE_EVENTS,
840 .state_changed = weston_pipewire_state_changed,
841};
James Hilliard80b585f2020-07-06 00:58:02 -0600842#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200843
844static int
845weston_pipewire_init(struct weston_pipewire *pipewire)
846{
847 struct wl_event_loop *loop;
848
849 pw_init(NULL, NULL);
850
851 pipewire->loop = pw_loop_new(NULL);
852 if (!pipewire->loop)
853 return -1;
854
855 pw_loop_enter(pipewire->loop);
856
James Hilliard80b585f2020-07-06 00:58:02 -0600857#if PW_CHECK_VERSION(0, 2, 90)
858 pipewire->context = pw_context_new(pipewire->loop, NULL, 0);
Ambareesh Balajia67393c2020-12-05 20:49:55 -0500859 pipewire->core = pw_context_connect(pipewire->context, NULL, 0);
James Hilliard80b585f2020-07-06 00:58:02 -0600860#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200861 pipewire->core = pw_core_new(pipewire->loop, NULL);
862 pipewire->t = pw_core_get_type(pipewire->core);
863 init_type(&pipewire->type, pipewire->t->map);
James Hilliard80b585f2020-07-06 00:58:02 -0600864#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200865
James Hilliard80b585f2020-07-06 00:58:02 -0600866#if PW_CHECK_VERSION(0, 2, 90)
867 pw_core_add_listener(pipewire->core,
868 &pipewire->core_listener,
869 &core_events, pipewire);
870#else
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200871 pipewire->remote = pw_remote_new(pipewire->core, NULL, 0);
872 pw_remote_add_listener(pipewire->remote,
873 &pipewire->remote_listener,
874 &remote_events, pipewire);
875
876 pw_remote_connect(pipewire->remote);
877
878 while (true) {
879 enum pw_remote_state state;
880 const char *error = NULL;
881 int ret;
882
883 state = pw_remote_get_state(pipewire->remote, &error);
884 if (state == PW_REMOTE_STATE_CONNECTED)
885 break;
886
887 if (state == PW_REMOTE_STATE_ERROR) {
888 weston_log("pipewire error: %s\n", error);
889 goto err;
890 }
891
892 ret = pw_loop_iterate(pipewire->loop, -1);
893 if (ret < 0) {
894 weston_log("pipewire_loop_iterate failed: %s",
895 spa_strerror(ret));
896 goto err;
897 }
898 }
James Hilliard80b585f2020-07-06 00:58:02 -0600899#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200900
901 loop = wl_display_get_event_loop(pipewire->compositor->wl_display);
902 pipewire->loop_source =
903 wl_event_loop_add_fd(loop, pw_loop_get_fd(pipewire->loop),
904 WL_EVENT_READABLE,
905 weston_pipewire_loop_handler,
906 pipewire);
907
908 return 0;
James Hilliard80b585f2020-07-06 00:58:02 -0600909#if !PW_CHECK_VERSION(0, 2, 90)
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200910err:
911 if (pipewire->remote)
912 pw_remote_destroy(pipewire->remote);
913 pw_loop_leave(pipewire->loop);
914 pw_loop_destroy(pipewire->loop);
915 return -1;
James Hilliard80b585f2020-07-06 00:58:02 -0600916#endif
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200917}
918
919static const struct weston_pipewire_api pipewire_api = {
920 pipewire_output_create,
921 pipewire_output_is_pipewire,
922 pipewire_output_set_mode,
923 pipewire_output_set_seat,
924};
925
926WL_EXPORT int
927weston_module_init(struct weston_compositor *compositor)
928{
929 int ret;
930 struct weston_pipewire *pipewire;
931 const struct weston_drm_virtual_output_api *api =
932 weston_drm_virtual_output_get_api(compositor);
933
934 if (!api)
935 return -1;
936
937 pipewire = zalloc(sizeof *pipewire);
938 if (!pipewire)
939 return -1;
940
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200941 if (!weston_compositor_add_destroy_listener_once(compositor,
942 &pipewire->destroy_listener,
943 weston_pipewire_destroy)) {
944 free(pipewire);
945 return 0;
946 }
947
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200948 pipewire->virtual_output_api = api;
949 pipewire->compositor = compositor;
950 wl_list_init(&pipewire->output_list);
951
952 ret = weston_plugin_api_register(compositor, WESTON_PIPEWIRE_API_NAME,
953 &pipewire_api, sizeof(pipewire_api));
954
955 if (ret < 0) {
956 weston_log("Failed to register pipewire API.\n");
957 goto failed;
958 }
959
960 ret = weston_pipewire_init(pipewire);
961 if (ret < 0) {
962 weston_log("Failed to initialize pipewire.\n");
963 goto failed;
964 }
965
Leandro Ribeiro5976dbb2019-12-18 15:21:03 -0300966 pipewire->debug =
Leandro Ribeiro23289352019-12-26 16:44:56 -0300967 weston_compositor_add_log_scope(compositor, "pipewire",
968 "Debug messages from pipewire plugin\n",
969 NULL, NULL, NULL);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200970
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200971 return 0;
972
973failed:
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200974 wl_list_remove(&pipewire->destroy_listener.link);
Michael Olbrichd5d5aa92019-04-23 12:34:05 +0200975 free(pipewire);
976 return -1;
977}