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>
50 #include "module-null-sink-symdef.h"
52 PA_MODULE_AUTHOR("Lennart Poettering")
53 PA_MODULE_DESCRIPTION("Clocked NULL sink")
54 PA_MODULE_VERSION(PACKAGE_VERSION
)
56 "format=<sample format> "
57 "channels=<number of channels> "
59 "sink_name=<name of sink>"
60 "channel_map=<channel map>"
61 "description=<description for the sink>")
63 #define DEFAULT_SINK_NAME "null"
70 pa_asyncmsgq
*asyncmsgq
;
73 struct timeval timestamp
;
76 static const char* const valid_modargs
[] = {
86 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, pa_memchunk
*chunk
) {
87 struct userdata
*u
= PA_SINK(o
)->userdata
;
90 case PA_SINK_MESSAGE_SET_STATE
:
92 if (PA_PTR_TO_UINT(data
) == PA_SINK_RUNNING
)
93 pa_gettimeofday(&u
->timestamp
);
97 case PA_SINK_MESSAGE_GET_LATENCY
: {
100 pa_gettimeofday(&now
);
102 if (pa_timeval_cmp(&u
->timestamp
, &now
) > 0)
103 *((pa_usec_t
*) data
) = 0;
105 *((pa_usec_t
*) data
) = pa_timeval_diff(&u
->timestamp
, &now
);
110 return pa_sink_process_msg(o
, code
, data
, chunk
);
113 static void thread_func(void *userdata
) {
114 struct userdata
*u
= userdata
;
115 struct pollfd pollfd
;
119 pa_log_debug("Thread starting up");
121 pa_gettimeofday(&u
->timestamp
);
123 memset(&pollfd
, 0, sizeof(pollfd
));
124 pollfd
.fd
= pa_asyncmsgq_get_fd(u
->asyncmsgq
);
125 pollfd
.events
= POLLIN
;
128 pa_msgobject
*object
;
135 /* Check whether there is a message for us to process */
136 if (pa_asyncmsgq_get(u
->asyncmsgq
, &object
, &code
, &data
, &chunk
, 0) == 0) {
139 if (!object
&& code
== PA_MESSAGE_SHUTDOWN
) {
140 pa_asyncmsgq_done(u
->asyncmsgq
, 0);
144 ret
= pa_asyncmsgq_dispatch(object
, code
, data
, &chunk
);
145 pa_asyncmsgq_done(u
->asyncmsgq
, ret
);
149 /* Render some data and drop it immediately */
150 if (u
->sink
->thread_info
.state
== PA_SINK_RUNNING
) {
151 pa_gettimeofday(&now
);
153 if (pa_timeval_cmp(&u
->timestamp
, &now
) <= 0) {
156 if (pa_sink_render(u
->sink
, u
->block_size
, &chunk
) >= 0) {
158 pa_memblock_unref(chunk
.memblock
);
162 pa_timeval_add(&u
->timestamp
, pa_bytes_to_usec(l
, &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
->asyncmsgq
) < 0)
178 r
= poll(&pollfd
, 1, timeout
);
179 pa_asyncmsgq_after_poll(u
->asyncmsgq
);
185 pa_log("poll() failed: %s", pa_cstrerror(errno
));
189 pa_assert(r
== 0 || pollfd
.revents
== POLLIN
);
193 /* We have to continue processing messages until we receive the
194 * SHUTDOWN message */
195 pa_asyncmsgq_post(u
->core
->asyncmsgq
, PA_MSGOBJECT(u
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, NULL
, NULL
);
196 pa_asyncmsgq_wait_for(u
->asyncmsgq
, PA_MESSAGE_SHUTDOWN
);
199 pa_log_debug("Thread shutting down");
202 int pa__init(pa_core
*c
, pa_module
*m
) {
203 struct userdata
*u
= NULL
;
206 pa_modargs
*ma
= NULL
;
211 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
212 pa_log("Failed to parse module arguments.");
216 ss
= c
->default_sample_spec
;
217 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
218 pa_log("Invalid sample format specification or channel map");
222 u
= pa_xnew0(struct userdata
, 1);
227 pa_assert_se(u
->asyncmsgq
= pa_asyncmsgq_new(0));
229 if (!(u
->sink
= pa_sink_new(c
, __FILE__
, pa_modargs_get_value(ma
, "sink_name", DEFAULT_SINK_NAME
), 0, &ss
, &map
))) {
230 pa_log("Failed to create sink.");
234 u
->sink
->parent
.process_msg
= sink_process_msg
;
235 u
->sink
->userdata
= u
;
237 pa_sink_set_module(u
->sink
, m
);
238 pa_sink_set_asyncmsgq(u
->sink
, u
->asyncmsgq
);
239 pa_sink_set_description(u
->sink
, pa_modargs_get_value(ma
, "description", "NULL sink"));
241 u
->block_size
= pa_bytes_per_second(&ss
) / 20; /* 50 ms */
242 if (u
->block_size
<= 0)
243 u
->block_size
= pa_frame_size(&ss
);
245 if (!(u
->thread
= pa_thread_new(thread_func
, u
))) {
246 pa_log("Failed to create thread.");
263 void pa__done(pa_core
*c
, pa_module
*m
) {
269 if (!(u
= m
->userdata
))
273 pa_sink_disconnect(u
->sink
);
276 pa_asyncmsgq_send(u
->asyncmsgq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, NULL
);
277 pa_thread_free(u
->thread
);
281 pa_asyncmsgq_free(u
->asyncmsgq
);
284 pa_sink_unref(u
->sink
);