prepare hardware mp3 playback
[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 GST_DEBUG_CATEGORY_STATIC (dvbaudiosink_debug);
99 #define GST_CAT_DEFAULT dvbaudiosink_debug
100
101 /* Filter signals and args */
102 enum {
103         /* FILL ME */
104         LAST_SIGNAL
105 };
106
107 enum {
108         ARG_0,
109         ARG_SILENT
110 };
111
112 static GstStaticPadTemplate sink_factory =
113 GST_STATIC_PAD_TEMPLATE (
114         "sink",
115         GST_PAD_SINK,
116         GST_PAD_ALWAYS,
117         GST_STATIC_CAPS ("audio/mpeg, "
118                 "mpegversion = (int) { 1, 2, 4 }; "
119                 "audio/x-private1-ac3;"
120                 "audio/x-ac3")
121 );
122
123 #define DEBUG_INIT(bla) \
124         GST_DEBUG_CATEGORY_INIT (dvbaudiosink_debug, "dvbaudiosink", 0, "dvbaudiosink element");
125
126 GST_BOILERPLATE_FULL (GstDVBAudioSink, gst_dvbaudiosink, GstBaseSink,
127         GST_TYPE_BASE_SINK, DEBUG_INIT);
128
129 static void     gst_dvbaudiosink_set_property (GObject *object, guint prop_id,
130                                                                                                                                                                                                         const GValue *value,
131                                                                                                                                                                                                         GParamSpec *pspec);
132 static void     gst_dvbaudiosink_get_property (GObject *object, guint prop_id,
133                                                                                                                                                                                                         GValue *value,
134                                                                                                                                                                                                         GParamSpec *pspec);
135
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,
140         GstBuffer * buffer);
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);
144
145 static void
146 gst_dvbaudiosink_base_init (gpointer klass)
147 {
148         static GstElementDetails element_details = {
149                 "A DVB audio sink",
150                 "Generic/DVBAudioSink",
151                 "Outputs a MPEG2 PES / ES into a DVB audio device for hardware playback",
152                 "Felix Domke <tmbinc@elitedvb.net>"
153         };
154         GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
155
156         gst_element_class_add_pad_template (element_class,
157                 gst_static_pad_template_get (&sink_factory));
158         gst_element_class_set_details (element_class, &element_details);
159 }
160
161 /* initialize the plugin's class */
162 static void
163 gst_dvbaudiosink_class_init (GstDVBAudioSinkClass *klass)
164 {
165         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
166         GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
167         
168         gobject_class->set_property = gst_dvbaudiosink_set_property;
169         gobject_class->get_property = gst_dvbaudiosink_get_property;
170         
171         gobject_class = G_OBJECT_CLASS (klass);
172         g_object_class_install_property (gobject_class, ARG_SILENT,
173                 g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
174                                                                                                         FALSE, G_PARAM_READWRITE));
175
176         gstbasesink_class->get_times = NULL;
177         gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_start);
178         gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_stop);
179         gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_render);
180         gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_event);
181         gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_unlock);
182         gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_dvbaudiosink_set_caps);
183 }
184
185 /* initialize the new element
186  * instantiate pads and add them to element
187  * set functions
188  * initialize structure
189  */
190 static void
191 gst_dvbaudiosink_init (GstDVBAudioSink *klass,
192                 GstDVBAudioSinkClass * gclass)
193 {
194         GstPad *pad;
195         
196         pad = GST_BASE_SINK_PAD (klass);
197         
198         gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_dvbaudiosink_query));
199         
200         klass->silent = FALSE;
201         GST_BASE_SINK (klass)->sync = FALSE;
202 }
203
204 static void
205 gst_dvbaudiosink_set_property (GObject *object, guint prop_id,
206                                                                                                                                         const GValue *value, GParamSpec *pspec)
207 {
208         GstDVBAudioSink *filter;
209
210         g_return_if_fail (GST_IS_DVBAUDIOSINK (object));
211         filter = GST_DVBAUDIOSINK (object);
212
213         switch (prop_id)
214         {
215         case ARG_SILENT:
216                 filter->silent = g_value_get_boolean (value);
217                 break;
218         default:
219                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
220                 break;
221         }
222 }
223
224 static void
225 gst_dvbaudiosink_get_property (GObject *object, guint prop_id,
226                                                                                                                                         GValue *value, GParamSpec *pspec)
227 {
228         GstDVBAudioSink *filter;
229
230         g_return_if_fail (GST_IS_DVBAUDIOSINK (object));
231         filter = GST_DVBAUDIOSINK (object);
232
233         switch (prop_id) {
234         case ARG_SILENT:
235                 g_value_set_boolean (value, filter->silent);
236                 break;
237         default:
238                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
239                 break;
240         }
241 }
242
243 static gboolean
244 gst_dvbaudiosink_query (GstPad * pad, GstQuery * query)
245 {
246         GstDVBAudioSink *self;
247 //      GstFormat format;
248         
249         self = GST_DVBAUDIOSINK (GST_PAD_PARENT (pad));
250         switch (GST_QUERY_TYPE (query)) {
251         default:
252                 return gst_pad_query_default (pad, query);
253         }
254 }
255
256 static gboolean gst_dvbaudiosink_unlock (GstBaseSink * basesink)
257 {
258         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
259
260         SEND_COMMAND (self, CONTROL_STOP);
261
262         return TRUE;
263 }
264
265 static gboolean 
266 gst_dvbaudiosink_set_caps (GstBaseSink * basesink, GstCaps * caps)
267 {
268         printf("DEBUG\n");
269         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
270
271         GstStructure *structure;
272         const char *type;
273         int bypass;
274         
275         if (self->fd < 0)
276                 return FALSE;
277         
278         structure = gst_caps_get_structure (caps, 0);   
279         type = gst_structure_get_name (structure);
280
281         self->skip = 0;
282         if (!strcmp(type, "audio/mpeg")) {
283                 gint mpegversion;
284                 gst_structure_get_int (structure, "mpegversion", &mpegversion);
285                 switch (mpegversion) {
286                         case 1:
287                         {
288                                 gint layer;
289                                 gst_structure_get_int (structure, "layer", &layer);
290                                 if ( layer == 3 )
291                                         bypass = 0xA;
292                                 else
293                                         bypass = 1;
294                                 printf("MIMETYPE %s version %d layer %d\n",type,mpegversion,layer);
295                                 break;
296                         }
297                         case 2:
298                                 bypass = 1;
299                                 printf("MIMETYPE %s version %d\n",type,mpegversion);
300                                 break;
301                         case 4:
302                                 bypass = 8;
303                                 printf("MIMETYPE %s version %d (AAC)\n",type,mpegversion);
304                                 break;
305                         default:
306                                 g_error("unhandled mpeg version");
307                                 break;
308                 }
309         }
310         else if (!strcmp(type, "audio/x-ac3") || !strcmp(type, "audio/ac3"))
311         {
312                 printf("MIMETYPE %s\n",type);
313                 bypass = 0;
314         }
315         else if (!strcmp(type, "audio/x-private1-ac3"))
316         {
317                 printf("MIMETYPE %s (DVD Audio - 2 byte skipping)\n",type);
318                 bypass = 0;
319                 self->skip = 2;
320         } else
321         {
322                 GST_DEBUG_OBJECT(self, "illegal streamtype %s!\n", type);
323                 return FALSE;
324         }
325
326         GST_DEBUG_OBJECT(self, "setting dvb mode 0x%02x\n", bypass);
327         ioctl(self->fd, AUDIO_SET_BYPASS_MODE, bypass);
328
329         return TRUE;
330 }
331
332 static gboolean
333 gst_dvbaudiosink_event (GstBaseSink * sink, GstEvent * event)
334 {
335         GstDVBAudioSink *self;
336         GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event)));
337
338         self = GST_DVBAUDIOSINK (sink);
339         switch (GST_EVENT_TYPE (event)) {
340         case GST_EVENT_FLUSH_START:
341                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
342                 break;
343         case GST_EVENT_FLUSH_STOP:
344                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
345                 while (1)
346                 {
347                         gchar command;
348                         int res;
349
350                         READ_COMMAND (self, command, res);
351                         if (res < 0)
352                                 break;
353                 }
354                 break;
355         default:
356                 return TRUE;
357         }
358 }
359
360 static GstFlowReturn
361 gst_dvbaudiosink_render (GstBaseSink * sink, GstBuffer * buffer)
362 {
363         unsigned char pes_header[19];
364         GstDVBAudioSink *self;
365         unsigned int size;
366         fd_set readfds;
367         fd_set writefds;
368         gint retval;
369         
370         self = GST_DVBAUDIOSINK (sink);
371
372         int skip = self->skip;
373         
374         size = GST_BUFFER_SIZE (buffer) - skip;
375         
376 //      printf("write %d, timestamp: %08llx\n", GST_BUFFER_SIZE (buffer), (long long)GST_BUFFER_TIMESTAMP(buffer));
377
378         FD_ZERO (&readfds);
379         FD_SET (READ_SOCKET (self), &readfds);
380
381         FD_ZERO (&writefds);
382         FD_SET (self->fd, &writefds);
383
384         do {
385                 GST_DEBUG_OBJECT (self, "going into select, have %d bytes to write",
386                                 size);
387                 retval = select (FD_SETSIZE, &readfds, &writefds, NULL, NULL);
388         } while ((retval == -1 && errno == EINTR));
389
390         if (retval == -1)
391                 goto select_error;
392
393         if (FD_ISSET (READ_SOCKET (self), &readfds)) {
394                 /* read all stop commands */
395                 while (TRUE) {
396                         gchar command;
397                         int res;
398
399                         READ_COMMAND (self, command, res);
400                         if (res < 0) {
401                                 GST_LOG_OBJECT (self, "no more commands");
402                                 /* no more commands */
403                                 break;
404                         }
405                 }
406                 goto stopped;
407         }
408
409         if (self->fd < 0)
410                 return GST_FLOW_OK;
411
412         pes_header[0] = 0;
413         pes_header[1] = 0;
414         pes_header[2] = 1;
415         pes_header[3] = 0xC0;
416
417 #if 1
418                 /* do we have a timestamp? */
419         if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE)
420         {
421                 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000; /* convert ns to 90kHz */
422                 
423                 pes_header[4] = (size + 13) >> 8;
424                 pes_header[5] = (size + 13) & 0xFF;
425                 
426                 pes_header[6] = 0x80;
427                 pes_header[7] = 0xC0;
428                 
429                 pes_header[8] = 10;
430                 
431                 pes_header[9]   = 0x31 | ((pts >> 29) & 0xE);
432                 pes_header[10] = pts >> 22;
433                 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
434                 pes_header[12] = pts >> 7;
435                 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
436                 
437                 int64_t dts = pts; /* what to use as DTS-PTS offset? */
438                 
439                 pes_header[14] = 0x11 | ((dts >> 29) & 0xE);
440                 pes_header[15] = dts >> 22;
441                 pes_header[16] = 0x01 | ((dts >> 14) & 0xFE);
442                 pes_header[17] = dts >> 7;
443                 pes_header[18] = 0x01 | ((dts << 1) & 0xFE);
444                 write(self->fd, pes_header, 19);
445         } else
446         {
447                 pes_header[4] = (size + 3) >> 8;
448                 pes_header[5] = (size + 3) & 0xFF;
449                 pes_header[6] = 0x80;
450                 pes_header[7] = 0x00;
451                 pes_header[8] = 0;
452                 write(self->fd, pes_header, 9);
453         }
454 #endif
455
456         write(self->fd, GST_BUFFER_DATA (buffer) + skip, GST_BUFFER_SIZE (buffer) - skip);
457
458         return GST_FLOW_OK;
459 select_error:
460         {
461                 GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL),
462                                 ("select on file descriptor: %s.", g_strerror (errno)));
463                 GST_DEBUG_OBJECT (self, "Error during select");
464                 return GST_FLOW_ERROR;
465         }
466 stopped:
467         {
468                 GST_DEBUG_OBJECT (self, "Select stopped");
469                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
470                 return GST_FLOW_WRONG_STATE;
471         }
472 }
473
474 static gboolean
475 gst_dvbaudiosink_start (GstBaseSink * basesink)
476 {
477         GstDVBAudioSink *self;
478         self = GST_DVBAUDIOSINK (basesink);
479         self->fd = open("/dev/dvb/adapter0/audio0", O_RDWR);
480 //      self->fd = open("/dump.pes", O_RDWR|O_CREAT, 0555);
481         
482         gint control_sock[2];
483
484         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
485                 goto socket_pair;
486
487         READ_SOCKET (self) = control_sock[0];
488         WRITE_SOCKET (self) = control_sock[1];
489
490         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
491         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
492
493         if (self->fd)
494         {
495                 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY);
496                 ioctl(self->fd, AUDIO_PLAY);
497                 ioctl(self->fd, AUDIO_SET_BYPASS_MODE, 0);
498         }
499         return TRUE;
500         /* ERRORS */
501 socket_pair:
502         {
503                 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL),
504                                 GST_ERROR_SYSTEM);
505                 return FALSE;
506         }
507 }
508
509 static gboolean
510 gst_dvbaudiosink_stop (GstBaseSink * basesink)
511 {
512         GstDVBAudioSink *self;
513         self = GST_DVBAUDIOSINK (basesink);
514         if (self->fd >= 0)
515         {
516                 ioctl(self->fd, AUDIO_STOP);
517                 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX);
518                 close(self->fd);
519         }
520         return TRUE;
521 }
522
523 /* entry point to initialize the plug-in
524  * initialize the plug-in itself
525  * register the element factories and pad templates
526  * register the features
527  *
528  * exchange the string 'plugin' with your elemnt name
529  */
530 static gboolean
531 plugin_init (GstPlugin *plugin)
532 {
533         return gst_element_register (plugin, "dvbaudiosink",
534                                                  GST_RANK_NONE,
535                                                  GST_TYPE_DVBAUDIOSINK);
536 }
537
538 /* this is the structure that gstreamer looks for to register plugins
539  *
540  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
541  * description
542  */
543 GST_PLUGIN_DEFINE (
544         GST_VERSION_MAJOR,
545         GST_VERSION_MINOR,
546         "dvb_audio_out",
547         "DVB Audio Output",
548         plugin_init,
549         VERSION,
550         "LGPL",
551         "GStreamer",
552         "http://gstreamer.net/"
553 )