UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
pipewire.cpp File Reference
#include <memory>
#include <vector>
#include <thread>
#include <chrono>
#include "audio/audio_capture.h"
#include "audio/types.h"
#include "pipewire_common.hpp"
#include "debug.h"
#include "lib_common.h"
#include "utils/color_out.h"
#include "utils/ring_buffer.h"
#include "utils/string_view_utils.hpp"
Include dependency graph for pipewire.cpp:

Go to the source code of this file.

Classes

struct  state_pipewire_cap
 

Macros

#define MOD_NAME   "[PW acap] "
 

Functions

static void audio_cap_pipewire_done (void *state)
 
static void * audio_cap_pipewire_init (struct module *parent, const char *cfg)
 
static void audio_cap_pipewire_probe (struct device_info **available_devices, int *count, void(**deleter)(void *))
 
static struct audio_frame * audio_cap_pipewire_read (void *state)
 
static void audio_cap_pw_help ()
 
static void on_param_changed (void *state, uint32_t id, const struct spa_pod *param)
 
static void on_process (void *state)
 
static void on_state_changed (void *state, enum pw_stream_state old, enum pw_stream_state new_state, const char *error)
 
 REGISTER_MODULE (pipewire, &acap_pipewire_info, LIBRARY_CLASS_AUDIO_CAPTURE, AUDIO_CAPTURE_ABI_VERSION)
 

Variables

static const struct audio_capture_info acap_pipewire_info
 
static const pw_stream_events stream_events
 

Detailed Description

Author
Martin Piatka piatk.nosp@m.a@ce.nosp@m.snet..nosp@m.cz

Definition in file pipewire.cpp.

Macro Definition Documentation

◆ MOD_NAME

Function Documentation

◆ audio_cap_pipewire_done()

static void audio_cap_pipewire_done ( void * state)
static

Definition at line 319 of file pipewire.cpp.

319 {
320 auto s = static_cast<state_pipewire_cap *>(state);
321
322 pw_thread_loop_stop(s->pw.pipewire_loop.get());
323 delete s;
324}
void ** state

References state.

◆ audio_cap_pipewire_init()

static void * audio_cap_pipewire_init ( struct module * parent,
const char * cfg )
static

Definition at line 204 of file pipewire.cpp.

