4 This file is part of PulseAudio.
6 Copyright 2004-2006 Lennart Poettering
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.
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.
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
38 #include <pulse/timeval.h>
39 #include <pulse/xmalloc.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/sink.h>
43 #include <pulsecore/module.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/core-error.h>
46 #include <pulsecore/modargs.h>
47 #include <pulsecore/log.h>
48 #include <pulsecore/thread.h>
49 #include <pulsecore/thread-mq.h>
51 #include "module-null-sink-symdef.h"
53 PA_MODULE_AUTHOR("Lennart Poettering")
54 PA_MODULE_DESCRIPTION("Clocked NULL sink")
55 PA_MODULE_VERSION(PACKAGE_VERSION
)
57 "format=<sample format> "
58 "channels=<number of channels> "
60 "sink_name=<name of sink>"
61 "channel_map=<channel map>"
62 "description=<description for the sink>")
64 #define DEFAULT_SINK_NAME "null"
71 pa_thread_mq thread_mq
;
74 struct timeval timestamp
;
77 static const char* const valid_modargs
[] = {
87 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
88 struct userdata
*u
= PA_SINK(o
)->userdata
;
91 case PA_SINK_MESSAGE_SET_STATE
:
93 if (PA_PTR_TO_UINT(data
) == PA_SINK_RUNNING
)
94 pa_gettimeofday(&u
->timestamp
);
98 case PA_SINK_MESSAGE_GET_LATENCY
: {
101 pa_gettimeofday(&now
);
103 if (pa_timeval_cmp(&u
->timestamp
, &now
) > 0)
104 *((pa_usec_t
*) data
) = 0;
106 *((pa_usec_t
*) data
) = pa_timeval_diff(&u
->timestamp
, &now
);
111 return pa_sink_process_msg(o
, code
, data
, offset
, chunk
);
114 static void thread_func(void *userdata
) {
115 struct userdata
*u
= userdata
;
116 struct pollfd pollfd
;
120 pa_log_debug("Thread starting up");
122 pa_thread_mq_install(&u
->thread_mq
);
124 pa_gettimeofday(&u
->timestamp
);
126 memset(&pollfd
, 0, sizeof(pollfd
));
127 pollfd
.fd
= pa_asyncmsgq_get_fd(u
->thread_mq
.inq
);
128 pollfd
.events
= POLLIN
;
131 pa_msgobject
*object
;
139 /* Check whether there is a message for us to process */
140 if (pa_asyncmsgq_get(u
->thread_mq
.inq
, &object
, &code
, &data
, &offset
, &chunk
, 0) == 0) {
143 if (!object
&& code
== PA_MESSAGE_SHUTDOWN
) {
144 pa_asyncmsgq_done(u
->thread_mq
.inq
, 0);
148 ret
= pa_asyncmsgq_dispatch(object
, code
, data
, offset
, &chunk
);
149 pa_asyncmsgq_done(u
->thread_mq
.inq
, ret
);
153 /* Render some data and drop it immediately */
154 if (u
->sink
->thread_info
.state
== PA_SINK_RUNNING
) {
155 pa_gettimeofday(&now
);
157 if (pa_timeval_cmp(&u
->timestamp
, &now
) <= 0) {
159 pa_sink_render(u
->sink
, u
->block_size
, &chunk
);
160 pa_memblock_unref(chunk
.memblock
);
162 pa_timeval_add(&u
->timestamp
, pa_bytes_to_usec(chunk
.length
, &u
->sink
->sample_spec
));
166 timeout
= pa_timeval_diff(&u
->timestamp
, &now
)/1000;
173 /* Hmm, nothing to do. Let's sleep */
175 if (pa_asyncmsgq_before_poll(u
->thread_mq
.inq
) < 0)
178 r
= poll(&pollfd
, 1, timeout
);
179 pa_asyncmsgq_after_poll(u
->thread_mq
.inq
);
182 if (errno
== EINTR
) {
187 pa_log("poll() failed: %s", pa_cstrerror(errno
));
191 pa_assert(r
== 0 || pollfd
.revents
== POLLIN
);
195 /* We have to continue processing messages until we receive the
196 * SHUTDOWN message */
197 pa_asyncmsgq_post(u
->thread_mq
.outq
, PA_MSGOBJECT(u
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, 0, NULL
, NULL
);
198 pa_asyncmsgq_wait_for(u
->thread_mq
.inq
, PA_MESSAGE_SHUTDOWN
);
201 pa_log_debug("Thread shutting down");
204 int pa__init(pa_module
*m
) {
205 struct userdata
*u
= NULL
;
208 pa_modargs
*ma
= NULL
;
212 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
213 pa_log("Failed to parse module arguments.");
217 ss
= m
->core
->default_sample_spec
;
218 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
219 pa_log("Invalid sample format specification or channel map");
223 u
= pa_xnew0(struct userdata
, 1);
228 pa_thread_mq_init(&u
->thread_mq
, m
->core
->mainloop
);
230 if (!(u
->sink
= pa_sink_new(m
->core
, __FILE__
, pa_modargs_get_value(ma
, "sink_name", DEFAULT_SINK_NAME
), 0, &ss
, &map
))) {
231 pa_log("Failed to create sink.");
235 u
->sink
->parent
.process_msg
= sink_process_msg
;
236 u
->sink
->userdata
= u
;
238 pa_sink_set_module(u
->sink
, m
);
239 pa_sink_set_asyncmsgq(u
->sink
, u
->thread_mq
.inq
);
240 pa_sink_set_description(u
->sink
, pa_modargs_get_value(ma
, "description", "NULL sink"));
242 u
->block_size
= pa_bytes_per_second(&ss
) / 20; /* 50 ms */
243 if (u
->block_size
<= 0)
244 u
->block_size
= pa_frame_size(&ss
);
246 if (!(u
->thread
= pa_thread_new(thread_func
, u
))) {
247 pa_log("Failed to create thread.");
264 void pa__done(pa_module
*m
) {
269 if (!(u
= m
->userdata
))
273 pa_sink_disconnect(u
->sink
);
276 pa_asyncmsgq_send(u
->thread_mq
.inq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, 0, NULL
);
277 pa_thread_free(u
->thread
);
280 pa_thread_mq_done(&u
->thread_mq
);
283 pa_sink_unref(u
->sink
);