2 * Copyright (C) 1999-2001 Erik Walthinsen <omega@cse.ogi.edu>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
26 #include <gst/audio/audio.h>
28 #include "gstplayondemand.h"
30 /* in these files, a 'tick' is a discrete unit of time, usually around the 1ms
31 * range. a tick is not divisible into smaller units of time. 1ms is probably
32 * way beyond what a real computer can actually keep track of, but hey ... */
34 /* some default values */
35 #define GST_POD_MAX_PLAYS 100 /* maximum simultaneous plays */
36 #define GST_POD_BUFFER_TIME 5.0 /* buffer length in seconds */
37 #define GST_POD_TICK_RATE 1e-6 /* ticks per second */
39 /* buffer pool fallback values ... use if no buffer pool is available */
40 #define GST_POD_BUFPOOL_SIZE 4096
41 #define GST_POD_BUFPOOL_NUM 6
43 static GstStaticPadTemplate play_on_demand_sink_template =
44 GST_STATIC_PAD_TEMPLATE ("sink",
47 GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
48 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
51 static GstStaticPadTemplate play_on_demand_src_template =
52 GST_STATIC_PAD_TEMPLATE ("src",
55 GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
56 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
60 /* GObject functionality */
61 static void play_on_demand_class_init (GstPlayOnDemandClass * klass);
62 static void play_on_demand_base_init (GstPlayOnDemandClass * klass);
63 static void play_on_demand_init (GstPlayOnDemand * filter);
64 static void play_on_demand_set_property (GObject * object, guint prop_id,
65 const GValue * value, GParamSpec * pspec);
66 static void play_on_demand_get_property (GObject * object, guint prop_id,
67 GValue * value, GParamSpec * pspec);
68 static void play_on_demand_finalize (GObject * object);
70 /* GStreamer functionality */
71 static GstPadLinkReturn play_on_demand_pad_link (GstPad * pad,
72 const GstCaps * caps);
73 static void play_on_demand_loop (GstElement * elem);
74 static gboolean play_on_demand_set_clock (GstElement * elem, GstClock * clock);
77 static void play_on_demand_play_handler (GstElement * elem);
78 static void play_on_demand_clear_handler (GstElement * elem);
79 static void play_on_demand_reset_handler (GstElement * elem);
81 /* utility functions */
82 static void play_on_demand_add_play_pointer (GstPlayOnDemand * filter,
84 static void play_on_demand_resize_buffer (GstPlayOnDemand * filter);
87 gst_play_on_demand_get_type (void)
89 static GType play_on_demand_type = 0;
91 if (!play_on_demand_type) {
92 static const GTypeInfo play_on_demand_info = {
93 sizeof (GstPlayOnDemandClass),
94 (GBaseInitFunc) play_on_demand_base_init,
96 (GClassInitFunc) play_on_demand_class_init,
99 sizeof (GstPlayOnDemand),
101 (GInstanceInitFunc) play_on_demand_init,
104 play_on_demand_type = g_type_register_static (GST_TYPE_ELEMENT,
105 "GstPlayOnDemand", &play_on_demand_info, 0);
107 return play_on_demand_type;
111 /* signals and properties */
114 /* add signals here */
134 static guint gst_pod_filter_signals[LAST_SIGNAL] = { 0 };
136 static GstElementClass *parent_class = NULL;
139 play_on_demand_base_init (GstPlayOnDemandClass * klass)
141 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
143 gst_element_class_add_pad_template (element_class,
144 gst_static_pad_template_get (&play_on_demand_src_template));
145 gst_element_class_add_pad_template (element_class,
146 gst_static_pad_template_get (&play_on_demand_sink_template));
147 gst_element_class_set_details_simple (element_class, "Play On Demand",
148 "Filter/Editor/Audio",
149 "Schedule a stream to play at specific times, or when a signal is received",
150 "Leif Morgan Johnson <leif@ambient.2y.net>");
154 play_on_demand_class_init (GstPlayOnDemandClass * klass)
156 GObjectClass *gobject_class;
157 GstElementClass *gstelement_class;
159 gobject_class = (GObjectClass *) klass;
160 gstelement_class = (GstElementClass *) klass;
162 gst_pod_filter_signals[PLAYED_SIGNAL] =
163 g_signal_new ("played", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
164 G_STRUCT_OFFSET (GstPlayOnDemandClass, played),
165 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
167 gst_pod_filter_signals[STOPPED_SIGNAL] =
168 g_signal_new ("stopped", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
169 G_STRUCT_OFFSET (GstPlayOnDemandClass, stopped),
170 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
172 gst_pod_filter_signals[PLAY_SIGNAL] =
173 g_signal_new ("play", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
174 G_STRUCT_OFFSET (GstPlayOnDemandClass, play),
175 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
177 gst_pod_filter_signals[CLEAR_SIGNAL] =
178 g_signal_new ("clear", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
179 G_STRUCT_OFFSET (GstPlayOnDemandClass, clear),
180 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
182 gst_pod_filter_signals[RESET_SIGNAL] =
183 g_signal_new ("reset", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
184 G_STRUCT_OFFSET (GstPlayOnDemandClass, reset),
185 NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
187 klass->play = play_on_demand_play_handler;
188 klass->clear = play_on_demand_clear_handler;
189 klass->reset = play_on_demand_reset_handler;
191 parent_class = g_type_class_peek_parent (klass);
193 gobject_class->set_property = play_on_demand_set_property;
194 gobject_class->get_property = play_on_demand_get_property;
195 gobject_class->finalize = play_on_demand_finalize;
197 gstelement_class->set_clock = play_on_demand_set_clock;
199 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MUTE,
200 g_param_spec_boolean ("mute", "Silence output", "Do not output any sound",
202 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
204 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BUFFER_TIME,
205 g_param_spec_float ("buffer-time", "Buffer length in seconds",
206 "Number of seconds of audio the buffer holds", 0.0, G_MAXFLOAT,
208 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
210 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_MAX_PLAYS,
211 g_param_spec_uint ("max-plays", "Maximum simultaneous playbacks",
212 "Maximum allowed number of simultaneous plays from the buffer", 1,
213 G_MAXUINT, GST_POD_MAX_PLAYS,
214 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
216 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TICK_RATE,
217 g_param_spec_float ("tick-rate", "Tick rate (ticks/second)",
218 "The rate of musical ticks, the smallest time unit in a song", 0,
219 G_MAXFLOAT, GST_POD_TICK_RATE,
220 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
222 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TOTAL_TICKS,
223 g_param_spec_uint ("total-ticks", "Total number of ticks",
224 "Total number of ticks in the tick array", 1, G_MAXUINT, 1,
225 G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
227 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TICKS,
228 g_param_spec_pointer ("ticks", "Ticks to play sample on",
229 "An array of ticks (musical times) at which to play the sample",
230 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
234 play_on_demand_init (GstPlayOnDemand * filter)
237 gst_pad_new_from_static_template (&play_on_demand_src_template, "src");
239 gst_pad_new_from_static_template (&play_on_demand_sink_template, "sink");
241 gst_pad_set_link_function (filter->sinkpad, play_on_demand_pad_link);
243 gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
244 gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
246 gst_element_set_loop_function (GST_ELEMENT (filter), play_on_demand_loop);
248 filter->clock = NULL;
252 filter->ticks = g_new (guint32, filter->total_ticks / 32 + 1);
253 filter->plays = g_new (guint, filter->max_plays);
255 play_on_demand_resize_buffer (filter);
256 play_on_demand_reset_handler (GST_ELEMENT (filter));
260 play_on_demand_set_property (GObject * object, guint prop_id,
261 const GValue * value, GParamSpec * pspec)
263 GstPlayOnDemand *filter;
265 guint new_size, min_size, *new_plays;
268 g_return_if_fail (GST_IS_PLAYONDEMAND (object));
269 filter = GST_PLAYONDEMAND (object);
273 filter->mute = g_value_get_boolean (value);
275 case PROP_BUFFER_TIME:
276 filter->buffer_time = g_value_get_float (value);
277 play_on_demand_resize_buffer (filter);
279 /* clear out now-invalid play pointers */
280 for (i = 0; i < filter->max_plays; i++)
281 filter->plays[i] = G_MAXUINT;
285 new_size = g_value_get_uint (value);
286 min_size = (new_size < filter->max_plays) ? new_size : filter->max_plays;
288 new_plays = g_new (guint, new_size);
289 for (i = 0; i < min_size; i++)
290 new_plays[i] = filter->plays[i];
291 for (i = min_size; i < new_size; i++)
292 new_plays[i] = G_MAXUINT;
294 g_free (filter->plays);
295 filter->plays = new_plays;
296 filter->max_plays = new_size;
300 filter->tick_rate = g_value_get_float (value);
302 case PROP_TOTAL_TICKS:
303 new_size = g_value_get_uint (value);
305 (new_size < filter->total_ticks) ? new_size : filter->total_ticks;
307 new_ticks = g_new (guint32, new_size / 32 + 1);
308 for (i = 0; i <= min_size / 32; i++)
309 new_ticks[i] = filter->ticks[i];
310 for (i = min_size / 32 + 1; i <= new_size / 32; i++)
313 g_free (filter->ticks);
314 filter->ticks = new_ticks;
315 filter->total_ticks = new_size;
319 new_ticks = (guint *) g_value_get_pointer (value);
321 g_free (filter->ticks);
322 filter->ticks = new_ticks;
331 play_on_demand_get_property (GObject * object, guint prop_id,
332 GValue * value, GParamSpec * pspec)
334 GstPlayOnDemand *filter;
336 g_return_if_fail (GST_IS_PLAYONDEMAND (object));
337 filter = GST_PLAYONDEMAND (object);
341 g_value_set_boolean (value, filter->mute);
343 case PROP_BUFFER_TIME:
344 g_value_set_float (value, filter->buffer_time);
347 g_value_set_uint (value, filter->max_plays);
350 g_value_set_float (value, filter->tick_rate);
352 case PROP_TOTAL_TICKS:
353 g_value_set_uint (value, filter->total_ticks);
356 g_value_set_pointer (value, (gpointer) filter->ticks);
359 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
365 play_on_demand_finalize (GObject * object)
367 GstPlayOnDemand *filter = GST_PLAYONDEMAND (object);
369 g_free (filter->ticks);
370 g_free (filter->plays);
371 g_free (filter->buffer);
373 G_OBJECT_CLASS (parent_class)->finalize (object);
376 static GstPadLinkReturn
377 play_on_demand_pad_link (GstPad * pad, const GstCaps * caps)
379 const gchar *mimetype;
380 GstPlayOnDemand *filter;
381 GstStructure *structure;
383 g_return_val_if_fail (caps != NULL, GST_PAD_LINK_DELAYED);
384 g_return_val_if_fail (pad != NULL, GST_PAD_LINK_DELAYED);
386 filter = GST_PLAYONDEMAND (GST_PAD_PARENT (pad));
388 structure = gst_caps_get_structure (caps, 0);
390 mimetype = gst_structure_get_name (structure);
391 gst_structure_get_int (structure, "rate", &filter->rate);
392 gst_structure_get_int (structure, "channels", &filter->channels);
394 if (strcmp (mimetype, "audio/x-raw-int") == 0) {
395 filter->format = GST_PLAYONDEMAND_FORMAT_INT;
396 gst_structure_get_int (structure, "width", &filter->width);
397 } else if (strcmp (mimetype, "audio/x-raw-float") == 0) {
398 filter->format = GST_PLAYONDEMAND_FORMAT_FLOAT;
401 play_on_demand_resize_buffer (filter);
403 return gst_pad_try_set_caps (filter->srcpad, caps);
407 play_on_demand_add_play_pointer (GstPlayOnDemand * filter, guint pos)
411 if (filter->rate && ((filter->buffer_time * filter->rate) > pos)) {
412 for (i = 0; i < filter->max_plays; i++) {
413 if (filter->plays[i] == G_MAXUINT) {
414 filter->plays[i] = pos;
415 /* emit a signal to indicate a sample being played */
416 g_signal_emit (filter, gst_pod_filter_signals[PLAYED_SIGNAL], 0);
424 play_on_demand_loop (GstElement * elem)
426 GstPlayOnDemand *filter = GST_PLAYONDEMAND (elem);
427 guint num_in, num_out, num_filter;
429 GstBuffer *out = NULL;
430 static guint last_tick = 0;
432 g_return_if_fail (filter != NULL);
433 g_return_if_fail (GST_IS_PLAYONDEMAND (filter));
435 in = (in == NULL && !filter->eos) ? gst_pad_pull (filter->sinkpad) : NULL;
437 if (filter->format == GST_PLAYONDEMAND_FORMAT_INT) {
438 if (filter->width == 16) {
443 #define _TYPE_ gint16
444 #include "filter.func"
446 } else if (filter->width == 8) {
452 #include "filter.func"
455 } else if (filter->format == GST_PLAYONDEMAND_FORMAT_FLOAT) {
460 #define _TYPE_ gfloat
461 #include "filter.func"
467 play_on_demand_set_clock (GstElement * elem, GstClock * clock)
469 GstPlayOnDemand *filter;
471 g_return_if_fail (elem != NULL);
472 g_return_if_fail (GST_IS_PLAYONDEMAND (elem));
473 filter = GST_PLAYONDEMAND (elem);
475 filter->clock = clock;
477 return GST_ELEMENT_CLASS (parent_class)->set_clock (elem, clock);
481 play_on_demand_play_handler (GstElement * elem)
483 GstPlayOnDemand *filter;
485 g_return_if_fail (elem != NULL);
486 g_return_if_fail (GST_IS_PLAYONDEMAND (elem));
487 filter = GST_PLAYONDEMAND (elem);
489 play_on_demand_add_play_pointer (filter, 0);
493 play_on_demand_clear_handler (GstElement * elem)
495 GstPlayOnDemand *filter;
498 g_return_if_fail (elem != NULL);
499 g_return_if_fail (GST_IS_PLAYONDEMAND (elem));
500 filter = GST_PLAYONDEMAND (elem);
505 for (i = 0; i < filter->max_plays; i++)
506 filter->plays[i] = G_MAXUINT;
507 for (i = 0; i < filter->buffer_bytes; i++)
508 filter->buffer[i] = (gchar) 0;
512 play_on_demand_reset_handler (GstElement * elem)
514 GstPlayOnDemand *filter;
517 play_on_demand_clear_handler (elem);
519 g_return_if_fail (elem != NULL);
520 g_return_if_fail (GST_IS_PLAYONDEMAND (elem));
521 filter = GST_PLAYONDEMAND (elem);
523 for (i = 0; i <= filter->total_ticks / 32; i++)
524 filter->ticks[i] = 0;
528 play_on_demand_resize_buffer (GstPlayOnDemand * filter)
531 guint new_size, min_size;
534 /* use a default sample rate of 44100, 1 channel, 1 byte per sample if caps
535 haven't been set yet */
536 new_size = (guint) filter->buffer_time;
537 new_size *= (filter->rate) ? filter->rate : 44100;
538 new_size *= (filter->channels) ? filter->channels : 1;
540 if (filter->format && filter->format == GST_PLAYONDEMAND_FORMAT_FLOAT)
541 new_size *= sizeof (gfloat);
543 new_size *= (filter->width) ? filter->width / 8 : 1;
546 (new_size < filter->buffer_bytes) ? new_size : filter->buffer_bytes;
548 new_buffer = g_new (gchar, new_size);
549 for (i = 0; i < min_size; i++)
550 new_buffer[i] = filter->buffer[i];
551 for (i = min_size; i < new_size; i++)
552 new_buffer[i] = (gchar) 0;
554 g_free (filter->buffer);
555 filter->buffer = new_buffer;
556 filter->buffer_bytes = new_size;
560 plugin_init (GstPlugin * plugin)
562 return gst_element_register (plugin, "playondemand",
563 GST_RANK_NONE, GST_TYPE_PLAYONDEMAND);
566 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
569 "Plays a stream at specific times, or when it receives a signal",
570 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)