]> code.delx.au - pulseaudio/blob - src/pulsecore/play-memblockq.c
5d3c2d391d3bfee9e87066c1e17d2abb86bcea0f
[pulseaudio] / src / pulsecore / play-memblockq.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2006 Lennart Poettering
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31
32 #include <pulse/xmalloc.h>
33
34 #include <pulsecore/sink-input.h>
35 #include <pulsecore/gccmacro.h>
36 #include <pulsecore/thread-mq.h>
37
38 #include "play-memblockq.h"
39
40 typedef struct memblockq_stream {
41 pa_msgobject parent;
42 pa_core *core;
43 pa_sink_input *sink_input;
44 pa_memblockq *memblockq;
45 } memblockq_stream;
46
47 enum {
48 MEMBLOCKQ_STREAM_MESSAGE_UNLINK,
49 };
50
51 PA_DECLARE_CLASS(memblockq_stream);
52 #define MEMBLOCKQ_STREAM(o) (memblockq_stream_cast(o))
53 static PA_DEFINE_CHECK_TYPE(memblockq_stream, pa_msgobject);
54
55 static void memblockq_stream_unlink(memblockq_stream *u) {
56 pa_assert(u);
57
58 if (!u->sink_input)
59 return;
60
61 pa_sink_input_unlink(u->sink_input);
62
63 pa_sink_input_unref(u->sink_input);
64 u->sink_input = NULL;
65
66 memblockq_stream_unref(u);
67 }
68
69 static void memblockq_stream_free(pa_object *o) {
70 memblockq_stream *u = MEMBLOCKQ_STREAM(o);
71 pa_assert(u);
72
73 memblockq_stream_unlink(u);
74
75 if (u->memblockq)
76 pa_memblockq_free(u->memblockq);
77
78 pa_xfree(u);
79 }
80
81 static int memblockq_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
82 memblockq_stream *u = MEMBLOCKQ_STREAM(o);
83 memblockq_stream_assert_ref(u);
84
85 switch (code) {
86 case MEMBLOCKQ_STREAM_MESSAGE_UNLINK:
87 memblockq_stream_unlink(u);
88 break;
89 }
90
91 return 0;
92 }
93
94 static void sink_input_kill_cb(pa_sink_input *i) {
95 pa_sink_input_assert_ref(i);
96
97 memblockq_stream_unlink(MEMBLOCKQ_STREAM(i->userdata));
98 }
99
100 static int sink_input_peek_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
101 memblockq_stream *u;
102
103 pa_assert(i);
104 pa_assert(chunk);
105 u = MEMBLOCKQ_STREAM(i->userdata);
106 memblockq_stream_assert_ref(u);
107
108 if (!u->memblockq)
109 return -1;
110
111 if (pa_memblockq_peek(u->memblockq, chunk) < 0) {
112 pa_memblockq_free(u->memblockq);
113 u->memblockq = NULL;
114 pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(u), MEMBLOCKQ_STREAM_MESSAGE_UNLINK, NULL, 0, NULL, NULL);
115 return -1;
116 }
117
118 return 0;
119 }
120
121 static void sink_input_drop_cb(pa_sink_input *i, size_t length) {
122 memblockq_stream *u;
123
124 pa_assert(i);
125 pa_assert(length > 0);
126 u = MEMBLOCKQ_STREAM(i->userdata);
127 memblockq_stream_assert_ref(u);
128
129 if (!u->memblockq)
130 return;
131
132 pa_memblockq_drop(u->memblockq, length);
133 }
134
135 pa_sink_input* pa_memblockq_sink_input_new(
136 pa_sink *sink,
137 const char *name,
138 const pa_sample_spec *ss,
139 const pa_channel_map *map,
140 pa_memblockq *q,
141 pa_cvolume *volume) {
142
143 memblockq_stream *u = NULL;
144 pa_sink_input_new_data data;
145
146 pa_assert(sink);
147 pa_assert(ss);
148
149 /* We allow creating this stream with no q set, so that it can be
150 * filled in later */
151
152 if (q && pa_memblockq_get_length(q) <= 0) {
153 pa_memblockq_free(q);
154 return NULL;
155 }
156
157 if (volume && pa_cvolume_is_muted(volume)) {
158 pa_memblockq_free(q);
159 return NULL;
160 }
161
162 u = pa_msgobject_new(memblockq_stream);
163 u->parent.parent.free = memblockq_stream_free;
164 u->parent.process_msg = memblockq_stream_process_msg;
165 u->core = sink->core;
166 u->sink_input = NULL;
167 u->memblockq = q;
168
169 pa_sink_input_new_data_init(&data);
170 data.sink = sink;
171 data.name = name;
172 data.driver = __FILE__;
173 pa_sink_input_new_data_set_sample_spec(&data, ss);
174 pa_sink_input_new_data_set_channel_map(&data, map);
175 pa_sink_input_new_data_set_volume(&data, volume);
176
177 if (!(u->sink_input = pa_sink_input_new(sink->core, &data, 0)))
178 goto fail;
179
180 u->sink_input->peek = sink_input_peek_cb;
181 u->sink_input->drop = sink_input_drop_cb;
182 u->sink_input->kill = sink_input_kill_cb;
183 u->sink_input->userdata = u;
184
185 if (q)
186 pa_memblockq_prebuf_disable(q);
187
188 /* The reference to u is dangling here, because we want
189 * to keep this stream around until it is fully played. */
190
191 /* This sink input is not "put" yet, i.e. pa_sink_input_put() has
192 * not been called! */
193
194 return pa_sink_input_ref(u->sink_input);
195
196 fail:
197 if (u)
198 memblockq_stream_unref(u);
199
200 return NULL;
201 }
202
203 int pa_play_memblockq(
204 pa_sink *sink,
205 const char *name,
206 const pa_sample_spec *ss,
207 const pa_channel_map *map,
208 pa_memblockq *q,
209 pa_cvolume *volume) {
210
211 pa_sink_input *i;
212
213 pa_assert(sink);
214 pa_assert(ss);
215 pa_assert(q);
216
217 if (!(i = pa_memblockq_sink_input_new(sink, name, ss, map, q, volume)))
218 return -1;
219
220 pa_sink_input_put(i);
221 pa_sink_input_unref(i);
222
223 return 0;
224 }
225
226 void pa_memblockq_sink_input_set_queue(pa_sink_input *i, pa_memblockq *q) {
227 memblockq_stream *u;
228
229 pa_sink_input_assert_ref(i);
230 u = MEMBLOCKQ_STREAM(i->userdata);
231 memblockq_stream_assert_ref(u);
232
233 if (u->memblockq)
234 pa_memblockq_free(u->memblockq);
235 u->memblockq = q;
236 }