report the stream framerate to the hardware .. this is used by new
[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 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
79 struct bitstream
80 {
81         guint8 *data;
82         guint8 last;
83         int avail;
84 };
85
86 void bitstream_init(struct bitstream *bit, const void *buffer, gboolean wr)
87 {
88         bit->data = (guint8*) buffer;
89         if (wr) {
90                 bit->avail = 0;
91                 bit->last = 0;
92         }
93         else {
94                 bit->avail = 8;
95                 bit->last = *bit->data++;
96         }
97 }
98
99 unsigned long bitstream_get(struct bitstream *bit, int bits)
100 {
101         unsigned long res=0;
102         while (bits)
103         {
104                 unsigned int d=bits;
105                 if (!bit->avail) {
106                         bit->last = *bit->data++;
107                         bit->avail = 8;
108                 }
109                 if (d > bit->avail)
110                         d=bit->avail;
111                 res<<=d;
112                 res|=(bit->last>>(bit->avail-d))&~(-1<<d);
113                 bit->avail-=d;
114                 bits-=d;
115         }
116         return res;
117 }
118
119 void bitstream_put(struct bitstream *bit, unsigned long val, int bits)
120 {
121         while (bits)
122         {
123                 bit->last |= ((val & (1 << (bits-1))) ? 1 : 0) << (7 - bit->avail);
124                 if (++bit->avail == 8) {
125                         *bit->data = bit->last;
126                         ++bit->data;
127                         bit->last = 0;
128                         bit->avail = 0;
129                 }
130                 --bits;
131         }
132 }
133 #endif
134
135 /* We add a control socket as in fdsrc to make it shutdown quickly when it's blocking on the fd.
136  * Select is used to determine when the fd is ready for use. When the element state is changed,
137  * it happens from another thread while fdsink is select'ing on the fd. The state-change thread 
138  * sends a control message, so fdsink wakes up and changes state immediately otherwise
139  * it would stay blocked until it receives some data. */
140
141 /* the select call is also performed on the control sockets, that way
142  * we can send special commands to unblock the select call */
143 #define CONTROL_STOP                                            'S'              /* stop the select call */
144 #define CONTROL_SOCKETS(sink)    sink->control_sock
145 #define WRITE_SOCKET(sink)                      sink->control_sock[1]
146 #define READ_SOCKET(sink)                        sink->control_sock[0]
147
148 #define SEND_COMMAND(sink, command)                                     \
149 G_STMT_START {                                                                                                                  \
150         unsigned char c; c = command;                                            \
151         write (WRITE_SOCKET(sink), &c, 1);                               \
152 } G_STMT_END
153
154 #define READ_COMMAND(sink, command, res)                                \
155 G_STMT_START {                                                                                                                           \
156         res = read(READ_SOCKET(sink), &command, 1);      \
157 } G_STMT_END
158
159 GST_DEBUG_CATEGORY_STATIC (dvbvideosink_debug);
160 #define GST_CAT_DEFAULT dvbvideosink_debug
161
162 /* Filter signals and args */
163 enum {
164         /* FILL ME */
165         LAST_SIGNAL
166 };
167
168 enum {
169         ARG_0,
170         ARG_SILENT
171 };
172
173 #define COMMON_VIDEO_CAPS \
174   "width = (int) [ 16, 4096 ], " \
175   "height = (int) [ 16, 4096 ], " \
176   "framerate = (fraction) [ 0, MAX ]"
177
178 static GstStaticPadTemplate sink_factory =
179 GST_STATIC_PAD_TEMPLATE (
180         "sink",
181         GST_PAD_SINK,
182         GST_PAD_ALWAYS,
183         GST_STATIC_CAPS (
184                 "video/mpeg, "
185                 "mpegversion = (int) { 1, 2, 4 }, "
186                 "systemstream = (boolean) false, "
187         COMMON_VIDEO_CAPS "; "
188                 "video/x-msmpeg, "
189         COMMON_VIDEO_CAPS ", mspegversion = (int) 43; "
190                 "video/x-h264, "
191         COMMON_VIDEO_CAPS "; "
192                 "video/x-h263, "
193         COMMON_VIDEO_CAPS "; "
194                 "video/x-divx, "
195         COMMON_VIDEO_CAPS ", divxversion = (int) [ 3, 5 ]; "
196                 "video/x-xvid, "
197         COMMON_VIDEO_CAPS "; ")
198 );
199
200 #define DEBUG_INIT(bla) \
201         GST_DEBUG_CATEGORY_INIT (dvbvideosink_debug, "dvbvideosink", 0, "dvbvideosink element");
202
203 GST_BOILERPLATE_FULL (GstDVBVideoSink, gst_dvbvideosink, GstBaseSink,
204         GST_TYPE_BASE_SINK, DEBUG_INIT);
205
206 static void     gst_dvbvideosink_set_property (GObject *object, guint prop_id,
207                                                                                                                                                                                                         const GValue *value,
208                                                                                                                                                                                                         GParamSpec *pspec);
209 static void     gst_dvbvideosink_get_property (GObject *object, guint prop_id,
210                                                                                                                                                                                                         GValue *value,
211                                                                                                                                                                                                         GParamSpec *pspec);
212
213 static gboolean gst_dvbvideosink_start (GstBaseSink * sink);
214 static gboolean gst_dvbvideosink_stop (GstBaseSink * sink);
215 static gboolean gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event);
216 static GstFlowReturn gst_dvbvideosink_render (GstBaseSink * sink,
217         GstBuffer * buffer);
218 static gboolean gst_dvbvideosink_query (GstElement * element, GstQuery * query);
219 static gboolean gst_dvbvideosink_set_caps (GstBaseSink * sink, GstCaps * caps);
220 static gboolean gst_dvbvideosink_unlock (GstBaseSink * basesink);
221
222 static void
223 gst_dvbvideosink_base_init (gpointer klass)
224 {
225         static GstElementDetails element_details = {
226                 "A DVB video sink",
227                 "Generic/DVBVideoSink",
228                 "Output video PES / ES into a DVB video device for hardware playback",
229                 "Felix Domke <tmbinc@elitedvb.net>"
230         };
231         GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
232
233         gst_element_class_add_pad_template (element_class,
234                 gst_static_pad_template_get (&sink_factory));
235         gst_element_class_set_details (element_class, &element_details);
236 }
237
238 /* initialize the plugin's class */
239 static void
240 gst_dvbvideosink_class_init (GstDVBVideoSinkClass *klass)
241 {
242         GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
243         GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
244         
245         gobject_class->set_property = gst_dvbvideosink_set_property;
246         gobject_class->get_property = gst_dvbvideosink_get_property;
247         
248         gobject_class = G_OBJECT_CLASS (klass);
249         g_object_class_install_property (gobject_class, ARG_SILENT,
250                 g_param_spec_boolean ("silent", "Silent", "Produce verbose output ?",
251                                                                                                         FALSE, G_PARAM_READWRITE));
252
253         gstbasesink_class->get_times = NULL;
254         gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_dvbvideosink_start);
255         gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_dvbvideosink_stop);
256         gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_dvbvideosink_render);
257         gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_dvbvideosink_event);
258         gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_dvbvideosink_unlock);
259         gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_dvbvideosink_set_caps);
260         GST_ELEMENT_CLASS (klass)->query = GST_DEBUG_FUNCPTR (gst_dvbvideosink_query);
261 }
262
263 /* initialize the new element
264  * instantiate pads and add them to element
265  * set functions
266  * initialize structure
267  */
268 static void
269 gst_dvbvideosink_init (GstDVBVideoSink *klass,
270                 GstDVBVideoSinkClass * gclass)
271 {
272         FILE *f = fopen("/proc/stb/vmpeg/0/fallback_framerate", "r");
273         klass->silent = FALSE;
274         klass->must_send_header = TRUE;
275         klass->codec_data = NULL;
276         klass->codec_type = CT_H264;
277 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
278         klass->must_pack_bitstream = 0;
279         klass->num_non_keyframes = 0;
280         klass->prev_frame = NULL;
281 #endif
282         if (f) {
283                 fgets(klass->saved_fallback_framerate, 16, f);
284                 fclose(f);
285         }
286         GST_BASE_SINK (klass)->sync = FALSE;
287 }
288
289 static void
290 gst_dvbvideosink_set_property (GObject *object, guint prop_id,
291                                                                                                                                         const GValue *value, GParamSpec *pspec)
292 {
293         GstDVBVideoSink *filter;
294
295         g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
296         filter = GST_DVBVIDEOSINK (object);
297
298         switch (prop_id)
299         {
300         case ARG_SILENT:
301                 filter->silent = g_value_get_boolean (value);
302                 break;
303         default:
304                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
305                 break;
306         }
307 }
308
309 static void
310 gst_dvbvideosink_get_property (GObject *object, guint prop_id,
311                                                                                                                                         GValue *value, GParamSpec *pspec)
312 {
313         GstDVBVideoSink *filter;
314
315         g_return_if_fail (GST_IS_DVBVIDEOSINK (object));
316         filter = GST_DVBVIDEOSINK (object);
317
318         switch (prop_id) {
319         case ARG_SILENT:
320                 g_value_set_boolean (value, filter->silent);
321                 break;
322         default:
323                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
324                 break;
325         }
326 }
327
328 static gboolean
329 gst_dvbvideosink_query (GstElement * element, GstQuery * query)
330 {
331         GstDVBVideoSink *self = GST_DVBVIDEOSINK (element);
332
333 //      printf("QUERY: type: %d (%d)\n", GST_QUERY_TYPE(query), GST_QUERY_POSITION);
334
335         switch (GST_QUERY_TYPE (query)) {
336         case GST_QUERY_POSITION:
337         {
338                 gint64 cur = 0;
339                 GstFormat format;
340
341                 gst_query_parse_position (query, &format, NULL);
342
343                 if (format != GST_FORMAT_TIME)
344                         goto query_default;
345
346                 ioctl(self->fd, VIDEO_GET_PTS, &cur);
347 //              printf("PTS: %08llx", cur);
348
349                 cur *= 11111;
350
351                 gst_query_set_position (query, format, cur);
352                 
353                 GST_DEBUG_OBJECT (self, "position format %d", format);
354                 return TRUE;
355         }
356         default:
357 query_default:
358                 return GST_ELEMENT_CLASS (parent_class)->query (element, query);
359         }
360 }
361
362 static gboolean gst_dvbvideosink_unlock (GstBaseSink * basesink)
363 {
364         GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
365
366         SEND_COMMAND (self, CONTROL_STOP);
367
368         return TRUE;
369 }
370
371 static gboolean
372 gst_dvbvideosink_event (GstBaseSink * sink, GstEvent * event)
373 {
374         GstDVBVideoSink *self = GST_DVBVIDEOSINK (sink);
375         GST_DEBUG_OBJECT (self, "EVENT %s", gst_event_type_get_name(GST_EVENT_TYPE (event)));
376
377         switch (GST_EVENT_TYPE (event)) {
378         case GST_EVENT_FLUSH_START:
379                 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
380                 self->must_send_header = TRUE;
381                 break;
382         case GST_EVENT_FLUSH_STOP:
383                 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
384                 self->must_send_header = TRUE;
385                 while (1)
386                 {
387                         gchar command;
388                         int res;
389
390                         READ_COMMAND (self, command, res);
391                         if (res < 0)
392                                 break;
393                 }
394         default:
395                 break;
396         }
397         return TRUE;
398 }
399
400 static GstFlowReturn
401 gst_dvbvideosink_render (GstBaseSink * sink, GstBuffer * buffer)
402 {
403         GstDVBVideoSink *self = GST_DVBVIDEOSINK (sink);
404         unsigned char *data = GST_BUFFER_DATA(buffer);
405         unsigned int data_len = GST_BUFFER_SIZE (buffer);
406         guint8 pes_header[2048];
407         unsigned int pes_header_len=0;
408         unsigned int payload_len=0;
409         fd_set readfds;
410         fd_set writefds;
411         fd_set priofds;
412         gint retval;
413 //      int i=0;
414
415 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
416         gboolean commit_prev_frame_data = FALSE,
417                         cache_prev_frame = FALSE;
418 #endif
419
420 //      for (;i < (data_len > 0xF ? 0xF : data_len); ++i)
421 //              printf("%02x ", data[i]);
422 //      printf("%d bytes\n", data_len);
423
424 //      printf("timestamp: %08llx\n", (long long)GST_BUFFER_TIMESTAMP(buffer));
425
426         FD_ZERO (&readfds);
427         FD_SET (READ_SOCKET (self), &readfds);
428
429         FD_ZERO (&writefds);
430         FD_SET (self->fd, &writefds);
431
432         FD_ZERO (&priofds);
433         FD_SET (self->fd, &priofds);
434
435         do {
436                 GST_DEBUG_OBJECT (self, "going into select, have %d bytes to write",
437                                 data_len);
438                 retval = select (FD_SETSIZE, &readfds, &writefds, &priofds, NULL);
439         } while ((retval == -1 && errno == EINTR));
440
441         if (retval == -1)
442                 goto select_error;
443
444         if (FD_ISSET (READ_SOCKET (self), &readfds)) {
445                 /* read all stop commands */
446                 while (TRUE) {
447                         gchar command;
448                         int res;
449
450                         READ_COMMAND (self, command, res);
451                         if (res < 0) {
452                                 GST_LOG_OBJECT (self, "no more commands");
453                                 /* no more commands */
454                                 break;
455                         }
456                 }
457                 goto stopped;
458         }
459
460         if (FD_ISSET (self->fd, &priofds))
461         {
462                 GstStructure *s;
463                 GstMessage *msg;
464
465                 struct video_event evt;
466                 if (ioctl(self->fd, VIDEO_GET_EVENT, &evt) < 0)
467                         g_warning ("failed to ioctl VIDEO_GET_EVENT!");
468                 else
469                 {
470                         if (evt.type == VIDEO_EVENT_SIZE_CHANGED)
471                         {
472                                 s = gst_structure_new ("eventSizeChanged",
473                                         "aspect_ratio", G_TYPE_INT, evt.u.size.aspect_ratio == 0 ? 2 : 3,
474                                         "width", G_TYPE_INT, evt.u.size.w,
475                                         "height", G_TYPE_INT, evt.u.size.h, NULL);
476                                 msg = gst_message_new_element (GST_OBJECT (sink), s);
477                                 gst_element_post_message (GST_ELEMENT (sink), msg);
478                         }
479                         else if (evt.type == VIDEO_EVENT_FRAME_RATE_CHANGED)
480                         {
481                                 s = gst_structure_new ("eventFrameRateChanged",
482                                         "frame_rate", G_TYPE_INT, evt.u.frame_rate, NULL);
483                                 msg = gst_message_new_element (GST_OBJECT (sink), s);
484                                 gst_element_post_message (GST_ELEMENT (sink), msg);
485                         }
486                         else if (evt.type == 16 /*VIDEO_EVENT_PROGRESSIVE_CHANGED*/)
487                         {
488                                 s = gst_structure_new ("eventProgressiveChanged",
489                                         "progressive", G_TYPE_INT, evt.u.frame_rate, NULL);
490                                 msg = gst_message_new_element (GST_OBJECT (sink), s);
491                                 gst_element_post_message (GST_ELEMENT (sink), msg);
492                         }
493                         else
494                                 g_warning ("unhandled DVBAPI Video Event %d", evt.type);
495                 }
496         }
497
498         if (self->fd < 0)
499                 return GST_FLOW_OK;
500
501 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
502         if (self->must_pack_bitstream == 1) {
503                 cache_prev_frame = TRUE;
504                 unsigned int pos = 0;
505                 while(pos < data_len) {
506                         if (data[pos++])
507                                 continue;
508                         if (data[pos++])
509                                 continue;
510                         while (!data[pos])
511                                 ++pos;
512                         if (data[pos++] != 1)
513                                 continue;
514                         if ((data[pos++] & 0xF0) == 0x20) { // we need time_inc_res
515                                 gboolean low_delay=FALSE;
516                                 unsigned int ver_id = 1, shape=0, time_inc_res=0, tmp=0;
517                                 struct bitstream bit;
518                                 bitstream_init(&bit, data+pos, 0);
519                                 bitstream_get(&bit, 9);
520                                 if (bitstream_get(&bit, 1)) {
521                                         ver_id = bitstream_get(&bit, 4); // ver_id
522                                         bitstream_get(&bit, 3);
523                                 }
524                                 if ((tmp=bitstream_get(&bit, 4)) == 15) { // Custom Aspect Ration
525                                         bitstream_get(&bit, 8); // skip AR width
526                                         bitstream_get(&bit, 8); // skip AR height
527                                 }
528                                 if (bitstream_get(&bit, 1)) {
529                                         bitstream_get(&bit, 2);
530                                         low_delay = bitstream_get(&bit, 1) ? TRUE : FALSE;
531                                         if (bitstream_get(&bit, 1)) {
532                                                 bitstream_get(&bit, 32);
533                                                 bitstream_get(&bit, 32);
534                                                 bitstream_get(&bit, 15);
535                                         }
536                                 }
537                                 shape = bitstream_get(&bit, 2);
538                                 if (ver_id != 1 && shape == 3 /* Grayscale */)
539                                         bitstream_get(&bit, 4);
540                                 bitstream_get(&bit, 1);
541                                 time_inc_res = bitstream_get(&bit, 16);
542                                 self->time_inc_bits = 0;
543                                 while (time_inc_res) { // count bits
544                                         ++self->time_inc_bits;
545                                         time_inc_res >>= 1;
546                                 }
547 //                              printf("%d time_inc_bits\n", self->time_inc_bits);
548                         }
549                 }
550         }
551
552         if (self->must_pack_bitstream == 1) {
553                 int tmp1, tmp2;
554                 unsigned char c1, c2;
555                 unsigned int pos = 0;
556                 while(pos < data_len) {
557                         if (data[pos++])
558                                 continue;
559                         if (data[pos++])
560                                 continue;
561                         while (!data[pos])
562                                 ++pos;
563                         if (data[pos++] != 1)
564                                 continue;
565                         if (data[pos++] != 0xB2)
566                                 continue;
567                         if (data_len - pos < 13)
568                                 break;
569                         if (sscanf((char*)data+pos, "DivX%d%c%d%cp", &tmp1, &c1, &tmp2, &c2) == 4 && (c1 == 'b' || c1 == 'B') && (c2 == 'p' || c2 == 'P')) {
570                                 printf("%s seen... already packed!\n", (char*)data+pos);
571                                 self->must_pack_bitstream = 0;
572                         }
573 //                      if (self->must_pack_bitstream)
574 //                              printf("pack needed\n");
575 //                      else
576 //                              printf("no pack needed\n");
577                 }
578         }
579 #endif
580
581         pes_header[0] = 0;
582         pes_header[1] = 0;
583         pes_header[2] = 1;
584         pes_header[3] = 0xE0;
585
586                 /* do we have a timestamp? */
587         if (GST_BUFFER_TIMESTAMP(buffer) != GST_CLOCK_TIME_NONE) {
588                 unsigned long long pts = GST_BUFFER_TIMESTAMP(buffer) * 9LL / 100000 /* convert ns to 90kHz */;
589
590                 pes_header[6] = 0x80;
591                 pes_header[7] = 0x80;
592                 
593                 pes_header[8] = 5;
594                 
595                 pes_header[9] =  0x21 | ((pts >> 29) & 0xE);
596                 pes_header[10] = pts >> 22;
597                 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
598                 pes_header[12] = pts >> 7;
599                 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
600
601                 pes_header_len = 14;
602
603                 if (self->codec_data) {
604                         if (self->must_send_header) {
605                                 unsigned char *codec_data = GST_BUFFER_DATA (self->codec_data);
606                                 unsigned int codec_data_len = GST_BUFFER_SIZE (self->codec_data);
607                                 if (self->codec_type == CT_DIVX311)
608                                         write(self->fd, codec_data, codec_data_len);
609                                 else {
610                                         memcpy(pes_header+pes_header_len, codec_data, codec_data_len);
611                                         pes_header_len += codec_data_len;
612                                 }
613                                 self->must_send_header = FALSE;
614                         }
615                         if (self->codec_type == CT_H264) {  // MKV stuff
616                                 unsigned int pos = 0;
617                                 while(TRUE) {
618                                         unsigned int pack_len = (data[pos] << 24) | (data[pos+1] << 16) | (data[pos+2] << 8) | data[pos+3];
619 //                                      printf("patch %02x %02x %02x %02x\n",
620 //                                              data[pos],
621 //                                              data[pos + 1],
622 //                                              data[pos + 2],
623 //                                              data[pos + 3]);
624                                         memcpy(data+pos, "\x00\x00\x00\x01", 4);
625 //                                      printf("pos %d, (%d) >= %d\n", pos, pos+pack_len, data_len);
626                                         pos += 4;
627                                         if ((pos + pack_len) >= data_len)
628                                                 break;
629                                         pos += pack_len;
630                                 }
631                         }
632                         else if (self->codec_type == CT_MPEG4_PART2) {
633                                 memcpy(pes_header+pes_header_len, "\x00\x00\x01", 3);
634                                 pes_header_len += 3;
635                         }
636                         else if (self->codec_type == CT_DIVX311) {
637                                 memcpy(pes_header+pes_header_len, "\x00\x00\x01\xb6", 4);
638                                 pes_header_len += 4;
639                         }
640                 }
641         }
642         else {
643 //              printf("no timestamp!\n");
644                 pes_header[6] = 0x80;
645                 pes_header[7] = 0x00;
646                 pes_header[8] = 0;
647                 pes_header_len = 9;
648         }
649
650 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
651         if (self->must_pack_bitstream == 1) {
652                 unsigned int pos = 0;
653                 gboolean i_frame = FALSE;
654 //              gboolean s_frame = FALSE;
655                 while(pos < data_len) {
656                         if (data[pos++])
657                                 continue;
658                         if (data[pos++])
659                                 continue;
660                         while (!data[pos])
661                                 ++pos;
662                         if (data[pos++] != 1)
663                                 continue;
664                         if (data[pos++] != 0xB6)
665                                 continue;
666                         switch ((data[pos] & 0xC0) >> 6) {
667                                 case 0: // I-Frame
668 //                                      printf("I ");
669                                         cache_prev_frame = FALSE;
670                                         i_frame = TRUE;
671                                 case 1: // P-Frame
672 //                                      if (!i_frame)
673 //                                              printf("P ");
674                                         if (self->prev_frame != buffer) {
675                                                 struct bitstream bit;
676                                                 gboolean store_frame=FALSE;
677                                                 if (self->prev_frame) {
678                                                         if (!self->num_non_keyframes) {
679 //                                                              printf("no non keyframes...immediate commit prev frame\n");
680                                                                 GstFlowReturn ret = gst_dvbvideosink_render(sink, self->prev_frame);
681                                                                 gst_buffer_unref(self->prev_frame);
682                                                                 self->prev_frame = NULL;
683                                                                 if (ret != GST_FLOW_OK)
684                                                                         return ret;
685                                                                 store_frame = TRUE;
686                                                         }
687                                                         else {
688 //                                                              int i=-4;
689                                                                 pes_header[pes_header_len++] = 0;
690                                                                 pes_header[pes_header_len++] = 0;
691                                                                 pes_header[pes_header_len++] = 1;
692                                                                 pes_header[pes_header_len++] = 0xB6;
693                                                                 bitstream_init(&bit, pes_header+pes_header_len, 1);
694                                                                 bitstream_put(&bit, 1, 2);
695                                                                 bitstream_put(&bit, 0, 1);
696                                                                 bitstream_put(&bit, 1, 1);
697                                                                 bitstream_put(&bit, self->time_inc, self->time_inc_bits);
698                                                                 bitstream_put(&bit, 1, 1);
699                                                                 bitstream_put(&bit, 0, 1);
700                                                                 bitstream_put(&bit, 0x7F >> bit.avail, 8 - bit.avail);
701 //                                                              printf(" insert pack frame %d non keyframes, time_inc %d, time_inc_bits %d -",
702 //                                                                      self->num_non_keyframes, self->time_inc, self->time_inc_bits);
703 //                                                              for (; i < (bit.data - (pes_header+pes_header_len)); ++i)
704 //                                                                      printf(" %02x", pes_header[pes_header_len+i]);
705 //                                                              printf("\nset data_len to 0!\n");
706                                                                 data_len = 0;
707                                                                 pes_header_len += bit.data - (pes_header+pes_header_len);
708                                                                 cache_prev_frame = TRUE;
709                                                         }
710                                                 }
711                                                 else if (!i_frame)
712                                                         store_frame = TRUE;
713
714                                                 self->num_non_keyframes=0;
715
716                                                 // extract time_inc
717                                                 bitstream_init(&bit, data+pos, 0);
718                                                 bitstream_get(&bit, 2); // skip coding_type
719                                                 while(bitstream_get(&bit, 1));
720                                                 bitstream_get(&bit, 1);
721                                                 self->time_inc = bitstream_get(&bit, self->time_inc_bits);
722 //                                              printf("\ntime_inc is %d\n", self->time_inc);
723
724                                                 if (store_frame) {
725 //                                                      printf("store frame\n");
726                                                         self->prev_frame = buffer;
727                                                         gst_buffer_ref (buffer);
728                                                         return GST_FLOW_OK;
729                                                 }
730                                         }
731                                         else {
732                                                 cache_prev_frame = FALSE;
733 //                                              printf(" I/P Frame without non key frame(s)!!\n");
734                                         }
735                                         break;
736                                 case 3: // S-Frame
737 //                                      printf("S ");
738 //                                      s_frame = TRUE;
739                                 case 2: // B-Frame
740 //                                      if (!s_frame)
741 //                                              printf("B ");
742                                         if (++self->num_non_keyframes == 1 && self->prev_frame) {
743 //                                              printf("send grouped with prev P!\n");
744                                                 commit_prev_frame_data = TRUE;
745                                         }
746                                         break;
747                                 case 4: // N-Frame
748                                 default:
749                                         printf("unhandled divx5/xvid frame type %d\n", (data[pos] & 0xC0) >> 6);
750                                         break;
751                         }
752                 }
753 //              printf("\n");
754         }
755 #endif
756
757         payload_len = data_len + pes_header_len - 6;
758
759 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
760         if (self->prev_frame && self->prev_frame != buffer) {
761                 unsigned long long pts = GST_BUFFER_TIMESTAMP(self->prev_frame) * 9LL / 100000 /* convert ns to 90kHz */;
762 //              printf("use prev timestamp: %08llx\n", (long long)GST_BUFFER_TIMESTAMP(self->prev_frame));
763
764                 pes_header[9] =  0x21 | ((pts >> 29) & 0xE);
765                 pes_header[10] = pts >> 22;
766                 pes_header[11] = 0x01 | ((pts >> 14) & 0xFE);
767                 pes_header[12] = pts >> 7;
768                 pes_header[13] = 0x01 | ((pts << 1) & 0xFE);
769         }
770
771         if (commit_prev_frame_data)
772                 payload_len += GST_BUFFER_SIZE (self->prev_frame);
773 #endif
774
775         if (payload_len <= 0xFFFF) {
776                 pes_header[4] = payload_len >> 8;
777                 pes_header[5] = payload_len & 0xFF;
778         }
779         else {
780                 pes_header[4] = 0;
781                 pes_header[5] = 0;
782         }
783
784         write(self->fd, pes_header, pes_header_len);
785
786 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
787         if (commit_prev_frame_data) {
788 //              printf("commit prev frame data\n");
789                 write(self->fd, GST_BUFFER_DATA (self->prev_frame), GST_BUFFER_SIZE (self->prev_frame));
790         }
791
792         if (self->prev_frame && self->prev_frame != buffer) {
793 //              printf("unref prev_frame buffer\n");
794                 gst_buffer_unref(self->prev_frame);
795                 self->prev_frame = NULL;
796         }
797
798         if (cache_prev_frame) {
799 //              printf("cache prev frame\n");
800                 gst_buffer_ref(buffer);
801                 self->prev_frame = buffer;
802         }
803 #endif
804
805         write(self->fd, data, data_len);
806
807         return GST_FLOW_OK;
808 select_error:
809         {
810                 GST_ELEMENT_ERROR (self, RESOURCE, READ, (NULL),
811                                 ("select on file descriptor: %s.", g_strerror (errno)));
812                 GST_DEBUG_OBJECT (self, "Error during select");
813                 return GST_FLOW_ERROR;
814         }
815 stopped:
816         {
817                 GST_DEBUG_OBJECT (self, "Select stopped");
818                 ioctl(self->fd, VIDEO_CLEAR_BUFFER);
819                 return GST_FLOW_WRONG_STATE;
820         }
821 }
822
823 static gboolean 
824 gst_dvbvideosink_set_caps (GstBaseSink * basesink, GstCaps * caps)
825 {
826         GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
827         GstStructure *structure = gst_caps_get_structure (caps, 0);
828         const char *mimetype = gst_structure_get_name (structure);
829         int streamtype = -1;
830
831         if (!strcmp (mimetype, "video/mpeg")) {
832                 gint mpegversion;
833                 gst_structure_get_int (structure, "mpegversion", &mpegversion);
834                 switch (mpegversion) {
835                         case 1:
836                                 streamtype = 6;
837                                 printf("MIMETYPE video/mpeg1 -> VIDEO_SET_STREAMTYPE, 6\n");
838                         break;
839                         case 2:
840                                 streamtype = 0;
841                                 printf("MIMETYPE video/mpeg2 -> VIDEO_SET_STREAMTYPE, 0\n");
842                         break;
843                         case 4:
844                         {
845                                 const GValue *codec_data = gst_structure_get_value (structure, "codec_data");
846                                 if (codec_data) {
847                                         printf("MPEG4 have codec data\n");
848                                         self->codec_data = gst_value_get_buffer (codec_data);
849                                         self->codec_type = CT_MPEG4_PART2;
850                                         gst_buffer_ref (self->codec_data);
851                                 }
852                                 streamtype = 4;
853                                 printf("MIMETYPE video/mpeg4 -> VIDEO_SET_STREAMTYPE, 4\n");
854                         }
855                         break;
856                         default:
857                                 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unhandled mpeg version %i", mpegversion));
858                         break;
859                 }
860         } else if (!strcmp (mimetype, "video/x-h264")) {
861                 const GValue *cd_data = gst_structure_get_value (structure, "codec_data");
862                 streamtype = 1;
863                 if (cd_data) {
864                         unsigned char tmp[2048];
865                         unsigned int tmp_len = 0;
866                         GstBuffer *codec_data = gst_value_get_buffer (cd_data);
867                         unsigned char *data = GST_BUFFER_DATA (codec_data);
868                         unsigned int cd_len = GST_BUFFER_SIZE (codec_data);
869                         unsigned int cd_pos = 0;
870                         printf("H264 have codec data..!\n");
871 //                      printf("1\n");
872                         if (cd_len > 7) {
873                                 unsigned short len = (data[6] << 8) | data[7];
874 //                              printf("2 %d bytes\n", len);
875                                 if (cd_len >= (len + 8)) {
876 //                                      int i=0;
877 //                                      printf("3\n");
878                                         memcpy(tmp, "\x00\x00\x00\x01", 4);
879                                         tmp_len += 4;
880 //                                      printf("header part1 ");
881 //                                      for (i = 0; i < len; ++i)
882 //                                              printf("%02x ", data[8+i]);
883 //                                      printf("\n");
884                                         memcpy(tmp+tmp_len, data+8, len);
885                                         if (!memcmp(tmp+tmp_len, "\x67\x64\x00", 3)) {
886                                                 tmp[tmp_len+3] = 0x29; // hardcode h264 level 4.1
887                                                 printf("h264 level patched!\n");
888                                         }
889                                         tmp_len += len;
890                                         cd_pos = 8 + len;
891                                         if (cd_len > (cd_pos + 2)) {
892                                                 len = (data[cd_pos+1] << 8) | data[cd_pos+2];
893 //                                              printf("4 %d bytes\n", len);
894                                                 cd_pos += 3;
895                                                 if (cd_len >= (cd_pos+len)) {
896 //                                                      printf("codec data ok!\n");
897                                                         memcpy(tmp+tmp_len, "\x00\x00\x00\x01", 4);
898                                                         tmp_len += 4;
899 //                                                      printf("header part2 %02x %02x %02x %02x ... %d bytes\n", data[cd_pos], data[cd_pos+1], data[cd_pos+2], data[cd_pos+3], len);
900                                                         memcpy(tmp+tmp_len, data+cd_pos, len);
901                                                         tmp_len += len;
902                                                         self->codec_data = gst_buffer_new_and_alloc(tmp_len);
903                                                         memcpy(GST_BUFFER_DATA(self->codec_data), tmp, tmp_len);
904                                                 }
905                                                 else
906                                                         printf("codec_data to short(4)\n");
907                                         }
908                                         else
909                                                 printf("codec_data to short(3)!\n");
910                                 }
911                                 else
912                                         printf("codec_data to short(2)!\n");
913                         }
914                         else
915                                 printf("codec_data to short(1)!\n");
916                 }
917                 printf("MIMETYPE video/x-h264 VIDEO_SET_STREAMTYPE, 1\n");
918         } else if (!strcmp (mimetype, "video/x-h263")) {
919                 streamtype = 2;
920                 printf("MIMETYPE video/x-h263 VIDEO_SET_STREAMTYPE, 2\n");
921         } else if (!strcmp (mimetype, "video/x-xvid")) {
922                 streamtype = 10;
923 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
924                 self->must_pack_bitstream = 1;
925 #endif
926                 printf("MIMETYPE video/x-xvid -> VIDEO_SET_STREAMTYPE, 10\n");
927         } else if (!strcmp (mimetype, "video/x-divx") || !strcmp (mimetype, "video/x-msmpeg")) {
928                 gint divxversion = -1;
929                 if (!gst_structure_get_int (structure, "divxversion", &divxversion) && !gst_structure_get_int (structure, "msmpegversion", &divxversion))
930                         ;
931                 switch (divxversion) {
932                         case 3:
933                         case 43:
934                         {
935                                 #define B_GET_BITS(w,e,b)  (((w)>>(b))&(((unsigned)(-1))>>((sizeof(unsigned))*8-(e+1-b))))
936                                 #define B_SET_BITS(name,v,e,b)  (((unsigned)(v))<<(b))
937                                 static const guint8 brcm_divx311_sequence_header[] = {
938                                         0x00, 0x00, 0x01, 0xE0, 0x00, 0x34, 0x80, 0x80, // PES HEADER
939                                         0x05, 0x2F, 0xFF, 0xFF, 0xFF, 0xFF, 
940                                         0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x20, /* 0 .. 7 */
941                                         0x08, 0xC8, 0x0D, 0x40, 0x00, 0x53, 0x88, 0x40, /* 8 .. 15 */
942                                         0x0C, 0x40, 0x01, 0x90, 0x00, 0x97, 0x53, 0x0A, /* 16 .. 24 */
943                                         0x00, 0x00, 0x00, 0x00,
944                                         0x30, 0x7F, 0x00, 0x00, 0x01, 0xB2, 0x44, 0x69, /* 0 .. 7 */
945                                         0x76, 0x58, 0x33, 0x31, 0x31, 0x41, 0x4E, 0x44  /* 8 .. 15 */
946                                 };
947                                 self->codec_data = gst_buffer_new_and_alloc(63);
948                                 guint8 *data = GST_BUFFER_DATA(self->codec_data);
949                                 gint height, width;
950                                 gst_structure_get_int (structure, "height", &height);
951                                 gst_structure_get_int (structure, "width", &width);
952                                 memcpy(data, brcm_divx311_sequence_header, 63);
953                                 data += 38;
954                                 data[0] = B_GET_BITS(width,11,4);
955                                 data[1] = B_SET_BITS("width [3..0]", B_GET_BITS(width,3,0), 7, 4) |
956                                         B_SET_BITS("'10'", 0x02, 3, 2) |
957                                         B_SET_BITS("height [11..10]", B_GET_BITS(height,11,10), 1, 0);
958                                 data[2] = B_GET_BITS(height,9,2);
959                                 data[3]= B_SET_BITS("height [1.0]", B_GET_BITS(height,1,0), 7, 6) |
960                                         B_SET_BITS("'100000'", 0x20, 5, 0);
961                                 streamtype = 13;
962                                 self->codec_type = CT_DIVX311;
963                                 printf("MIMETYPE video/x-divx vers. 3 -> VIDEO_SET_STREAMTYPE, 13\n");
964                         }
965                         break;
966                         case 4:
967                                 streamtype = 14;
968                                 printf("MIMETYPE video/x-divx vers. 4 -> VIDEO_SET_STREAMTYPE, 14\n");
969                         break;
970                         case 6:
971                         case 5:
972                                 streamtype = 15;
973 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
974                                 self->must_pack_bitstream = 1;
975 #endif
976                                 printf("MIMETYPE video/x-divx vers. 5 -> VIDEO_SET_STREAMTYPE, 15\n");
977                         break;
978                         default:
979                                 GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unhandled divx version %i", divxversion));
980                         break;
981                 }
982         }
983         if (streamtype != -1) {
984                 gint numerator, denominator;
985                 if (gst_structure_get_fraction (structure, "framerate", &numerator, &denominator)) {
986                         FILE *f = fopen("/proc/stb/vmpeg/0/fallback_framerate", "w");
987                         if (f) {
988                                 int valid_framerates[] = { 23976, 24000, 25000, 29970, 30000, 50000, 59940, 60000 };
989                                 int framerate = (int)(((double)numerator * 1000) / denominator);
990                                 int diff = 60000;
991                                 int best = 0;
992                                 int i = 0;
993                                 for (; i < 7; ++i) {
994                                         int ndiff = abs(framerate - valid_framerates[i]);
995                                         if (ndiff < diff) {
996                                                 diff = ndiff;
997                                                 best = i;
998                                         }
999                                 }
1000                                 fprintf(f, "%d", valid_framerates[best]);
1001                                 fclose(f);
1002                         }
1003                 }
1004                 if (ioctl(self->fd, VIDEO_SET_STREAMTYPE, streamtype) < 0 )
1005                         if ( streamtype != 0 && streamtype != 6 )
1006                                 GST_ELEMENT_ERROR (self, STREAM, CODEC_NOT_FOUND, (NULL), ("hardware decoder can't handle streamtype %i", streamtype));
1007         } else
1008                 GST_ELEMENT_ERROR (self, STREAM, TYPE_NOT_FOUND, (NULL), ("unimplemented stream type %s", mimetype));
1009
1010         return TRUE;
1011 }
1012
1013 static int readMpegProc(char *str, int decoder)
1014 {
1015         int val = -1;
1016         char tmp[64];
1017         sprintf(tmp, "/proc/stb/vmpeg/%d/%s", decoder, str);
1018         FILE *f = fopen(tmp, "r");
1019         if (f)
1020         {
1021                 fscanf(f, "%x", &val);
1022                 fclose(f);
1023         }
1024         return val;
1025 }
1026
1027 static int readApiSize(int fd, int *xres, int *yres, int *aspect)
1028 {
1029         video_size_t size;
1030         if (!ioctl(fd, VIDEO_GET_SIZE, &size))
1031         {
1032                 *xres = size.w;
1033                 *yres = size.h;
1034                 *aspect = size.aspect_ratio == 0 ? 2 : 3;  // convert dvb api to etsi
1035                 return 0;
1036         }
1037         return -1;
1038 }
1039
1040 static int readApiFrameRate(int fd, int *framerate)
1041 {
1042         unsigned int frate;
1043         if (!ioctl(fd, VIDEO_GET_FRAME_RATE, &frate))
1044         {
1045                 *framerate = frate;
1046                 return 0;
1047         }
1048         return -1;
1049 }
1050
1051 static gboolean
1052 gst_dvbvideosink_start (GstBaseSink * basesink)
1053 {
1054         GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
1055         self->fd = open("/dev/dvb/adapter0/video0", O_RDWR);
1056 //      self->fd = open("/dump.pes", O_RDWR|O_CREAT|O_TRUNC, 0555);
1057
1058         gint control_sock[2];
1059
1060         if (socketpair (PF_UNIX, SOCK_STREAM, 0, control_sock) < 0)
1061                 goto socket_pair;
1062
1063         READ_SOCKET (self) = control_sock[0];
1064         WRITE_SOCKET (self) = control_sock[1];
1065
1066         fcntl (READ_SOCKET (self), F_SETFL, O_NONBLOCK);
1067         fcntl (WRITE_SOCKET (self), F_SETFL, O_NONBLOCK);
1068
1069         if (self->fd >= 0)
1070         {
1071                 GstStructure *s = 0;
1072                 GstMessage *msg = 0;
1073                 int aspect = -1, width = -1, height = -1, framerate = -1,
1074                         progressive = readMpegProc("progressive", 0);
1075
1076                 if (readApiSize(self->fd, &width, &height, &aspect) == -1)
1077                 {
1078                         aspect = readMpegProc("aspect", 0);
1079                         width = readMpegProc("xres", 0);
1080                         height = readMpegProc("yres", 0);
1081                 }
1082                 else
1083                         aspect = aspect == 0 ? 2 : 3; // dvb api to etsi
1084                 if (readApiFrameRate(self->fd, &framerate) == -1)
1085                         framerate = readMpegProc("framerate", 0);
1086
1087                 s = gst_structure_new ("eventSizeAvail",
1088                         "aspect_ratio", G_TYPE_INT, aspect == 0 ? 2 : 3,
1089                         "width", G_TYPE_INT, width,
1090                         "height", G_TYPE_INT, height, NULL);
1091                 msg = gst_message_new_element (GST_OBJECT (basesink), s);
1092                 gst_element_post_message (GST_ELEMENT (basesink), msg);
1093
1094                 s = gst_structure_new ("eventFrameRateAvail",
1095                         "frame_rate", G_TYPE_INT, framerate, NULL);
1096                 msg = gst_message_new_element (GST_OBJECT (basesink), s);
1097                 gst_element_post_message (GST_ELEMENT (basesink), msg);
1098
1099                 s = gst_structure_new ("eventProgressiveAvail",
1100                         "progressive", G_TYPE_INT, progressive, NULL);
1101                 msg = gst_message_new_element (GST_OBJECT (basesink), s);
1102                 gst_element_post_message (GST_ELEMENT (basesink), msg);
1103
1104                 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_MEMORY);
1105                 ioctl(self->fd, VIDEO_PLAY);
1106         }
1107
1108         return TRUE;
1109         /* ERRORS */
1110 socket_pair:
1111         {
1112                 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE, (NULL),
1113                                 GST_ERROR_SYSTEM);
1114                 return FALSE;
1115         }
1116 }
1117
1118 static gboolean
1119 gst_dvbvideosink_stop (GstBaseSink * basesink)
1120 {
1121         GstDVBVideoSink *self = GST_DVBVIDEOSINK (basesink);
1122         FILE *f = fopen("/proc/stb/vmpeg/0/fallback_framerate", "w");
1123         if (self->fd >= 0)
1124         {
1125                 ioctl(self->fd, VIDEO_STOP);
1126                 ioctl(self->fd, VIDEO_SELECT_SOURCE, VIDEO_SOURCE_DEMUX);
1127                 close(self->fd);
1128         }
1129
1130         close (READ_SOCKET (self));
1131         close (WRITE_SOCKET (self));
1132
1133         if (self->codec_data)
1134                 gst_buffer_unref(self->codec_data);
1135
1136 #ifdef PACK_UNPACKED_XVID_DIVX5_BITSTREAM
1137         if (self->prev_frame)
1138                 gst_buffer_unref(self->prev_frame);
1139 #endif
1140
1141         if (f) {
1142                 fputs(self->saved_fallback_framerate, f);
1143                 fclose(f);
1144         }
1145
1146         return TRUE;
1147 }
1148
1149 /* entry point to initialize the plug-in
1150  * initialize the plug-in itself
1151  * register the element factories and pad templates
1152  * register the features
1153  *
1154  * exchange the string 'plugin' with your elemnt name
1155  */
1156 static gboolean
1157 plugin_init (GstPlugin *plugin)
1158 {
1159         return gst_element_register (plugin, "dvbvideosink",
1160                                                  GST_RANK_PRIMARY,
1161                                                  GST_TYPE_DVBVIDEOSINK);
1162 }
1163
1164 /* this is the structure that gstreamer looks for to register plugins
1165  *
1166  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
1167  * description
1168  */
1169 GST_PLUGIN_DEFINE (
1170         GST_VERSION_MAJOR,
1171         GST_VERSION_MINOR,
1172         "dvb_video_out",
1173         "DVB Video Output",
1174         plugin_init,
1175         VERSION,
1176         "LGPL",
1177         "GStreamer",
1178         "http://gstreamer.net/"
1179 )