initial import
[gst-plugin-dvbmediasink.git] / src / gstdvbvideosink.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 <sys/ioctl.h>
65 #include <linux/dvb/video.h>
66 #include <fcntl.h>
67
68 #include <gst/gst.h>
69
70 #include "gstdvbvideosink.h"
71
72 GST_DEBUG_CATEGORY_STATIC (dvbvideosink_debug);
73 #define GST_CAT_DEFAULT dvbvideosink_debug
74
75 /* Filter signals and args */
76 enum {
77         /* FILL ME */
78         LAST_SIGNAL
79 };
80
81 enum {
82         ARG_0,
83         ARG_SILENT
84 };
85
86 static GstStaticPadTemplate sink_factory =
87 GST_STATIC_PAD_TEMPLATE (
88         "sink",
89         GST_PAD_SINK,
90         GST_PAD_ALWAYS,
91         GST_STATIC_CAPS ("video/mpeg, "
92                 "mpegversion = (int) [ 1, 2 ], " "systemstream = (boolean) false")
93 );
94
95 #define DEBUG_INIT(bla) \
96   GST_DEBUG_CATEGORY_INIT (dvbvideosink_debug, "dvbvideosink", 0, "dvbvideosink element");
97
98 GST_BOILERPLATE_FULL (GstDVBVideoSink, gst_dvbvideosink, GstBaseSink,
99         GST_TYPE_BASE_SINK, DEBUG_INIT);
100
101 static void     gst_dvbvideosink_set_property (GObject *object, guint prop_id,
102                                                                                                                                                                                                         const GValue *value,
103                                                                                                                                                                                                         GParamSpec *pspec);
104 static void     gst_dvbvideosink_get_property (GObject *object, guint prop_id,
105                                                                                                                                                                                                         GValue *value,
106                                                                                                                                                                                                         GParamSpec *pspec);
107
108 static gboolean gst_dvbvideosink_start (GstBaseSink * sink);
109 static gboolean gst_dvbvideosink_stop (GstBaseSink * sink);
110 static gboolean gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event);
111 static GstFlowReturn gst_dvbvideosink_render (GstBaseSink * sink,
112         GstBuffer * buffer);
113 static gboolean gst_dvbvideosink_query (GstPad * pad, GstQuery * query);
114
115
116 static void
117 gst_dvbvideosink_base_init (gpointer klass)
118 {
119         static GstElementDetails element_details = {
120                 "A DVB video sink",
121                 "Generic/DVBVideoSink",
122                 "Outputs a MPEG2 PES / ES into a DVB video device for hardware playback",
123                 "Felix Domke <tmbinc@elitedvb.net>"
124         };
125         GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
126
127         gst_element_class_add_pad_template (element_class,
128                 gst_static_pad_template_get (&sink_factory));
129         gst_element_class_set_details (element_class, &element_details);
130 }
131
132 /* initialize the plugin's class */
133 static void
134 gst_dvbvideosink_class_init (GstDVBVideoSinkClass *klass)
135 {
136         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
137         GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
138         
139         gobject_class->set_property = gst_dvbvideosink_set_property;
140         gobject_class->get_property = gst_dvbvideosink_get_property;
141         
142         gobject_class = G_OBJECT_CLASS (klass);
143         g_object_class_install_property (gobject_class, ARG_SILENT,
144                 g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
145                                                                                                         FALSE, G_PARAM_READWRITE));
146
147         gstbasesink_class->get_times = NULL;
148         gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dvbvideosink_start);
149         gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dvbvideosink_stop);
150         gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dvbvideosink_render);
151         gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_dvbvideosink_event);
152 }
153
154 /* initialize the new element
155  * instantiate pads and add them to element
156  * set functions
157  * initialize structure
158  */
159 static void
160 gst_dvbvideosink_init (GstDVBVideoSink *klass,
161                 GstDVBVideoSinkClass * gclass)
162 {
163         GstPad *pad;
164         
165         pad = GST_BASE_SINK_PAD (klass);
166         
167         gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_dvbvideosink_query));
168         
169         klass->silent = FALSE;
170 }
171
172 static void
173 gst_dvbvideosink_set_property (GObject *object, guint prop_id,
174                                                                                                                                         const GValue *value, GParamSpec *pspec)
175 {
176         GstDVBVideoSink *filter;
177
178         g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
179         filter = GST_DVBVIDEOSINK (object);
180
181         switch (prop_id)
182         {
183         case ARG_SILENT:
184                 filter->silent = g_value_get_boolean (value);
185                 break;
186         default:
187                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188                 break;
189         }
190 }
191
192 static void
193 gst_dvbvideosink_get_property (GObject *object, guint prop_id,
194                                                                                                                                         GValue *value, GParamSpec *pspec)
195 {
196         GstDVBVideoSink *filter;
197
198         g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
199         filter = GST_DVBVIDEOSINK (object);
200
201         switch (prop_id) {
202         case ARG_SILENT:
203                 g_value_set_boolean (value, filter->silent);
204                 break;
205         default:
206                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
207                 break;
208         }
209 }
210
211 static gboolean
212 gst_dvbvideosink_query (GstPad * pad, GstQuery * query)
213 {
214         GstDVBVideoSink *self;
215 //      GstFormat format;
216         
217         self = GST_DVBVIDEOSINK (GST_PAD_PARENT (pad));
218         switch (GST_QUERY_TYPE (query)) {
219         default:
220                 return gst_pad_query_default (pad, query);
221         }
222 }
223
224 static gboolean
225 gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event)
226 {
227         GstEventType type;
228         GstDVBVideoSink *self;
229         self = GST_DVBVIDEOSINK (sink);
230         switch (type) {
231         case GST_EVENT_FLUSH_START:
232         case GST_EVENT_FLUSH_STOP:
233                 printf("DVB video sink FLUSH\n");
234                 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
235                 break;
236         default:
237                 printf("dvb video sink: unknown event type %d\n", type); 
238                 return FALSE;
239         }
240         return TRUE;
241 }
242
243 static GstFlowReturn
244 gst_dvbvideosink_render (GstBaseSink * sink, GstBuffer * buffer)
245 {
246         unsigned char pes_header[19];
247         GstDVBVideoSink *self;
248         unsigned int size;
249         self = GST_DVBVIDEOSINK (sink);
250         
251         size = GST_BUFFER_SIZE (buffer);
252         
253 //      printf("write %d, timestamp: %08llx\n", GST_BUFFER_SIZE (buffer), (long long)GST_BUFFER_TIMESTAMP(buffer));
254
255         if (self->fd < 0)
256                 return GST_FLOW_OK;
257
258         pes_header[0] = 0;
259         pes_header[1] = 0;
260         pes_header[2] = 1;
261         pes_header[3] = 0xE0;
262         
263                 /* do we have a timestamp? */
264         if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE)
265         {
266                 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000; /* convert ns to 90kHz */
267                 
268                 pes_header[4] = (size + 13) >> 8;
269                 pes_header[5] = (size + 13) & 0xFF;
270                 
271                 pes_header[6] = 0x80;
272                 pes_header[7] = 0xC0;
273                 
274                 pes_header[8] = 10;
275                 
276                 pes_header[9]  = 0x31 | ((pts >> 29) & 0xE);
277                 pes_header[10] = pts >> 22;
278                 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
279                 pes_header[12] = pts >> 7;
280                 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
281                 
282                 int64_t dts = pts; /* what to use as DTS-PTS offset? */
283                 
284                 pes_header[14] = 0x11 | ((dts >> 29) & 0xE);
285                 pes_header[15] = dts >> 22;
286                 pes_header[16] = 0x01 | ((dts >> 14) & 0xFE);
287                 pes_header[17] = dts >> 7;
288                 pes_header[18] = 0x01 | ((dts << 1) & 0xFE);
289                 write(self->fd, pes_header, 19);
290         } else
291         {
292                 pes_header[4] = (size + 3) >> 8;
293                 pes_header[5] = (size + 3) & 0xFF;
294                 pes_header[6] = 0x80;
295                 pes_header[7] = 0x00;
296                 pes_header[8] = 0;
297                 write(self->fd, pes_header, 9);
298         }
299         
300         write(self->fd, GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer));
301
302         return GST_FLOW_OK;
303 }
304
305 static gboolean
306 gst_dvbvideosink_start (GstBaseSink * basesink)
307 {
308         GstDVBVideoSink *self;
309         self = GST_DVBVIDEOSINK (basesink);
310         printf("start\n");
311         self->fd = open("/dev/dvb/adapter0/video0", O_RDWR);
312 //      self->fd = open("/dump.pes", O_RDWR|O_CREAT, 0555);
313         
314         if (self->fd >= 0)
315         {
316                 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
317                 ioctl(self->fd, VIDEO_PLAY);
318         }
319         return TRUE;
320 }
321
322 static gboolean
323 gst_dvbvideosink_stop (GstBaseSink * basesink)
324 {
325         GstDVBVideoSink *self;
326         self = GST_DVBVIDEOSINK (basesink);
327         printf("stop\n");
328         if (self->fd >= 0)
329         {
330                 ioctl(self->fd, VIDEO_STOP);
331                 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
332                 close(self->fd);
333         }
334         return TRUE;
335 }
336
337 /* entry point to initialize the plug-in
338  * initialize the plug-in itself
339  * register the element factories and pad templates
340  * register the features
341  *
342  * exchange the string 'plugin' with your elemnt name
343  */
344 static gboolean
345 plugin_init (GstPlugin *plugin)
346 {
347         return gst_element_register (plugin, "dvbvideosink",
348                                                  GST_RANK_NONE,
349                                                  GST_TYPE_DVBVIDEOSINK);
350 }
351
352 /* this is the structure that gstreamer looks for to register plugins
353  *
354  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
355  * description
356  */
357 GST_PLUGIN_DEFINE (
358         GST_VERSION_MAJOR,
359         GST_VERSION_MINOR,
360         "dvb_video_out",
361         "DVB Video Output",
362         plugin_init,
363         VERSION,
364         "LGPL",
365         "GStreamer",
366         "http://gstreamer.net/"
367 )