mkv cleanup (some .mkv files now work here)
[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 <sys/socket.h>
66 #include <linux/dvb/video.h>
67 #include <fcntl.h>
68 #include <string.h>
69
70 #include <gst/gst.h>
71
72 #include "gstdvbvideosink.h"
73
74 #ifndef VIDEO_GET_PTS
75 #define VIDEO_GET_PTS              _IOR('o', 57, gint64)
76 #endif
77
78 /* We add a control socket as in fdsrc to make it shutdown quickly when it's blocking on the fd.
79  * Select is used to determine when the fd is ready for use. When the element state is changed,
80  * it happens from another thread while fdsink is select'ing on the fd. The state-change thread 
81  * sends a control message, so fdsink wakes up and changes state immediately otherwise
82  * it would stay blocked until it receives some data. */
83
84 /* the select call is also performed on the control sockets, that way
85  * we can send special commands to unblock the select call */
86 #define CONTROL_STOP                                            'S'              /* stop the select call */
87 #define CONTROL_SOCKETS(sink)    sink->control_sock
88 #define WRITE_SOCKET(sink)                      sink->control_sock[1]
89 #define READ_SOCKET(sink)                        sink->control_sock[0]
90
91 #define SEND_COMMAND(sink, command)                                     \
92 G_STMT_START {                                                                                                                  \
93         unsigned char c; c = command;                                            \
94         write (WRITE_SOCKET(sink), &c, 1);                               \
95 } G_STMT_END
96
97 #define READ_COMMAND(sink, command, res)                                \
98 G_STMT_START {                                                                                                                           \
99         res = read(READ_SOCKET(sink), &command, 1);      \
100 } G_STMT_END
101
102 GST_DEBUG_CATEGORY_STATIC (dvbvideosink_debug);
103 #define GST_CAT_DEFAULT dvbvideosink_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 #define COMMON_VIDEO_CAPS \
117   "width = (int) [ 16, 4096 ], " \
118   "height = (int) [ 16, 4096 ], " \
119   "framerate = (fraction) [ 0, MAX ]"
120
121 static GstStaticPadTemplate sink_factory =
122 GST_STATIC_PAD_TEMPLATE (
123         "sink",
124         GST_PAD_SINK,
125         GST_PAD_ALWAYS,
126         GST_STATIC_CAPS ("video/mpeg, "
127                 "mpegversion = (int) { 1, 2, 4 }, "
128                 "systemstream = (boolean) false, "
129         COMMON_VIDEO_CAPS "; "
130                 "video/x-wmv, "
131         COMMON_VIDEO_CAPS "; "
132                 "video/x-h264, "
133         COMMON_VIDEO_CAPS "; "
134                 "video/x-h263, "
135         COMMON_VIDEO_CAPS "; "
136                 "video/x-divx, "
137         COMMON_VIDEO_CAPS ", divxversion = (int) [ 3, 5 ]; "
138                 "video/x-xvid, " COMMON_VIDEO_CAPS )
139 );
140
141 #define DEBUG_INIT(bla) \
142         GST_DEBUG_CATEGORY_INIT (dvbvideosink_debug, "dvbvideosink", 0, "dvbvideosink element");
143
144 GST_BOILERPLATE_FULL (GstDVBVideoSink, gst_dvbvideosink, GstBaseSink,
145         GST_TYPE_BASE_SINK, DEBUG_INIT);
146
147 static void     gst_dvbvideosink_set_property (GObject *object, guint prop_id,
148                                                                                                                                                                                                         const GValue *value,
149                                                                                                                                                                                                         GParamSpec *pspec);
150 static void     gst_dvbvideosink_get_property (GObject *object, guint prop_id,
151                                                                                                                                                                                                         GValue *value,
152                                                                                                                                                                                                         GParamSpec *pspec);
153
154 static gboolean gst_dvbvideosink_start (GstBaseSink * sink);
155 static gboolean gst_dvbvideosink_stop (GstBaseSink * sink);
156 static gboolean gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event);
157 static GstFlowReturn gst_dvbvideosink_render (GstBaseSink * sink,
158         GstBuffer * buffer);
159 static gboolean gst_dvbvideosink_query (GstPad * pad, GstQuery * query);
160 static gboolean gst_dvbvideosink_set_caps (GstPad * pad, GstCaps * vscaps);
161 static gboolean gst_dvbvideosink_unlock (GstBaseSink * basesink);
162
163 static void
164 gst_dvbvideosink_base_init (gpointer klass)
165 {
166         static GstElementDetails element_details = {
167                 "A DVB video sink",
168                 "Generic/DVBVideoSink",
169                 "Outputs a MPEG2 or .H264 PES / ES into a DVB video device for hardware playback",
170                 "Felix Domke <tmbinc@elitedvb.net>"
171         };
172         GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
173
174         gst_element_class_add_pad_template (element_class,
175                 gst_static_pad_template_get (&sink_factory));
176         gst_element_class_set_details (element_class, &element_details);
177 }
178
179 /* initialize the plugin's class */
180 static void
181 gst_dvbvideosink_class_init (GstDVBVideoSinkClass *klass)
182 {
183         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
184         GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
185         
186         gobject_class->set_property = gst_dvbvideosink_set_property;
187         gobject_class->get_property = gst_dvbvideosink_get_property;
188         
189         gobject_class = G_OBJECT_CLASS (klass);
190         g_object_class_install_property (gobject_class, ARG_SILENT,
191                 g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
192                                                                                                         FALSE, G_PARAM_READWRITE));
193
194         gstbasesink_class->get_times = NULL;
195         gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dvbvideosink_start);
196         gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dvbvideosink_stop);
197         gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dvbvideosink_render);
198         gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_dvbvideosink_event);
199         gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dvbvideosink_unlock);
200 }
201
202 /* initialize the new element
203  * instantiate pads and add them to element
204  * set functions
205  * initialize structure
206  */
207 static void
208 gst_dvbvideosink_init (GstDVBVideoSink *klass,
209                 GstDVBVideoSinkClass * gclass)
210 {
211         GstPad *pad = GST_BASE_SINK_PAD (klass);
212         
213         gst_pad_set_setcaps_function (pad, GST_DEBUG_FUNCPTR (gst_dvbvideosink_set_caps));
214         gst_pad_set_query_function (pad, GST_DEBUG_FUNCPTR (gst_dvbvideosink_query));
215         
216         klass->silent = FALSE;
217         klass->divx311_header = NULL;
218         klass->must_send_header = FALSE;
219         klass->codec_data = NULL;
220
221         GST_BASE_SINK (klass)->sync = FALSE;
222 }
223
224 static void
225 gst_dvbvideosink_set_property (GObject *object, guint prop_id,
226                                                                                                                                         const GValue *value, GParamSpec *pspec)
227 {
228         GstDVBVideoSink *filter;
229
230         g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
231         filter = GST_DVBVIDEOSINK (object);
232
233         switch (prop_id)
234         {
235         case ARG_SILENT:
236                 filter->silent = g_value_get_boolean (value);
237                 break;
238         default:
239                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
240                 break;
241         }
242 }
243
244 static void
245 gst_dvbvideosink_get_property (GObject *object, guint prop_id,
246                                                                                                                                         GValue *value, GParamSpec *pspec)
247 {
248         GstDVBVideoSink *filter;
249
250         g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
251         filter = GST_DVBVIDEOSINK (object);
252
253         switch (prop_id) {
254         case ARG_SILENT:
255                 g_value_set_boolean (value, filter->silent);
256                 break;
257         default:
258                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
259                 break;
260         }
261 }
262
263 static gboolean
264 gst_dvbvideosink_query (GstPad * pad, GstQuery * query)
265 {
266         GstDVBVideoSink *self = GST_DVBVIDEOSINK (GST_PAD_PARENT (pad));
267 //      GstFormat format;
268         
269         printf("QUERY: type: %d\n", GST_QUERY_TYPE(query));
270         switch (GST_QUERY_TYPE (query)) {
271         case GST_QUERY_POSITION:
272         {
273                 gint64 cur = 0;
274                 GstFormat format;
275
276                 gst_query_parse_position (query, &format, NULL);
277                 
278                 if (format != GST_FORMAT_TIME)
279                         return gst_pad_query_default (pad, query);
280
281                 ioctl(self->fd, VIDEO_GET_PTS, &cur);
282                 printf("PTS: %08llx", cur);
283                 
284                 cur *= 11111;
285                 
286                 gst_query_set_position (query, format, cur);
287                 
288                 GST_DEBUG_OBJECT (self, "position format %d", format);
289                 return TRUE;
290         }
291         default:
292                 return gst_pad_query_default (pad, query);
293         }
294 }
295
296 static gboolean gst_dvbvideosink_unlock (GstBaseSink * basesink)
297 {
298         GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
299
300         SEND_COMMAND (self, CONTROL_STOP);
301
302         return TRUE;
303 }
304
305 static gboolean
306 gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event)
307 {
308         GstDVBVideoSink *self = GST_DVBVIDEOSINK (sink);
309         GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event)));
310
311         switch (GST_EVENT_TYPE (event)) {
312         case GST_EVENT_FLUSH_START:
313                 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
314                 self->must_send_header = TRUE;
315                 break;
316         case GST_EVENT_FLUSH_STOP:
317                 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
318                 self->must_send_header = TRUE;
319                 while (1)
320                 {
321                         gchar command;
322                         int res;
323
324                         READ_COMMAND (self, command, res);
325                         if (res < 0)
326                                 break;
327                 }
328         default:
329                 break;
330         }
331         return TRUE;
332 }
333
334 static GstFlowReturn
335 gst_dvbvideosink_render (GstBaseSink * sink, GstBuffer * buffer)
336 {
337         GstDVBVideoSink *self = GST_DVBVIDEOSINK (sink);
338         unsigned char *data = GST_BUFFER_DATA(buffer);
339         unsigned int data_len = GST_BUFFER_SIZE (buffer);
340         guint8 pes_header[2048];
341         unsigned int pes_header_len=0;
342         unsigned int payload_len=0;
343         fd_set readfds;
344         fd_set writefds;
345         gint retval;
346
347 //      printf("write %d, timestamp: %08llx\n", GST_BUFFER_SIZE (buffer), (long long)GST_BUFFER_TIMESTAMP(buffer));
348
349         FD_ZERO (&readfds);
350         FD_SET (READ_SOCKET (self), &readfds);
351
352         FD_ZERO (&writefds);
353         FD_SET (self->fd, &writefds);
354         
355         do {
356                 GST_DEBUG_OBJECT (self, "going into select, have %d bytes to write",
357                                 data_len);
358                 retval = select (FD_SETSIZE, &readfds, &writefds, NULL, NULL);
359         } while ((retval == -1 && errno == EINTR));
360
361         if (retval == -1)
362                 goto select_error;
363
364         if (FD_ISSET (READ_SOCKET (self), &readfds)) {
365                 /* read all stop commands */
366                 while (TRUE) {
367                         gchar command;
368                         int res;
369
370                         READ_COMMAND (self, command, res);
371                         if (res < 0) {
372                                 GST_LOG_OBJECT (self, "no more commands");
373                                 /* no more commands */
374                                 break;
375                         }
376                 }
377                 goto stopped;
378         }
379
380         if (self->fd < 0)
381                 return GST_FLOW_OK;
382
383         pes_header[0] = 0;
384         pes_header[1] = 0;
385         pes_header[2] = 1;
386         pes_header[3] = 0xE0;
387
388                 /* do we have a timestamp? */
389         if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE) {
390                 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000 /* convert ns to 90kHz */;
391
392                 pes_header[6] = 0x80;
393                 pes_header[7] = 0xC0;
394                 
395                 pes_header[8] = 10;
396                 
397                 pes_header[9] = 0x31 | ((pts >> 29) & 0xE);
398                 pes_header[10] = pts >> 22;
399                 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
400                 pes_header[12] = pts >> 7;
401                 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
402
403                 int64_t dts = pts > 7508 ? pts - 7508 : pts; /* what to use as DTS-PTS offset? */
404
405                 pes_header[14] = 0x11 | ((dts >> 29) & 0xE);
406                 pes_header[15] = dts >> 22;
407                 pes_header[16] = 0x01 | ((dts >> 14) & 0xFE);
408                 pes_header[17] = dts >> 7;
409                 pes_header[18] = 0x01 | ((dts << 1) & 0xFE);
410
411                 pes_header_len = 19;
412
413                 if (self->divx311_header) {
414                         if (self->must_send_header) {
415                                 write(self->fd, self->divx311_header, 63);
416                                 self->must_send_header = FALSE;
417                         }
418                         pes_header[19] = 0;
419                         pes_header[20] = 0;
420                         pes_header[21] = 1;
421                         pes_header[22] = 0xb6;
422                         pes_header_len += 4;
423                 }
424                 else if (self->codec_data) {
425                         unsigned int pos = 0;
426                         while(TRUE) {
427                                 unsigned int pack_len = (data[pos] << 24) | (data[pos+1] << 16) | (data[pos+2] << 8) | data[pos+3];
428 //                              printf("patch %02x %02x %02x %02x\n",
429 //                                      data[pos],
430 //                                      data[pos + 1],
431 //                                      data[pos + 2],
432 //                                      data[pos + 3]);
433                                 memcpy(data+pos, "\x00\x00\x00\x01", 4);
434 //                              printf("pos %d, (%d) >= %d\n", pos, pos+pack_len, data_len);
435                                 pos += 4;
436                                 if ((pos + pack_len) >= data_len)
437                                         break;
438                                 pos += pack_len;
439                         }
440                         if (self->must_send_header) {
441                                 unsigned char *codec_data = GST_BUFFER_DATA (self->codec_data);
442                                 unsigned int codec_data_len = GST_BUFFER_SIZE (self->codec_data);
443                                 unsigned int pos=0;
444 //                              printf("1\n");
445                                 if (codec_data_len > 7) {
446                                         unsigned short len = (codec_data[6] << 8) | codec_data[7];
447 //                                      printf("2 %d bytes\n", len);
448                                         if (codec_data_len >= (len + 8)) {
449 //                                              printf("3\n");
450                                                 memcpy(pes_header+pes_header_len, "\x00\x00\x00\x01", 4);
451                                                 pes_header_len += 4;
452                                                 memcpy(pes_header+pes_header_len, codec_data+8, len);
453                                                 pes_header_len += len;
454                                                 pos = 8 + len;
455                                                 if (codec_data_len > (pos + 2)) {
456                                                         len = (codec_data[pos+1] << 8) | codec_data[pos+2];
457 //                                                      printf("4 %d bytes\n", len);
458                                                         pos += 3;
459                                                         if (codec_data_len >= (pos+len)) {
460                                                                 printf("codec data ok!\n");
461                                                                 memcpy(pes_header+pes_header_len, "\x00\x00\x00\x01", 4);
462                                                                 pes_header_len += 4;
463                                                                 memcpy(pes_header+pes_header_len, codec_data+pos, len);
464                                                                 pes_header_len += len;
465                                                         }
466                                                         else
467                                                                 printf("codec_data to short(4)\n");
468                                                 }
469                                                 else
470                                                         printf("codec_data to short(3)!\n");
471                                         }
472                                         else
473                                                 printf("codec_data to short(2)!\n");
474                                 }
475                                 else
476                                         printf("codec_data to short(1)!\n");
477                                 self->must_send_header = FALSE;
478                         }
479                 }
480         }
481         else {
482 //              printf("no timestamp!\n");
483                 pes_header[6] = 0x80;
484                 pes_header[7] = 0x00;
485                 pes_header[8] = 0;
486                 pes_header_len = 9;
487         }
488
489         payload_len = data_len + pes_header_len - 6;
490
491         if (payload_len <= 0xFFFF) {
492                 pes_header[4] = payload_len >> 8;
493                 pes_header[5] = payload_len & 0xFF;
494         }
495         else {
496                 pes_header[4] = 0;
497                 pes_header[5] = 0;
498         }
499
500         write(self->fd, pes_header, pes_header_len);
501         write(self->fd, data, data_len);
502
503         return GST_FLOW_OK;
504 select_error:
505         {
506                 GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL),
507                                 ("select on file descriptor: %s.", g_strerror (errno)));
508                 GST_DEBUG_OBJECT (self, "Error during select");
509                 return GST_FLOW_ERROR;
510         }
511 stopped:
512         {
513                 GST_DEBUG_OBJECT (self, "Select stopped");
514                 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
515                 return GST_FLOW_WRONG_STATE;
516         }
517 }
518
519 static gboolean 
520 gst_dvbvideosink_set_caps (GstPad * pad, GstCaps * vscaps)
521 {
522         GstStructure *structure = gst_caps_get_structure (vscaps, 0);
523         const gchar *mimetype = gst_structure_get_name (structure);
524         GstDVBVideoSink *self = GST_DVBVIDEOSINK (GST_PAD_PARENT (pad));
525         int streamtype = -1;
526
527         if (!strcmp (mimetype, "video/mpeg")) {
528                 gint mpegversion;
529                 gst_structure_get_int (structure, "mpegversion", &mpegversion);
530                 switch (mpegversion) {
531                         case 1:
532                                 streamtype = 6;
533                                 printf("MIMETYPE video/mpeg1 -> VIDEO_SET_STREAMTYPE, 6\n");
534                         break;
535                         case 2:
536                                 streamtype = 0;
537                                 printf("MIMETYPE video/mpeg2 -> VIDEO_SET_STREAMTYPE, 0\n");
538                         break;
539                         case 4:
540                                 streamtype = 4;
541                                 printf("MIMETYPE video/mpeg4 -> VIDEO_SET_STREAMTYPE, 4\n");
542                         break;
543                         default:
544                                 g_error("unhandled mpeg version: %d",mpegversion);
545                         break;
546                 }
547         } else if (!strcmp (mimetype, "video/x-h264")) {
548                 const GValue *codec_data = gst_structure_get_value (structure, "codec_data");
549                 streamtype = 1;
550                 if (codec_data) {
551                         printf("H264 have codec data.. force mkv!\n");
552                         self->codec_data = gst_value_get_buffer (codec_data);
553                         gst_buffer_ref (self->codec_data);
554                 }
555                 self->must_send_header = TRUE;
556                 printf("MIMETYPE video/x-h264 VIDEO_SET_STREAMTYPE, 1\n");
557         } else if (!strcmp (mimetype, "video/x-h263")) {
558                 streamtype = 2;
559                 printf("MIMETYPE video/x-h263 VIDEO_SET_STREAMTYPE, 2\n");
560         } else if (!strcmp (mimetype, "video/x-xvid")) {
561                 streamtype = 10;
562                 printf("MIMETYPE video/x-xvid -> VIDEO_SET_STREAMTYPE, 10\n");
563         } else if (!strcmp (mimetype, "video/x-divx")) {
564                 gint divxversion;
565                 gst_structure_get_int (structure, "divxversion", &divxversion);
566                 switch (divxversion) {
567                         case 3:
568                         {
569                                 #define B_GET_BITS(w,e,b)  (((w)>>(b))&(((unsigned)(-1))>>((sizeof(unsigned))*8-(e+1-b))))
570                                 #define B_SET_BITS(name,v,e,b)  (((unsigned)(v))<<(b))
571                                 static const guint8 brcm_divx311_sequence_header[] = {
572                                         0x00, 0x00, 0x01, 0xE0, 0x00, 0x39, 0x80, 0xC0, // PES HEADER
573                                         0x0A, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, // ..
574                                         0xFF, 0xFF, 0xFF, // ..
575                                         0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, /* 0 .. 7 */
576                                         0x08, 0xC8, 0x0D, 0x40, 0x00, 0x53, 0x88, 0x40, /* 8 .. 15 */
577                                         0x0C, 0x40, 0x01, 0x90, 0x00, 0x97, 0x53, 0x0A, /* 16 .. 24 */
578                                         0x00, 0x00, 0x00, 0x00,
579                                         0x30, 0x7F, 0x00, 0x00, 0x01, 0xB2, 0x44, 0x69, /* 0 .. 7 */
580                                         0x76, 0x58, 0x33, 0x31, 0x31, 0x41, 0x4E, 0x44  /* 8 .. 15 */
581                                 };
582                                 guint8 *data = malloc(63);
583                                 gint height, width;
584                                 gst_structure_get_int (structure, "height", &height);
585                                 gst_structure_get_int (structure, "width", &width);
586                                 memcpy(data, brcm_divx311_sequence_header, 63);
587                                 self->divx311_header = data;
588                                 data += 43;
589                                 data[0] = B_GET_BITS(width,11,4);
590                                 data[1] = B_SET_BITS("width [3..0]", B_GET_BITS(width,3,0), 7, 4) |
591                                         B_SET_BITS("'10'", 0x02, 3, 2) |
592                                         B_SET_BITS("height [11..10]", B_GET_BITS(height,11,10), 1, 0);
593                                 data[2] = B_GET_BITS(height,9,2);
594                                 data[3]= B_SET_BITS("height [1.0]", B_GET_BITS(height,1,0), 7, 6) |
595                                         B_SET_BITS("'100000'", 0x20, 5, 0);
596                                 streamtype = 13;
597                                 self->must_send_header = TRUE;
598                                 printf("MIMETYPE video/x-divx vers. 3 -> VIDEO_SET_STREAMTYPE, 13\n");
599                         }
600                         break;
601                         case 4:
602                                 streamtype = 14;
603                                 printf("MIMETYPE video/x-divx vers. 4 -> VIDEO_SET_STREAMTYPE, 14\n");
604                         break;
605                         case 5:
606                                 streamtype = 15;
607                                 printf("MIMETYPE video/x-divx vers. 5 -> VIDEO_SET_STREAMTYPE, 15\n");
608                         break;
609                         default:
610                                 g_error("unhandled divx version");
611                         break;
612                 }
613         }
614         if (streamtype != -1) {
615                 if (ioctl(self->fd, VIDEO_SET_STREAMTYPE, streamtype) < 0)
616                         perror("VIDEO_SET_STREAMTYPE");
617         } else
618                 g_error("unsupported stream type %s",mimetype);
619         return TRUE;
620 }
621
622 static gboolean
623 gst_dvbvideosink_start (GstBaseSink * basesink)
624 {
625         GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
626         self->fd = open("/dev/dvb/adapter0/video0", O_RDWR);
627 //      self->fd = open("/dump.pes", O_RDWR|O_CREAT, 0555);
628
629         gint control_sock[2];
630
631         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
632                 goto socket_pair;
633
634         READ_SOCKET (self) = control_sock[0];
635         WRITE_SOCKET (self) = control_sock[1];
636
637         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
638         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
639
640         if (self->fd >= 0)
641         {
642                 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
643                 ioctl(self->fd, VIDEO_PLAY);
644         }
645
646         return TRUE;
647         /* ERRORS */
648 socket_pair:
649         {
650                 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL),
651                                 GST_ERROR_SYSTEM);
652                 return FALSE;
653         }
654 }
655
656 static gboolean
657 gst_dvbvideosink_stop (GstBaseSink * basesink)
658 {
659         GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
660         if (self->fd >= 0)
661         {
662                 ioctl(self->fd, VIDEO_STOP);
663                 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
664                 close(self->fd);
665         }
666
667         close (READ_SOCKET (self));
668         close (WRITE_SOCKET (self));
669
670         if (self->divx311_header)
671                 free(self->divx311_header);
672
673         if (self->codec_data)
674                 gst_buffer_unref(self->codec_data);
675
676         return TRUE;
677 }
678
679 /* entry point to initialize the plug-in
680  * initialize the plug-in itself
681  * register the element factories and pad templates
682  * register the features
683  *
684  * exchange the string 'plugin' with your elemnt name
685  */
686 static gboolean
687 plugin_init (GstPlugin *plugin)
688 {
689         return gst_element_register (plugin, "dvbvideosink",
690                                                  GST_RANK_NONE,
691                                                  GST_TYPE_DVBVIDEOSINK);
692 }
693
694 /* this is the structure that gstreamer looks for to register plugins
695  *
696  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
697  * description
698  */
699 GST_PLUGIN_DEFINE (
700         GST_VERSION_MAJOR,
701         GST_VERSION_MINOR,
702         "dvb_video_out",
703         "DVB Video Output",
704         plugin_init,
705         VERSION,
706         "LGPL",
707         "GStreamer",
708         "http://gstreamer.net/"
709 )