improve error handling for unsupported stream types
[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 gboolean bypass_set = FALSE;
146
147 static void
148 gst_dvbaudiosink_base_init (gpointer klass)
149 {
150         static GstElementDetails element_details = {
151                 "A DVB audio sink",
152                 "Generic/DVBAudioSink",
153                 "Outputs a MPEG2 PES / ES into a DVB audio device for hardware playback",
154                 "Felix Domke <tmbinc@elitedvb.net>"
155         };
156         GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
157
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);
161 }
162
163 /* initialize the plugin's class */
164 static void
165 gst_dvbaudiosink_class_init (GstDVBAudioSinkClass *klass)
166 {
167         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
168         GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
169         
170         gobject_class->set_property = gst_dvbaudiosink_set_property;
171         gobject_class->get_property = gst_dvbaudiosink_get_property;
172         
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));
177
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);
185 }
186
187 /* initialize the new element
188  * instantiate pads and add them to element
189  * set functions
190  * initialize structure
191  */
192 static void
193 gst_dvbaudiosink_init (GstDVBAudioSink *klass,
194                 GstDVBAudioSinkClass * gclass)
195 {
196         GstPad *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         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
269
270         GstStructure *structure;
271         const char *type;
272         int bypass;
273         
274         if (self->fd < 0)
275                 return FALSE;
276         
277         structure = gst_caps_get_structure (caps, 0);   
278         type = gst_structure_get_name (structure);
279
280         self->skip = 0;
281         if (!strcmp(type, "audio/mpeg")) {
282                 gint mpegversion;
283                 gst_structure_get_int (structure, "mpegversion", &mpegversion);
284                 switch (mpegversion) {
285                         case 1:
286                         {
287                                 gint layer;
288                                 gst_structure_get_int (structure, "layer", &layer);
289                                 if ( layer == 3 )
290                                         bypass = 0xA;
291                                 else
292                                         bypass = 1;
293                                 printf("MIMETYPE %s version %d layer %d\n",type,mpegversion,layer);
294                                 break;
295                         }
296                         case 2:
297                                 bypass = 1;
298                                 printf("MIMETYPE %s version %d\n",type,mpegversion);
299                                 break;
300                         case 4:
301                                 bypass = 8;
302                                 printf("MIMETYPE %s version %d (AAC)\n",type,mpegversion);
303                                 break;
304                         default:
305                                 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unhandled mpeg version %i", mpegversion));
306                                 break;
307                 }
308         }
309         else if (!strcmp(type, "audio/x-ac3") || !strcmp(type, "audio/ac3"))
310         {
311                 printf("MIMETYPE %s\n",type);
312                 bypass = 0;
313         }
314         else if (!strcmp(type, "audio/x-private1-ac3"))
315         {
316                 printf("MIMETYPE %s (DVD Audio - 2 byte skipping)\n",type);
317                 bypass = 0;
318                 self->skip = 2;
319         } else
320         {
321                 GST_ELEMENT_ERROR (self, STREAM, TYPE_NOT_FOUND, (NULL), ("unimplemented stream type %s", type));
322                 return FALSE;
323         }
324
325         GST_DEBUG_OBJECT(self, "setting dvb mode 0x%02x\n", bypass);
326
327         if (ioctl(self->fd, AUDIO_SET_BYPASS_MODE, bypass) < 0)
328         {
329                 GST_ELEMENT_ERROR (self, STREAM, DECODE, (NULL), ("hardware decoder can't be set to bypass mode %i", bypass));
330                 return FALSE;
331         }
332         bypass_set = TRUE;
333         return TRUE;
334 }
335
336 static gboolean
337 gst_dvbaudiosink_event (GstBaseSink * sink, GstEvent * event)
338 {
339         GstDVBAudioSink *self = GST_DVBAUDIOSINK (sink);
340         GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event)));
341
342         switch (GST_EVENT_TYPE (event)) {
343         case GST_EVENT_FLUSH_START:
344                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
345                 break;
346         case GST_EVENT_FLUSH_STOP:
347                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
348                 while (1)
349                 {
350                         gchar command;
351                         int res;
352
353                         READ_COMMAND (self, command, res);
354                         if (res < 0)
355                                 break;
356                 }
357                 break;
358         default:
359                 break;
360         }
361         return TRUE;
362 }
363
364 static GstFlowReturn
365 gst_dvbaudiosink_render (GstBaseSink * sink, GstBuffer * buffer)
366 {
367         unsigned char pes_header[19];
368         GstDVBAudioSink *self = GST_DVBAUDIOSINK (sink);
369         int skip = self->skip;
370         unsigned int size = GST_BUFFER_SIZE (buffer) - skip;
371         fd_set readfds;
372         fd_set writefds;
373         gint retval;
374
375 //      printf("write %d, timestamp: %08llx\n", GST_BUFFER_SIZE (buffer), (long long)GST_BUFFER_TIMESTAMP(buffer));
376
377         if ( !bypass_set )
378         {
379                 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("hardware decoder not setup (no caps in pipeline?)"));
380                 return GST_FLOW_ERROR;
381         }
382
383         FD_ZERO (&readfds);
384         FD_SET (READ_SOCKET (self), &readfds);
385
386         FD_ZERO (&writefds);
387         FD_SET (self->fd, &writefds);
388
389         do {
390                 GST_DEBUG_OBJECT (self, "going into select, have %d bytes to write",
391                                 size);
392                 retval = select (FD_SETSIZE, &readfds, &writefds, NULL, NULL);
393         } while ((retval == -1 && errno == EINTR));
394
395         if (retval == -1)
396                 goto select_error;
397
398         if (FD_ISSET (READ_SOCKET (self), &readfds)) {
399                 /* read all stop commands */
400                 while (TRUE) {
401                         gchar command;
402                         int res;
403
404                         READ_COMMAND (self, command, res);
405                         if (res < 0) {
406                                 GST_LOG_OBJECT (self, "no more commands");
407                                 /* no more commands */
408                                 break;
409                         }
410                 }
411                 goto stopped;
412         }
413
414         if (self->fd < 0)
415                 return GST_FLOW_OK;
416
417         pes_header[0] = 0;
418         pes_header[1] = 0;
419         pes_header[2] = 1;
420         pes_header[3] = 0xC0;
421
422 #if 1
423                 /* do we have a timestamp? */
424         if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE)
425         {
426                 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000; /* convert ns to 90kHz */
427                 
428                 pes_header[4] = (size + 13) >> 8;
429                 pes_header[5] = (size + 13) & 0xFF;
430                 
431                 pes_header[6] = 0x80;
432                 pes_header[7] = 0xC0;
433                 
434                 pes_header[8] = 10;
435                 
436                 pes_header[9]   = 0x31 | ((pts >> 29) & 0xE);
437                 pes_header[10] = pts >> 22;
438                 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
439                 pes_header[12] = pts >> 7;
440                 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
441                 
442                 int64_t dts = pts; /* what to use as DTS-PTS offset? */
443                 
444                 pes_header[14] = 0x11 | ((dts >> 29) & 0xE);
445                 pes_header[15] = dts >> 22;
446                 pes_header[16] = 0x01 | ((dts >> 14) & 0xFE);
447                 pes_header[17] = dts >> 7;
448                 pes_header[18] = 0x01 | ((dts << 1) & 0xFE);
449                 write(self->fd, pes_header, 19);
450         } else
451         {
452                 pes_header[4] = (size + 3) >> 8;
453                 pes_header[5] = (size + 3) & 0xFF;
454                 pes_header[6] = 0x80;
455                 pes_header[7] = 0x00;
456                 pes_header[8] = 0;
457                 write(self->fd, pes_header, 9);
458         }
459 #endif
460
461         write(self->fd, GST_BUFFER_DATA (buffer) + skip, GST_BUFFER_SIZE (buffer) - skip);
462
463         return GST_FLOW_OK;
464 select_error:
465         {
466                 GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL),
467                                 ("select on file descriptor: %s.", g_strerror (errno)));
468                 GST_DEBUG_OBJECT (self, "Error during select");
469                 return GST_FLOW_ERROR;
470         }
471 stopped:
472         {
473                 GST_DEBUG_OBJECT (self, "Select stopped");
474                 ioctl(self->fd, AUDIO_CLEAR_BUFFER);
475                 return GST_FLOW_WRONG_STATE;
476         }
477 }
478
479 static gboolean
480 gst_dvbaudiosink_start (GstBaseSink * basesink)
481 {
482         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
483         self->fd = open("/dev/dvb/adapter0/audio0", O_RDWR);
484 //      self->fd = open("/dump.pes", O_RDWR|O_CREAT, 0555);
485         
486         gint control_sock[2];
487
488         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
489                 goto socket_pair;
490
491         READ_SOCKET (self) = control_sock[0];
492         WRITE_SOCKET (self) = control_sock[1];
493
494         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
495         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
496
497         if (self->fd)
498         {
499                 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY);
500                 ioctl(self->fd, AUDIO_PLAY);
501                 ioctl(self->fd, AUDIO_SET_BYPASS_MODE, 0);
502         }
503         return TRUE;
504         /* ERRORS */
505 socket_pair:
506         {
507                 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL),
508                                 GST_ERROR_SYSTEM);
509                 return FALSE;
510         }
511 }
512
513 static gboolean
514 gst_dvbaudiosink_stop (GstBaseSink * basesink)
515 {
516         GstDVBAudioSink *self = GST_DVBAUDIOSINK (basesink);
517         if (self->fd >= 0)
518         {
519                 ioctl(self->fd, AUDIO_STOP);
520                 ioctl(self->fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_DEMUX);
521                 close(self->fd);
522         }
523         return TRUE;
524 }
525
526 /* entry point to initialize the plug-in
527  * initialize the plug-in itself
528  * register the element factories and pad templates
529  * register the features
530  *
531  * exchange the string 'plugin' with your elemnt name
532  */
533 static gboolean
534 plugin_init (GstPlugin *plugin)
535 {
536         return gst_element_register (plugin, "dvbaudiosink",
537                                                  GST_RANK_NONE,
538                                                  GST_TYPE_DVBAUDIOSINK);
539 }
540
541 /* this is the structure that gstreamer looks for to register plugins
542  *
543  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
544  * description
545  */
546 GST_PLUGIN_DEFINE (
547         GST_VERSION_MAJOR,
548         GST_VERSION_MINOR,
549         "dvb_audio_out",
550         "DVB Audio Output",
551         plugin_init,
552         VERSION,
553         "LGPL",
554         "GStreamer",
555         "http://gstreamer.net/"
556 )