204 {
205 UNUSED(parent);
206 auto s = std::make_unique<state_pipewire_cap>();
207
208 std::string_view cfg_sv(cfg);
209 while(!cfg_sv.empty()){
210 auto tok = tokenize(cfg_sv, ':', '"');
211
212 auto key = tokenize(tok, '=');
213 auto val = tokenize(tok, '=');
214
215 if(key == "help"){
217 return INIT_NOERR;
218 } else if(key == "target"){
219 s->target = val;
220 } else if(key == "channels"){
221 parse_num(val, s->ch_count);
222 } else if(key == "buffer-len"){
223 parse_num(val, s->buf_len_ms);
224 } else if(key == "sample-rate"){
225 parse_num(val, s->sample_rate);
226 }
227 }
228
230
231 log_msg(LOG_LEVEL_INFO, MOD_NAME "Compiled with libpipewire %s\n", pw_get_headers_version());
232 log_msg(LOG_LEVEL_INFO, MOD_NAME "Linked with libpipewire %s\n", pw_get_library_version());
233
234 auto props = pw_properties_new(
235 PW_KEY_MEDIA_TYPE, "Audio",
236 PW_KEY_MEDIA_CATEGORY, "Capture",
237 PW_KEY_MEDIA_ROLE, "Communication",
238 PW_KEY_APP_NAME, "UltraGrid",
239 PW_KEY_APP_ICON_NAME, "ultragrid",
240 PW_KEY_NODE_NAME, "ug capture",
241 STREAM_TARGET_PROPERTY_KEY, s->target.c_str(),
242 nullptr);
243
244 spa_audio_format format = get_pw_format_from_bps(s->bps);
245
246 pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%u", s->sample_rate);
247 pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", s->quant, s->sample_rate);
248
249 std::byte buffer[1024];
250 auto pod_builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
251
252 auto audio_info = SPA_AUDIO_INFO_RAW_INIT(
253 .format = format,
254 .rate = s->sample_rate,
255 .channels = s->ch_count,
256 );
257
258 const spa_pod *params = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_EnumFormat,
259 &audio_info);
260
261 /*
262 * Pipewire thread loop lock
263 */
264 pipewire_thread_loop_lock_guard lock(s->pw.pipewire_loop.get());
265
266 s->stream.reset(pw_stream_new(
267 s->pw.pipewire_core.get(),
268 "UltraGrid capture",
269 props));
270
271 pw_stream_add_listener(
272 s->stream.get(),
273 &s->stream_listener.get(),
275 s.get());
276
277 int ring_size = s->bps * s->ch_count * (s->sample_rate * s->buf_len_ms * 2 / 1000);
278 s->ring_buf.reset(ring_buffer_init(ring_size));
279
280 pw_stream_connect(s->stream.get(),
281 PW_DIRECTION_INPUT,
282 PW_ID_ANY,
283 static_cast<pw_stream_flags>(
284 PW_STREAM_FLAG_AUTOCONNECT |
285 PW_STREAM_FLAG_MAP_BUFFERS |
286 PW_STREAM_FLAG_RT_PROCESS),
287 &params, 1);
288
289 while(true){
290 pw_thread_loop_wait(s->pw.pipewire_loop.get());
291 const char *error = nullptr;
292 auto stream_state = pw_stream_get_state(s->stream.get(), &error);
293 if(stream_state == PW_STREAM_STATE_STREAMING){
294 break;
295 }
296 if(stream_state == PW_STREAM_STATE_ERROR){
297 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Failed to initialize stream: %s\n", error ? error : "(no err msg)");
298 return nullptr;
299 }
300 }
301
302 return s.release();
303}
#define UNUSED(x)
int void * val
static const pw_stream_events stream_events
Definition pipewire.cpp:177
static void audio_cap_pw_help()
Definition pipewire.cpp:194
#define MOD_NAME
Definition pipewire.cpp:57
void log_msg(int level, const char *format,...)
Definition debug.cpp:130
#define LOG_LEVEL_INFO
normal reporting
Definition debug.h:62
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define INIT_NOERR
Definition host.h:73
bool initialize_pw_common(pipewire_state_common &s, int fd)
#define PW_KEY_NODE_RATE
spa_audio_format get_pw_format_from_bps(unsigned bps)
#define STREAM_TARGET_PROPERTY_KEY
struct ring_buffer * ring_buffer_init(int size)
const char * parent
Definition settings.cpp:198
std::string_view tokenize(std::string_view &str, char delim, char quot)
Tokenizer for string_view.
bool parse_num(std::string_view sv, T &res, int base=10)
const char * key
Definition cineform.cpp:125

References audio_cap_pw_help(), get_pw_format_from_bps(), INIT_NOERR, initialize_pw_common(), key, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, log_msg(), MOD_NAME, parent, parse_num(), PW_KEY_NODE_RATE, ring_buffer_init(), stream_events, STREAM_TARGET_PROPERTY_KEY, tokenize(), UNUSED, and val.

Here is the call graph for this function:

◆ audio_cap_pipewire_probe()

static void audio_cap_pipewire_probe ( struct device_info ** available_devices,
int * count,
void(** deleter )(void *) )
static

Definition at line 326 of file pipewire.cpp.

327{
328 *deleter = free;
329 *available_devices = static_cast<device_info *>(calloc(1, sizeof(device_info)));
330 strcpy((*available_devices)[0].dev, "");
331 strcpy((*available_devices)[0].name, "Default pipewire capture");
332 *count = 1;
333}
int void(** deleter)(void *))
int count
Definition hd-rum.c:44
const char * name
Definition settings.cpp:193

References count, deleter, and name.

◆ audio_cap_pipewire_read()

static struct audio_frame * audio_cap_pipewire_read ( void * state)
static

