54#define MOD_NAME "[PW aplay] "
74 strcpy((*available_devices)[0].dev,
"");
75 strcpy((*available_devices)[0].
name,
"Default pipewire output");
97 struct pw_buffer *b = pw_stream_dequeue_buffer(s->stream.get());
99 pw_log_warn(
"out of buffers: %m");
102 struct spa_buffer *buf = b->buffer;
104 char *dst =
static_cast<char *
>(buf->datas[0].data);
108 int to_write = std::min<int>(buf->datas[0].maxsize, avail);
113 buf->datas[0].chunk->offset = 0;
114 buf->datas[0].chunk->stride = s->desc.ch_count * s->desc.bps;
115 buf->datas[0].chunk->size = to_write;
117 pw_stream_queue_buffer(s->stream.get(), b);
122 auto s = std::make_unique<state_pipewire_play>();
124 std::string_view cfg_sv(cfg);
125 while(!cfg_sv.empty()){
126 auto tok =
tokenize(cfg_sv,
':',
'"');
133 }
else if(
key ==
"target"){
135 }
else if(
key ==
"buffer-len"){
137 }
else if(
key ==
"quant"){
154 auto to_write =
frame->data_len;
155 if(to_write > avail){
186static void on_state_changed(
void *
state,
enum pw_stream_state old,
enum pw_stream_state new_state,
const char *error)
190 pw_stream_state_as_string(old),
191 pw_stream_state_as_string(new_state));
197 pw_thread_loop_signal(s->pw.pipewire_loop.get(),
false);
203 if(!
param ||
id != SPA_PARAM_Format)
206 spa_audio_info audio_params;
207 int res = spa_format_parse(
param, &audio_params.media_type, &audio_params.media_subtype);
209 || audio_params.media_type != SPA_MEDIA_TYPE_audio
210 || audio_params.media_subtype != SPA_MEDIA_SUBTYPE_raw)
215 spa_format_audio_raw_parse(
param, &audio_params.info.raw);
218 spa_debug_type_find_short_name(spa_type_audio_format, audio_params.info.raw.format),
219 audio_params.info.raw.channels,
220 audio_params.info.raw.rate);
222 assert(audio_params.info.raw.rate == (
unsigned) s->desc.sample_rate);
223 assert(audio_params.info.raw.channels == (
unsigned) s->desc.ch_count);
226 std::byte buffer[1024];
227 auto pod_builder = SPA_POD_BUILDER_INIT(buffer,
sizeof(buffer));
229 unsigned buffer_size = (s->buf_len_ms * s->desc.sample_rate / 1000) * s->desc.ch_count * s->desc.bps;
233 spa_pod *new_params = (spa_pod *) spa_pod_builder_add_object(&pod_builder,
234 SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
235 SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
236 SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(
buffer_size, 0, INT32_MAX),
237 SPA_PARAM_BUFFERS_stride, SPA_POD_Int(s->desc.ch_count * s->desc.bps));
244 if (pw_stream_update_params(s->stream.get(),
const_cast<const spa_pod **
>(&new_params), 1) < 0) {
250 .version = PW_VERSION_STREAM_EVENTS,
253 .control_info =
nullptr,
254 .io_changed =
nullptr,
256 .add_buffer =
nullptr,
257 .remove_buffer =
nullptr,
260#if PW_MAJOR > 0 || PW_MINOR > 3 || (PW_MINOR == 3 && PW_MICRO > 39)
262 .trigger_done =
nullptr,
269 unsigned rate =
desc.sample_rate;
272 auto props = pw_properties_new(
273 PW_KEY_MEDIA_TYPE,
"Audio",
274 PW_KEY_MEDIA_CATEGORY,
"Playback",
275 PW_KEY_MEDIA_ROLE,
"Communication",
276 PW_KEY_APP_NAME,
"UltraGrid",
277 PW_KEY_APP_ICON_NAME,
"ultragrid",
278 PW_KEY_NODE_NAME,
"ug play",
283 pw_properties_setf(props, PW_KEY_NODE_LATENCY,
"%u/%u", s->quant, rate);
285 std::byte buffer[1024];
286 auto pod_builder = SPA_POD_BUILDER_INIT(buffer,
sizeof(buffer));
288 auto audio_info = SPA_AUDIO_INFO_RAW_INIT(
293 const spa_pod *
params = spa_format_audio_raw_build(&pod_builder, SPA_PARAM_EnumFormat,
303 s->stream.reset(pw_stream_new(
304 s->pw.pipewire_core.get(),
305 "UltraGrid playback",
308 pw_stream_add_listener(
310 &s->stream_listener.get(),
314 unsigned ring_size = (s->buf_len_ms *
desc.sample_rate / 1000) *
desc.ch_count *
desc.bps * 2;
317 pw_stream_connect(s->stream.get(),
320 static_cast<pw_stream_flags
>(
321 PW_STREAM_FLAG_AUTOCONNECT |
322 PW_STREAM_FLAG_MAP_BUFFERS |
323 PW_STREAM_FLAG_RT_PROCESS),
327 pw_thread_loop_wait(s->pw.pipewire_loop.get());
328 const char *error =
nullptr;
329 auto stream_state = pw_stream_get_state(s->stream.get(), &error);
330 if(stream_state == PW_STREAM_STATE_STREAMING){
333 if(stream_state == PW_STREAM_STATE_ERROR){
345 pw_thread_loop_stop(s->pw.pipewire_loop.get());
struct video_frame * frame
int void(** deleter)(void *))
static void on_state_changed(void *state, enum pw_stream_state old, enum pw_stream_state new_state, const char *error)
static const pw_stream_events stream_events
static void on_process(void *state)
static void on_param_changed(void *state, uint32_t id, const struct spa_pod *param)
static bool audio_play_pw_ctl(void *state, int request, void *data, size_t *len)
static bool audio_play_pw_reconfigure(void *state, struct audio_desc desc)
static bool is_format_supported(void *data, size_t *len)
static void audio_play_pw_probe(struct device_info **available_devices, int *count, void(**deleter)(void *))
static const struct audio_playback_info aplay_pw_info
static void * audio_play_pw_init(const char *cfg)
static void audio_play_pw_done(void *state)
static void audio_play_pw_help()
static void audio_play_pw_put_frame(void *state, const struct audio_frame *frame)
This file contains definition of some common audio types.
#define AUDIO_PLAYBACK_CTL_QUERY_FORMAT
#define AUDIO_PLAYBACK_ABI_VERSION
int color_printf(const char *format,...)
void log_msg(int level, const char *format,...)
#define LOG_LEVEL_NOTICE
information that may be interesting
#define LOG_LEVEL_INFO
normal reporting
#define LOG_LEVEL_ERROR
general errors
#define LOG_LEVEL_VERBOSE
#define LOG_LEVEL_WARNING
less severe errors
static int buffer_size(int sock, int optname, int size)
@ LIBRARY_CLASS_AUDIO_PLAYBACK
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
void print_devices(std::string_view media_class)
bool initialize_pw_common(pipewire_state_common &s, int fd)
raii_uniq_handle< pw_stream, pw_stream_destroy > pw_stream_uniq
spa_audio_format get_pw_format_from_bps(unsigned bps)
#define STREAM_TARGET_PROPERTY_KEY
int ring_get_current_size(struct ring_buffer *ring)
struct ring_buffer * ring_buffer_init(int size)
int ring_buffer_read(struct ring_buffer *ring, char *out, int max_len)
void ring_buffer_write(struct ring_buffer *ring, const char *in, int len)
int ring_get_available_write_size(struct ring_buffer *ring)
std::unique_ptr< ring_buffer, ring_buf_deleter > ring_buffer_uniq
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)
struct used to comunicate with audio capture/playback device
spa_hook_uniq stream_listener
ring_buffer_uniq ring_buf