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