Definition at line 305 of file pipewire.cpp.

305 {
306 auto s = static_cast<state_pipewire_cap *>(state);
307
308 s->frame.data_len = ring_buffer_read(s->ring_buf.get(), s->frame.data, s->frame.max_size);
309
310 if(!s->frame.data_len){
311 timespec ts{0, 1000000};
312 nanosleep(&ts, nullptr);
313 return nullptr;
314 }
315
316 return &s->frame;
317}
int ring_buffer_read(struct ring_buffer *ring, char *out, int max_len)
int data_len
Definition types.h:103
struct audio_frame frame
Definition pipewire.cpp:70
uint32_t ts
Definition tfrc.c:101

References audio_frame::data_len, state_pipewire_cap::frame, ring_buffer_read(), state, and ts.

Here is the call graph for this function:

◆ audio_cap_pw_help()

static void audio_cap_pw_help ( )
static

Definition at line 194 of file pipewire.cpp.

194 {
195 color_printf("Pipewire audio capture.\n");
196 color_printf("Usage\n");
197 color_printf(TERM_BOLD TERM_FG_RED "\t-r pipewire" TERM_FG_RESET "[:target=<device>][:buffer-len=<millis>][:channels=<ch_count>][:sample-rate=<rate>]\n" TERM_RESET);
198 color_printf("\n");
199
200 color_printf("Devices:\n");
201 print_devices("Audio/Source");
202}
int color_printf(const char *format,...)
#define TERM_BOLD
Definition color_out.h:48
#define TERM_FG_RED
Definition color_out.h:50
#define TERM_RESET
Definition color_out.h:47
#define TERM_FG_RESET
Definition color_out.h:59
void print_devices(std::string_view media_class)

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

Referenced by audio_cap_pipewire_init().

Here is the call graph for this function:

◆ on_param_changed()

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

Definition at line 103 of file pipewire.cpp.

103 {
104 auto s = static_cast<state_pipewire_cap *>(state);
105 spa_audio_info audio_params;
106
107 if(!param || id != SPA_PARAM_Format)
108 return;
109
110 int res = spa_format_parse(param, &audio_params.media_type, &audio_params.media_subtype);
111 if(res < 0
112 || audio_params.media_type != SPA_MEDIA_TYPE_audio
113 || audio_params.media_subtype != SPA_MEDIA_SUBTYPE_raw)
114 {
115 return;
116 }
117
118 spa_format_audio_raw_parse(param, &audio_params.info.raw);
119
120 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Pipewire format change: %s, %u ch, %uHz\n",
121 spa_debug_type_find_short_name(spa_type_audio_format, audio_params.info.raw.format),
122 audio_params.info.raw.channels,
123 audio_params.info.raw.rate);
124
125 /* It is not expected that params change while the stream is active,
126 * therefore this should run only during during init (we wait for stream to
127 * change into the streaming state there). As a result, we probably don't
128 * need any extra synchronization between this and
129 * audio_cap_pipewire_read().
130 */
131 s->frame.ch_count = audio_params.info.raw.channels;
132 s->frame.bps = get_bps_from_pw_format(audio_params.info.raw.format);
133 s->frame.sample_rate = audio_params.info.raw.rate;
134 s->frame.max_size = s->frame.ch_count * s->frame.bps * s->frame.sample_rate;
135 s->frame_data.resize(s->frame.max_size);
136 s->frame.data = s->frame_data.data();
137
138 std::byte buffer[1024];
139 auto pod_builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
140
141 unsigned buffer_size = (s->buf_len_ms * s->frame.sample_rate / 1000) * s->frame.ch_count * s->frame.bps;
142
143 log_msg(LOG_LEVEL_VERBOSE, MOD_NAME "Requesting buffer size %u\n", buffer_size);
144
145 spa_pod *new_params = (spa_pod *) spa_pod_builder_add_object(&pod_builder,
146 SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
147 SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
148 SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(buffer_size, 0, INT32_MAX),
149 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(s->frame.ch_count * s->frame.bps));
150
151 if(!new_params){
152 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Failed to build pw buffer params pod\n");
153 return;
154 }
155
156 if (pw_stream_update_params(s->stream.get(), const_cast<const spa_pod **>(&new_params), 1) < 0) {
157 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Failed to set stream params\n");
158 }
159
160}
#define LOG_LEVEL_NOTICE
information that may be interesting
Definition debug.h:61
#define LOG_LEVEL_VERBOSE
Definition debug.h:63
static int buffer_size(int sock, int optname, int size)
Definition hd-rum.c:64
unsigned get_bps_from_pw_format(spa_audio_format fmt)
const char * param
Definition settings.cpp:195

