UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
pipewire.cpp
Go to the documentation of this file.
1
6/*
7 * Copyright (c) 2023 CESNET z.s.p.o.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, is permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * 3. Neither the name of CESNET nor the names of its contributors may be
22 * used to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
27 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
28 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
29 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
35 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
36 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include "config.h"
40
41#include <stdlib.h>
42#include <string.h>
43#include <iostream>
44#include <chrono>
45#include <cassert>
46#include <algorithm>
47#include <vector>
48#include <mutex>
49#include <unistd.h>
50#include <pipewire/pipewire.h>
51#include <pipewire/version.h>
52#include <spa/utils/result.h>
53#include <spa/param/video/format-utils.h>
54#include <spa/param/props.h>
55#include <spa/debug/types.h>
56
57#include "debug.h"
58#include "lib_common.h"
59#include "utils/color_out.h"
60#include "utils/dbus_portal.hpp"
63#include "video_frame.h"
64#include "video_codec.h"
65#include "video_capture.h"
66#include "pipewire_common.hpp"
67#include "pixfmt_conv.h"
68
69#define MOD_NAME "[PW vcap] "
70
71static constexpr int DEFAULT_BUFFERS_PW = 2;
72static constexpr int MIN_BUFFERS_PW = 2;
73static constexpr int MAX_BUFFERS_PW = 10;
74static constexpr int QUEUE_SIZE = 3;
75static constexpr int DEFAULT_EXPECTING_FPS = 30;
76
77struct frame_deleter{ void operator()(video_frame *f){ vf_free(f); } };
78using unique_frame = std::unique_ptr<video_frame, frame_deleter>;
79
82
83 // used exlusively by ultragrid thread
85
86 std::mutex mut;
87 std::vector<unique_frame> blank_frames;
89
91
92#ifdef HAVE_DBUS_SCREENCAST
93 ScreenCastPortal portal;
94#endif
95 int fd = -1;
96 uint32_t node = PW_ID_ANY;
97
100 struct spa_video_info format = {};
101
106
107 struct {
108 bool show_cursor = false;
109 std::string restore_file = "";
110 uint32_t fps = 0;
111 bool crop = true;
112 std::string target = "";
114
115};
116
117static void on_stream_state_changed(void * /*state*/, enum pw_stream_state old, enum pw_stream_state state, const char *error) {
118 LOG(LOG_LEVEL_INFO) << MOD_NAME "stream state changed \"" << pw_stream_state_as_string(old)
119 << "\" -> \""<<pw_stream_state_as_string(state)<<"\"\n";
120
121 if (error != nullptr) {
122 LOG(LOG_LEVEL_ERROR) << MOD_NAME "stream error: '"<< error << "'\n";
123 }
124}
125
126
127static void on_stream_param_changed(void *state, uint32_t id, const struct spa_pod *param) {
128 auto s = static_cast<vcap_pw_state *>(state);
129 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME "param changed:\n";
130
131 if (param == nullptr || id != SPA_PARAM_Format)
132 return;
133
134 int parse_format_ret = spa_format_parse(param, &s->format.media_type, &s->format.media_subtype);
135 assert(parse_format_ret > 0);
136
137 if(s->format.media_type != SPA_MEDIA_TYPE_video
138 || s->format.media_subtype != SPA_MEDIA_SUBTYPE_raw)
139 {
140 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Format not video/raw!\n");
141 return;
142 }
143
144 auto& raw_format = s->format.info.raw;
145 spa_format_video_raw_parse(param, &raw_format);
146
147 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Got format: %s\n", spa_debug_type_find_name(spa_type_video_format, raw_format.format));
148
149 s->desc.width = raw_format.size.width;
150 s->desc.height = raw_format.size.height;
151 if(raw_format.framerate.num != 0){
152 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Got framerate: %d / %d\n", raw_format.framerate.num, raw_format.framerate.denom);
153 s->desc.fps = static_cast<double>(raw_format.framerate.num) / raw_format.framerate.denom;
154 } else {
155 //Variable framerate
156 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Got variable framerate: %d / %d\n", raw_format.max_framerate.num, raw_format.max_framerate.denom);
157 if(raw_format.max_framerate.num != 0){
158 s->desc.fps = static_cast<double>(raw_format.max_framerate.num) / raw_format.max_framerate.denom;
159 } else {
160 s->desc.fps = 60;
161 log_msg(LOG_LEVEL_WARNING, MOD_NAME "Invalid max framerate, using %f instead\n", s->desc.fps);
162 }
163 }
164 s->desc.color_spec = uv_codec_from_pw_fmt(raw_format.format);
165 s->desc.interlacing = PROGRESSIVE;
166 s->desc.tile_count = 1;
167
168 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "size: %dx%d\n", s->desc.width, s->desc.height);
169
170 int linesize = vc_get_linesize(s->desc.width, s->desc.color_spec);
171 int32_t size = linesize * s->desc.height;
172
173 uint8_t params_buffer[1024];
174
175 struct spa_pod_builder builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
176 const struct spa_pod *params[3] = {};
177 int n_params = 0;
178
179 params[n_params++] = static_cast<spa_pod *>(spa_pod_builder_add_object(&builder,
180 SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
181 SPA_PARAM_BUFFERS_buffers,
182 SPA_POD_CHOICE_RANGE_Int(DEFAULT_BUFFERS_PW, MIN_BUFFERS_PW, MAX_BUFFERS_PW),
183 SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
184 SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
185 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(linesize),
186 SPA_PARAM_BUFFERS_dataType,
187 SPA_POD_CHOICE_FLAGS_Int((1 << SPA_DATA_MemPtr)))
188 );
189
190 if(s->user_options.crop) {
191 params[n_params++] = static_cast<spa_pod *>(spa_pod_builder_add_object(&builder,
192 SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
193 SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoCrop),
194 SPA_PARAM_META_size,
195 SPA_POD_Int(sizeof(struct spa_meta_region)))
196 );
197 }
198
199 pw_stream_update_params(s->stream.get(), params, n_params);
200}
201
202static void pw_frame_to_uv_frame_memcpy(video_frame *dst, spa_buffer *src, spa_video_format fmt, spa_rectangle size, const spa_region *crop){
203 auto offset = src->datas[0].chunk->offset;
204 auto chunk_size = src->datas[0].chunk->size;
205 auto stride = src->datas[0].chunk->stride;
206
207 auto width = size.width;
208 auto height = size.height;
209
210 unsigned start_x = 0;
211 unsigned start_y = 0;
212 if(crop){
213 width = crop->size.width;
214 height = crop->size.height;
215 start_x = crop->position.x;
216 start_y = crop->position.y;
217 }
218
219 if(stride == 0)
220 stride = chunk_size / size.height;
221
222 auto linesize = vc_get_linesize(width, dst->color_spec);
223 auto skip = vc_get_linesize(start_x, dst->color_spec);
224 bool swap_red_blue = fmt == SPA_VIDEO_FORMAT_BGRA || fmt == SPA_VIDEO_FORMAT_BGRx; //TODO
225 for(unsigned i = 0; i < height; i++){
226 auto src_p = static_cast<unsigned char *>(src->datas[0].data) + offset + skip + stride * (i + start_y);
227 auto dst_p = reinterpret_cast<unsigned char *>(dst->tiles[0].data) + linesize * i;
228 /* It would probably be better to have separate functions for
229 * pipweire to uv frame conversions like lavd has. For now,
230 * let's handle BGRA to RGBA like this.
231 */
232 if(swap_red_blue)
233 vc_copylineRGBA(dst_p, src_p, linesize, 16, 8, 0);
234 else
235 memcpy(dst_p, src_p, linesize);
236 }
237
238 dst->tiles[0].width = width;
239 dst->tiles[0].height = height;
240 dst->tiles[0].data_len = linesize * height;
241}
242
243static void on_process(void *state) {
245
246 auto s= static_cast<vcap_pw_state *>(state);
247 pw_buffer *buffer;
248 [[maybe_unused]] int n_buffers_from_pw = 0;
249 while((buffer = pw_stream_dequeue_buffer(s->stream.get())) != nullptr){
250 ++n_buffers_from_pw;
251
252 unique_frame next_frame;
253
254 assert(buffer->buffer != nullptr);
255 assert(buffer->buffer->datas != nullptr);
256 assert(buffer->buffer->n_datas == 1);
257 assert(buffer->buffer->datas[0].data != nullptr);
258
259 if(buffer->buffer->datas[0].chunk == nullptr || buffer->buffer->datas[0].chunk->size == 0) {
260 LOG(LOG_LEVEL_DEBUG) << MOD_NAME "dropping - empty pw frame " << "\n";
261 pw_stream_queue_buffer(s->stream.get(), buffer);
262 continue;
263 }
264
265 {
266 std::lock_guard<std::mutex> lock(s->mut);
267 if(!s->blank_frames.empty()){
268 next_frame = std::move(s->blank_frames.back());
269 s->blank_frames.pop_back();
270 }
271 }
272
273 if(!next_frame) {
274 LOG(LOG_LEVEL_DEBUG) << MOD_NAME "dropping frame (no blank frames)\n";
275 pw_stream_queue_buffer(s->stream.get(), buffer);
276 continue;
277 }
278
279 spa_region *crop_region = nullptr;
280 if (s->user_options.crop) {
281 spa_meta_region *meta_crop_region = static_cast<spa_meta_region*>(spa_buffer_find_meta_data(buffer->buffer, SPA_META_VideoCrop, sizeof(*meta_crop_region)));
282 if (meta_crop_region != nullptr && spa_meta_region_is_valid(meta_crop_region))
283 crop_region = &meta_crop_region->region;
284 }
285
286 if(crop_region){
287 //Update desc so that we don't reallocate on each frame
288 //TODO: Figure what to do when we can't actually crop (MJPEG)
289 s->desc.width = crop_region->size.width;
290 s->desc.height = crop_region->size.height;
291 }
292
293 if(!next_frame || !video_desc_eq(video_desc_from_frame(next_frame.get()), s->desc)){
294 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Desc changed, allocating new video_frame\n");
295 next_frame.reset(vf_alloc_desc_data(s->desc));
296 }
297
298
299 auto& raw_format = s->format.info.raw;
300
301 //copy_frame(s->pw.video_format(), buffer->buffer, next_frame.get(), s->desc.width, s->desc.height, crop_region);
302 pw_frame_to_uv_frame_memcpy(next_frame.get(), buffer->buffer, raw_format.format, raw_format.size, crop_region);
303
304 s->sending_frames.push(std::move(next_frame));
305 pw_stream_queue_buffer(s->stream.get(), buffer);
306 }
307
308 //LOG(LOG_LEVEL_DEBUG) << "[screen_pw]: from pw: "<< n_buffers_from_pw << "\t sending: "<<s->sending_frames.size_approx() << "\t blank: " << s->blank_frames.size_approx() << "\n";
309
310}
311
312static void on_drained(void*)
313{
314 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME "pipewire: drained\n";
315}
316
317static void on_add_buffer(void * /*state*/, struct pw_buffer *)
318{
319 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME "pipewire: add_buffer\n";
320}
321
322static void on_remove_buffer(void * /*state*/, struct pw_buffer *)
323{
324 LOG(LOG_LEVEL_VERBOSE) << MOD_NAME "pipewire: remove_buffer\n";
325}
326
327static const struct pw_stream_events stream_events = {
328 .version = PW_VERSION_STREAM_EVENTS,
329 .destroy = nullptr,
330 .state_changed = on_stream_state_changed,
331 .control_info = nullptr,
332 .io_changed = nullptr,
333 .param_changed = on_stream_param_changed,
334 .add_buffer = on_add_buffer,
335 .remove_buffer = on_remove_buffer,
336 .process = on_process,
337 .drained = on_drained,
338#if PW_MAJOR > 0 || PW_MINOR > 3 || (PW_MINOR == 3 && PW_MICRO > 39)
339 .command = nullptr,
340 .trigger_done = nullptr,
341#endif
342};
343
345{
346 const struct spa_pod *params[2] = {};
347 uint8_t params_buffer[1024];
348 struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer));
349
350 initialize_pw_common(s->pw, s->fd);
351
352 std::string node_name = "ultragrid_in_";
353 {
354 char buf[32];
355 snprintf(buf, sizeof(buf), "%ld", (long) getpid());
356 node_name += buf;
357 }
358
359 auto props = pw_properties_new(
360 PW_KEY_MEDIA_TYPE, "Video",
361 PW_KEY_MEDIA_CATEGORY, "Capture",
362 PW_KEY_MEDIA_ROLE, "Communication",
363 PW_KEY_APP_NAME, "UltraGrid",
364 PW_KEY_APP_ICON_NAME, "ultragrid",
365 PW_KEY_NODE_NAME, node_name.c_str(),
366 PW_KEY_NODE_DESCRIPTION, "UltraGrid capture",
367 nullptr);
368
369 if(!s->user_options.target.empty()){
370 pw_properties_set(props, STREAM_TARGET_PROPERTY_KEY, s->user_options.target.c_str());
371 }
372
374
375 s->stream.reset(pw_stream_new(s->pw.pipewire_core.get(),
376 s->mode == vcap_pw_state::Mode::Screen_capture ? "ug_screencapture" : "ug_videocap",
377 props));
378
379 assert(s->stream != nullptr);
380
381 pw_stream_add_listener(
382 s->stream.get(),
383 &s->stream_listener.get(),
385 s);
386
387 auto size_rect_def = SPA_RECTANGLE(1920, 1080);
388 auto size_rect_min = SPA_RECTANGLE(1, 1);
389 auto size_rect_max = SPA_RECTANGLE(3840, 2160);
390
391 auto framerate_def = SPA_FRACTION(s->user_options.fps > 0 ? s->user_options.fps : DEFAULT_EXPECTING_FPS, 1);
392 auto framerate_min = SPA_FRACTION(0, 1);
393 auto framerate_max = SPA_FRACTION(600, 1);
394
395 const int n_params = 1;
396 params[0] = static_cast<spa_pod *> (spa_pod_builder_add_object(
397 &pod_builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
398 SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
399 SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
400 SPA_FORMAT_VIDEO_format,
401 SPA_POD_CHOICE_ENUM_Id(8,
402 SPA_VIDEO_FORMAT_UYVY,
403 SPA_VIDEO_FORMAT_UYVY,
404 SPA_VIDEO_FORMAT_RGB,
405 SPA_VIDEO_FORMAT_RGBA,
406 SPA_VIDEO_FORMAT_RGBx,
407 SPA_VIDEO_FORMAT_YUY2,
408 SPA_VIDEO_FORMAT_BGRA,
409 SPA_VIDEO_FORMAT_BGRx
410 ),
411 SPA_FORMAT_VIDEO_size,
412 SPA_POD_CHOICE_RANGE_Rectangle(
413 &size_rect_def,
414 &size_rect_min,
415 &size_rect_max),
416 SPA_FORMAT_VIDEO_framerate,
417 SPA_POD_CHOICE_RANGE_Fraction(
418 &framerate_def,
419 &framerate_min,
420 &framerate_max)
421 ));
422
423 auto flags = PW_STREAM_FLAG_MAP_BUFFERS |
424 PW_STREAM_FLAG_DONT_RECONNECT;
425
426 if(!s->user_options.target.empty()){
427 flags |= PW_STREAM_FLAG_AUTOCONNECT;
428 }
429
430 int res = pw_stream_connect(s->stream.get(),
431 PW_DIRECTION_INPUT,
432 s->node,
433 static_cast<pw_stream_flags>(flags),
434 params, n_params);
435 if (res < 0) {
436 fprintf(stderr, MOD_NAME "can't connect: %s\n", spa_strerror(res));
437 return -1;
438 }
439
440 pw_stream_set_active(s->stream.get(), true);
441 return 0;
442}
443
444static void vidcap_screen_pw_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
445{
446 *deleter = free;
447 *count = 1;
448 *available_devices = (struct device_info *) calloc(*count, sizeof(struct device_info));
449 // (*available_devices)[0].dev can be "" since screen cap. doesn't require parameters
450 snprintf((*available_devices)[0].name, sizeof (*available_devices)[0].name, "Screen capture PipeWire");
451}
452
453static void vidcap_pw_probe(struct device_info **available_devices, int *count, void (**deleter)(void *))
454{
455 *deleter = free;
456 *count = 1;
457 *available_devices = (struct device_info *) calloc(*count, sizeof(struct device_info));
458 // (*available_devices)[0].dev can be "" since screen cap. doesn't require parameters
459 snprintf((*available_devices)[0].name, sizeof (*available_devices)[0].name, "Generic PipeWire video capture");
460}
461
462static void show_screen_help() {
463 auto param = [](const char* name) -> std::ostream& {
464 col() << " " << SBOLD(name) << " - ";
465 return std::cout;
466 };
467
468 std::cout << "Screen capture using PipeWire and ScreenCast freedesktop portal API\n";
469 std::cout << "Usage: -t screen_pw[:cursor|:nocrop|:fps=<fps>|:restore=<token_file>]]\n";
470 param("cursor") << "make the cursor visible (default hidden)\n";
471 param("nocrop") << "when capturing a window do not crop out the empty background\n";
472 param("<fps>") << "prefered FPS passed to PipeWire (PipeWire may ignore it)\n";
473 param("<token_file>") << "restore the selected window/display from a file.\n\t\tIf not possible, display the selection dialog and save the token to the file specified.\n";
474}
475
476static void show_generic_help(){
477 color_printf("Pipewire video capture.\n");
478 color_printf("Usage\n");
479 color_printf(TERM_BOLD TERM_FG_RED "\t-t pipewire" TERM_FG_RESET "[:fps=<fps>][:target=<device>]\n" TERM_RESET);
480 color_printf("\n");
481
482 color_printf("Devices:\n");
483 print_devices("Video/Source");
484}
485
486
488 if(const char *fmt = vidcap_params_get_fmt(params)) {
489 std::istringstream params_stream(fmt);
490
491 std::string param;
492 while (std::getline(params_stream, param, ':')) {
493 if (param == "help") {
496 else
498 return VIDCAP_INIT_NOERR;
499 } else if (param == "cursor") {
500 s->user_options.show_cursor = true;
501 } else if (param == "nocrop") {
502 s->user_options.crop = false;
503 } else {
504 auto split_index = param.find('=');
505 if(split_index != std::string::npos && split_index != 0){
506 std::string name = param.substr(0, split_index);
507 std::string value = param.substr(split_index + 1);
508
509 if (name == "fps" || name == "FPS"){
510 std::istringstream is(value);
511 is >> s->user_options.fps;
512 continue;
513 }else if(name == "restore"){
514 s->user_options.restore_file = std::move(value);
515 continue;
516 } else if(name =="target"){
517 s->user_options.target = std::move(value);
518 continue;
519 }
520 }
521
522 LOG(LOG_LEVEL_ERROR) << MOD_NAME "invalid option: \"" << param << "\"\n";
523 return VIDCAP_INIT_FAIL;
524 }
525 }
526 }
527 return VIDCAP_INIT_OK;
528}
529
530#ifdef HAVE_DBUS_SCREENCAST
531static int vidcap_screen_pw_init(struct vidcap_params *params, void **state)
532{
535 }
536
537 auto s = std::make_unique<vcap_pw_state>();
538
540
541 LOG(LOG_LEVEL_DEBUG) << MOD_NAME "init\n";
542
543 int params_ok = parse_params(params, s.get());
544 if(params_ok != VIDCAP_INIT_OK)
545 return params_ok;
546
547 for(int i = 0; i < QUEUE_SIZE; i++)
548 s->blank_frames.emplace_back(vf_alloc(1));
549
550 auto portalResult = s->portal.run(s->user_options.restore_file, s->user_options.show_cursor);
551
552 if (portalResult.pipewire_fd == -1) {
553 return VIDCAP_INIT_FAIL;
554 }
555
556 s->fd = portalResult.pipewire_fd;
557 /* TODO: The node target_id param when calling stream_connect should be
558 * always set to PW_ID_ANY as using object ids is now deprecated.
559 * However, the dbus ScreenCast portal doesn't yet expose the object
560 * serial which shoud be used instead.
561 */
562 s->node = portalResult.pipewire_node;
563
564 LOG(LOG_LEVEL_DEBUG) << MOD_NAME "init ok\n";
565 start_pipewire(s.get());
566
567 *state = s.release();
568
569 return VIDCAP_INIT_OK;
570}
571#endif
572
573static int vidcap_pw_init(struct vidcap_params *params, void **state)
574{
577 }
578
579 auto s = std::make_unique<vcap_pw_state>();
580
581 LOG(LOG_LEVEL_DEBUG) << MOD_NAME "init\n";
582
583 int params_ok = parse_params(params, s.get());
584 if(params_ok != VIDCAP_INIT_OK)
585 return params_ok;
586
587 for(int i = 0; i < QUEUE_SIZE; i++)
588 s->blank_frames.emplace_back(vf_alloc(1));
589
590 s->fd = -1;
591
592 LOG(LOG_LEVEL_DEBUG) << MOD_NAME "init ok\n";
593 start_pipewire(s.get());
594
595 *state = s.release();
596
597 return VIDCAP_INIT_OK;
598}
599
600static void vidcap_screen_pw_done(void *state)
601{
602 auto s = static_cast<vcap_pw_state *>(state);
603
604 {
605 pipewire_thread_loop_lock_guard lock(s->pw.pipewire_loop.get());
606 pw_stream_disconnect(s->stream.get());
607 }
608
609 pw_thread_loop_stop(s->pw.pipewire_loop.get());
610 LOG(LOG_LEVEL_DEBUG) << MOD_NAME "done\n";
611 delete s;
612}
613
615{
617
618 assert(state != nullptr);
619 auto s = static_cast<vcap_pw_state *>(state);
620 *audio = nullptr;
621
622 if(s->in_flight_frame.get() != nullptr){
623 s->blank_frames.push_back(std::move(s->in_flight_frame));
624 }
625
626 using namespace std::chrono_literals;
627 s->sending_frames.timed_pop(s->in_flight_frame, 500ms);
628 return s->in_flight_frame.get();
629}
630
631#ifdef HAVE_DBUS_SCREENCAST
632
633static const struct video_capture_info vidcap_screen_pw_info = {
635 vidcap_screen_pw_init,
638 MOD_NAME,
639};
640
642
643#endif
644
652
654
655/* vim: set expandtab sw=8: */
void ** state
const char * fmt
const char unsigned int flags
struct audio_frame ** audio
int void(** deleter)(void *))
struct video_frame * vf_alloc(int count)
Allocates blank video frame.
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_data(struct video_desc desc)
static const pw_stream_events stream_events
Definition pipewire.cpp:177
static void on_process(void *state)
Definition pipewire.cpp:83
#define MOD_NAME
Definition pipewire.cpp:57
simple blocking synchronized queue
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 SBOLD(x)
Definition color_out.h:80
#define TERM_FG_RESET
Definition color_out.h:59
static __device__ const float offset
Definition cuda_dxt.cu:139
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
#define VIDCAP_INIT_NOERR
state not initialized, other action performed (typically help)
#define VIDCAP_INIT_OK
initialization successful
#define VIDEO_CAPTURE_ABI_VERSION
#define VIDCAP_INIT_FAIL
error ocured
#define VIDCAP_INIT_AUDIO_NOT_SUPPORTED
card does not support audio
int count
Definition hd-rum.c:44
#define LOG_LEVEL_DEBUG
Definition kernels.cu:48
@ LIBRARY_CLASS_VIDEO_CAPTURE
Definition lib_common.h:78
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
void print_devices(std::string_view media_class)
bool initialize_pw_common(pipewire_state_common &s, int fd)
codec_t uv_codec_from_pw_fmt(spa_video_format fmt)
raii_uniq_handle< pw_stream, pw_stream_destroy > pw_stream_uniq
#define STREAM_TARGET_PROPERTY_KEY
decoder_func_t vc_copylineRGBA
#define PROFILE_FUNC
const char * param
Definition settings.cpp:195
const char * name
Definition settings.cpp:193
struct used to comunicate with audio capture/playback device
Definition types.h:99
void operator()(video_frame *f)
Definition pipewire.cpp:77
pw_thread_loop_uniq pipewire_loop
spa_hook & get()
unsigned int width
Definition types.h:280
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
unsigned int height
Definition types.h:281
enum vcap_pw_state::Mode mode
struct spa_video_info format
Definition pipewire.cpp:100
pw_stream_uniq stream
Definition pipewire.cpp:98
std::string target
Definition pipewire.cpp:112
pipewire_state_common pw
Definition pipewire.cpp:81
struct vcap_pw_state::@71 user_options
unique_frame in_flight_frame
Definition pipewire.cpp:84
std::mutex mut
Definition pipewire.cpp:86
synchronized_queue< unique_frame, QUEUE_SIZE > sending_frames
Definition pipewire.cpp:88
video_desc desc
Definition pipewire.cpp:90
uint32_t fps
Definition pipewire.cpp:110
uint32_t node
Definition pipewire.cpp:96
std::vector< unique_frame > blank_frames
Definition pipewire.cpp:87
spa_hook_uniq stream_listener
Definition pipewire.cpp:99
std::string restore_file
Definition pipewire.cpp:109
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
codec_t color_spec
Definition types.h:306
@ PROGRESSIVE
progressive frame
Definition types.h:164
static constexpr int MIN_BUFFERS_PW
Definition pipewire.cpp:72
static void vidcap_screen_pw_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition pipewire.cpp:444
static void show_screen_help()
Definition pipewire.cpp:462
static constexpr int DEFAULT_BUFFERS_PW
Definition pipewire.cpp:71
static constexpr int DEFAULT_EXPECTING_FPS
Definition pipewire.cpp:75
static void on_remove_buffer(void *, struct pw_buffer *)
Definition pipewire.cpp:322
static constexpr int QUEUE_SIZE
Definition pipewire.cpp:74
static void on_stream_param_changed(void *state, uint32_t id, const struct spa_pod *param)
Definition pipewire.cpp:127
static void vidcap_screen_pw_done(void *state)
Definition pipewire.cpp:600
static void on_stream_state_changed(void *, enum pw_stream_state old, enum pw_stream_state state, const char *error)
Definition pipewire.cpp:117
static void vidcap_pw_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition pipewire.cpp:453
static void show_generic_help()
Definition pipewire.cpp:476
static const struct video_capture_info vidcap_pw_info
Definition pipewire.cpp:645
static struct video_frame * vidcap_screen_pw_grab(void *state, struct audio_frame **audio)
Definition pipewire.cpp:614
static int vidcap_pw_init(struct vidcap_params *params, void **state)
Definition pipewire.cpp:573
static void on_drained(void *)
Definition pipewire.cpp:312
static int start_pipewire(vcap_pw_state *s)
Definition pipewire.cpp:344
static void pw_frame_to_uv_frame_memcpy(video_frame *dst, spa_buffer *src, spa_video_format fmt, spa_rectangle size, const spa_region *crop)
Definition pipewire.cpp:202
static int parse_params(struct vidcap_params *params, vcap_pw_state *s)
Definition pipewire.cpp:487
static constexpr int MAX_BUFFERS_PW
Definition pipewire.cpp:73
static void on_add_buffer(void *, struct pw_buffer *)
Definition pipewire.cpp:317
std::unique_ptr< video_frame, frame_deleter > unique_frame
Definition pipewire.cpp:78
const char * vidcap_params_get_fmt(const struct vidcap_params *params)
unsigned int vidcap_params_get_flags(const struct vidcap_params *params)
#define VIDCAP_FLAG_AUDIO_ANY
bool video_desc_eq(struct video_desc a, struct video_desc b)
Compares two video descriptions.