3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
16 * Boston, MA 02110-1301, USA.
20 * SECTION:element-tinycompresssink
21 * @title: tinycompresssink
23 * Render MP3 data in compress device using tinycompress
25 * ## Example launch line
27 * gst-launch-1.0 filesrc location=sample.mp3 ! mpegaudioparse ! tinycompresssink
28 * ]| test audio rendering in tinycompress
36 #include "gsttinycompresssink.h"
42 #include <sound_manager.h>
43 #include <asoundlib.h>
45 /* FIXME: This might be configurable (eg. property) */
46 #define ALSA_DEFAULT_CARD "Exynos9110Sound"
48 #define COMPRESS_VOL_CTRL "ComprTx0 Volume"
50 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
55 #define DEFAULT_SYNC TRUE
56 #define DEFAULT_DUMP FALSE
57 #define DEFAULT_CARD 0
58 #define DEFAULT_DEVICE 5
66 GST_DEBUG_CATEGORY_STATIC (gst_tinycompress_sink_debug);
67 #define GST_CAT_DEFAULT gst_tinycompress_sink_debug
68 #define gst_tinycompress_sink_parent_class parent_class
69 G_DEFINE_TYPE (GstTinycompressSink, gst_tinycompress_sink, GST_TYPE_BASE_SINK);
71 static void gst_tinycompress_sink_set_property (GObject * object, guint prop_id,
72 const GValue * value, GParamSpec * pspec);
73 static void gst_tinycompress_sink_get_property (GObject * object, guint prop_id,
74 GValue * value, GParamSpec * pspec);
75 static void gst_tinycompress_sink_finalize (GObject * object);
77 static GstStateChangeReturn gst_tinycompress_sink_change_state (GstElement *
78 element, GstStateChange transition);
79 static GstFlowReturn gst_tinycompress_sink_render (GstBaseSink * bsink,
81 static GstCaps *gst_tinycompress_sink_get_caps (GstBaseSink * bsink,
83 static gboolean gst_tinycompress_sink_set_caps (GstBaseSink * bsink,
85 static void gst_tinycompress_sink_get_times (GstBaseSink * bsink,
86 GstBuffer * buf, GstClockTime * start, GstClockTime * end);
87 static gboolean gst_tinycompress_sink_event (GstBaseSink * bsink,
91 _dump_compress_status (GstTinycompressSink * sink, struct compress *compress)
94 unsigned int sampling_rate = 0;
95 unsigned int samples = 0;
96 struct timespec tstamp;
98 g_return_if_fail (compress != NULL);
100 compress_get_tstamp (compress, &samples, &sampling_rate);
102 if (compress_get_hpointer (compress, &avail, &tstamp) != 0) {
103 GST_ERROR_OBJECT (sink, "Error querying timestamp : %s",
104 compress_get_error (compress));
106 GST_DEBUG_OBJECT (sink,
107 "samples:%10d, avail:%7d, DSP played:%4jd.%03jd\n", samples, avail,
108 (intmax_t) (tstamp.tv_sec / sink->t_codec.ch_in),
109 (intmax_t) (tstamp.tv_nsec / sink->t_codec.ch_in) / 1000000);
114 _mixer_control_set_value (GstTinycompressSink * sink, const char *ctl_name,
118 snd_ctl_elem_value_t *control;
119 snd_ctl_elem_id_t *id;
120 snd_ctl_elem_info_t *info;
121 snd_ctl_elem_type_t type;
126 ret = snd_ctl_open (&handle, ALSA_DEFAULT_CARD, 0);
128 GST_ERROR_OBJECT (sink, "snd_ctl_open error, card: %s: %s",
129 ALSA_DEFAULT_CARD, snd_strerror (ret));
133 snd_ctl_elem_id_alloca (&id);
134 snd_ctl_elem_info_alloca (&info);
135 snd_ctl_elem_value_alloca (&control);
137 snd_ctl_elem_id_set_interface (id, SND_CTL_ELEM_IFACE_MIXER);
138 snd_ctl_elem_id_set_name (id, ctl_name);
140 snd_ctl_elem_info_set_id (info, id);
141 if (snd_ctl_elem_info (handle, info) < 0) {
142 GST_ERROR_OBJECT (sink, "Cannot find control element: %s", ctl_name);
145 snd_ctl_elem_info_get_id (info, id);
147 type = snd_ctl_elem_info_get_type (info);
148 count = snd_ctl_elem_info_get_count (info);
150 snd_ctl_elem_value_set_id (control, id);
152 snd_ctl_elem_read (handle, control);
155 case SND_CTL_ELEM_TYPE_BOOLEAN:
156 for (i = 0; i < count; i++)
157 snd_ctl_elem_value_set_boolean (control, i, val);
160 case SND_CTL_ELEM_TYPE_INTEGER:
161 for (i = 0; i < count; i++)
162 snd_ctl_elem_value_set_integer (control, i, val);
165 case SND_CTL_ELEM_TYPE_ENUMERATED:
166 for (i = 0; i < count; i++)
167 snd_ctl_elem_value_set_enumerated (control, i, val);
171 GST_WARNING_OBJECT (sink, "unsupported control element type");
175 snd_ctl_elem_write (handle, control);
177 snd_ctl_close (handle);
179 GST_INFO_OBJECT (sink, "set mixer(%s) = %d success", ctl_name, val);
184 GST_ERROR_OBJECT (sink, "Error");
185 snd_ctl_close (handle);
191 gst_tinycompress_set_codec_param (GstTinycompressSink * sink, GstCaps * caps)
193 GstStructure *structure;
197 g_return_val_if_fail (sink != NULL, FALSE);
198 g_return_val_if_fail (caps != NULL, FALSE);
200 structure = gst_caps_get_structure (caps, 0);
201 gst_structure_get_int (structure, "rate", &rate);
202 gst_structure_get_int (structure, "channels", &channels);
204 sink->t_codec.id = SND_AUDIOCODEC_MP3;
205 sink->t_codec.ch_in = channels;
206 sink->t_codec.ch_out = channels;
207 sink->t_codec.sample_rate = rate;
208 sink->t_codec.bit_rate = 0;
209 sink->t_codec.rate_control = 0;
210 sink->t_codec.profile = 0;
211 sink->t_codec.level = 0;
212 sink->t_codec.ch_mode = 0;
213 sink->t_codec.format = 0;
215 GST_INFO_OBJECT (sink, "t_codec: id(%d), channel(%d), sample_rate(%d)",
216 SND_AUDIOCODEC_MP3, channels, rate);
222 gst_tinycompress_sink_class_init (GstTinycompressSinkClass * klass)
224 GObjectClass *gobject_class;
225 GstElementClass *gstelement_class;
226 GstBaseSinkClass *gstbase_sink_class;
228 gobject_class = G_OBJECT_CLASS (klass);
229 gstelement_class = GST_ELEMENT_CLASS (klass);
230 gstbase_sink_class = GST_BASE_SINK_CLASS (klass);
232 gobject_class->set_property = gst_tinycompress_sink_set_property;
233 gobject_class->get_property = gst_tinycompress_sink_get_property;
234 gobject_class->finalize = gst_tinycompress_sink_finalize;
236 g_object_class_install_property (gobject_class, PROP_DUMP,
237 g_param_spec_boolean ("dump", "Dump", "Dump buffer contents to stdout",
239 G_PARAM_READWRITE | GST_PARAM_MUTABLE_PLAYING |
240 G_PARAM_STATIC_STRINGS));
242 gst_element_class_set_static_metadata (gstelement_class,
244 "Sink", "Render MP3 data in compress device using tinycompress", "Tizen");
246 gst_element_class_add_static_pad_template (gstelement_class, &sinktemplate);
248 gstelement_class->change_state =
249 GST_DEBUG_FUNCPTR (gst_tinycompress_sink_change_state);
251 gstbase_sink_class->render = GST_DEBUG_FUNCPTR (gst_tinycompress_sink_render);
252 gstbase_sink_class->get_caps =
253 GST_DEBUG_FUNCPTR (gst_tinycompress_sink_get_caps);
254 gstbase_sink_class->set_caps =
255 GST_DEBUG_FUNCPTR (gst_tinycompress_sink_set_caps);
256 gstbase_sink_class->get_times =
257 GST_DEBUG_FUNCPTR (gst_tinycompress_sink_get_times);
258 gstbase_sink_class->event = GST_DEBUG_FUNCPTR (gst_tinycompress_sink_event);
262 _volume_changed_cb (sound_type_e type, unsigned int volume, void *user_data)
264 GstTinycompressSink *sink = (GstTinycompressSink *) user_data;
266 GST_INFO_OBJECT (sink, " type %d, volume %u", type, volume);
268 /* FIXME: For now current volume range for media is 0~15,
269 corresponding mixer control range is 0~8192.
270 As level matching will be part of tunning issue in the future,
271 we just set value to level multiplied by 100 which seems to be fine */
272 if (type == SOUND_TYPE_MEDIA)
273 _mixer_control_set_value (sink, COMPRESS_VOL_CTRL, volume * 100);
277 gst_tinycompress_sink_init (GstTinycompressSink * sink)
279 int media_volume = -1;
281 sink->dump = DEFAULT_DUMP;
282 sink->card = DEFAULT_CARD;
283 sink->device = DEFAULT_DEVICE;
284 sink->compress_paused = FALSE;
285 sink->volume_cb_id = -1;
287 if (sound_manager_get_volume (SOUND_TYPE_MEDIA,
288 &media_volume) != SOUND_MANAGER_ERROR_NONE)
289 GST_ERROR_OBJECT (sink, "failed to get media volume");
291 if (sound_manager_add_volume_changed_cb (_volume_changed_cb, sink,
292 &sink->volume_cb_id) != SOUND_MANAGER_ERROR_NONE)
293 GST_ERROR_OBJECT (sink, "failed to add volume changed cb");
295 GST_INFO_OBJECT (sink, "Initial media volume was %d, added volume cb %d",
296 media_volume, sink->volume_cb_id);
298 _mixer_control_set_value (sink, "ComprTx0 Volume", media_volume * 100);
302 gst_tinycompress_sink_finalize (GObject * object)
304 GstTinycompressSink *sink = GST_TINYCOMPRESS_SINK (object);
306 if (sink->volume_cb_id != -1)
307 if (sound_manager_remove_volume_changed_cb (sink->volume_cb_id) !=
308 SOUND_MANAGER_ERROR_NONE)
309 GST_ERROR_OBJECT (sink, "failed to remove volume changed cb (id:%d)",
312 G_OBJECT_CLASS (parent_class)->finalize (object);
316 gst_tinycompress_sink_set_property (GObject * object, guint prop_id,
317 const GValue * value, GParamSpec * pspec)
319 GstTinycompressSink *sink = GST_TINYCOMPRESS_SINK (object);
323 sink->dump = g_value_get_boolean (value);
327 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
333 gst_tinycompress_sink_get_property (GObject * object, guint prop_id,
334 GValue * value, GParamSpec * pspec)
336 GstTinycompressSink *sink;
338 sink = GST_TINYCOMPRESS_SINK (object);
342 g_value_set_boolean (value, sink->dump);
346 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
352 gst_tinycompress_sink_render (GstBaseSink * bsink, GstBuffer * buf)
354 GstTinycompressSink *sink = GST_TINYCOMPRESS_SINK_CAST (bsink);
355 int ret = GST_FLOW_OK;
358 g_return_val_if_fail (sink->compress != NULL, FALSE);
360 GST_OBJECT_LOCK (sink);
362 gst_buffer_map (buf, &info, GST_MAP_READ);
364 gst_util_dump_mem (info.data, info.size);
367 ret = compress_write (sink->compress, info.data, info.size);
368 if (ret != info.size) {
369 GST_ERROR_OBJECT (sink, "Could not write data to device");
370 gst_buffer_unmap (buf, &info);
371 GST_OBJECT_UNLOCK (sink);
372 return GST_FLOW_ERROR;
374 sink->written += info.size;
375 GST_DEBUG_OBJECT (sink, " >> Wrote %u bytes, accum %u", info.size,
378 /* FIXEME: needs separate property for control */
379 _dump_compress_status (sink, sink->compress);
381 if (!is_compress_running (sink->compress)
382 && sink->written >= sink->start_threashold) {
383 GST_INFO_OBJECT (sink, "Start Now!!!");
384 if (compress_start (sink->compress) != 0) {
385 GST_ERROR_OBJECT (sink, "Could not start!!!");
387 GST_INFO_OBJECT (sink, "Started done!!!");
388 sink->compress_paused = FALSE;
392 gst_buffer_unmap (buf, &info);
393 GST_OBJECT_UNLOCK (sink);
399 gst_tinycompress_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
401 GstTinycompressSink *sink = GST_TINYCOMPRESS_SINK (bsink);
402 GstCaps *caps = NULL;
404 GST_OBJECT_LOCK (sink);
405 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
406 GST_OBJECT_UNLOCK (sink);
408 GST_INFO_OBJECT (sink, "Got caps %" GST_PTR_FORMAT, caps);
410 if (caps && filter) {
411 GstCaps *intersection =
412 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
414 gst_caps_unref (caps);
418 GST_DEBUG_OBJECT (sink, "result get caps: %" GST_PTR_FORMAT, caps);
424 gst_tinycompress_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
426 GstTinycompressSink *sink;
428 sink = GST_TINYCOMPRESS_SINK (bsink);
429 GST_INFO_OBJECT (sink, "set caps %" GST_PTR_FORMAT, caps);
431 gst_tinycompress_set_codec_param (sink, caps);
437 gst_tinycompress_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
438 GstClockTime * start, GstClockTime * end)
440 /* to check valid buffer based on the start and end time */
441 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
442 *start = GST_BUFFER_TIMESTAMP (buf);
444 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
445 *end = *start + GST_BUFFER_DURATION (buf);
451 gst_tinycompress_resume (GstTinycompressSink * sink)
454 g_return_val_if_fail (sink != NULL, FALSE);
456 GST_INFO_OBJECT (sink, "gst_tinycompress_resume");
461 if (!sink->compress_paused)
464 if (!is_compress_running (sink->compress))
467 ret = compress_resume (sink->compress);
468 GST_INFO_OBJECT (sink, "resuming the device, ret=%d", ret);
470 GST_ERROR_OBJECT (sink, "Error %d resuming device\n", ret);
474 sink->compress_paused = FALSE;
479 GST_ERROR_OBJECT (sink, "Error: Tinycompress device is not opened yet!");
483 GST_ERROR_OBJECT (sink, "Error: Tinycompress device is not paused yet!");
487 GST_ERROR_OBJECT (sink, "Note: Tinycompress device is not started!");
492 gst_tinycompress_pause (GstTinycompressSink * sink)
496 g_return_val_if_fail (sink != NULL, FALSE);
498 GST_INFO_OBJECT (sink, "gst_tinycompress_pause");
503 if (!is_compress_running (sink->compress))
506 ret = compress_pause (sink->compress);
507 GST_INFO_OBJECT (sink, "pausing the device, ret=%d", ret);
509 GST_ERROR_OBJECT (sink, "Error %d pausing device, %s\n", ret,
510 compress_get_error (sink->compress));
513 sink->compress_paused = TRUE;
518 GST_ERROR_OBJECT (sink, "Error: Tinycompress device is not opened yet!");
522 GST_ERROR_OBJECT (sink, "Note: Tinycompress device is not started!");
527 gst_tinycompress_unprepare (GstTinycompressSink * sink)
530 if (compress_stop (sink->compress) != 0)
531 GST_ERROR_OBJECT (sink, "Could not stop device");
533 GST_OBJECT_LOCK (sink);
536 compress_close (sink->compress);
537 sink->compress = NULL;
539 GST_INFO_OBJECT (sink, "Compress device %d:%d closed", sink->card,
542 sink->compress_paused = FALSE;
545 GST_OBJECT_UNLOCK (sink);
551 gst_tinycompress_open (GstTinycompressSink * sink)
553 g_return_val_if_fail (sink != NULL, FALSE);
555 GST_INFO_OBJECT (sink, "gst_tinycompress_open");
557 sink->t_config.codec = &sink->t_codec;
559 GST_OBJECT_LOCK (sink);
562 compress_open (sink->card, sink->device, COMPRESS_IN, &sink->t_config);
563 if (!sink->compress) {
564 GST_ERROR_OBJECT (sink, "Unable to open Compress device %d:%d", sink->card,
569 /* FIXME: need to property, wait for writting */
570 compress_set_max_poll_wait (sink->compress, 3000);
572 sink->start_threashold =
573 sink->t_config.fragment_size * sink->t_config.fragments;
574 GST_INFO_OBJECT (sink, "Got fragment_size: %d, fragments :%d",
575 sink->t_config.fragment_size, sink->t_config.fragments);
576 GST_INFO_OBJECT (sink, "buffer size for buffering : %d",
577 sink->start_threashold);
579 if (!is_compress_ready (sink->compress)) {
580 GST_ERROR_OBJECT (sink, "ERR: %s\n", compress_get_error (sink->compress));
584 GST_INFO_OBJECT (sink, "Compress device %d:%d opened", sink->card,
589 GST_OBJECT_UNLOCK (sink);
594 GST_OBJECT_UNLOCK (sink);
596 if (sink->compress) {
597 compress_close (sink->compress);
598 sink->compress = NULL;
604 static GstStateChangeReturn
605 gst_tinycompress_sink_change_state (GstElement * element,
606 GstStateChange transition)
608 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
609 GstTinycompressSink *sink = GST_TINYCOMPRESS_SINK (element);
611 switch (transition) {
612 case GST_STATE_CHANGE_NULL_TO_READY:
613 GST_INFO_OBJECT (sink, "NULL_TO_READY");
616 case GST_STATE_CHANGE_READY_TO_PAUSED:
617 GST_INFO_OBJECT (sink, "READY_TO_PAUSED");
620 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
621 GST_INFO_OBJECT (sink, "PAUSED_TO_PLAYING");
623 gst_tinycompress_open (sink);
624 if (sink->compress_paused)
625 gst_tinycompress_resume (sink);
632 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
634 switch (transition) {
635 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
636 GST_INFO_OBJECT (sink, "PLAYING_TO_PAUSED");
637 gst_tinycompress_pause (sink);
640 case GST_STATE_CHANGE_PAUSED_TO_READY:
641 GST_INFO_OBJECT (sink, "PAUSED_TO_READY");
642 gst_tinycompress_unprepare (sink);
645 case GST_STATE_CHANGE_READY_TO_NULL:
646 GST_INFO_OBJECT (sink, "READY_TO_NULL");
656 gst_tinycompress_sink_event (GstBaseSink * bsink, GstEvent * event)
658 GstTinycompressSink *sink;
659 sink = GST_TINYCOMPRESS_SINK (bsink);
661 GST_INFO_OBJECT (sink, "got event (%s)",
662 gst_event_type_get_name (GST_EVENT_TYPE (event)));
664 switch (GST_EVENT_TYPE (event)) {
666 GST_INFO_OBJECT (sink, "get GST_EVENT_EOS event..state is %d",
674 return GST_BASE_SINK_CLASS (parent_class)->event (bsink, event);
678 plugin_init (GstPlugin * plugin)
680 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "tinycompresssink", 0,
681 "Tinycompress sink plugin");
683 return gst_element_register (plugin, "tinycompresssink", GST_RANK_NONE,
684 GST_TYPE_TINYCOMPRESS_SINK);
687 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, tinycompress,
688 "tinycompress plugin library", plugin_init, VERSION, "LGPL",
689 GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)