ws fixes
[platform/upstream/gst-plugins-good.git] / ext / dv / gstdvdec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <string.h>
21
22 /* First, include the header file for the plugin, to bring in the
23  * object definition and other useful things.
24  */
25 #include "gstdvdec.h"
26
27 #define NTSC_HEIGHT 480
28 #define NTSC_BUFFER 120000
29 #define PAL_HEIGHT 576
30 #define PAL_BUFFER 144000
31
32 /* The ElementDetails structure gives a human-readable description
33  * of the plugin, as well as author and version data.
34  */
35 static GstElementDetails dvdec_details = {
36   "DV (smpte314) decoder plugin",
37   "Decoder/DV",
38   "Uses libdv to decode DV video (libdv.sourceforge.net)",
39   VERSION,
40   "Erik Walthinsen <omega@cse.ogi.edu>\n"
41   "Wim Taymans <wim.taymans@tvd.be>",
42   "(C) 2001-2002",
43 };
44
45
46 /* These are the signals that this element can fire.  They are zero-
47  * based because the numbers themselves are private to the object.
48  * LAST_SIGNAL is used for initialization of the signal array.
49  */
50 enum {
51   ASDF,
52   /* FILL ME */
53   LAST_SIGNAL
54 };
55
56 /* Arguments are identified the same way, but cannot be zero, so you
57  * must leave the ARG_0 entry in as a placeholder.
58  */
59 enum {
60   ARG_0,
61   /* FILL ME */
62 };
63
64 /* The PadFactory structures describe what pads the element has or
65  * can have.  They can be quite complex, but for this dvdec plugin
66  * they are rather simple.
67  */
68 GST_PAD_TEMPLATE_FACTORY (sink_temp,
69   "sink",
70   GST_PAD_SINK,
71   GST_PAD_ALWAYS,
72   GST_CAPS_NEW (
73     "dv_dec_sink",
74     "video/dv",
75     "format",   GST_PROPS_LIST (
76                   GST_PROPS_STRING ("PAL"),
77                   GST_PROPS_STRING ("NTSC")
78                )
79   )
80 )
81
82
83 GST_PAD_TEMPLATE_FACTORY (video_src_temp,
84   "video",
85   GST_PAD_SRC,
86   GST_PAD_ALWAYS,
87   GST_CAPS_NEW (
88     "dv_dec_src",
89     "video/raw",
90     "format",           GST_PROPS_FOURCC (GST_MAKE_FOURCC ('Y','U','Y','2')),
91     "width",            GST_PROPS_INT (720),
92     "height",           GST_PROPS_INT_RANGE (NTSC_HEIGHT, PAL_HEIGHT)
93   ),
94   GST_CAPS_NEW (
95     "dv_dec_src",
96     "video/raw",
97     "format",           GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')),
98     "bpp",              GST_PROPS_INT(32),
99     "depth",            GST_PROPS_INT(32),
100     "endianness",       GST_PROPS_INT (G_LITTLE_ENDIAN),
101     "red_mask",         GST_PROPS_INT(0x00ff0000),
102     "green_mask",       GST_PROPS_INT(0x0000ff00),
103     "blue_mask",        GST_PROPS_INT(0x000000ff),
104     "width",            GST_PROPS_INT (720),
105     "height",           GST_PROPS_INT_RANGE (NTSC_HEIGHT, PAL_HEIGHT)
106   ),
107   GST_CAPS_NEW (
108     "dv_dec_src",
109     "video/raw",
110     "format",           GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')),
111     "bpp",              GST_PROPS_INT(24),
112     "depth",            GST_PROPS_INT(24),
113     "endianness",       GST_PROPS_INT (G_LITTLE_ENDIAN),
114     "red_mask",         GST_PROPS_INT(0x0000ff),
115     "green_mask",       GST_PROPS_INT(0x00ff00),
116     "blue_mask",        GST_PROPS_INT(0xff0000),
117     "width",            GST_PROPS_INT (720),
118     "height",           GST_PROPS_INT_RANGE (NTSC_HEIGHT, PAL_HEIGHT)
119   )
120 )
121
122 GST_PAD_TEMPLATE_FACTORY ( audio_src_temp,
123   "audio",
124   GST_PAD_SRC,
125   GST_PAD_ALWAYS,
126   GST_CAPS_NEW (
127     "arts_sample",
128     "audio/raw",
129     "format",           GST_PROPS_STRING ("int"),
130     "law",              GST_PROPS_INT (0),
131     "depth",            GST_PROPS_INT (16),
132     "width",            GST_PROPS_INT (16),
133     "signed",           GST_PROPS_BOOLEAN (TRUE),
134     "channels",         GST_PROPS_INT (2),
135     "endianness",       GST_PROPS_INT (G_LITTLE_ENDIAN)
136   )
137 )
138
139 /* typefind stuff */
140 static GstCaps*
141 dv_type_find (GstBuffer *buf, gpointer private)
142 {
143   gulong head = GULONG_FROM_BE(*((gulong *)GST_BUFFER_DATA(buf)));
144   GstCaps *new = NULL;
145
146   /* check for DIF  and DV flag */
147   if ((head & 0xffffff00) == 0x1f070000 && !(GST_BUFFER_DATA(buf)[4] & 0x01)) {
148     gchar *format;
149
150     if ((head & 0x000000ff) & 0x80)
151       format = "PAL";
152     else
153       format = "NTSC";
154     
155     new = GST_CAPS_NEW ("dv_type_find",
156                         "video/dv",
157                           "format",   GST_PROPS_STRING (format)
158                        );
159   }
160   return new;
161 }
162
163 static GstTypeDefinition dv_definition = {
164   "dv_video/dv", "video/dv", ".dv", dv_type_find 
165 };
166
167 /* A number of functon prototypes are given so we can refer to them later. */
168 static void             gst_dvdec_class_init            (GstDVDecClass *klass);
169 static void             gst_dvdec_init                  (GstDVDec *dvdec);
170 static void             gst_dvdec_destroy               (GObject *object);
171
172 static gboolean         gst_dvdec_src_query             (GstPad *pad, GstPadQueryType type,
173                                                          GstFormat *format, gint64 *value);
174 static gboolean         gst_dvdec_sink_convert          (GstPad *pad, GstFormat src_format, gint64 src_value,
175                                                          GstFormat *dest_format, gint64 *dest_value);
176 static gboolean         gst_dvdec_src_convert           (GstPad *pad, GstFormat src_format, gint64 src_value,
177                                                          GstFormat *dest_format, gint64 *dest_value);
178
179 static gboolean         gst_dvdec_handle_src_event      (GstPad *pad, GstEvent *event);
180
181 static void             gst_dvdec_loop                  (GstElement *element);
182
183 static GstElementStateReturn    
184                         gst_dvdec_change_state          (GstElement *element);
185
186 static void             gst_dvdec_set_property          (GObject *object, guint prop_id, 
187                                                          const GValue *value, GParamSpec *pspec);
188 static void             gst_dvdec_get_property          (GObject *object, guint prop_id, 
189                                                          GValue *value, GParamSpec *pspec);
190
191 /* The parent class pointer needs to be kept around for some object
192  * operations.
193  */
194 static GstElementClass *parent_class = NULL;
195
196 /* This array holds the ids of the signals registered for this object.
197  * The array indexes are based on the enum up above.
198  */
199 /*static guint gst_dvdec_signals[LAST_SIGNAL] = { 0 }; */
200
201 /* This function is used to register and subsequently return the type
202  * identifier for this object class.  On first invocation, it will
203  * register the type, providing the name of the class, struct sizes,
204  * and pointers to the various functions that define the class.
205  */
206 GType
207 gst_dvdec_get_type (void)
208 {
209   static GType dvdec_type = 0;
210
211   if (!dvdec_type) {
212     static const GTypeInfo dvdec_info = {
213       sizeof (GstDVDecClass),      
214       NULL,      
215       NULL,
216       (GClassInitFunc) gst_dvdec_class_init,
217       NULL,
218       NULL,
219       sizeof (GstDVDec),
220       0,
221       (GInstanceInitFunc) gst_dvdec_init,
222     };
223     dvdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstDVDec", &dvdec_info, 0);
224   }
225   return dvdec_type;
226 }
227
228 /* In order to create an instance of an object, the class must be
229  * initialized by this function.  GObject will take care of running
230  * it, based on the pointer to the function provided above.
231  */
232 static void
233 gst_dvdec_class_init (GstDVDecClass *klass)
234 {
235   /* Class pointers are needed to supply pointers to the private
236    * implementations of parent class methods.
237    */
238   GObjectClass *gobject_class;
239   GstElementClass *gstelement_class;
240
241   /* Since the dvdec class contains the parent classes, you can simply
242    * cast the pointer to get access to the parent classes.
243    */
244   gobject_class = (GObjectClass*) klass;
245   gstelement_class = (GstElementClass*) klass;
246
247   /* The parent class is needed for class method overrides. */
248   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
249
250   gobject_class->set_property = gst_dvdec_set_property;
251   gobject_class->get_property = gst_dvdec_get_property;
252   gobject_class->dispose = gst_dvdec_destroy;
253   
254   gstelement_class->change_state = gst_dvdec_change_state;
255
256   /* table initialization, only do once */
257   dv_init(0, 0);
258 }
259
260 static void
261 gst_dvdec_destroy (GObject *object)
262 {
263   GstDVDec *dvdec = GST_DVDEC (object);
264
265   dv_decoder_free (dvdec->decoder);
266 }
267
268 /* This function is responsible for initializing a specific instance of
269  * the plugin.
270  */
271 static void
272 gst_dvdec_init(GstDVDec *dvdec)
273 {
274   gint i;
275
276   dvdec->sinkpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (sink_temp), "sink");
277   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->sinkpad);
278   gst_pad_set_query_function (dvdec->sinkpad, NULL);
279   gst_pad_set_convert_function (dvdec->sinkpad, gst_dvdec_sink_convert);
280
281   dvdec->videosrcpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (video_src_temp), "video");
282   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->videosrcpad);
283   gst_pad_set_query_function (dvdec->videosrcpad, gst_dvdec_src_query);
284   gst_pad_set_event_function (dvdec->videosrcpad, gst_dvdec_handle_src_event);
285   gst_pad_set_convert_function (dvdec->videosrcpad, gst_dvdec_src_convert);
286
287   dvdec->audiosrcpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET(audio_src_temp), "audio");
288   gst_element_add_pad(GST_ELEMENT(dvdec),dvdec->audiosrcpad);
289   gst_pad_set_query_function (dvdec->audiosrcpad, gst_dvdec_src_query);
290   gst_pad_set_event_function (dvdec->audiosrcpad, gst_dvdec_handle_src_event);
291   gst_pad_set_convert_function (dvdec->audiosrcpad, gst_dvdec_src_convert);
292
293   gst_element_set_loop_function (GST_ELEMENT (dvdec), gst_dvdec_loop);
294
295   dvdec->decoder = dv_decoder_new (0, 0, 0);
296   dvdec->decoder->quality = DV_QUALITY_BEST;
297   dvdec->pool = NULL;
298   dvdec->length = 0;
299   dvdec->next_ts = 0LL;
300   dvdec->need_discont = FALSE;
301   dvdec->framerate = 0;
302   dvdec->height = 0;
303
304   for (i = 0; i <4; i++) {
305     dvdec->audio_buffers[i] = (gint16 *)g_malloc (DV_AUDIO_MAX_SAMPLES * sizeof (gint16));
306   }
307 }
308
309 static gboolean
310 gst_dvdec_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
311                        GstFormat *dest_format, gint64 *dest_value)
312 {
313   gboolean res = TRUE;
314   GstDVDec *dvdec;
315   gint scale = 1;
316         
317   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
318
319   if (dvdec->length == 0)
320     return FALSE;
321
322   switch (src_format) {
323     case GST_FORMAT_BYTES:
324       switch (*dest_format) {
325         case GST_FORMAT_DEFAULT:
326           *dest_format = GST_FORMAT_TIME;
327         case GST_FORMAT_TIME:
328         default:
329           res = FALSE;
330       }
331       break;
332     case GST_FORMAT_TIME:
333       switch (*dest_format) {
334         case GST_FORMAT_DEFAULT:
335           *dest_format = GST_FORMAT_BYTES;
336         case GST_FORMAT_BYTES:
337           if (pad == dvdec->videosrcpad)
338             scale = 720 * dvdec->height * dvdec->bpp;
339           else if (pad == dvdec->audiosrcpad)
340             scale = dvdec->decoder->audio->num_channels * 2;
341           /* fallthrough */
342         case GST_FORMAT_UNITS:
343           if (pad == dvdec->videosrcpad)
344             *dest_value = src_value * dvdec->framerate * scale / GST_SECOND;
345           else if (pad == dvdec->audiosrcpad)
346             *dest_value = src_value * dvdec->decoder->audio->frequency * scale / GST_SECOND;
347           break;
348         default:
349           res = FALSE;
350       }
351       break;
352     default:
353       res = FALSE;
354   }
355   return res;
356 }
357
358 static gboolean
359 gst_dvdec_sink_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
360                         GstFormat *dest_format, gint64 *dest_value)
361 {
362   gboolean res = TRUE;
363   GstDVDec *dvdec;
364         
365   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
366
367   if (dvdec->length == 0)
368     return FALSE;
369
370   switch (src_format) {
371     case GST_FORMAT_BYTES:
372       switch (*dest_format) {
373         case GST_FORMAT_DEFAULT:
374           *dest_format = GST_FORMAT_TIME;
375         case GST_FORMAT_TIME:
376           *dest_value = src_value * GST_SECOND / (dvdec->length * dvdec->framerate); 
377           break;
378         default:
379           res = FALSE;
380       }
381       break;
382     case GST_FORMAT_TIME:
383       switch (*dest_format) {
384         case GST_FORMAT_DEFAULT:
385           *dest_format = GST_FORMAT_BYTES;
386         case GST_FORMAT_BYTES:
387         {
388           guint64 frame;
389           /* calculate the frame */
390           frame = src_value * dvdec->framerate / GST_SECOND;
391           /* calculate the offset */
392           *dest_value = frame * dvdec->length;
393           break;
394         }
395         default:
396           res = FALSE;
397       }
398       break;
399     default:
400       res = FALSE;
401   }
402   return res;
403 }
404
405 static gboolean
406 gst_dvdec_src_query (GstPad *pad, GstPadQueryType type,
407                      GstFormat *format, gint64 *value)
408 {
409   gboolean res = TRUE;
410   GstDVDec *dvdec;
411
412   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
413
414   switch (type) {
415     case GST_PAD_QUERY_TOTAL:
416       switch (*format) {
417         case GST_FORMAT_DEFAULT:
418           *format = GST_FORMAT_TIME;
419         default:
420         {
421           guint64 len;
422           GstFormat tmp_format;
423
424           len = gst_bytestream_length (dvdec->bs);
425           tmp_format = GST_FORMAT_TIME;
426           if (len == -1 || !gst_pad_convert (dvdec->sinkpad, GST_FORMAT_BYTES, len, &tmp_format, value)) {
427             return FALSE;
428           }
429           if (!gst_pad_convert (pad, GST_FORMAT_TIME, *value, format, value)) {
430             return FALSE;
431           }
432           break;
433         }
434       }
435       break;
436     case GST_PAD_QUERY_POSITION:
437       switch (*format) {
438         case GST_FORMAT_DEFAULT:
439           *format = GST_FORMAT_TIME;
440         default:
441           res = gst_pad_convert (pad, GST_FORMAT_TIME, dvdec->next_ts, format, value);
442           break;
443       }
444       break;
445     default:
446       res = FALSE;
447       break;
448   }
449   return res;
450
451
452 static gboolean
453 gst_dvdec_handle_sink_event (GstDVDec *dvdec)
454 {
455   guint32 remaining;
456   GstEvent *event;
457   GstEventType type;
458                 
459   gst_bytestream_get_status (dvdec->bs, &remaining, &event);
460                   
461   type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
462                       
463   switch (type) {
464     case GST_EVENT_EOS:
465       gst_pad_event_default (dvdec->sinkpad, event);
466       return TRUE;
467     case GST_EVENT_FLUSH:
468       break;
469     case GST_EVENT_DISCONTINUOUS:
470     {
471       gint i;
472       gboolean found = FALSE;
473       GstFormat format;
474
475       format = GST_FORMAT_TIME;
476       /* try to get a timestamp from the discont formats */
477       for (i = 0; i < GST_EVENT_DISCONT_OFFSET_LEN(event); i++) {
478         if (gst_pad_convert (dvdec->sinkpad, GST_EVENT_DISCONT_OFFSET(event,i).format, GST_EVENT_DISCONT_OFFSET(event,i).value,
479                                 &format, &dvdec->next_ts)) 
480         {
481           found = TRUE;
482           break;
483         }
484       }
485       /* assume 0 then */
486       if (!found) {
487         dvdec->next_ts = 0LL;
488       }
489       dvdec->need_discont = TRUE;
490       break;
491     }
492     default:
493       g_warning ("unhandled event %d\n", type);
494       break;
495   }
496   gst_event_unref (event);
497   return TRUE;
498 }
499
500 static gboolean
501 gst_dvdec_handle_src_event (GstPad *pad, GstEvent *event)
502 {
503   gboolean res = TRUE;
504   GstDVDec *dvdec;
505
506   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
507  
508   switch (GST_EVENT_TYPE (event)) {
509     case GST_EVENT_SEEK:
510     {
511        gint64 position;
512        GstFormat format;
513
514        /* first bring the format to time */
515        format = GST_FORMAT_TIME;
516        if (!gst_pad_convert (pad, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event),
517                         &format, &position)) {
518          /* could not convert seek format to byte offset */
519          res = FALSE;
520          break;
521        }
522        /* then try to figure out the byteoffset for this time */
523        format = GST_FORMAT_BYTES;
524        if (!gst_pad_convert (dvdec->sinkpad, GST_FORMAT_TIME, position,
525                         &format, &position)) {
526          /* could not convert seek format to byte offset */
527          res = FALSE;
528          break;
529        }
530        /* seek to offset */
531        if (!gst_bytestream_seek (dvdec->bs, position, GST_SEEK_METHOD_SET)) {
532          res = FALSE;
533        }
534        break;
535     }
536     default:
537       res = FALSE;
538       break;
539   }
540   gst_event_unref (event);
541   return res;
542 }
543
544 static void
545 gst_dvdec_loop (GstElement *element)
546 {   
547   GstDVDec *dvdec;
548   GstBuffer *buf, *outbuf;
549   guint8 *inframe;
550   gint height;
551   guint32 length, got_bytes;
552   GstFormat format;
553   guint64 ts;
554
555   dvdec = GST_DVDEC (element);
556
557   /* first read enough bytes to parse the header */
558   got_bytes = gst_bytestream_peek_bytes (dvdec->bs, &inframe, header_size);
559   if (got_bytes < header_size) {
560     gst_dvdec_handle_sink_event (dvdec);
561     return;
562   }
563   dv_parse_header (dvdec->decoder, inframe);
564   /* after parsing the header we know the size of the data */
565   dvdec->PAL = dv_system_50_fields (dvdec->decoder);
566
567   dvdec->framerate = (dvdec->PAL ? 25 : 30);
568   dvdec->height = height = (dvdec->PAL ? PAL_HEIGHT : NTSC_HEIGHT);
569   length = (dvdec->PAL ? PAL_BUFFER : NTSC_BUFFER);
570
571   if (length != dvdec->length) {
572     dvdec->length = length;
573     gst_bytestream_size_hint (dvdec->bs, length);
574   }
575
576   /* then read the read data */
577   got_bytes = gst_bytestream_read (dvdec->bs, &buf, length);
578   if (got_bytes < length) {
579     gst_dvdec_handle_sink_event (dvdec);
580     return;
581   }
582
583   /* if we did not negotiate yet, do it now */
584   if (!GST_PAD_CAPS (dvdec->videosrcpad)) {
585     GstCaps *allowed;
586     GstCaps *trylist;
587     
588     /* we what we are allowed to do */
589     allowed = gst_pad_get_allowed_caps (dvdec->videosrcpad);
590
591     /* try to fix our height */
592     trylist = gst_caps_intersect (allowed,
593                                     GST_CAPS_NEW (
594                                       "dvdec_negotiate",
595                                       "video/raw",
596                                         "height",       GST_PROPS_INT (height)
597                                     ));
598     
599     /* prepare for looping */
600     trylist = gst_caps_normalize (trylist);
601
602     while (trylist) {
603       GstCaps *to_try = gst_caps_copy_1 (trylist);
604
605       /* try each format */
606       if (gst_pad_try_set_caps (dvdec->videosrcpad, to_try)) {
607         guint32 fourcc;
608
609         /* it worked, try to find what it was again */
610         gst_caps_get_fourcc_int (to_try, "format", &fourcc);
611
612         if (fourcc == GST_STR_FOURCC ("RGB ")) {
613           gint bpp;
614
615           gst_caps_get_int (to_try, "bpp", &bpp);
616           if (bpp == 24) {
617             dvdec->space = e_dv_color_rgb;
618             dvdec->bpp = 3;
619           }
620           else {
621             dvdec->space = e_dv_color_bgr0;
622             dvdec->bpp = 4;
623           }
624         }
625         else {
626           dvdec->space = e_dv_color_yuv;
627           dvdec->bpp = 2;
628         }
629         break;
630       }
631       trylist = trylist->next;
632     }
633     /* oops list exhausted an nothing was found... */
634     if (!trylist) {
635       gst_element_error (element, "could not negotiate");
636       return;
637     }
638   }
639
640   format = GST_FORMAT_TIME;
641   gst_pad_query (dvdec->videosrcpad, GST_PAD_QUERY_POSITION, &format, &ts);
642
643   if (GST_PAD_IS_CONNECTED (dvdec->audiosrcpad)) {
644     gint16 *a_ptr;
645     gint i, j;
646
647     dv_decode_full_audio (dvdec->decoder, GST_BUFFER_DATA (buf), dvdec->audio_buffers);
648
649     /* if we did not negotiate yet, do it now */
650     if (!GST_PAD_CAPS (dvdec->audiosrcpad)) {
651       gst_pad_try_set_caps (dvdec->audiosrcpad,
652                           GST_CAPS_NEW (
653                                   "dvdec_audio_caps",
654                                   "audio/raw",
655                                     "format",           GST_PROPS_STRING ("int"),
656                                     "rate",             GST_PROPS_INT (dvdec->decoder->audio->frequency),
657                                     "law",              GST_PROPS_INT (0),
658                                     "depth",            GST_PROPS_INT (16),
659                                     "width",            GST_PROPS_INT (16),
660                                     "signed",           GST_PROPS_BOOLEAN (TRUE),
661                                     "channels",         GST_PROPS_INT (dvdec->decoder->audio->num_channels),
662                                     "endianness",       GST_PROPS_INT (G_LITTLE_ENDIAN)
663                           ));
664     }
665
666     outbuf = gst_buffer_new ();
667     GST_BUFFER_SIZE (outbuf) = dvdec->decoder->audio->samples_this_frame * sizeof (gint16) * dvdec->decoder->audio->num_channels;
668     GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
669
670     a_ptr = (gint16 *) GST_BUFFER_DATA (outbuf);
671
672     for (i = 0; i < dvdec->decoder->audio->samples_this_frame; i++) {
673       for (j = 0; j < dvdec->decoder->audio->num_channels; j++) {
674         *(a_ptr++) = dvdec->audio_buffers[j][i];
675       }
676     }
677     GST_BUFFER_TIMESTAMP (outbuf) = ts;
678
679     if (dvdec->need_discont) {
680       GstEvent *discont;
681
682       discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, ts, NULL);
683       gst_pad_push (dvdec->audiosrcpad, GST_BUFFER (discont));
684     }
685
686     gst_pad_push (dvdec->audiosrcpad, outbuf);
687   }
688
689   if (GST_PAD_IS_CONNECTED (dvdec->videosrcpad)) {
690     guint8 *outframe;
691     guint8 *outframe_ptrs[3];
692     gint outframe_pitches[3];
693
694     /* try to grab a pool */
695     if (!dvdec->pool) {
696       dvdec->pool = gst_pad_get_bufferpool (dvdec->videosrcpad);
697     }
698
699     outbuf = NULL;
700     /* try to get a buffer from the pool if we have one */
701     if (dvdec->pool) {
702       outbuf = gst_buffer_new_from_pool (dvdec->pool, 0, 0);
703     }
704     /* no buffer from pool, allocate one ourselves */
705     if (!outbuf) {
706       outbuf = gst_buffer_new ();
707     
708       GST_BUFFER_SIZE (outbuf) = (720 * height) * dvdec->bpp;
709       GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
710     }
711     
712     outframe = GST_BUFFER_DATA (outbuf);
713
714     outframe_ptrs[0] = outframe;
715     outframe_pitches[0] = 720 * dvdec->bpp;
716
717     /* the rest only matters for YUY2 */
718     if (dvdec->bpp < 3) {
719       outframe_ptrs[1] = outframe_ptrs[0] + 720 * height;
720       outframe_ptrs[2] = outframe_ptrs[1] + 360 * height;
721   
722       outframe_pitches[1] = height / 2;
723       outframe_pitches[2] = outframe_pitches[1];
724     }
725
726     dv_decode_full_frame (dvdec->decoder, GST_BUFFER_DATA (buf), 
727                           dvdec->space, outframe_ptrs, outframe_pitches);
728
729     GST_BUFFER_TIMESTAMP (outbuf) = ts;
730
731     if (dvdec->need_discont) {
732       GstEvent *discont;
733
734       discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, ts, NULL);
735       gst_pad_push (dvdec->videosrcpad, GST_BUFFER (discont));
736     }
737
738     gst_pad_push (dvdec->videosrcpad, outbuf);
739   }
740
741   /* FIXME this is inaccurate for NTSC */
742   dvdec->next_ts += GST_SECOND / dvdec->framerate;
743
744   if (dvdec->need_discont) {
745     dvdec->need_discont = FALSE;
746   }
747
748   gst_buffer_unref (buf);
749 }
750
751 static GstElementStateReturn
752 gst_dvdec_change_state (GstElement *element)
753 {
754   GstDVDec *dvdec = GST_DVDEC (element);
755             
756   switch (GST_STATE_TRANSITION (element)) {
757     case GST_STATE_NULL_TO_READY:
758       break;
759     case GST_STATE_READY_TO_PAUSED:
760       dvdec->bs = gst_bytestream_new (dvdec->sinkpad);
761       break;
762     case GST_STATE_PAUSED_TO_PLAYING:
763       break;
764     case GST_STATE_PLAYING_TO_PAUSED:
765       if (dvdec->pool)
766         gst_buffer_pool_unref (dvdec->pool);
767       dvdec->pool = NULL;
768       break;
769     case GST_STATE_PAUSED_TO_READY:
770       gst_bytestream_destroy (dvdec->bs);
771       break;
772     case GST_STATE_READY_TO_NULL:
773       break;
774     default:
775       break;
776   }
777               
778   parent_class->change_state (element);
779                 
780   return GST_STATE_SUCCESS;
781 }
782
783 /* Arguments are part of the Gtk+ object system, and these functions
784  * enable the element to respond to various arguments.
785  */
786 static void
787 gst_dvdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
788 {
789   GstDVDec *dvdec;
790
791   /* It's not null if we got it, but it might not be ours */
792   g_return_if_fail(GST_IS_DVDEC(object));
793
794   /* Get a pointer of the right type. */
795   dvdec = GST_DVDEC(object);
796
797   /* Check the argument id to see which argument we're setting. */
798   switch (prop_id) {
799     default:
800       break;
801   }
802 }
803
804 /* The set function is simply the inverse of the get fuction. */
805 static void
806 gst_dvdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
807 {
808   GstDVDec *dvdec;
809
810   /* It's not null if we got it, but it might not be ours */
811   g_return_if_fail(GST_IS_DVDEC(object));
812   dvdec = GST_DVDEC(object);
813
814   switch (prop_id) {
815     default:
816       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
817       break;
818   }
819 }
820
821 /* This is the entry into the plugin itself.  When the plugin loads,
822  * this function is called to register everything that the plugin provides.
823  */
824 static gboolean
825 plugin_init (GModule *module, GstPlugin *plugin)
826 {
827   GstElementFactory *factory;
828   GstTypeFactory *type;
829
830   if (!gst_library_load ("gstbytestream")) {
831     gst_info("dvdec: could not load support library: 'gstbytestream'\n");
832     return FALSE;
833   }
834
835   /* We need to create an ElementFactory for each element we provide.
836    * This consists of the name of the element, the GType identifier,
837    * and a pointer to the details structure at the top of the file.
838    */
839   factory = gst_element_factory_new("dvdec", GST_TYPE_DVDEC, &dvdec_details);
840   g_return_val_if_fail(factory != NULL, FALSE);
841
842   gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_PRIMARY);
843   /* The pad templates can be easily generated from the factories above,
844    * and then added to the list of padtemplates for the elementfactory.
845    * Note that the generated padtemplates are stored in static global
846    * variables, for the gst_dvdec_init function to use later on.
847    */
848   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(sink_temp));
849   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(video_src_temp));
850   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(audio_src_temp));
851
852   /* The very last thing is to register the elementfactory with the plugin. */
853   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
854
855   type = gst_type_factory_new (&dv_definition);
856   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
857
858   return TRUE;
859 }
860
861 GstPluginDesc plugin_desc = {
862   GST_VERSION_MAJOR,
863   GST_VERSION_MINOR,
864   "dvdec",
865   plugin_init
866 };
867