static bool set_format(struct ao *ao, pa_format_info *format) { ao->format = af_fmt_from_planar(ao->format); format->encoding = map_digital_format(ao->format); if (format->encoding == PA_ENCODING_PCM) { const struct format_map *fmt_map = format_maps; while (fmt_map->mp_format != ao->format) { if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { MP_VERBOSE(ao, "Unsupported format, using default\n"); fmt_map = format_maps; break; } fmt_map++; } ao->format = fmt_map->mp_format; pa_format_info_set_sample_format(format, fmt_map->pa_format); } struct pa_channel_map map; if (!select_chmap(ao, &map)) return false; pa_format_info_set_rate(format, ao->samplerate); pa_format_info_set_channels(format, ao->channels.num); pa_format_info_set_channel_map(format, &map); return ao->samplerate < PA_RATE_MAX && pa_format_info_valid(format); }
static int init(struct ao *ao) { struct pa_sample_spec ss; struct pa_channel_map map; pa_proplist *proplist = NULL; struct priv *priv = ao->priv; char *host = priv->cfg_host && priv->cfg_host[0] ? priv->cfg_host : NULL; char *sink = priv->cfg_sink && priv->cfg_sink[0] ? priv->cfg_sink : NULL; const char *version = pa_get_library_version(); ao->per_application_mixer = true; priv->broken_pause = false; /* not sure which versions are affected, assume 0.9.11* to 0.9.14* * known bad: 0.9.14, 0.9.13 * known good: 0.9.9, 0.9.10, 0.9.15 * To test: pause, wait ca. 5 seconds, framestep and see if MPlayer * hangs somewhen. */ if (strncmp(version, "0.9.1", 5) == 0 && version[5] >= '1' && version[5] <= '4') { MP_WARN(ao, "working around probably broken pause functionality,\n" " see http://www.pulseaudio.org/ticket/440\n"); priv->broken_pause = true; } if (!(priv->mainloop = pa_threaded_mainloop_new())) { MP_ERR(ao, "Failed to allocate main loop\n"); goto fail; } if (!(priv->context = pa_context_new(pa_threaded_mainloop_get_api( priv->mainloop), PULSE_CLIENT_NAME))) { MP_ERR(ao, "Failed to allocate context\n"); goto fail; } pa_context_set_state_callback(priv->context, context_state_cb, ao); if (pa_context_connect(priv->context, host, 0, NULL) < 0) goto fail; pa_threaded_mainloop_lock(priv->mainloop); if (pa_threaded_mainloop_start(priv->mainloop) < 0) goto unlock_and_fail; /* Wait until the context is ready */ pa_threaded_mainloop_wait(priv->mainloop); if (pa_context_get_state(priv->context) != PA_CONTEXT_READY) goto unlock_and_fail; ss.channels = ao->channels.num; ss.rate = ao->samplerate; ao->format = af_fmt_from_planar(ao->format); const struct format_map *fmt_map = format_maps; while (fmt_map->mp_format != ao->format) { if (fmt_map->mp_format == AF_FORMAT_UNKNOWN) { MP_VERBOSE(ao, "Unsupported format, using default\n"); fmt_map = format_maps; break; } fmt_map++; } ao->format = fmt_map->mp_format; ss.format = fmt_map->pa_format; if (!pa_sample_spec_valid(&ss)) { MP_ERR(ao, "Invalid sample spec\n"); goto unlock_and_fail; } if (!select_chmap(ao, &map)) goto unlock_and_fail; if (!(proplist = pa_proplist_new())) { MP_ERR(ao, "Failed to allocate proplist\n"); goto unlock_and_fail; } (void)pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video"); if (!(priv->stream = pa_stream_new_with_proplist(priv->context, "audio stream", &ss, &map, proplist))) goto unlock_and_fail; pa_proplist_free(proplist); proplist = NULL; pa_stream_set_state_callback(priv->stream, stream_state_cb, ao); pa_stream_set_write_callback(priv->stream, stream_request_cb, ao); pa_stream_set_latency_update_callback(priv->stream, stream_latency_update_cb, ao); pa_buffer_attr bufattr = { .maxlength = -1, .tlength = pa_usec_to_bytes(1000000, &ss), .prebuf = -1, .minreq = -1, .fragsize = -1, }; if (pa_stream_connect_playback(priv->stream, sink, &bufattr, PA_STREAM_NOT_MONOTONIC, NULL, NULL) < 0) goto unlock_and_fail; /* Wait until the stream is ready */ pa_threaded_mainloop_wait(priv->mainloop); if (pa_stream_get_state(priv->stream) != PA_STREAM_READY) goto unlock_and_fail; pa_threaded_mainloop_unlock(priv->mainloop); return 0; unlock_and_fail: if (priv->mainloop) pa_threaded_mainloop_unlock(priv->mainloop); fail: if (priv->context) { if (!(pa_context_errno(priv->context) == PA_ERR_CONNECTIONREFUSED && ao->probing)) GENERIC_ERR_MSG("Init failed"); } if (proplist) pa_proplist_free(proplist); uninit(ao, true); return -1; } static void cork(struct ao *ao, bool pause) { struct priv *priv = ao->priv; pa_threaded_mainloop_lock(priv->mainloop); priv->retval = 0; if (!waitop(priv, pa_stream_cork(priv->stream, pause, success_cb, ao)) || !priv->retval) GENERIC_ERR_MSG("pa_stream_cork() failed"); } // Play the specified data to the pulseaudio server static int play(struct ao *ao, void **data, int samples, int flags) { struct priv *priv = ao->priv; pa_threaded_mainloop_lock(priv->mainloop); if (pa_stream_write(priv->stream, data[0], samples * ao->sstride, NULL, 0, PA_SEEK_RELATIVE) < 0) { GENERIC_ERR_MSG("pa_stream_write() failed"); samples = -1; } if (flags & AOPLAY_FINAL_CHUNK) { // Force start in case the stream was too short for prebuf pa_operation *op = pa_stream_trigger(priv->stream, NULL, NULL); pa_operation_unref(op); } pa_threaded_mainloop_unlock(priv->mainloop); return samples; } // Reset the audio stream, i.e. flush the playback buffer on the server side static void reset(struct ao *ao) { // pa_stream_flush() works badly if not corked cork(ao, true); struct priv *priv = ao->priv; pa_threaded_mainloop_lock(priv->mainloop); priv->retval = 0; if (!waitop(priv, pa_stream_flush(priv->stream, success_cb, ao)) || !priv->retval) GENERIC_ERR_MSG("pa_stream_flush() failed"); cork(ao, false); }