Prefer 32 bits over 24 bits output
[platform/upstream/gstreamer.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
171 static gboolean         gst_dvdec_src_query             (GstPad *pad, GstPadQueryType type,
172                                                          GstFormat *format, gint64 *value);
173 static gboolean         gst_dvdec_sink_convert          (GstPad *pad, GstFormat src_format, gint64 src_value,
174                                                          GstFormat *dest_format, gint64 *dest_value);
175 static gboolean         gst_dvdec_src_convert           (GstPad *pad, GstFormat src_format, gint64 src_value,
176                                                          GstFormat *dest_format, gint64 *dest_value);
177
178 static gboolean         gst_dvdec_handle_src_event      (GstPad *pad, GstEvent *event);
179
180 static void             gst_dvdec_loop                  (GstElement *element);
181
182 static GstElementStateReturn    
183                         gst_dvdec_change_state          (GstElement *element);
184
185 static void             gst_dvdec_set_property          (GObject *object, guint prop_id, 
186                                                          const GValue *value, GParamSpec *pspec);
187 static void             gst_dvdec_get_property          (GObject *object, guint prop_id, 
188                                                          GValue *value, GParamSpec *pspec);
189
190 /* The parent class pointer needs to be kept around for some object
191  * operations.
192  */
193 static GstElementClass *parent_class = NULL;
194
195 /* This array holds the ids of the signals registered for this object.
196  * The array indexes are based on the enum up above.
197  */
198 /*static guint gst_dvdec_signals[LAST_SIGNAL] = { 0 }; */
199
200 /* This function is used to register and subsequently return the type
201  * identifier for this object class.  On first invocation, it will
202  * register the type, providing the name of the class, struct sizes,
203  * and pointers to the various functions that define the class.
204  */
205 GType
206 gst_dvdec_get_type (void)
207 {
208   static GType dvdec_type = 0;
209
210   if (!dvdec_type) {
211     static const GTypeInfo dvdec_info = {
212       sizeof (GstDVDecClass),      
213       NULL,      
214       NULL,
215       (GClassInitFunc) gst_dvdec_class_init,
216       NULL,
217       NULL,
218       sizeof (GstDVDec),
219       0,
220       (GInstanceInitFunc) gst_dvdec_init,
221     };
222     dvdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstDVDec", &dvdec_info, 0);
223   }
224   return dvdec_type;
225 }
226
227 /* In order to create an instance of an object, the class must be
228  * initialized by this function.  GObject will take care of running
229  * it, based on the pointer to the function provided above.
230  */
231 static void
232 gst_dvdec_class_init (GstDVDecClass *klass)
233 {
234   /* Class pointers are needed to supply pointers to the private
235    * implementations of parent class methods.
236    */
237   GObjectClass *gobject_class;
238   GstElementClass *gstelement_class;
239
240   /* Since the dvdec class contains the parent classes, you can simply
241    * cast the pointer to get access to the parent classes.
242    */
243   gobject_class = (GObjectClass*) klass;
244   gstelement_class = (GstElementClass*) klass;
245
246   /* The parent class is needed for class method overrides. */
247   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
248
249   gobject_class->set_property = gst_dvdec_set_property;
250   gobject_class->get_property = gst_dvdec_get_property;
251
252   gstelement_class->change_state = gst_dvdec_change_state;
253
254   /* table initialization, only do once */
255   dv_init(0, 0);
256 }
257
258 /* This function is responsible for initializing a specific instance of
259  * the plugin.
260  */
261 static void
262 gst_dvdec_init(GstDVDec *dvdec)
263 {
264   gint i;
265
266   dvdec->sinkpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (sink_temp), "sink");
267   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->sinkpad);
268   gst_pad_set_query_function (dvdec->sinkpad, NULL);
269   gst_pad_set_convert_function (dvdec->sinkpad, gst_dvdec_sink_convert);
270
271   dvdec->videosrcpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (video_src_temp), "video");
272   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->videosrcpad);
273   gst_pad_set_query_function (dvdec->videosrcpad, gst_dvdec_src_query);
274   gst_pad_set_event_function (dvdec->videosrcpad, gst_dvdec_handle_src_event);
275   gst_pad_set_convert_function (dvdec->videosrcpad, gst_dvdec_src_convert);
276
277   dvdec->audiosrcpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET(audio_src_temp), "audio");
278   gst_element_add_pad(GST_ELEMENT(dvdec),dvdec->audiosrcpad);
279   gst_pad_set_query_function (dvdec->audiosrcpad, gst_dvdec_src_query);
280   gst_pad_set_event_function (dvdec->audiosrcpad, gst_dvdec_handle_src_event);
281   gst_pad_set_convert_function (dvdec->audiosrcpad, gst_dvdec_src_convert);
282
283   gst_element_set_loop_function (GST_ELEMENT (dvdec), gst_dvdec_loop);
284
285   dvdec->decoder = dv_decoder_new (0, 0, 0);
286   dvdec->decoder->quality = DV_QUALITY_BEST;
287   dvdec->pool = NULL;
288   dvdec->length = 0;
289   dvdec->next_ts = 0LL;
290   dvdec->need_discont = FALSE;
291   dvdec->framerate = 0;
292   dvdec->height = 0;
293
294   for (i = 0; i <4; i++) {
295     dvdec->audio_buffers[i] = (gint16 *)g_malloc (DV_AUDIO_MAX_SAMPLES * sizeof (gint16));
296   }
297 }
298
299 static gboolean
300 gst_dvdec_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
301                        GstFormat *dest_format, gint64 *dest_value)
302 {
303   gboolean res = TRUE;
304   GstDVDec *dvdec;
305   gint scale = 1;
306         
307   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
308
309   if (dvdec->length == 0)
310     return FALSE;
311
312   switch (src_format) {
313     case GST_FORMAT_BYTES:
314       switch (*dest_format) {
315         case GST_FORMAT_DEFAULT:
316           *dest_format = GST_FORMAT_TIME;
317         case GST_FORMAT_TIME:
318         default:
319           res = FALSE;
320       }
321       break;
322     case GST_FORMAT_TIME:
323       switch (*dest_format) {
324         case GST_FORMAT_DEFAULT:
325           *dest_format = GST_FORMAT_BYTES;
326         case GST_FORMAT_BYTES:
327           if (pad == dvdec->videosrcpad)
328             scale = 720 * dvdec->height * dvdec->bpp;
329           else if (pad == dvdec->audiosrcpad)
330             scale = dvdec->decoder->audio->num_channels * 2;
331           /* fallthrough */
332         case GST_FORMAT_UNITS:
333           if (pad == dvdec->videosrcpad)
334             *dest_value = src_value * dvdec->framerate * scale / GST_SECOND;
335           else if (pad == dvdec->audiosrcpad)
336             *dest_value = src_value * dvdec->decoder->audio->frequency * scale / GST_SECOND;
337           break;
338         default:
339           res = FALSE;
340       }
341       break;
342     default:
343       res = FALSE;
344   }
345   return res;
346 }
347
348 static gboolean
349 gst_dvdec_sink_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
350                         GstFormat *dest_format, gint64 *dest_value)
351 {
352   gboolean res = TRUE;
353   GstDVDec *dvdec;
354         
355   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
356
357   if (dvdec->length == 0)
358     return FALSE;
359
360   switch (src_format) {
361     case GST_FORMAT_BYTES:
362       switch (*dest_format) {
363         case GST_FORMAT_DEFAULT:
364           *dest_format = GST_FORMAT_TIME;
365         case GST_FORMAT_TIME:
366           *dest_value = src_value * GST_SECOND / (dvdec->length * dvdec->framerate); 
367           break;
368         default:
369           res = FALSE;
370       }
371       break;
372     case GST_FORMAT_TIME:
373       switch (*dest_format) {
374         case GST_FORMAT_DEFAULT:
375           *dest_format = GST_FORMAT_BYTES;
376         case GST_FORMAT_BYTES:
377         {
378           guint64 frame;
379           /* calculate the frame */
380           frame = src_value * dvdec->framerate / GST_SECOND;
381           /* calculate the offset */
382           *dest_value = frame * dvdec->length;
383           break;
384         }
385         default:
386           res = FALSE;
387       }
388       break;
389     default:
390       res = FALSE;
391   }
392   return res;
393 }
394
395 static gboolean
396 gst_dvdec_src_query (GstPad *pad, GstPadQueryType type,
397                      GstFormat *format, gint64 *value)
398 {
399   gboolean res = TRUE;
400   GstDVDec *dvdec;
401
402   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
403
404   switch (type) {
405     case GST_PAD_QUERY_TOTAL:
406       switch (*format) {
407         case GST_FORMAT_DEFAULT:
408           *format = GST_FORMAT_TIME;
409         default:
410         {
411           guint64 len;
412           GstFormat tmp_format;
413
414           len = gst_bytestream_length (dvdec->bs);
415           tmp_format = GST_FORMAT_TIME;
416           if (len == -1 || !gst_pad_convert (dvdec->sinkpad, GST_FORMAT_BYTES, len, &tmp_format, value)) {
417             return FALSE;
418           }
419           if (!gst_pad_convert (pad, GST_FORMAT_TIME, *value, format, value)) {
420             return FALSE;
421           }
422           break;
423         }
424       }
425       break;
426     case GST_PAD_QUERY_POSITION:
427       switch (*format) {
428         case GST_FORMAT_DEFAULT:
429           *format = GST_FORMAT_TIME;
430         default:
431           res = gst_pad_convert (pad, GST_FORMAT_TIME, dvdec->next_ts, format, value);
432           break;
433       }
434       break;
435     default:
436       res = FALSE;
437       break;
438   }
439   return res;
440
441
442 static gboolean
443 gst_dvdec_handle_sink_event (GstDVDec *dvdec)
444 {
445   guint32 remaining;
446   GstEvent *event;
447   GstEventType type;
448                 
449   gst_bytestream_get_status (dvdec->bs, &remaining, &event);
450                   
451   type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
452                       
453   switch (type) {
454     case GST_EVENT_EOS:
455       gst_pad_event_default (dvdec->sinkpad, event);
456       break;
457     case GST_EVENT_FLUSH:
458       break;
459     case GST_EVENT_DISCONTINUOUS:
460     {
461       gint i;
462       gboolean found = FALSE;
463       GstFormat format;
464
465       format = GST_FORMAT_TIME;
466       /* try to get a timestamp from the discont formats */
467       for (i = 0; i < GST_EVENT_DISCONT_OFFSET_LEN(event); i++) {
468         if (gst_pad_convert (dvdec->sinkpad, GST_EVENT_DISCONT_OFFSET(event,i).format, GST_EVENT_DISCONT_OFFSET(event,i).value,
469                                 &format, &dvdec->next_ts)) 
470         {
471           found = TRUE;
472           break;
473         }
474       }
475       /* assume 0 then */
476       if (!found) {
477         dvdec->next_ts = 0LL;
478       }
479       dvdec->need_discont = TRUE;
480       break;
481     }
482     default:
483       g_warning ("unhandled event %d\n", type);
484       break;
485   }
486   gst_event_free (event);
487   return TRUE;
488 }
489
490 static gboolean
491 gst_dvdec_handle_src_event (GstPad *pad, GstEvent *event)
492 {
493   gboolean res = TRUE;
494   GstDVDec *dvdec;
495
496   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
497  
498   switch (GST_EVENT_TYPE (event)) {
499     case GST_EVENT_SEEK:
500     {
501        gint64 position;
502        GstFormat format;
503
504        /* first bring the format to time */
505        format = GST_FORMAT_TIME;
506        if (!gst_pad_convert (pad, GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event),
507                         &format, &position)) {
508          /* could not convert seek format to byte offset */
509          res = FALSE;
510          break;
511        }
512        /* then try to figure out the byteoffset for this time */
513        format = GST_FORMAT_BYTES;
514        if (!gst_pad_convert (dvdec->sinkpad, GST_FORMAT_TIME, position,
515                         &format, &position)) {
516          /* could not convert seek format to byte offset */
517          res = FALSE;
518          break;
519        }
520        /* seek to offset */
521        if (!gst_bytestream_seek (dvdec->bs, position, GST_SEEK_METHOD_SET)) {
522          res = FALSE;
523        }
524        break;
525     }
526     default:
527       res = FALSE;
528       break;
529   }
530   return res;
531 }
532
533 static void
534 gst_dvdec_loop (GstElement *element)
535 {   
536   GstDVDec *dvdec;
537   GstBuffer *buf, *outbuf;
538   guint8 *inframe;
539   gint height;
540   guint32 length, got_bytes;
541   GstFormat format;
542   guint64 ts;
543
544   dvdec = GST_DVDEC (element);
545
546   /* first read enough bytes to parse the header */
547   got_bytes = gst_bytestream_peek_bytes (dvdec->bs, &inframe, header_size);
548   if (got_bytes < header_size) {
549     gst_dvdec_handle_sink_event (dvdec);
550     return;
551   }
552   dv_parse_header (dvdec->decoder, inframe);
553   /* after parsing the header we know the size of the data */
554   dvdec->PAL = dv_system_50_fields (dvdec->decoder);
555
556   dvdec->framerate = (dvdec->PAL ? 25 : 30);
557   dvdec->height = height = (dvdec->PAL ? PAL_HEIGHT : NTSC_HEIGHT);
558   length = (dvdec->PAL ? PAL_BUFFER : NTSC_BUFFER);
559
560   if (length != dvdec->length) {
561     dvdec->length = length;
562     gst_bytestream_size_hint (dvdec->bs, length);
563   }
564
565   /* then read the read data */
566   got_bytes = gst_bytestream_read (dvdec->bs, &buf, length);
567   if (got_bytes < length) {
568     gst_dvdec_handle_sink_event (dvdec);
569     return;
570   }
571
572   /* if we did not negotiate yet, do it now */
573   if (!GST_PAD_CAPS (dvdec->videosrcpad)) {
574     GstCaps *allowed;
575     GstCaps *trylist;
576     
577     /* we what we are allowed to do */
578     allowed = gst_pad_get_allowed_caps (dvdec->videosrcpad);
579
580     /* try to fix our height */
581     trylist = gst_caps_intersect (allowed,
582                                     GST_CAPS_NEW (
583                                       "dvdec_negotiate",
584                                       "video/raw",
585                                         "height",       GST_PROPS_INT (height)
586                                     ));
587     
588     /* prepare for looping */
589     trylist = gst_caps_normalize (trylist);
590
591     while (trylist) {
592       GstCaps *to_try = gst_caps_copy_1 (trylist);
593
594       /* try each format */
595       if (gst_pad_try_set_caps (dvdec->videosrcpad, to_try)) {
596         guint32 fourcc;
597
598         /* it worked, try to find what it was again */
599         gst_caps_get_fourcc_int (to_try, "format", &fourcc);
600
601         if (fourcc == GST_STR_FOURCC ("RGB ")) {
602           gint bpp;
603
604           gst_caps_get_int (to_try, "bpp", &bpp);
605           if (bpp == 24) {
606             dvdec->space = e_dv_color_rgb;
607             dvdec->bpp = 3;
608           }
609           else {
610             dvdec->space = e_dv_color_bgr0;
611             dvdec->bpp = 4;
612           }
613         }
614         else {
615           dvdec->space = e_dv_color_yuv;
616           dvdec->bpp = 2;
617         }
618         break;
619       }
620       trylist = trylist->next;
621     }
622     /* oops list exhausted an nothing was found... */
623     if (!trylist) {
624       gst_element_error (element, "could not negotiate");
625       return;
626     }
627   }
628
629   format = GST_FORMAT_TIME;
630   gst_pad_query (dvdec->videosrcpad, GST_PAD_QUERY_POSITION, &format, &ts);
631
632   if (GST_PAD_IS_CONNECTED (dvdec->audiosrcpad)) {
633     gint16 *a_ptr;
634     gint i, j;
635
636     dv_decode_full_audio (dvdec->decoder, GST_BUFFER_DATA (buf), dvdec->audio_buffers);
637
638     /* if we did not negotiate yet, do it now */
639     if (!GST_PAD_CAPS (dvdec->audiosrcpad)) {
640       gst_pad_try_set_caps (dvdec->audiosrcpad,
641                           GST_CAPS_NEW (
642                                   "dvdec_audio_caps",
643                                   "audio/raw",
644                                     "format",           GST_PROPS_STRING ("int"),
645                                     "rate",             GST_PROPS_INT (dvdec->decoder->audio->frequency),
646                                     "law",              GST_PROPS_INT (0),
647                                     "depth",            GST_PROPS_INT (16),
648                                     "width",            GST_PROPS_INT (16),
649                                     "signed",           GST_PROPS_BOOLEAN (TRUE),
650                                     "channels",         GST_PROPS_INT (dvdec->decoder->audio->num_channels),
651                                     "endianness",       GST_PROPS_INT (G_LITTLE_ENDIAN)
652                           ));
653     }
654
655     outbuf = gst_buffer_new ();
656     GST_BUFFER_SIZE (outbuf) = dvdec->decoder->audio->samples_this_frame * sizeof (gint16) * dvdec->decoder->audio->num_channels;
657     GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
658
659     a_ptr = (gint16 *) GST_BUFFER_DATA (outbuf);
660
661     for (i = 0; i < dvdec->decoder->audio->samples_this_frame; i++) {
662       for (j = 0; j < dvdec->decoder->audio->num_channels; j++) {
663         *(a_ptr++) = dvdec->audio_buffers[j][i];
664       }
665     }
666     GST_BUFFER_TIMESTAMP (outbuf) = ts;
667
668     if (dvdec->need_discont) {
669       GstEvent *discont;
670
671       discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, ts, NULL);
672       gst_pad_push (dvdec->audiosrcpad, GST_BUFFER (discont));
673     }
674
675     gst_pad_push (dvdec->audiosrcpad, outbuf);
676   }
677
678   if (GST_PAD_IS_CONNECTED (dvdec->videosrcpad)) {
679     guint8 *outframe;
680     guint8 *outframe_ptrs[3];
681     gint outframe_pitches[3];
682
683     /* try to grab a pool */
684     if (!dvdec->pool) {
685       dvdec->pool = gst_pad_get_bufferpool (dvdec->videosrcpad);
686     }
687
688     outbuf = NULL;
689     /* try to get a buffer from the pool if we have one */
690     if (dvdec->pool) {
691       outbuf = gst_buffer_new_from_pool (dvdec->pool, 0, 0);
692     }
693     /* no buffer from pool, allocate one ourselves */
694     if (!outbuf) {
695       outbuf = gst_buffer_new ();
696     
697       GST_BUFFER_SIZE (outbuf) = (720 * height) * dvdec->bpp;
698       GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
699     }
700     
701     outframe = GST_BUFFER_DATA (outbuf);
702
703     outframe_ptrs[0] = outframe;
704     outframe_pitches[0] = 720 * dvdec->bpp;
705
706     /* the rest only matters for YUY2 */
707     if (dvdec->bpp < 3) {
708       outframe_ptrs[1] = outframe_ptrs[0] + 720 * height;
709       outframe_ptrs[2] = outframe_ptrs[1] + 360 * height;
710   
711       outframe_pitches[1] = height / 2;
712       outframe_pitches[2] = outframe_pitches[1];
713     }
714
715     dv_decode_full_frame (dvdec->decoder, GST_BUFFER_DATA (buf), 
716                           dvdec->space, outframe_ptrs, outframe_pitches);
717
718     GST_BUFFER_TIMESTAMP (outbuf) = ts;
719
720     if (dvdec->need_discont) {
721       GstEvent *discont;
722
723       discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, ts, NULL);
724       gst_pad_push (dvdec->videosrcpad, GST_BUFFER (discont));
725     }
726
727     gst_pad_push (dvdec->videosrcpad, outbuf);
728   }
729
730   /* FIXME this is inaccurate for NTSC */
731   dvdec->next_ts += GST_SECOND / dvdec->framerate;
732
733   if (dvdec->need_discont) {
734     dvdec->need_discont = FALSE;
735   }
736
737   gst_buffer_unref (buf);
738 }
739
740 static GstElementStateReturn
741 gst_dvdec_change_state (GstElement *element)
742 {
743   GstDVDec *dvdec = GST_DVDEC (element);
744             
745   switch (GST_STATE_TRANSITION (element)) {
746     case GST_STATE_NULL_TO_READY:
747       break;
748     case GST_STATE_READY_TO_PAUSED:
749       dvdec->bs = gst_bytestream_new (dvdec->sinkpad);
750       break;
751     case GST_STATE_PAUSED_TO_PLAYING:
752       break;
753     case GST_STATE_PLAYING_TO_PAUSED:
754       dvdec->pool = NULL;
755       break;
756     case GST_STATE_PAUSED_TO_READY:
757       gst_bytestream_destroy (dvdec->bs);
758       break;
759     case GST_STATE_READY_TO_NULL:
760       break;
761     default:
762       break;
763   }
764               
765   parent_class->change_state (element);
766                 
767   return GST_STATE_SUCCESS;
768 }
769
770 /* Arguments are part of the Gtk+ object system, and these functions
771  * enable the element to respond to various arguments.
772  */
773 static void
774 gst_dvdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
775 {
776   GstDVDec *dvdec;
777
778   /* It's not null if we got it, but it might not be ours */
779   g_return_if_fail(GST_IS_DVDEC(object));
780
781   /* Get a pointer of the right type. */
782   dvdec = GST_DVDEC(object);
783
784   /* Check the argument id to see which argument we're setting. */
785   switch (prop_id) {
786     default:
787       break;
788   }
789 }
790
791 /* The set function is simply the inverse of the get fuction. */
792 static void
793 gst_dvdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
794 {
795   GstDVDec *dvdec;
796
797   /* It's not null if we got it, but it might not be ours */
798   g_return_if_fail(GST_IS_DVDEC(object));
799   dvdec = GST_DVDEC(object);
800
801   switch (prop_id) {
802     default:
803       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
804       break;
805   }
806 }
807
808 /* This is the entry into the plugin itself.  When the plugin loads,
809  * this function is called to register everything that the plugin provides.
810  */
811 static gboolean
812 plugin_init (GModule *module, GstPlugin *plugin)
813 {
814   GstElementFactory *factory;
815   GstTypeFactory *type;
816
817   if (!gst_library_load ("gstbytestream")) {
818     gst_info("dvdec: could not load support library: 'gstbytestream'\n");
819     return FALSE;
820   }
821
822   /* We need to create an ElementFactory for each element we provide.
823    * This consists of the name of the element, the GType identifier,
824    * and a pointer to the details structure at the top of the file.
825    */
826   factory = gst_element_factory_new("dvdec", GST_TYPE_DVDEC, &dvdec_details);
827   g_return_val_if_fail(factory != NULL, FALSE);
828
829   gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_PRIMARY);
830   /* The pad templates can be easily generated from the factories above,
831    * and then added to the list of padtemplates for the elementfactory.
832    * Note that the generated padtemplates are stored in static global
833    * variables, for the gst_dvdec_init function to use later on.
834    */
835   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(sink_temp));
836   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(video_src_temp));
837   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(audio_src_temp));
838
839   /* The very last thing is to register the elementfactory with the plugin. */
840   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
841
842   type = gst_type_factory_new (&dv_definition);
843   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
844
845   return TRUE;
846 }
847
848 GstPluginDesc plugin_desc = {
849   GST_VERSION_MAJOR,
850   GST_VERSION_MINOR,
851   "dvdec",
852   plugin_init
853 };
854