2 * GStreamer DVB Media Sink
3 * Copyright 2006 Felix Domke <tmbinc@elitedvb.net>
5 * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
6 * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * Alternatively, the contents of this file may be used under the
27 * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
28 * which case the following provisions apply instead of the ones
31 * This library is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU Library General Public
33 * License as published by the Free Software Foundation; either
34 * version 2 of the License, or (at your option) any later version.
36 * This library is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 * Library General Public License for more details.
41 * You should have received a copy of the GNU Library General Public
42 * License along with this library; if not, write to the
43 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
44 * Boston, MA 02111-1307, USA.
48 * SECTION:element-plugin
51 * <title>Example launch line</title>
54 * gst-launch -v -m audiotestsrc ! plugin ! fakesink silent=TRUE
65 #include <sys/ioctl.h>
66 #include <sys/socket.h>
67 #include <linux/dvb/audio.h>
72 /* We add a control socket as in fdsrc to make it shutdown quickly when it's blocking on the fd.
73 * Select is used to determine when the fd is ready for use. When the element state is changed,
74 * it happens from another thread while fdsink is select'ing on the fd. The state-change thread
75 * sends a control message, so fdsink wakes up and changes state immediately otherwise
76 * it would stay blocked until it receives some data. */
78 /* the select call is also performed on the control sockets, that way
79 * we can send special commands to unblock the select call */
80 #define CONTROL_STOP 'S' /* stop the select call */
81 #define CONTROL_SOCKETS(sink) sink->control_sock
82 #define WRITE_SOCKET(sink) sink->control_sock[1]
83 #define READ_SOCKET(sink) sink->control_sock[0]
85 #define SEND_COMMAND(sink, command) \
87 unsigned char c; c = command; \
88 write (WRITE_SOCKET(sink), &c, 1); \
91 #define READ_COMMAND(sink, command, res) \
93 res = read(READ_SOCKET(sink), &command, 1); \
96 #include "gstdvbaudiosink.h"
98 GST_DEBUG_CATEGORY_STATIC (dvbaudiosink_debug);
99 #define GST_CAT_DEFAULT dvbaudiosink_debug
101 /* Filter signals and args */
112 static GstStaticPadTemplate sink_factory =
113 GST_STATIC_PAD_TEMPLATE (
117 GST_STATIC_CAPS ("audio/mpeg, "
118 "mpegversion = (int) { 1, 2, 4 }; "
119 "audio/x-private1-ac3;"
123 #define DEBUG_INIT(bla) \
124 GST_DEBUG_CATEGORY_INIT (dvbaudiosink_debug, "dvbaudiosink", 0, "dvbaudiosink element");
126 GST_BOILERPLATE_FULL (GstDVBAudioSink, gst_dvbaudiosink, GstBaseSink,
127 GST_TYPE_BASE_SINK, DEBUG_INIT);
129 static void gst_dvbaudiosink_set_property (GObject *object, guint prop_id,
132 static void gst_dvbaudiosink_get_property (GObject *object, guint prop_id,
136 static gboolean gst_dvbaudiosink_start (GstBaseSink * sink);
137 static gboolean gst_dvbaudiosink_stop (GstBaseSink * sink);
138 static gboolean gst_dvbaudiosink_event (GstBaseSink * sink, GstEvent * event);
139 static GstFlowReturn gst_dvbaudiosink_render (GstBaseSink * sink,
141 static gboolean gst_dvbaudiosink_query (GstPad * pad, GstQuery * query);
142 static gboolean gst_dvbaudiosink_unlock (GstBaseSink * basesink);
143 static gboolean gst_dvbaudiosink_set_caps (GstBaseSink * sink, GstCaps * caps);
145 gboolean bypass_set = FALSE;
148 gst_dvbaudiosink_base_init (gpointer klass)
150 static GstElementDetails element_details = {
152 "Generic/DVBAudioSink",
153 "Outputs a MPEG2 PES / ES into a DVB audio device for hardware playback",
154 "Felix Domke <tmbinc@elitedvb.net>"
156 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
158 gst_element_class_add_pad_template (element_class,
159 gst_static_pad_template_get (&sink_factory));
160 gst_element_class_set_details (element_class, &element_details);
163 /* initialize the plugin's class */
165 gst_dvbaudiosink_class_init (GstDVBAudioSinkClass *klass)
167 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
168 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
170 gobject_class->set_property = gst_dvbaudiosink_set_property;
171 gobject_class->get_property = gst_dvbaudiosink_get_property;
173 gobject_class = G_OBJECT_CLASS (klass);
174 g_object_class_install_property (gobject_class, ARG_SILENT,
175 g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
176 FALSE, G_PARAM_READWRITE));
178 gstbasesink_class->get_times = NULL;
179 gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_start);
180 gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_stop);
181 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_render);
182 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_event);
183 gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_unlock);
184 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_set_caps);
187 /* initialize the new element
188 * instantiate pads and add them to element
190 * initialize structure
193 gst_dvbaudiosink_init (GstDVBAudioSink *klass,
194 GstDVBAudioSinkClass * gclass)
196 GstPad *pad = GST_BASE_SINK_PAD (klass);
198 gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_dvbaudiosink_query));
200 klass->silent = FALSE;
201 klass->aac_adts_header_valid = FALSE;
203 GST_BASE_SINK (klass)->sync = FALSE;
207 gst_dvbaudiosink_set_property (GObject *object, guint prop_id,
208 const GValue *value, GParamSpec *pspec)
210 GstDVBAudioSink *filter;
212 g_return_if_fail (GST_IS_DVBAUDIOSINK (object));
213 filter = GST_DVBAUDIOSINK (object);
218 filter->silent = g_value_get_boolean (value);
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227 gst_dvbaudiosink_get_property (GObject *object, guint prop_id,
228 GValue *value, GParamSpec *pspec)
230 GstDVBAudioSink *filter;
232 g_return_if_fail (GST_IS_DVBAUDIOSINK (object));
233 filter = GST_DVBAUDIOSINK (object);
237 g_value_set_boolean (value, filter->silent);
240 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246 gst_dvbaudiosink_query (GstPad * pad, GstQuery * query)
248 GstDVBAudioSink *self;
251 self = GST_DVBAUDIOSINK (GST_PAD_PARENT (pad));
252 switch (GST_QUERY_TYPE (query)) {
254 return gst_pad_query_default (pad, query);
258 static gboolean gst_dvbaudiosink_unlock (GstBaseSink * basesink)
260 GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
262 SEND_COMMAND (self, CONTROL_STOP);
268 gst_dvbaudiosink_set_caps (GstBaseSink * basesink, GstCaps * caps)
270 GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
272 GstStructure *structure;
279 structure = gst_caps_get_structure (caps, 0);
280 type = gst_structure_get_name (structure);
283 if (!strcmp(type, "audio/mpeg")) {
285 gst_structure_get_int (structure, "mpegversion", &mpegversion);
286 switch (mpegversion) {
290 gst_structure_get_int (structure, "layer", &layer);
295 printf("MIMETYPE %s version %d layer %d\n",type,mpegversion,layer);
300 printf("MIMETYPE %s version %d\n",type,mpegversion);
304 const GValue *codec_data = gst_structure_get_value (structure, "codec_data");
305 printf("MIMETYPE %s version %d (AAC)\n", type, mpegversion);
307 guint8 *h = GST_BUFFER_DATA(gst_value_get_buffer (codec_data));
308 guint8 obj_type = ((h[0] & 0xC) >> 2) + 1;
309 guint8 rate_idx = ((h[0] & 0x3) << 1) | ((h[1] & 0x80) >> 7);
310 guint8 channels = (h[1] & 0x78) >> 3;
311 // printf("have codec data -> obj_type = %d, rate_idx = %d, channels = %d\n",
312 // obj_type, rate_idx, channels);
313 /* Sync point over a full byte */
314 self->aac_adts_header[0] = 0xFF;
315 /* Sync point continued over first 4 bits + static 4 bits
316 * (ID, layer, protection)*/
317 self->aac_adts_header[1] = 0xF1;
318 /* Object type over first 2 bits */
319 self->aac_adts_header[2] = obj_type << 6;
320 /* rate index over next 4 bits */
321 self->aac_adts_header[2] |= rate_idx << 2;
322 /* channels over last 2 bits */
323 self->aac_adts_header[2] |= (channels & 0x4) >> 2;
324 /* channels continued over next 2 bits + 4 bits at zero */
325 self->aac_adts_header[3] = (channels & 0x3) << 6;
326 self->aac_adts_header_valid = TRUE;
329 printf("no codec data!!!\n");
334 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unhandled mpeg version %i", mpegversion));
338 else if (!strcmp(type, "audio/x-ac3") || !strcmp(type, "audio/ac3"))
340 printf("MIMETYPE %s\n",type);
343 else if (!strcmp(type, "audio/x-private1-ac3"))
345 printf("MIMETYPE %s (DVD Audio - 2 byte skipping)\n",type);
350 GST_ELEMENT_ERROR (self, STREAM, TYPE_NOT_FOUND, (NULL), ("unimplemented stream type %s", type));
354 GST_DEBUG_OBJECT(self, "setting dvb mode 0x%02x\n", bypass);
356 if (ioctl(self->fd, AUDIO_SET_BYPASS_MODE, bypass) < 0)
358 GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL), ("hardware decoder can't be set to bypass mode %i", bypass));
366 gst_dvbaudiosink_event (GstBaseSink * sink, GstEvent * event)
368 GstDVBAudioSink *self = GST_DVBAUDIOSINK (sink);
369 GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event)));
371 switch (GST_EVENT_TYPE (event)) {
372 case GST_EVENT_FLUSH_START:
373 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
375 case GST_EVENT_FLUSH_STOP:
376 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
382 READ_COMMAND (self, command, res);
394 gst_dvbaudiosink_render (GstBaseSink * sink, GstBuffer * buffer)
396 GstDVBAudioSink *self = GST_DVBAUDIOSINK (sink);
397 unsigned char pes_header[64];
398 int skip = self->skip;
399 unsigned int size = GST_BUFFER_SIZE (buffer) - skip;
403 size_t pes_header_size;
405 // printf("write %d, timestamp: %08llx\n", GST_BUFFER_SIZE (buffer), (long long)GST_BUFFER_TIMESTAMP(buffer));
409 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("hardware decoder not setup (no caps in pipeline?)"));
410 return GST_FLOW_ERROR;
414 FD_SET (READ_SOCKET (self), &readfds);
417 FD_SET (self->fd, &writefds);
420 GST_DEBUG_OBJECT (self, "going into select, have %d bytes to write",
422 retval = select (FD_SETSIZE, &readfds, &writefds, NULL, NULL);
423 } while ((retval == -1 && errno == EINTR));
428 if (FD_ISSET (READ_SOCKET (self), &readfds)) {
429 /* read all stop commands */
434 READ_COMMAND (self, command, res);
436 GST_LOG_OBJECT (self, "no more commands");
437 /* no more commands */
450 pes_header[3] = 0xC0;
452 if (self->aac_adts_header_valid)
455 /* do we have a timestamp? */
456 if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE)
458 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000 /* convert ns to 90kHz */;
460 pes_header[4] = (size + 8) >> 8;
461 pes_header[5] = (size + 8) & 0xFF;
463 pes_header[6] = 0x80;
464 pes_header[7] = 0x80;
468 pes_header[9] = 0x21 | ((pts >> 29) & 0xE);
469 pes_header[10] = pts >> 22;
470 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
471 pes_header[12] = pts >> 7;
472 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
473 pes_header_size = 14;
476 pes_header[4] = (size + 3) >> 8;
477 pes_header[5] = (size + 3) & 0xFF;
478 pes_header[6] = 0x80;
479 pes_header[7] = 0x00;
484 if (self->aac_adts_header_valid) {
485 self->aac_adts_header[3] &= 0xC0;
486 /* frame size over last 2 bits */
487 self->aac_adts_header[3] |= (size & 0x1800) >> 11;
488 /* frame size continued over full byte */
489 self->aac_adts_header[4] = (size & 0x1FF8) >> 3;
490 /* frame size continued first 3 bits */
491 self->aac_adts_header[5] = (size & 7) << 5;
492 /* buffer fullness (0x7FF for VBR) over 5 last bits */
493 self->aac_adts_header[5] |= 0x1F;
494 /* buffer fullness (0x7FF for VBR) continued over 6 first bits + 2 zeros for
495 * number of raw data blocks */
496 self->aac_adts_header[6] = 0xFC;
497 memcpy(pes_header + pes_header_size, self->aac_adts_header, 7);
498 pes_header_size += 7;
501 write(self->fd, pes_header, pes_header_size);
502 write(self->fd, GST_BUFFER_DATA (buffer) + skip, GST_BUFFER_SIZE (buffer) - skip);
507 GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL),
508 ("select on file descriptor: %s.", g_strerror (errno)));
509 GST_DEBUG_OBJECT (self, "Error during select");
510 return GST_FLOW_ERROR;
514 GST_DEBUG_OBJECT (self, "Select stopped");
515 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
516 return GST_FLOW_WRONG_STATE;
521 gst_dvbaudiosink_start (GstBaseSink * basesink)
523 GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
524 self->fd = open("/dev/dvb/adapter0/audio0", O_RDWR);
525 // self->fd = open("/dump.pes", O_RDWR|O_CREAT, 0555);
527 gint control_sock[2];
529 if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
532 READ_SOCKET (self) = control_sock[0];
533 WRITE_SOCKET (self) = control_sock[1];
535 fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
536 fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
540 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY);
541 ioctl(self->fd, AUDIO_PLAY);
542 // ioctl(self->fd, AUDIO_SET_BYPASS_MODE, 0);
548 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL),
555 gst_dvbaudiosink_stop (GstBaseSink * basesink)
557 GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
560 ioctl(self->fd, AUDIO_STOP);
561 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX);
568 /* entry point to initialize the plug-in
569 * initialize the plug-in itself
570 * register the element factories and pad templates
571 * register the features
573 * exchange the string 'plugin' with your elemnt name
576 plugin_init (GstPlugin *plugin)
578 return gst_element_register (plugin, "dvbaudiosink",
580 GST_TYPE_DVBAUDIOSINK);
583 /* this is the structure that gstreamer looks for to register plugins
585 * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
597 "http://gstreamer.net/"