References buffer_size(), get_bps_from_pw_format(), LOG_LEVEL_ERROR, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, log_msg(), MOD_NAME, param, and state.

Here is the call graph for this function:

◆ on_process()

static void on_process ( void * state)
static

Definition at line 83 of file pipewire.cpp.

83 {
84 auto s = static_cast<state_pipewire_cap *>(state);
85
86 struct pw_buffer *b = pw_stream_dequeue_buffer(s->stream.get());
87 if (!b) {
88 pw_log_warn("out of buffers: %m");
89 return;
90 }
91 struct spa_buffer *buf = b->buffer;
92
93 char *src = static_cast<char *>(buf->datas[0].data);
94 if (!src)
95 return;
96
97 assert(buf->datas[0].chunk->offset == 0);
98 ring_buffer_write(s->ring_buf.get(), src, buf->datas[0].chunk->size);
99
100 pw_stream_queue_buffer(s->stream.get(), b);
101}
void ring_buffer_write(struct ring_buffer *ring, const char *in, int len)

References ring_buffer_write(), and state.

Here is the call graph for this function:

◆ on_state_changed()

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

Definition at line 162 of file pipewire.cpp.

163{
164 auto s = static_cast<state_pipewire_cap *>(state);
165 log_msg(LOG_LEVEL_NOTICE, MOD_NAME "Stream state change: %s -> %s\n",
166 pw_stream_state_as_string(old),
167 pw_stream_state_as_string(new_state));
168
169 if(error){
170 log_msg(LOG_LEVEL_ERROR, MOD_NAME "Stream error: %s\n", error);
171 }
172
173 pw_thread_loop_signal(s->pw.pipewire_loop.get(), false);
174}

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

Here is the call graph for this function:

◆ REGISTER_MODULE()

REGISTER_MODULE ( pipewire ,
& acap_pipewire_info,
LIBRARY_CLASS_AUDIO_CAPTURE ,
AUDIO_CAPTURE_ABI_VERSION  )

Variable Documentation

◆ acap_pipewire_info

const struct audio_capture_info acap_pipewire_info
static
Initial value:
= {
}
static void audio_cap_pipewire_done(void *state)
Definition pipewire.cpp:319
static void audio_cap_pipewire_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
Definition pipewire.cpp:326
static void * audio_cap_pipewire_init(struct module *parent, const char *cfg)
Definition pipewire.cpp:204
static struct audio_frame * audio_cap_pipewire_read(void *state)
Definition pipewire.cpp:305

Definition at line 335 of file pipewire.cpp.

◆ stream_events

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

Definition at line 177 of file pipewire.cpp.

177 {
178 .version = PW_VERSION_STREAM_EVENTS,
179 .destroy = nullptr,
180 .state_changed = on_state_changed,
181 .control_info = nullptr,
182 .io_changed = nullptr,
183 .param_changed = on_param_changed,
184 .add_buffer = nullptr,
185 .remove_buffer = nullptr,
186 .process = on_process,
187 .drained = nullptr,
188#if PW_MAJOR > 0 || PW_MINOR > 3 || (PW_MINOR == 3 && PW_MICRO > 39)
189 .command = nullptr,
190 .trigger_done = nullptr,
191#endif
192};

Referenced by audio_cap_pipewire_init(), audio_play_pw_reconfigure(), display_pw_reconfigure(), and start_pipewire().