153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2006-2008 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stdlib.h> 2553a5a1b3Sopenharmony_ci#include <stdio.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h> 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include "play-memblockq.h" 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_citypedef struct memblockq_stream { 3553a5a1b3Sopenharmony_ci pa_msgobject parent; 3653a5a1b3Sopenharmony_ci pa_core *core; 3753a5a1b3Sopenharmony_ci pa_sink_input *sink_input; 3853a5a1b3Sopenharmony_ci pa_memblockq *memblockq; 3953a5a1b3Sopenharmony_ci} memblockq_stream; 4053a5a1b3Sopenharmony_ci 4153a5a1b3Sopenharmony_cienum { 4253a5a1b3Sopenharmony_ci MEMBLOCKQ_STREAM_MESSAGE_UNLINK, 4353a5a1b3Sopenharmony_ci}; 4453a5a1b3Sopenharmony_ci 4553a5a1b3Sopenharmony_ciPA_DEFINE_PRIVATE_CLASS(memblockq_stream, pa_msgobject); 4653a5a1b3Sopenharmony_ci#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o)) 4753a5a1b3Sopenharmony_ci 4853a5a1b3Sopenharmony_cistatic void memblockq_stream_unlink(memblockq_stream *u) { 4953a5a1b3Sopenharmony_ci pa_assert(u); 5053a5a1b3Sopenharmony_ci 5153a5a1b3Sopenharmony_ci if (!u->sink_input) 5253a5a1b3Sopenharmony_ci return; 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_ci pa_sink_input_unlink(u->sink_input); 5553a5a1b3Sopenharmony_ci pa_sink_input_unref(u->sink_input); 5653a5a1b3Sopenharmony_ci u->sink_input = NULL; 5753a5a1b3Sopenharmony_ci 5853a5a1b3Sopenharmony_ci memblockq_stream_unref(u); 5953a5a1b3Sopenharmony_ci} 6053a5a1b3Sopenharmony_ci 6153a5a1b3Sopenharmony_cistatic void memblockq_stream_free(pa_object *o) { 6253a5a1b3Sopenharmony_ci memblockq_stream *u = MEMBLOCKQ_STREAM(o); 6353a5a1b3Sopenharmony_ci pa_assert(u); 6453a5a1b3Sopenharmony_ci 6553a5a1b3Sopenharmony_ci if (u->memblockq) 6653a5a1b3Sopenharmony_ci pa_memblockq_free(u->memblockq); 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci pa_xfree(u); 6953a5a1b3Sopenharmony_ci} 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_cistatic int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { 7253a5a1b3Sopenharmony_ci memblockq_stream *u = MEMBLOCKQ_STREAM(o); 7353a5a1b3Sopenharmony_ci memblockq_stream_assert_ref(u); 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_ci switch (code) { 7653a5a1b3Sopenharmony_ci case MEMBLOCKQ_STREAM_MESSAGE_UNLINK: 7753a5a1b3Sopenharmony_ci memblockq_stream_unlink(u); 7853a5a1b3Sopenharmony_ci break; 7953a5a1b3Sopenharmony_ci } 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_ci return 0; 8253a5a1b3Sopenharmony_ci} 8353a5a1b3Sopenharmony_ci 8453a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i) { 8553a5a1b3Sopenharmony_ci memblockq_stream *u; 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 8853a5a1b3Sopenharmony_ci u = MEMBLOCKQ_STREAM(i->userdata); 8953a5a1b3Sopenharmony_ci memblockq_stream_assert_ref(u); 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_ci memblockq_stream_unlink(u); 9253a5a1b3Sopenharmony_ci} 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci/* Called from IO thread context */ 9553a5a1b3Sopenharmony_cistatic void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { 9653a5a1b3Sopenharmony_ci memblockq_stream *u; 9753a5a1b3Sopenharmony_ci 9853a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 9953a5a1b3Sopenharmony_ci u = MEMBLOCKQ_STREAM(i->userdata); 10053a5a1b3Sopenharmony_ci memblockq_stream_assert_ref(u); 10153a5a1b3Sopenharmony_ci 10253a5a1b3Sopenharmony_ci /* If we are added for the first time, ask for a rewinding so that 10353a5a1b3Sopenharmony_ci * we are heard right-away. */ 10453a5a1b3Sopenharmony_ci if (PA_SINK_INPUT_IS_LINKED(state) && 10553a5a1b3Sopenharmony_ci i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) 10653a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(i, 0, false, true, true); 10753a5a1b3Sopenharmony_ci} 10853a5a1b3Sopenharmony_ci 10953a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { 11053a5a1b3Sopenharmony_ci memblockq_stream *u; 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 11353a5a1b3Sopenharmony_ci pa_assert(chunk); 11453a5a1b3Sopenharmony_ci u = MEMBLOCKQ_STREAM(i->userdata); 11553a5a1b3Sopenharmony_ci memblockq_stream_assert_ref(u); 11653a5a1b3Sopenharmony_ci 11753a5a1b3Sopenharmony_ci if (!u->memblockq) 11853a5a1b3Sopenharmony_ci return -1; 11953a5a1b3Sopenharmony_ci 12053a5a1b3Sopenharmony_ci if (pa_memblockq_peek(u->memblockq, chunk) < 0) { 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_ci if (pa_sink_input_safe_to_remove(i)) { 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci pa_memblockq_free(u->memblockq); 12553a5a1b3Sopenharmony_ci u->memblockq = NULL; 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_ci pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); 12853a5a1b3Sopenharmony_ci } 12953a5a1b3Sopenharmony_ci 13053a5a1b3Sopenharmony_ci return -1; 13153a5a1b3Sopenharmony_ci } 13253a5a1b3Sopenharmony_ci 13353a5a1b3Sopenharmony_ci /* If there's no memblock, there's going to be data in the memblockq after 13453a5a1b3Sopenharmony_ci * a gap with length chunk->length. Drop the gap and peek the actual 13553a5a1b3Sopenharmony_ci * data. There should always be some data coming - hence the assert. The 13653a5a1b3Sopenharmony_ci * gap will occur if the memblockq is rewound beyond index 0.*/ 13753a5a1b3Sopenharmony_ci if (!chunk->memblock) { 13853a5a1b3Sopenharmony_ci pa_memblockq_drop(u->memblockq, chunk->length); 13953a5a1b3Sopenharmony_ci pa_assert_se(pa_memblockq_peek(u->memblockq, chunk) >= 0); 14053a5a1b3Sopenharmony_ci } 14153a5a1b3Sopenharmony_ci 14253a5a1b3Sopenharmony_ci chunk->length = PA_MIN(chunk->length, nbytes); 14353a5a1b3Sopenharmony_ci pa_memblockq_drop(u->memblockq, chunk->length); 14453a5a1b3Sopenharmony_ci 14553a5a1b3Sopenharmony_ci return 0; 14653a5a1b3Sopenharmony_ci} 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { 14953a5a1b3Sopenharmony_ci memblockq_stream *u; 15053a5a1b3Sopenharmony_ci 15153a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 15253a5a1b3Sopenharmony_ci u = MEMBLOCKQ_STREAM(i->userdata); 15353a5a1b3Sopenharmony_ci memblockq_stream_assert_ref(u); 15453a5a1b3Sopenharmony_ci 15553a5a1b3Sopenharmony_ci if (!u->memblockq) 15653a5a1b3Sopenharmony_ci return; 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci pa_memblockq_rewind(u->memblockq, nbytes); 15953a5a1b3Sopenharmony_ci} 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { 16253a5a1b3Sopenharmony_ci memblockq_stream *u; 16353a5a1b3Sopenharmony_ci 16453a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 16553a5a1b3Sopenharmony_ci u = MEMBLOCKQ_STREAM(i->userdata); 16653a5a1b3Sopenharmony_ci memblockq_stream_assert_ref(u); 16753a5a1b3Sopenharmony_ci 16853a5a1b3Sopenharmony_ci if (!u->memblockq) 16953a5a1b3Sopenharmony_ci return; 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_ci pa_memblockq_set_maxrewind(u->memblockq, nbytes); 17253a5a1b3Sopenharmony_ci} 17353a5a1b3Sopenharmony_ci 17453a5a1b3Sopenharmony_cipa_sink_input* pa_memblockq_sink_input_new( 17553a5a1b3Sopenharmony_ci pa_sink *sink, 17653a5a1b3Sopenharmony_ci const pa_sample_spec *ss, 17753a5a1b3Sopenharmony_ci const pa_channel_map *map, 17853a5a1b3Sopenharmony_ci pa_memblockq *q, 17953a5a1b3Sopenharmony_ci pa_cvolume *volume, 18053a5a1b3Sopenharmony_ci pa_proplist *p, 18153a5a1b3Sopenharmony_ci pa_sink_input_flags_t flags) { 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci memblockq_stream *u = NULL; 18453a5a1b3Sopenharmony_ci pa_sink_input_new_data data; 18553a5a1b3Sopenharmony_ci 18653a5a1b3Sopenharmony_ci pa_assert(sink); 18753a5a1b3Sopenharmony_ci pa_assert(ss); 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci /* We allow creating this stream with no q set, so that it can be 19053a5a1b3Sopenharmony_ci * filled in later */ 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_ci u = pa_msgobject_new(memblockq_stream); 19353a5a1b3Sopenharmony_ci u->parent.parent.free = memblockq_stream_free; 19453a5a1b3Sopenharmony_ci u->parent.process_msg = memblockq_stream_process_msg; 19553a5a1b3Sopenharmony_ci u->core = sink->core; 19653a5a1b3Sopenharmony_ci u->sink_input = NULL; 19753a5a1b3Sopenharmony_ci u->memblockq = NULL; 19853a5a1b3Sopenharmony_ci 19953a5a1b3Sopenharmony_ci pa_sink_input_new_data_init(&data); 20053a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sink(&data, sink, false, true); 20153a5a1b3Sopenharmony_ci data.driver = __FILE__; 20253a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sample_spec(&data, ss); 20353a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_channel_map(&data, map); 20453a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_volume(&data, volume); 20553a5a1b3Sopenharmony_ci pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); 20653a5a1b3Sopenharmony_ci data.flags |= flags; 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci pa_sink_input_new(&u->sink_input, sink->core, &data); 20953a5a1b3Sopenharmony_ci pa_sink_input_new_data_done(&data); 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci if (!u->sink_input) 21253a5a1b3Sopenharmony_ci goto fail; 21353a5a1b3Sopenharmony_ci 21453a5a1b3Sopenharmony_ci u->sink_input->pop = sink_input_pop_cb; 21553a5a1b3Sopenharmony_ci u->sink_input->process_rewind = sink_input_process_rewind_cb; 21653a5a1b3Sopenharmony_ci u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; 21753a5a1b3Sopenharmony_ci u->sink_input->kill = sink_input_kill_cb; 21853a5a1b3Sopenharmony_ci u->sink_input->state_change = sink_input_state_change_cb; 21953a5a1b3Sopenharmony_ci u->sink_input->userdata = u; 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci if (q) 22253a5a1b3Sopenharmony_ci pa_memblockq_sink_input_set_queue(u->sink_input, q); 22353a5a1b3Sopenharmony_ci 22453a5a1b3Sopenharmony_ci /* The reference to u is dangling here, because we want 22553a5a1b3Sopenharmony_ci * to keep this stream around until it is fully played. */ 22653a5a1b3Sopenharmony_ci 22753a5a1b3Sopenharmony_ci /* This sink input is not "put" yet, i.e. pa_sink_input_put() has 22853a5a1b3Sopenharmony_ci * not been called! */ 22953a5a1b3Sopenharmony_ci 23053a5a1b3Sopenharmony_ci return pa_sink_input_ref(u->sink_input); 23153a5a1b3Sopenharmony_ci 23253a5a1b3Sopenharmony_cifail: 23353a5a1b3Sopenharmony_ci if (u) 23453a5a1b3Sopenharmony_ci memblockq_stream_unref(u); 23553a5a1b3Sopenharmony_ci 23653a5a1b3Sopenharmony_ci return NULL; 23753a5a1b3Sopenharmony_ci} 23853a5a1b3Sopenharmony_ci 23953a5a1b3Sopenharmony_ciint pa_play_memblockq( 24053a5a1b3Sopenharmony_ci pa_sink *sink, 24153a5a1b3Sopenharmony_ci const pa_sample_spec *ss, 24253a5a1b3Sopenharmony_ci const pa_channel_map *map, 24353a5a1b3Sopenharmony_ci pa_memblockq *q, 24453a5a1b3Sopenharmony_ci pa_cvolume *volume, 24553a5a1b3Sopenharmony_ci pa_proplist *p, 24653a5a1b3Sopenharmony_ci pa_sink_input_flags_t flags, 24753a5a1b3Sopenharmony_ci uint32_t *sink_input_index) { 24853a5a1b3Sopenharmony_ci 24953a5a1b3Sopenharmony_ci pa_sink_input *i; 25053a5a1b3Sopenharmony_ci 25153a5a1b3Sopenharmony_ci pa_assert(sink); 25253a5a1b3Sopenharmony_ci pa_assert(ss); 25353a5a1b3Sopenharmony_ci pa_assert(q); 25453a5a1b3Sopenharmony_ci 25553a5a1b3Sopenharmony_ci if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p, flags))) 25653a5a1b3Sopenharmony_ci return -1; 25753a5a1b3Sopenharmony_ci 25853a5a1b3Sopenharmony_ci pa_sink_input_put(i); 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci if (sink_input_index) 26153a5a1b3Sopenharmony_ci *sink_input_index = i->index; 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_ci pa_sink_input_unref(i); 26453a5a1b3Sopenharmony_ci 26553a5a1b3Sopenharmony_ci return 0; 26653a5a1b3Sopenharmony_ci} 26753a5a1b3Sopenharmony_ci 26853a5a1b3Sopenharmony_civoid pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) { 26953a5a1b3Sopenharmony_ci memblockq_stream *u; 27053a5a1b3Sopenharmony_ci 27153a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 27253a5a1b3Sopenharmony_ci u = MEMBLOCKQ_STREAM(i->userdata); 27353a5a1b3Sopenharmony_ci memblockq_stream_assert_ref(u); 27453a5a1b3Sopenharmony_ci 27553a5a1b3Sopenharmony_ci if (u->memblockq) 27653a5a1b3Sopenharmony_ci pa_memblockq_free(u->memblockq); 27753a5a1b3Sopenharmony_ci 27853a5a1b3Sopenharmony_ci if ((u->memblockq = q)) { 27953a5a1b3Sopenharmony_ci pa_memblockq_set_prebuf(q, 0); 28053a5a1b3Sopenharmony_ci pa_memblockq_set_silence(q, NULL); 28153a5a1b3Sopenharmony_ci pa_memblockq_willneed(q); 28253a5a1b3Sopenharmony_ci } 28353a5a1b3Sopenharmony_ci} 284