fix position query, small cleanup
[gst-plugin-dvbmediasink.git] / src / gstdvbaudiosink.c
1 /*
2  * GStreamer DVB Media Sink
3  * Copyright 2006 Felix Domke <tmbinc@elitedvb.net>
4  * based on code by:
5  * Copyright 2005 Thomas Vander Stichele <thomas@apestaart.org>
6  * Copyright 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
7  * 
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:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
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.
25  *
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
29  * mentioned above:
30  *
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.
35  *
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.
40  *
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.
45  */
46
47 /**
48  * SECTION:element-plugin
49  *
50  * <refsect2>
51  * <title>Example launch line</title>
52  * <para>
53  * <programlisting>
54  * gst-launch -v -m audiotestsrc ! plugin ! fakesink silent=TRUE
55  * </programlisting>
56  * </para>
57  * </refsect2>
58  */
59
60 #ifdef HAVE_CONFIG_H
61 #include <config.h>
62 #endif
63 #include <unistd.h>
64 #include <string.h>
65 #include <sys/ioctl.h>
66 #include <sys/socket.h>
67 #include <linux/dvb/audio.h>
68 #include <fcntl.h>
69
70 #include <gst/gst.h>
71
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. */
77
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]
84
85 #define SEND_COMMAND(sink, command)                                     \
86 G_STMT_START {                                                                                                                  \
87         unsigned char c; c = command;                                            \
88         write (WRITE_SOCKET(sink), &c, 1);                               \
89 } G_STMT_END
90
91 #define READ_COMMAND(sink, command, res)                                \
92 G_STMT_START {                                                                                                                           \
93         res = read(READ_SOCKET(sink), &command, 1);      \
94 } G_STMT_END
95
96 #include "gstdvbaudiosink.h"
97
98 #ifndef AUDIO_GET_PTS
99 #define AUDIO_GET_PTS              _IOR('o', 19, gint64)
100 #endif
101
102 GST_DEBUG_CATEGORY_STATIC (dvbaudiosink_debug);
103 #define GST_CAT_DEFAULT dvbaudiosink_debug
104
105 /* Filter signals and args */
106 enum {
107         /* FILL ME */
108         LAST_SIGNAL
109 };
110
111 enum {
112         ARG_0,
113         ARG_SILENT
114 };
115
116 static GstStaticPadTemplate sink_factory =
117 GST_STATIC_PAD_TEMPLATE (
118         "sink",
119         GST_PAD_SINK,
120         GST_PAD_ALWAYS,
121         GST_STATIC_CAPS ("audio/mpeg, "
122                 "mpegversion = (int) { 1, 2, 4 }; "
123                 "audio/x-private1-ac3;"
124                 "audio/x-ac3")
125 );
126
127 #define DEBUG_INIT(bla) \
128         GST_DEBUG_CATEGORY_INIT (dvbaudiosink_debug, "dvbaudiosink", 0, "dvbaudiosink element");
129
130 GST_BOILERPLATE_FULL (GstDVBAudioSink, gst_dvbaudiosink, GstBaseSink,
131         GST_TYPE_BASE_SINK, DEBUG_INIT);
132
133 static void     gst_dvbaudiosink_set_property (GObject *object, guint prop_id,
134                                                                                                                                                                                                         const GValue *value,
135                                                                                                                                                                                                         GParamSpec *pspec);
136 static void     gst_dvbaudiosink_get_property (GObject *object, guint prop_id,
137                                                                                                                                                                                                         GValue *value,
138                                                                                                                                                                                                         GParamSpec *pspec);
139
140 static gboolean gst_dvbaudiosink_start (GstBaseSink * sink);
141 static gboolean gst_dvbaudiosink_stop (GstBaseSink * sink);
142 static gboolean gst_dvbaudiosink_event (GstBaseSink * sink, GstEvent * event);
143 static GstFlowReturn gst_dvbaudiosink_render (GstBaseSink * sink,
144         GstBuffer * buffer);
145 static gboolean gst_dvbaudiosink_query (GstElement * element, GstQuery * query);
146 static gboolean gst_dvbaudiosink_unlock (GstBaseSink * basesink);
147 static gboolean gst_dvbaudiosink_set_caps (GstBaseSink * sink, GstCaps * caps);
148
149 gboolean bypass_set = FALSE;
150
151 static void
152 gst_dvbaudiosink_base_init (gpointer klass)
153 {
154         static GstElementDetails element_details = {
155                 "A DVB audio sink",
156                 "Generic/DVBAudioSink",
157                 "Outputs a MPEG2 PES / ES into a DVB audio device for hardware playback",
158                 "Felix Domke <tmbinc@elitedvb.net>"
159         };
160         GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
161
162         gst_element_class_add_pad_template (element_class,
163                 gst_static_pad_template_get (&sink_factory));
164         gst_element_class_set_details (element_class, &element_details);
165 }
166
167 /* initialize the plugin's class */
168 static void
169 gst_dvbaudiosink_class_init (GstDVBAudioSinkClass *klass)
170 {
171         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
172         GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
173         
174         gobject_class->set_property = gst_dvbaudiosink_set_property;
175         gobject_class->get_property = gst_dvbaudiosink_get_property;
176         
177         gobject_class = G_OBJECT_CLASS (klass);
178         g_object_class_install_property (gobject_class, ARG_SILENT,
179                 g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
180                                                                                                         FALSE, G_PARAM_READWRITE));
181
182         gstbasesink_class->get_times = NULL;
183         gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_start);
184         gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_stop);
185         gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_render);
186         gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_event);
187         gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_unlock);
188         gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_set_caps);
189         GST_ELEMENT_CLASS (klass)->query = GST_DEBUG_FUNCPTR(gst_dvbaudiosink_query);
190 }
191
192 /* initialize the new element
193  * instantiate pads and add them to element
194  * set functions
195  * initialize structure
196  */
197 static void
198 gst_dvbaudiosink_init (GstDVBAudioSink *klass,
199                 GstDVBAudioSinkClass * gclass)
200 {
201         klass->silent = FALSE;
202         klass->aac_adts_header_valid = FALSE;
203
204         GST_BASE_SINK (klass)->sync = FALSE;
205 }
206
207 static void
208 gst_dvbaudiosink_set_property (GObject *object, guint prop_id,
209                                                                                                                                         const GValue *value, GParamSpec *pspec)
210 {
211         GstDVBAudioSink *filter;
212
213         g_return_if_fail (GST_IS_DVBAUDIOSINK (object));
214         filter = GST_DVBAUDIOSINK (object);
215
216         switch (prop_id)
217         {
218         case ARG_SILENT:
219                 filter->silent = g_value_get_boolean (value);
220                 break;
221         default:
222                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
223                 break;
224         }
225 }
226
227 static void
228 gst_dvbaudiosink_get_property (GObject *object, guint prop_id,
229                                                                                                                                         GValue *value, GParamSpec *pspec)
230 {
231         GstDVBAudioSink *filter;
232
233         g_return_if_fail (GST_IS_DVBAUDIOSINK (object));
234         filter = GST_DVBAUDIOSINK (object);
235
236         switch (prop_id) {
237         case ARG_SILENT:
238                 g_value_set_boolean (value, filter->silent);
239                 break;
240         default:
241                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
242                 break;
243         }
244 }
245
246 static gboolean
247 gst_dvbaudiosink_query (GstElement * element, GstQuery * query)
248 {
249         GstDVBAudioSink *self = GST_DVBAUDIOSINK (element);
250
251 //      printf("query %d %d\n", GST_QUERY_TYPE(query), GST_QUERY_POSITION);
252
253         switch (GST_QUERY_TYPE (query)) {
254         case GST_QUERY_POSITION:
255         {
256                 gint64 cur = 0;
257                 GstFormat format;
258
259                 gst_query_parse_position (query, &format, NULL);
260
261                 if (format != GST_FORMAT_TIME)
262                         goto query_default;
263
264                 ioctl(self->fd, AUDIO_GET_PTS, &cur);
265 //              printf("PTS: %08llx", cur);
266
267                 cur *= 11111;
268
269                 gst_query_set_position (query, format, cur);
270
271                 GST_DEBUG_OBJECT (self, "position format %d", format);
272                 return TRUE;
273         }
274         default:
275 query_default:
276                 return GST_ELEMENT_CLASS (parent_class)->query (element, query);
277         }
278 }
279
280 static gboolean gst_dvbaudiosink_unlock (GstBaseSink * basesink)
281 {
282         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
283
284         SEND_COMMAND (self, CONTROL_STOP);
285
286         return TRUE;
287 }
288
289 static gboolean 
290 gst_dvbaudiosink_set_caps (GstBaseSink * basesink, GstCaps * caps)
291 {
292         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
293         GstStructure *structure = gst_caps_get_structure (caps, 0);
294         const char *type = gst_structure_get_name (structure);
295         int bypass = -1;
296
297         self->skip = 0;
298         if (!strcmp(type, "audio/mpeg")) {
299                 gint mpegversion;
300                 gst_structure_get_int (structure, "mpegversion", &mpegversion);
301                 switch (mpegversion) {
302                         case 1:
303                         {
304                                 gint layer;
305                                 gst_structure_get_int (structure, "layer", &layer);
306                                 if ( layer == 3 )
307                                         bypass = 0xA;
308                                 else
309                                         bypass = 1;
310                                 printf("MIMETYPE %s version %d layer %d\n",type,mpegversion,layer);
311                                 break;
312                         }
313                         case 2:
314                                 bypass = 1;
315                                 printf("MIMETYPE %s version %d\n",type,mpegversion);
316                                 break;
317                         case 4:
318                         {
319                                 const GValue *codec_data = gst_structure_get_value (structure, "codec_data");
320                                 printf("MIMETYPE %s version %d (AAC)\n", type, mpegversion);
321                                 if (codec_data) {
322                                         guint8 *h = GST_BUFFER_DATA(gst_value_get_buffer (codec_data));
323                                         guint8 obj_type = ((h[0] & 0xC) >> 2) + 1;
324                                         guint8 rate_idx = ((h[0] & 0x3) << 1) | ((h[1] & 0x80) >> 7);
325                                         guint8 channels = (h[1] & 0x78) >> 3;
326 //                                      printf("have codec data -> obj_type = %d, rate_idx = %d, channels = %d\n",
327 //                                              obj_type, rate_idx, channels);
328                                         /* Sync point over a full byte */
329                                         self->aac_adts_header[0] = 0xFF;
330                                         /* Sync point continued over first 4 bits + static 4 bits
331                                          * (ID, layer, protection)*/
332                                         self->aac_adts_header[1] = 0xF1;
333                                         /* Object type over first 2 bits */
334                                         self->aac_adts_header[2] = obj_type << 6;
335                                         /* rate index over next 4 bits */
336                                         self->aac_adts_header[2] |= rate_idx << 2;
337                                         /* channels over last 2 bits */
338                                         self->aac_adts_header[2] |= (channels & 0x4) >> 2;
339                                         /* channels continued over next 2 bits + 4 bits at zero */
340                                         self->aac_adts_header[3] = (channels & 0x3) << 6;
341                                         self->aac_adts_header_valid = TRUE;
342                                 }
343                                 else
344                                         printf("no codec data!!!\n");
345                                 bypass = 8;
346                                 break;
347                         }
348                         default:
349                                 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unhandled mpeg version %i", mpegversion));
350                                 break;
351                 }
352         }
353         else if (!strcmp(type, "audio/x-ac3") || !strcmp(type, "audio/ac3"))
354         {
355                 printf("MIMETYPE %s\n",type);
356                 bypass = 0;
357         }
358         else if (!strcmp(type, "audio/x-dts") || !strcmp(type, "audio/dts"))
359         {
360                 printf("MIMETYPE %s\n",type);
361                 bypass = 2;
362         }
363         else if (!strcmp(type, "audio/x-private1-dts"))
364         {
365                 printf("MIMETYPE %s (DVD Audio - 2 byte skipping)\n",type);
366                 bypass = 2;
367                 self->skip = 2;
368         }
369         else if (!strcmp(type, "audio/x-private1-ac3"))
370         {
371                 printf("MIMETYPE %s (DVD Audio - 2 byte skipping)\n",type);
372                 bypass = 0;
373                 self->skip = 2;
374         } else
375         {
376                 GST_ELEMENT_ERROR (self, STREAM, TYPE_NOT_FOUND, (NULL), ("unimplemented stream type %s", type));
377                 return FALSE;
378         }
379
380         GST_DEBUG_OBJECT(self, "setting dvb mode 0x%02x\n", bypass);
381
382         if (ioctl(self->fd, AUDIO_SET_BYPASS_MODE, bypass) < 0)
383         {
384                 GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL), ("hardware decoder can't be set to bypass mode %i", bypass));
385                 return FALSE;
386         }
387         bypass_set = TRUE;
388         return TRUE;
389 }
390
391 static gboolean
392 gst_dvbaudiosink_event (GstBaseSink * sink, GstEvent * event)
393 {
394         GstDVBAudioSink *self = GST_DVBAUDIOSINK (sink);
395         GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event)));
396
397         switch (GST_EVENT_TYPE (event)) {
398         case GST_EVENT_FLUSH_START:
399                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
400                 break;
401         case GST_EVENT_FLUSH_STOP:
402                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
403                 while (1)
404                 {
405                         gchar command;
406                         int res;
407
408                         READ_COMMAND (self, command, res);
409                         if (res < 0)
410                                 break;
411                 }
412                 break;
413         default:
414                 break;
415         }
416         return TRUE;
417 }
418
419 static GstFlowReturn
420 gst_dvbaudiosink_render (GstBaseSink * sink, GstBuffer * buffer)
421 {
422         GstDVBAudioSink *self = GST_DVBAUDIOSINK (sink);
423         unsigned char pes_header[64];
424         int skip = self->skip;
425         unsigned int size = GST_BUFFER_SIZE (buffer) - skip;
426         fd_set readfds;
427         fd_set writefds;
428         gint retval;
429         size_t pes_header_size;
430 //      int i=0;
431
432 //      for (;i < (size > 0x1F ? 0x1F : size); ++i)
433 //              printf("%02x ", data[i]);
434 //      printf("%d bytes\n", size);
435
436 //      printf("timestamp: %08llx\n", (long long)GST_BUFFER_TIMESTAMP(buffer));
437
438         if ( !bypass_set )
439         {
440                 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("hardware decoder not setup (no caps in pipeline?)"));
441                 return GST_FLOW_ERROR;
442         }
443
444         FD_ZERO (&readfds);
445         FD_SET (READ_SOCKET (self), &readfds);
446
447         FD_ZERO (&writefds);
448         FD_SET (self->fd, &writefds);
449
450         do {
451                 GST_DEBUG_OBJECT (self, "going into select, have %d bytes to write",
452                                 size);
453                 retval = select (FD_SETSIZE, &readfds, &writefds, NULL, NULL);
454         } while ((retval == -1 && errno == EINTR));
455
456         if (retval == -1)
457                 goto select_error;
458
459         if (FD_ISSET (READ_SOCKET (self), &readfds)) {
460                 /* read all stop commands */
461                 while (TRUE) {
462                         gchar command;
463                         int res;
464
465                         READ_COMMAND (self, command, res);
466                         if (res < 0) {
467                                 GST_LOG_OBJECT (self, "no more commands");
468                                 /* no more commands */
469                                 break;
470                         }
471                 }
472                 goto stopped;
473         }
474
475         if (self->fd < 0)
476                 return GST_FLOW_OK;
477
478         pes_header[0] = 0;
479         pes_header[1] = 0;
480         pes_header[2] = 1;
481         pes_header[3] = 0xC0;
482
483         if (self->aac_adts_header_valid)
484                 size += 7;
485
486                 /* do we have a timestamp? */
487         if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE)
488         {
489                 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000 /* convert ns to 90kHz */;
490
491                 pes_header[4] = (size + 8) >> 8;
492                 pes_header[5] = (size + 8) & 0xFF;
493                 
494                 pes_header[6] = 0x80;
495                 pes_header[7] = 0x80;
496                 
497                 pes_header[8] = 5;
498                 
499                 pes_header[9]  = 0x21 | ((pts >> 29) & 0xE);
500                 pes_header[10] = pts >> 22;
501                 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
502                 pes_header[12] = pts >> 7;
503                 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
504                 pes_header_size = 14;
505         } else
506         {
507                 pes_header[4] = (size + 3) >> 8;
508                 pes_header[5] = (size + 3) & 0xFF;
509                 pes_header[6] = 0x80;
510                 pes_header[7] = 0x00;
511                 pes_header[8] = 0;
512                 pes_header_size = 9;
513         }
514
515         if (self->aac_adts_header_valid) {
516                 self->aac_adts_header[3] &= 0xC0;
517                 /* frame size over last 2 bits */
518                 self->aac_adts_header[3] |= (size & 0x1800) >> 11;
519                 /* frame size continued over full byte */
520                 self->aac_adts_header[4] = (size & 0x1FF8) >> 3;
521                 /* frame size continued first 3 bits */
522                 self->aac_adts_header[5] = (size & 7) << 5;
523                 /* buffer fullness (0x7FF for VBR) over 5 last bits */
524                 self->aac_adts_header[5] |= 0x1F;
525                 /* buffer fullness (0x7FF for VBR) continued over 6 first bits + 2 zeros for
526                  * number of raw data blocks */
527                 self->aac_adts_header[6] = 0xFC;
528                 memcpy(pes_header + pes_header_size, self->aac_adts_header, 7);
529                 pes_header_size += 7;
530         }
531
532         write(self->fd, pes_header, pes_header_size);
533         write(self->fd, GST_BUFFER_DATA (buffer) + skip, GST_BUFFER_SIZE (buffer) - skip);
534
535         return GST_FLOW_OK;
536 select_error:
537         {
538                 GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL),
539                                 ("select on file descriptor: %s.", g_strerror (errno)));
540                 GST_DEBUG_OBJECT (self, "Error during select");
541                 return GST_FLOW_ERROR;
542         }
543 stopped:
544         {
545                 GST_DEBUG_OBJECT (self, "Select stopped");
546                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
547                 return GST_FLOW_WRONG_STATE;
548         }
549 }
550
551 static gboolean
552 gst_dvbaudiosink_start (GstBaseSink * basesink)
553 {
554         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
555         self->fd = open("/dev/dvb/adapter0/audio0", O_RDWR);
556 //      self->fd = open("/dump.pes", O_RDWR|O_CREAT, 0555);
557         
558         gint control_sock[2];
559
560         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
561                 goto socket_pair;
562
563         READ_SOCKET (self) = control_sock[0];
564         WRITE_SOCKET (self) = control_sock[1];
565
566         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
567         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
568
569         if (self->fd)
570         {
571                 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY);
572                 ioctl(self->fd, AUDIO_PLAY);
573 //              ioctl(self->fd, AUDIO_SET_BYPASS_MODE, 0);
574         }
575         return TRUE;
576         /* ERRORS */
577 socket_pair:
578         {
579                 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL),
580                                 GST_ERROR_SYSTEM);
581                 return FALSE;
582         }
583 }
584
585 static gboolean
586 gst_dvbaudiosink_stop (GstBaseSink * basesink)
587 {
588         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
589         if (self->fd >= 0)
590         {
591                 ioctl(self->fd, AUDIO_STOP);
592                 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX);
593                 close(self->fd);
594         }
595
596         return TRUE;
597 }
598
599 /* entry point to initialize the plug-in
600  * initialize the plug-in itself
601  * register the element factories and pad templates
602  * register the features
603  *
604  * exchange the string 'plugin' with your elemnt name
605  */
606 static gboolean
607 plugin_init (GstPlugin *plugin)
608 {
609         return gst_element_register (plugin, "dvbaudiosink",
610                                                  GST_RANK_PRIMARY,
611                                                  GST_TYPE_DVBAUDIOSINK);
612 }
613
614 /* this is the structure that gstreamer looks for to register plugins
615  *
616  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
617  * description
618  */
619 GST_PLUGIN_DEFINE (
620         GST_VERSION_MAJOR,
621         GST_VERSION_MINOR,
622         "dvb_audio_out",
623         "DVB Audio Output",
624         plugin_init,
625         VERSION,
626         "LGPL",
627         "GStreamer",
628         "http://gstreamer.net/"
629 )