1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2006-2008 Lennart Poettering 5 6 PulseAudio is free software; you can redistribute it and/or modify 7 it under the terms of the GNU Lesser General Public License as published 8 by the Free Software Foundation; either version 2.1 of the License, 9 or (at your option) any later version. 10 11 PulseAudio is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public License 17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 18***/ 19 20#ifdef HAVE_CONFIG_H 21#include <config.h> 22#endif 23 24#include <stdlib.h> 25#include <stdio.h> 26 27#include <pulse/xmalloc.h> 28 29#include <pulsecore/sink-input.h> 30#include <pulsecore/thread-mq.h> 31 32#include "play-memblockq.h" 33 34typedef struct memblockq_stream { 35 pa_msgobject parent; 36 pa_core *core; 37 pa_sink_input *sink_input; 38 pa_memblockq *memblockq; 39} memblockq_stream; 40 41enum { 42 MEMBLOCKQ_STREAM_MESSAGE_UNLINK, 43}; 44 45PA_DEFINE_PRIVATE_CLASS(memblockq_stream, pa_msgobject); 46#define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o)) 47 48static void memblockq_stream_unlink(memblockq_stream *u) { 49 pa_assert(u); 50 51 if (!u->sink_input) 52 return; 53 54 pa_sink_input_unlink(u->sink_input); 55 pa_sink_input_unref(u->sink_input); 56 u->sink_input = NULL; 57 58 memblockq_stream_unref(u); 59} 60 61static void memblockq_stream_free(pa_object *o) { 62 memblockq_stream *u = MEMBLOCKQ_STREAM(o); 63 pa_assert(u); 64 65 if (u->memblockq) 66 pa_memblockq_free(u->memblockq); 67 68 pa_xfree(u); 69} 70 71static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) { 72 memblockq_stream *u = MEMBLOCKQ_STREAM(o); 73 memblockq_stream_assert_ref(u); 74 75 switch (code) { 76 case MEMBLOCKQ_STREAM_MESSAGE_UNLINK: 77 memblockq_stream_unlink(u); 78 break; 79 } 80 81 return 0; 82} 83 84static void sink_input_kill_cb(pa_sink_input *i) { 85 memblockq_stream *u; 86 87 pa_sink_input_assert_ref(i); 88 u = MEMBLOCKQ_STREAM(i->userdata); 89 memblockq_stream_assert_ref(u); 90 91 memblockq_stream_unlink(u); 92} 93 94/* Called from IO thread context */ 95static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t state) { 96 memblockq_stream *u; 97 98 pa_sink_input_assert_ref(i); 99 u = MEMBLOCKQ_STREAM(i->userdata); 100 memblockq_stream_assert_ref(u); 101 102 /* If we are added for the first time, ask for a rewinding so that 103 * we are heard right-away. */ 104 if (PA_SINK_INPUT_IS_LINKED(state) && 105 i->thread_info.state == PA_SINK_INPUT_INIT && i->sink) 106 pa_sink_input_request_rewind(i, 0, false, true, true); 107} 108 109static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { 110 memblockq_stream *u; 111 112 pa_sink_input_assert_ref(i); 113 pa_assert(chunk); 114 u = MEMBLOCKQ_STREAM(i->userdata); 115 memblockq_stream_assert_ref(u); 116 117 if (!u->memblockq) 118 return -1; 119 120 if (pa_memblockq_peek(u->memblockq, chunk) < 0) { 121 122 if (pa_sink_input_safe_to_remove(i)) { 123 124 pa_memblockq_free(u->memblockq); 125 u->memblockq = NULL; 126 127 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL); 128 } 129 130 return -1; 131 } 132 133 /* If there's no memblock, there's going to be data in the memblockq after 134 * a gap with length chunk->length. Drop the gap and peek the actual 135 * data. There should always be some data coming - hence the assert. The 136 * gap will occur if the memblockq is rewound beyond index 0.*/ 137 if (!chunk->memblock) { 138 pa_memblockq_drop(u->memblockq, chunk->length); 139 pa_assert_se(pa_memblockq_peek(u->memblockq, chunk) >= 0); 140 } 141 142 chunk->length = PA_MIN(chunk->length, nbytes); 143 pa_memblockq_drop(u->memblockq, chunk->length); 144 145 return 0; 146} 147 148static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { 149 memblockq_stream *u; 150 151 pa_sink_input_assert_ref(i); 152 u = MEMBLOCKQ_STREAM(i->userdata); 153 memblockq_stream_assert_ref(u); 154 155 if (!u->memblockq) 156 return; 157 158 pa_memblockq_rewind(u->memblockq, nbytes); 159} 160 161static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { 162 memblockq_stream *u; 163 164 pa_sink_input_assert_ref(i); 165 u = MEMBLOCKQ_STREAM(i->userdata); 166 memblockq_stream_assert_ref(u); 167 168 if (!u->memblockq) 169 return; 170 171 pa_memblockq_set_maxrewind(u->memblockq, nbytes); 172} 173 174pa_sink_input* pa_memblockq_sink_input_new( 175 pa_sink *sink, 176 const pa_sample_spec *ss, 177 const pa_channel_map *map, 178 pa_memblockq *q, 179 pa_cvolume *volume, 180 pa_proplist *p, 181 pa_sink_input_flags_t flags) { 182 183 memblockq_stream *u = NULL; 184 pa_sink_input_new_data data; 185 186 pa_assert(sink); 187 pa_assert(ss); 188 189 /* We allow creating this stream with no q set, so that it can be 190 * filled in later */ 191 192 u = pa_msgobject_new(memblockq_stream); 193 u->parent.parent.free = memblockq_stream_free; 194 u->parent.process_msg = memblockq_stream_process_msg; 195 u->core = sink->core; 196 u->sink_input = NULL; 197 u->memblockq = NULL; 198 199 pa_sink_input_new_data_init(&data); 200 pa_sink_input_new_data_set_sink(&data, sink, false, true); 201 data.driver = __FILE__; 202 pa_sink_input_new_data_set_sample_spec(&data, ss); 203 pa_sink_input_new_data_set_channel_map(&data, map); 204 pa_sink_input_new_data_set_volume(&data, volume); 205 pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p); 206 data.flags |= flags; 207 208 pa_sink_input_new(&u->sink_input, sink->core, &data); 209 pa_sink_input_new_data_done(&data); 210 211 if (!u->sink_input) 212 goto fail; 213 214 u->sink_input->pop = sink_input_pop_cb; 215 u->sink_input->process_rewind = sink_input_process_rewind_cb; 216 u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; 217 u->sink_input->kill = sink_input_kill_cb; 218 u->sink_input->state_change = sink_input_state_change_cb; 219 u->sink_input->userdata = u; 220 221 if (q) 222 pa_memblockq_sink_input_set_queue(u->sink_input, q); 223 224 /* The reference to u is dangling here, because we want 225 * to keep this stream around until it is fully played. */ 226 227 /* This sink input is not "put" yet, i.e. pa_sink_input_put() has 228 * not been called! */ 229 230 return pa_sink_input_ref(u->sink_input); 231 232fail: 233 if (u) 234 memblockq_stream_unref(u); 235 236 return NULL; 237} 238 239int pa_play_memblockq( 240 pa_sink *sink, 241 const pa_sample_spec *ss, 242 const pa_channel_map *map, 243 pa_memblockq *q, 244 pa_cvolume *volume, 245 pa_proplist *p, 246 pa_sink_input_flags_t flags, 247 uint32_t *sink_input_index) { 248 249 pa_sink_input *i; 250 251 pa_assert(sink); 252 pa_assert(ss); 253 pa_assert(q); 254 255 if (!(i = pa_memblockq_sink_input_new(sink, ss, map, q, volume, p, flags))) 256 return -1; 257 258 pa_sink_input_put(i); 259 260 if (sink_input_index) 261 *sink_input_index = i->index; 262 263 pa_sink_input_unref(i); 264 265 return 0; 266} 267 268void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) { 269 memblockq_stream *u; 270 271 pa_sink_input_assert_ref(i); 272 u = MEMBLOCKQ_STREAM(i->userdata); 273 memblockq_stream_assert_ref(u); 274 275 if (u->memblockq) 276 pa_memblockq_free(u->memblockq); 277 278 if ((u->memblockq = q)) { 279 pa_memblockq_set_prebuf(q, 0); 280 pa_memblockq_set_silence(q, NULL); 281 pa_memblockq_willneed(q); 282 } 283} 284