diff options
Diffstat (limited to 'audio/out/ao_pipewire.c')
-rw-r--r-- | audio/out/ao_pipewire.c | 107 |
1 files changed, 79 insertions, 28 deletions
diff --git a/audio/out/ao_pipewire.c b/audio/out/ao_pipewire.c index 3fbcbf6..94d393a 100644 --- a/audio/out/ao_pipewire.c +++ b/audio/out/ao_pipewire.c @@ -27,6 +27,7 @@ #include <spa/utils/result.h> #include <math.h> +#include "common/common.h" #include "common/msg.h" #include "options/m_config.h" #include "options/m_option.h" @@ -46,6 +47,15 @@ static inline int pw_stream_get_time_n(struct pw_stream *stream, struct pw_time #define spa_hook_remove(hook) if ((hook)->link.prev) spa_hook_remove(hook) #endif +#if !PW_CHECK_VERSION(1, 0, 4) +static uint64_t pw_stream_get_nsec(struct pw_stream *stream) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return SPA_TIMESPEC_TO_NSEC(&ts); +} +#endif + enum init_state { INIT_STATE_NONE, INIT_STATE_SUCCESS, @@ -86,7 +96,7 @@ struct id_list { struct spa_list node; }; -static enum spa_audio_format af_fmt_to_pw(struct ao *ao, enum af_format format) +static enum spa_audio_format af_fmt_to_pw(enum af_format format) { switch (format) { case AF_FORMAT_U8: return SPA_AUDIO_FORMAT_U8; @@ -99,9 +109,21 @@ static enum spa_audio_format af_fmt_to_pw(struct ao *ao, enum af_format format) case AF_FORMAT_S32P: return SPA_AUDIO_FORMAT_S32P; case AF_FORMAT_FLOATP: return SPA_AUDIO_FORMAT_F32P; case AF_FORMAT_DOUBLEP: return SPA_AUDIO_FORMAT_F64P; - default: - MP_WARN(ao, "Unhandled format %d\n", format); - return SPA_AUDIO_FORMAT_UNKNOWN; + default: return SPA_AUDIO_FORMAT_UNKNOWN; + } +} + +static enum spa_audio_iec958_codec af_fmt_to_codec(enum af_format format) +{ + switch (format) { + case AF_FORMAT_S_AAC: return SPA_AUDIO_IEC958_CODEC_MPEG2_AAC; + case AF_FORMAT_S_AC3: return SPA_AUDIO_IEC958_CODEC_AC3; + case AF_FORMAT_S_DTS: return SPA_AUDIO_IEC958_CODEC_DTS; + case AF_FORMAT_S_DTSHD: return SPA_AUDIO_IEC958_CODEC_DTSHD; + case AF_FORMAT_S_EAC3: return SPA_AUDIO_IEC958_CODEC_EAC3; + case AF_FORMAT_S_MP3: return SPA_AUDIO_IEC958_CODEC_MPEG; + case AF_FORMAT_S_TRUEHD: return SPA_AUDIO_IEC958_CODEC_TRUEHD; + default: return SPA_AUDIO_IEC958_CODEC_UNKNOWN; } } @@ -154,14 +176,13 @@ static void on_process(void *userdata) void *data[MP_NUM_CHANNELS]; if ((b = pw_stream_dequeue_buffer(p->stream)) == NULL) { - MP_WARN(ao, "out of buffers: %s\n", strerror(errno)); + MP_WARN(ao, "out of buffers: %s\n", mp_strerror(errno)); return; } struct spa_buffer *buf = b->buffer; - int bytes_per_channel = buf->datas[0].maxsize / ao->channels.num; - int nframes = bytes_per_channel / ao->sstride; + int nframes = buf->datas[0].maxsize / ao->sstride; #if PW_CHECK_VERSION(0, 3, 49) if (b->requested != 0) nframes = MPMIN(b->requested, nframes); @@ -177,9 +198,13 @@ static void on_process(void *userdata) time.rate.num = 1; int64_t end_time = mp_time_ns(); - /* time.queued is always going to be 0, so we don't need to care */ - end_time += (nframes * 1e9 / ao->samplerate) + - ((double) time.delay * SPA_NSEC_PER_SEC * time.rate.num / time.rate.denom); + end_time += MP_TIME_S_TO_NS(nframes) / ao->samplerate; + end_time += MP_TIME_S_TO_NS(time.delay) * time.rate.num / time.rate.denom; + end_time += MP_TIME_S_TO_NS(time.queued) / ao->samplerate; +#if PW_CHECK_VERSION(0, 3, 50) + end_time += MP_TIME_S_TO_NS(time.buffered) / ao->samplerate; +#endif + end_time -= pw_stream_get_nsec(p->stream) - time.now; int samples = ao_read_data_nonblocking(ao, data, nframes, end_time); b->size = samples; @@ -214,7 +239,7 @@ static void on_param_changed(void *userdata, uint32_t id, const struct spa_pod * if (param == NULL || id != SPA_PARAM_Format) return; - int buffer_size = ao->device_buffer * af_fmt_to_bytes(ao->format) * ao->channels.num; + int buffer_size = ao->device_buffer * ao->sstride; params[0] = spa_pod_builder_add_object(&b, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, @@ -506,7 +531,7 @@ static int pipewire_init_boilerplate(struct ao *ao) if (!p->core) { MP_MSG(ao, ao->probing ? MSGL_V : MSGL_ERR, "Could not connect to context '%s': %s\n", - p->options.remote, strerror(errno)); + p->options.remote, mp_strerror(errno)); pw_context_destroy(context); goto error; } @@ -580,24 +605,41 @@ static int init(struct ao *ao) pw_properties_setf(props, PW_KEY_NODE_RATE, "1/%d", ao->samplerate); - enum spa_audio_format spa_format = af_fmt_to_pw(ao, ao->format); - if (spa_format == SPA_AUDIO_FORMAT_UNKNOWN) { - ao->format = AF_FORMAT_FLOATP; - spa_format = SPA_AUDIO_FORMAT_F32P; - } + if (af_fmt_is_spdif(ao->format)) { + enum spa_audio_iec958_codec spa_codec = af_fmt_to_codec(ao->format); + if (spa_codec == SPA_AUDIO_IEC958_CODEC_UNKNOWN) { + MP_ERR(ao, "Unhandled codec %d\n", ao->format); + goto error_props; + } - struct spa_audio_info_raw audio_info = { - .format = spa_format, - .rate = ao->samplerate, - .channels = ao->channels.num, - }; + struct spa_audio_info_iec958 audio_info = { + .codec = spa_codec, + .rate = ao->samplerate, + }; - for (int i = 0; i < ao->channels.num; i++) - audio_info.position[i] = mp_speaker_id_to_spa(ao, ao->channels.speaker[i]); + params[0] = spa_format_audio_iec958_build(&b, SPA_PARAM_EnumFormat, &audio_info); + if (!params[0]) + goto error_props; + } else { + enum spa_audio_format spa_format = af_fmt_to_pw(ao->format); + if (spa_format == SPA_AUDIO_FORMAT_UNKNOWN) { + MP_ERR(ao, "Unhandled format %d\n", ao->format); + goto error_props; + } - params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &audio_info); - if (!params[0]) - goto error_props; + struct spa_audio_info_raw audio_info = { + .format = spa_format, + .rate = ao->samplerate, + .channels = ao->channels.num, + }; + + for (int i = 0; i < ao->channels.num; i++) + audio_info.position[i] = mp_speaker_id_to_spa(ao, ao->channels.speaker[i]); + + params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &audio_info); + if (!params[0]) + goto error_props; + } if (af_fmt_is_planar(ao->format)) { ao->num_planes = ao->channels.num; @@ -664,6 +706,15 @@ static void start(struct ao *ao) pw_thread_loop_unlock(p->loop); } +static bool set_pause(struct ao *ao, bool paused) +{ + struct priv *p = ao->priv; + pw_thread_loop_lock(p->loop); + pw_stream_set_active(p->stream, !paused); + pw_thread_loop_unlock(p->loop); + return true; +} + #define CONTROL_RET(r) (!r ? CONTROL_OK : CONTROL_ERROR) static int control(struct ao *ao, enum aocontrol cmd, void *arg) @@ -855,7 +906,7 @@ const struct ao_driver audio_out_pipewire = { .uninit = uninit, .reset = reset, .start = start, - + .set_pause = set_pause, .control = control, .hotplug_init = hotplug_init, |