change NULL to (NULL) for GST_ELEMENT_ERROR
[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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 #include <string.h>
24
25 /* First, include the header file for the plugin, to bring in the
26  * object definition and other useful things.
27  */
28 #include "gstdvdec.h"
29
30 #define NTSC_HEIGHT 480
31 #define NTSC_BUFFER 120000
32 #define NTSC_FRAMERATE 29.997
33
34 #define PAL_HEIGHT 576
35 #define PAL_BUFFER 144000
36 #define PAL_FRAMERATE 25.0
37
38 /* The ElementDetails structure gives a human-readable description
39  * of the plugin, as well as author and version data.
40  */
41 static GstElementDetails dvdec_details = GST_ELEMENT_DETAILS (
42   "DV (smpte314) decoder plugin",
43   "Codec/Decoder/Video",
44   "Uses libdv to decode DV video (libdv.sourceforge.net)",
45   "Erik Walthinsen <omega@cse.ogi.edu>\n"
46   "Wim Taymans <wim.taymans@tvd.be>"
47 );
48
49
50 /* These are the signals that this element can fire.  They are zero-
51  * based because the numbers themselves are private to the object.
52  * LAST_SIGNAL is used for initialization of the signal array.
53  */
54 enum {
55   /* FILL ME */
56   LAST_SIGNAL
57 };
58
59 /* Arguments are identified the same way, but cannot be zero, so you
60  * must leave the ARG_0 entry in as a placeholder.
61  */
62 enum {
63   ARG_0,
64   ARG_CLAMP_LUMA,
65   ARG_CLAMP_CHROMA,
66   ARG_QUALITY,
67   /* FILL ME */
68 };
69
70 /* The PadFactory structures describe what pads the element has or
71  * can have.  They can be quite complex, but for this dvdec plugin
72  * they are rather simple.
73  */
74 static GstStaticPadTemplate sink_temp =
75 GST_STATIC_PAD_TEMPLATE 
76 (
77   "sink" , 
78   GST_PAD_SINK,
79   GST_PAD_ALWAYS,
80   GST_STATIC_CAPS (
81     "video/x-dv, systemstream = (boolean) true"
82   )
83 );
84
85 static GstStaticPadTemplate video_src_temp =
86 GST_STATIC_PAD_TEMPLATE
87 (
88   "video",
89   GST_PAD_SRC,
90   GST_PAD_ALWAYS,
91   GST_STATIC_CAPS (
92     "video/x-raw-yuv, "
93     "format = (fourcc) YUY2, " 
94     "width = (int) 720, "
95     "height = (int) { " 
96         G_STRINGIFY(NTSC_HEIGHT) ", "
97         G_STRINGIFY(PAL_HEIGHT)
98     " }, "
99     "framerate = (double) { "
100         G_STRINGIFY(PAL_FRAMERATE) ", "
101         G_STRINGIFY(NTSC_FRAMERATE) 
102     " }; "
103
104     "video/x-raw-rgb, "
105     "bpp = (int) 32, "
106     "depth = (int) 32, "
107     "endianness = (int) " G_STRINGIFY(G_BIG_ENDIAN) ", "
108     "red_mask =   (int) 0x000000ff, "
109     "green_mask = (int) 0x0000ff00, "
110     "blue_mask =  (int) 0x00ff0000, "
111     "width = (int) 720, "
112     "height = (int) { "
113         G_STRINGIFY(NTSC_HEIGHT) ", "
114         G_STRINGIFY(PAL_HEIGHT)
115     " }, "
116     "framerate = (double) { "
117         G_STRINGIFY(PAL_FRAMERATE) ", "
118         G_STRINGIFY(NTSC_FRAMERATE) 
119     " }; "
120
121     "video/x-raw-rgb, "
122     "bpp = (int) 24, "
123     "depth = (int) 24, "
124     "endianness = (int) " G_STRINGIFY(G_BIG_ENDIAN) ", "
125     "red_mask =   (int) 0x000000ff, "
126     "green_mask = (int) 0x0000ff00, "
127     "blue_mask =  (int) 0x00ff0000, "
128     "width = (int) 720, "
129     "height = (int) { "
130         G_STRINGIFY(NTSC_HEIGHT) ", "
131         G_STRINGIFY(PAL_HEIGHT)
132     " }, "
133     "framerate = (double) { "
134         G_STRINGIFY(PAL_FRAMERATE) ", "
135         G_STRINGIFY(NTSC_FRAMERATE) 
136     " }"
137   )
138 );
139
140 static GstStaticPadTemplate audio_src_temp =
141 GST_STATIC_PAD_TEMPLATE
142 (
143   "audio",
144   GST_PAD_SRC,
145   GST_PAD_ALWAYS,
146   GST_STATIC_CAPS (
147     "audio/x-raw-int, "
148     "depth = (int) 16, "
149     "width = (int) 16, "
150     "signed = (boolean) TRUE, "
151     "channels = (int) 2, "
152     "endianness = (int) " G_STRINGIFY(G_LITTLE_ENDIAN) ", "
153     "rate = (int) [ 4000, 48000 ]"
154   )
155 );
156
157 #define GST_TYPE_DVDEC_QUALITY (gst_dvdec_quality_get_type())
158 GType
159 gst_dvdec_quality_get_type (void)
160 {
161   static GType qtype = 0;
162   if (qtype == 0) {
163     static const GFlagsValue values[] = {
164       { DV_QUALITY_COLOR, "DV_QUALITY_COLOR", "Color or monochrome decoding" },
165       { DV_QUALITY_AC_1,  "DV_QUALITY_AC_1",  "AC 1 something" },
166       { DV_QUALITY_AC_2,  "DV_QUALITY_AC_2",  "AC 2 something" },
167       { 0, NULL, NULL }
168     };
169     qtype = g_flags_register_static ("GstDVDecQualityFlags", values);
170   }
171   return qtype;
172 }
173
174 /* A number of functon prototypes are given so we can refer to them later. */
175 static void             gst_dvdec_base_init             (gpointer g_class);
176 static void             gst_dvdec_class_init            (GstDVDecClass *klass);
177 static void             gst_dvdec_init                  (GstDVDec *dvdec);
178
179 static const GstQueryType* 
180                         gst_dvdec_get_src_query_types   (GstPad *pad);
181 static gboolean         gst_dvdec_src_query             (GstPad *pad, GstQueryType type,
182                                                          GstFormat *format, gint64 *value);
183 static const GstFormat* gst_dvdec_get_formats           (GstPad *pad);
184 static gboolean         gst_dvdec_sink_convert          (GstPad *pad, GstFormat src_format, gint64 src_value,
185                                                          GstFormat *dest_format, gint64 *dest_value);
186 static gboolean         gst_dvdec_src_convert           (GstPad *pad, GstFormat src_format, gint64 src_value,
187                                                          GstFormat *dest_format, gint64 *dest_value);
188
189 static const GstEventMask*
190                         gst_dvdec_get_event_masks       (GstPad *pad);
191 static gboolean         gst_dvdec_handle_src_event      (GstPad *pad, GstEvent *event);
192
193 static void             gst_dvdec_loop                  (GstElement *element);
194
195 static GstElementStateReturn    
196                         gst_dvdec_change_state          (GstElement *element);
197
198 static void             gst_dvdec_set_property          (GObject *object, guint prop_id, 
199                                                          const GValue *value, GParamSpec *pspec);
200 static void             gst_dvdec_get_property          (GObject *object, guint prop_id, 
201                                                          GValue *value, GParamSpec *pspec);
202
203 /* The parent class pointer needs to be kept around for some object
204  * operations.
205  */
206 static GstElementClass *parent_class = NULL;
207
208 /* This array holds the ids of the signals registered for this object.
209  * The array indexes are based on the enum up above.
210  */
211 /*static guint gst_dvdec_signals[LAST_SIGNAL] = { 0 }; */
212
213 /* This function is used to register and subsequently return the type
214  * identifier for this object class.  On first invocation, it will
215  * register the type, providing the name of the class, struct sizes,
216  * and pointers to the various functions that define the class.
217  */
218 GType
219 gst_dvdec_get_type (void)
220 {
221   static GType dvdec_type = 0;
222
223   if (!dvdec_type) {
224     static const GTypeInfo dvdec_info = {
225       sizeof (GstDVDecClass),      
226       gst_dvdec_base_init,      
227       NULL,
228       (GClassInitFunc) gst_dvdec_class_init,
229       NULL,
230       NULL,
231       sizeof (GstDVDec),
232       0,
233       (GInstanceInitFunc) gst_dvdec_init,
234     };
235     dvdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstDVDec", &dvdec_info, 0);
236   }
237   return dvdec_type;
238 }
239
240 static void
241 gst_dvdec_base_init (gpointer g_class)
242 {
243   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
244
245   /* The pad templates can be easily generated from the factories above,
246    * and then added to the list of padtemplates for the elementfactory.
247    * Note that the generated padtemplates are stored in static global
248    * variables, for the gst_dvdec_init function to use later on.
249    */
250   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get(&sink_temp));
251   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get(&video_src_temp));
252   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get(&audio_src_temp));
253
254   gst_element_class_set_details (element_class, &dvdec_details);
255 }
256
257 /* In order to create an instance of an object, the class must be
258  * initialized by this function.  GObject will take care of running
259  * it, based on the pointer to the function provided above.
260  */
261 static void
262 gst_dvdec_class_init (GstDVDecClass *klass)
263 {
264   /* Class pointers are needed to supply pointers to the private
265    * implementations of parent class methods.
266    */
267   GObjectClass *gobject_class;
268   GstElementClass *gstelement_class;
269
270   /* Since the dvdec class contains the parent classes, you can simply
271    * cast the pointer to get access to the parent classes.
272    */
273   gobject_class = (GObjectClass*) klass;
274   gstelement_class = (GstElementClass*) klass;
275
276   /* The parent class is needed for class method overrides. */
277   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
278
279   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CLAMP_LUMA,
280     g_param_spec_boolean ("clamp_luma", "Clamp luma", "Clamp luma",
281                           FALSE, G_PARAM_READWRITE));
282   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CLAMP_CHROMA,
283     g_param_spec_boolean ("clamp_chroma", "Clamp chroma", "Clamp chroma",
284                           FALSE, G_PARAM_READWRITE));
285   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
286     g_param_spec_flags ("quality", "Quality", "Decoding quality",
287                         GST_TYPE_DVDEC_QUALITY, DV_QUALITY_BEST, G_PARAM_READWRITE));
288
289   gobject_class->set_property = gst_dvdec_set_property;
290   gobject_class->get_property = gst_dvdec_get_property;
291   
292   gstelement_class->change_state = gst_dvdec_change_state;
293
294   /* table initialization, only do once */
295   dv_init(0, 0);
296 }
297
298 /* This function is responsible for initializing a specific instance of
299  * the plugin.
300  */
301 static void
302 gst_dvdec_init(GstDVDec *dvdec)
303 {
304   gint i;
305
306   dvdec->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_temp), "sink");
307   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->sinkpad);
308   gst_pad_set_query_function (dvdec->sinkpad, NULL);
309   gst_pad_set_convert_function (dvdec->sinkpad, GST_DEBUG_FUNCPTR (gst_dvdec_sink_convert));
310   gst_pad_set_formats_function (dvdec->sinkpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_formats));
311
312   dvdec->videosrcpad = gst_pad_new_from_template (gst_static_pad_template_get (&video_src_temp), "video");
313   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->videosrcpad);
314   gst_pad_set_query_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_query));
315   gst_pad_set_query_type_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_src_query_types));
316   gst_pad_set_event_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_handle_src_event));
317   gst_pad_set_event_mask_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_event_masks));
318   gst_pad_set_convert_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_convert));
319   gst_pad_set_formats_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_formats));
320
321   dvdec->audiosrcpad = gst_pad_new_from_template (gst_static_pad_template_get (&audio_src_temp), "audio");
322   gst_element_add_pad(GST_ELEMENT(dvdec),dvdec->audiosrcpad);
323   gst_pad_set_query_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_query));
324   gst_pad_set_query_type_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_src_query_types));
325   gst_pad_set_event_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_handle_src_event));
326   gst_pad_set_event_mask_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_event_masks));
327   gst_pad_set_convert_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_convert));
328   gst_pad_set_formats_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_formats));
329
330   gst_element_set_loop_function (GST_ELEMENT (dvdec), gst_dvdec_loop);
331
332   dvdec->length = 0;
333   dvdec->next_ts = 0LL;
334   dvdec->end_position = -1LL;
335   dvdec->need_discont = FALSE;
336   dvdec->framerate = 0;
337   dvdec->height = 0;
338   dvdec->clamp_luma = FALSE;
339   dvdec->clamp_chroma = FALSE;
340   dvdec->quality = DV_QUALITY_BEST;
341   dvdec->loop = FALSE;
342
343   for (i = 0; i <4; i++) {
344     dvdec->audio_buffers[i] = (gint16 *)g_malloc (DV_AUDIO_MAX_SAMPLES * sizeof (gint16));
345   }
346 }
347
348 static const GstFormat*
349 gst_dvdec_get_formats (GstPad *pad)
350 {
351   static const GstFormat src_formats[] = {
352     GST_FORMAT_BYTES,
353     GST_FORMAT_DEFAULT,
354     GST_FORMAT_TIME,
355     0
356   };
357   static const GstFormat sink_formats[] = {
358     GST_FORMAT_BYTES,
359     GST_FORMAT_TIME,
360     0
361   };
362              
363   return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
364
365
366 static gboolean
367 gst_dvdec_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
368                        GstFormat *dest_format, gint64 *dest_value)
369 {
370   gboolean res = TRUE;
371   GstDVDec *dvdec;
372   gint scale = 1;
373         
374   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
375
376   if (dvdec->length == 0)
377     return FALSE;
378
379   if (dvdec->decoder == NULL)
380     return FALSE;
381
382   switch (src_format) {
383     case GST_FORMAT_BYTES:
384       switch (*dest_format) {
385         case GST_FORMAT_TIME:
386         default:
387           res = FALSE;
388       }
389       break;
390     case GST_FORMAT_TIME:
391       switch (*dest_format) {
392         case GST_FORMAT_BYTES:
393           if (pad == dvdec->videosrcpad)
394             scale = 720 * dvdec->height * dvdec->bpp;
395           else if (pad == dvdec->audiosrcpad)
396             scale = dvdec->decoder->audio->num_channels * 2;
397           /* fallthrough */
398         case GST_FORMAT_DEFAULT:
399           if (pad == dvdec->videosrcpad)
400             *dest_value = src_value * dvdec->framerate * scale / (GST_SECOND*100);
401           else if (pad == dvdec->audiosrcpad)
402             *dest_value = src_value * dvdec->decoder->audio->frequency * scale / GST_SECOND;
403           break;
404         default:
405           res = FALSE;
406       }
407       break;
408     default:
409       res = FALSE;
410   }
411   return res;
412 }
413
414 static gboolean
415 gst_dvdec_sink_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
416                         GstFormat *dest_format, gint64 *dest_value)
417 {
418   gboolean res = TRUE;
419   GstDVDec *dvdec;
420         
421   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
422
423   if (dvdec->length == 0)
424     return FALSE;
425
426   switch (src_format) {
427     case GST_FORMAT_BYTES:
428       switch (*dest_format) {
429         case GST_FORMAT_TIME:
430         {
431           guint64 frame;
432           /* get frame number */
433           frame = src_value / dvdec->length;
434
435           *dest_value = (frame * GST_SECOND * 100) / dvdec->framerate;
436           break;
437         }
438         default:
439           res = FALSE;
440       }
441       break;
442     case GST_FORMAT_TIME:
443       switch (*dest_format) {
444         case GST_FORMAT_BYTES:
445         {
446           guint64 frame;
447           /* calculate the frame */
448           frame = src_value * dvdec->framerate / (GST_SECOND*100);
449           /* calculate the offset */
450           *dest_value = frame * dvdec->length;
451           break;
452         }
453         default:
454           res = FALSE;
455       }
456       break;
457     default:
458       res = FALSE;
459   }
460   return res;
461 }
462
463 static const GstQueryType*
464 gst_dvdec_get_src_query_types (GstPad *pad)
465 {   
466   static const GstQueryType src_query_types[] = {
467     GST_QUERY_TOTAL,
468     GST_QUERY_POSITION,
469     0
470   };
471   return src_query_types;
472 }
473
474 static gboolean
475 gst_dvdec_src_query (GstPad *pad, GstQueryType type,
476                      GstFormat *format, gint64 *value)
477 {
478   gboolean res = TRUE;
479   GstDVDec *dvdec;
480
481   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
482
483   switch (type) {
484     case GST_QUERY_TOTAL:
485       switch (*format) {
486         default:
487         {
488           guint64 len;
489           GstFormat tmp_format;
490
491           len = gst_bytestream_length (dvdec->bs);
492           tmp_format = GST_FORMAT_TIME;
493           if (len == -1 || !gst_pad_convert (dvdec->sinkpad, 
494                                              GST_FORMAT_BYTES, 
495                                              len, 
496                                              &tmp_format, value)) 
497           {
498             return FALSE;
499           }
500           if (!gst_pad_convert (pad, GST_FORMAT_TIME, *value, format, value)) {
501             return FALSE;
502           }
503           break;
504         }
505       }
506       break;
507     case GST_QUERY_POSITION:
508       switch (*format) {
509         default:
510           res = gst_pad_convert (pad, GST_FORMAT_TIME, dvdec->next_ts, format, value);
511           break;
512       }
513       break;
514     default:
515       res = FALSE;
516       break;
517   }
518   return res;
519
520
521 static const GstEventMask*
522 gst_dvdec_get_event_masks (GstPad *pad)
523
524   static const GstEventMask src_event_masks[] = {
525     { GST_EVENT_SEEK, GST_SEEK_METHOD_SET |
526                       GST_SEEK_FLAG_FLUSH },
527     { 0, }
528   };
529   static const GstEventMask sink_event_masks[] = {
530     { GST_EVENT_EOS, 0 },
531     { GST_EVENT_DISCONTINUOUS, 0 },
532     { GST_EVENT_FLUSH, 0 },
533     { 0, }
534   };
535
536   return (GST_PAD_IS_SRC (pad) ? src_event_masks : sink_event_masks);
537
538
539 static gboolean
540 gst_dvdec_handle_sink_event (GstDVDec *dvdec)
541 {
542   guint32 remaining;
543   GstEvent *event;
544   GstEventType type;
545                 
546   gst_bytestream_get_status (dvdec->bs, &remaining, &event);
547                   
548   type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
549                       
550   switch (type) {
551     case GST_EVENT_EOS:
552       gst_pad_event_default (dvdec->sinkpad, event);
553       return TRUE;
554     case GST_EVENT_FLUSH:
555       break;
556     case GST_EVENT_DISCONTINUOUS:
557     {
558       gint i;
559       gboolean found = FALSE;
560       GstFormat format;
561
562       format = GST_FORMAT_TIME;
563       /* try to get a timestamp from the discont formats */
564       for (i = 0; i < GST_EVENT_DISCONT_OFFSET_LEN(event); i++) {
565         if (gst_pad_convert (dvdec->sinkpad, 
566                              GST_EVENT_DISCONT_OFFSET(event,i).format, 
567                              GST_EVENT_DISCONT_OFFSET(event,i).value,
568                              &format, &dvdec->next_ts)) 
569         {
570           found = TRUE;
571           break;
572         }
573       }
574       /* assume 0 then */
575       if (!found) {
576         dvdec->next_ts = 0LL;
577       }
578       dvdec->need_discont = TRUE;
579       break;
580     }
581     default:
582       g_warning ("unhandled event %d\n", type);
583       break;
584   }
585   gst_event_unref (event);
586   return TRUE;
587 }
588
589 static gboolean
590 gst_dvdec_handle_src_event (GstPad *pad, GstEvent *event)
591 {
592   gboolean res = TRUE;
593   GstDVDec *dvdec;
594
595   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
596  
597   switch (GST_EVENT_TYPE (event)) {
598     case GST_EVENT_SEEK_SEGMENT:
599     {
600       gint64 position;
601       GstFormat format;
602
603       /* first bring the format to time */
604       format = GST_FORMAT_TIME;
605       if (!gst_pad_convert (pad, 
606                             GST_EVENT_SEEK_FORMAT (event), 
607                             GST_EVENT_SEEK_ENDOFFSET (event),
608                             &format, &position)) 
609       {
610         /* could not convert seek format to time offset */
611         res = FALSE;
612         break;
613       }
614
615       dvdec->end_position = position;
616       dvdec->loop = GST_EVENT_SEEK_TYPE (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
617     }
618     case GST_EVENT_SEEK:
619     {
620       gint64 position;
621       GstFormat format;
622
623       /* first bring the format to time */
624       format = GST_FORMAT_TIME;
625       if (!gst_pad_convert (pad, 
626                             GST_EVENT_SEEK_FORMAT (event), 
627                             GST_EVENT_SEEK_OFFSET (event),
628                             &format, &position)) 
629       {
630         /* could not convert seek format to time offset */
631         res = FALSE;
632         break;
633       }
634       dvdec->next_ts = position;
635       /* then try to figure out the byteoffset for this time */
636       format = GST_FORMAT_BYTES;
637       if (!gst_pad_convert (dvdec->sinkpad, GST_FORMAT_TIME, position,
638                         &format, &position)) 
639       {
640         /* could not convert seek format to byte offset */
641         res = FALSE;
642         break;
643       }
644       /* seek to offset */
645       if (!gst_bytestream_seek (dvdec->bs, position, GST_SEEK_METHOD_SET)) {
646         res = FALSE;
647       }
648       if (GST_EVENT_TYPE (event) != GST_EVENT_SEEK_SEGMENT)
649         dvdec->end_position = -1;
650       break;
651     }
652     default:
653       res = FALSE;
654       break;
655   }
656   gst_event_unref (event);
657   return res;
658 }
659
660 static void
661 gst_dvdec_push (GstDVDec *dvdec, GstBuffer *outbuf, GstPad *pad, GstClockTime ts)
662 {   
663   GST_BUFFER_TIMESTAMP (outbuf) = ts;
664
665   if (dvdec->need_discont) {
666     GstEvent *discont;
667
668     discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, ts, NULL);
669     gst_pad_push (pad, GST_DATA (discont));
670   }
671
672   gst_pad_push (pad, GST_DATA (outbuf));
673
674   if ((dvdec->end_position != -1) &&
675       (dvdec->next_ts >= dvdec->end_position)) {
676     if (dvdec->loop) 
677       gst_pad_push (pad, GST_DATA(gst_event_new (GST_EVENT_SEGMENT_DONE)));
678     else
679       gst_pad_push (pad, GST_DATA(gst_event_new (GST_EVENT_EOS)));
680   }
681 }
682
683 static void
684 gst_dvdec_loop (GstElement *element)
685 {   
686   GstDVDec *dvdec;
687   GstBuffer *buf, *outbuf;
688   guint8 *inframe;
689   gint height;
690   guint32 length, got_bytes;
691   GstFormat format;
692   guint64 ts;
693   gfloat fps;
694
695   dvdec = GST_DVDEC (element);
696
697   /* first read enough bytes to parse the header */
698   got_bytes = gst_bytestream_peek_bytes (dvdec->bs, &inframe, header_size);
699   if (got_bytes < header_size) {
700     gst_dvdec_handle_sink_event (dvdec);
701     return;
702   }
703   dv_parse_header (dvdec->decoder, inframe);
704   /* after parsing the header we know the size of the data */
705   dvdec->PAL = dv_system_50_fields (dvdec->decoder);
706
707   dvdec->framerate = (dvdec->PAL ? 2500 : 2997);
708   fps = (dvdec->PAL ? PAL_FRAMERATE : NTSC_FRAMERATE);
709   dvdec->height = height = (dvdec->PAL ? PAL_HEIGHT : NTSC_HEIGHT);
710   length = (dvdec->PAL ? PAL_BUFFER : NTSC_BUFFER);
711
712   if (length != dvdec->length) {
713     dvdec->length = length;
714     gst_bytestream_size_hint (dvdec->bs, length);
715   }
716
717   /* then read the read data */
718   got_bytes = gst_bytestream_read (dvdec->bs, &buf, length);
719   if (got_bytes < length) {
720     gst_dvdec_handle_sink_event (dvdec);
721     return;
722   }
723
724   /* if we did not negotiate yet, do it now */
725   if (!GST_PAD_CAPS (dvdec->videosrcpad)) {
726     GstCaps *caps = NULL;
727     GstCaps *negotiated_caps = NULL;
728     GstPadTemplate *src_pad_template;
729     int i;
730     
731     /* try to fix our height */
732     src_pad_template = gst_static_pad_template_get (&video_src_temp);
733     caps = gst_caps_copy(gst_pad_template_get_caps (src_pad_template));
734
735     for (i = 0; i < gst_caps_get_size (caps); i++)
736     {
737         GstStructure *structure = gst_caps_get_structure (caps, i);
738         gst_structure_set(structure, 
739                 "height", G_TYPE_INT, height,
740                 "framerate", G_TYPE_INT, fps, NULL
741               );
742     }
743
744     for (i=0; i < gst_caps_get_size(caps); i++) {
745       GstStructure *to_try_struct = gst_caps_get_structure (caps, i);
746       GstCaps *try_caps = 
747         gst_caps_new_full (gst_structure_copy(to_try_struct), NULL);
748
749       /* try each format */
750       if (gst_pad_try_set_caps (dvdec->videosrcpad, try_caps) > 0) {
751         negotiated_caps = try_caps;
752         break;
753       }
754
755       gst_caps_free(try_caps);
756     }
757
758     gst_caps_free (caps);
759
760     /* Check if we negotiated caps successfully */
761     if (negotiated_caps != NULL) {
762         GstStructure *structure = gst_caps_get_structure (negotiated_caps, 0);
763         guint32 fourcc;
764
765         /* it worked, try to find what it was again */
766         gst_structure_get_fourcc (structure, "format", &fourcc);
767
768         if (fourcc == GST_STR_FOURCC ("RGB ")) {
769           gint bpp;
770
771           gst_structure_get_int (structure, "bpp", &bpp);
772           if (bpp == 24) {
773             dvdec->space = e_dv_color_rgb;
774             dvdec->bpp = 3;
775           }
776           else {
777             dvdec->space = e_dv_color_bgr0;
778             dvdec->bpp = 4;
779           }
780         }
781         else {
782           dvdec->space = e_dv_color_yuv;
783           dvdec->bpp = 2;
784         }
785     } else {
786       GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL), (NULL));
787       return;
788     }
789   }
790
791   format = GST_FORMAT_TIME;
792   gst_pad_query (dvdec->videosrcpad, GST_QUERY_POSITION, &format, &ts);
793
794   dvdec->next_ts += (GST_SECOND*100) / dvdec->framerate;
795
796   if (GST_PAD_IS_LINKED (dvdec->audiosrcpad)) {
797     gint16 *a_ptr;
798     gint i, j;
799
800     dv_decode_full_audio (dvdec->decoder, GST_BUFFER_DATA (buf), dvdec->audio_buffers);
801
802     /* if we did not negotiate yet, do it now */
803     if (!GST_PAD_CAPS (dvdec->audiosrcpad)) {
804       gst_pad_try_set_caps (dvdec->audiosrcpad,
805         gst_caps_new_simple (
806           "audio/x-raw-int", 
807             "rate", G_TYPE_INT, dvdec->decoder->audio->frequency,
808             "depth", G_TYPE_INT,  16,
809             "width", G_TYPE_INT,  16,
810             "signed", G_TYPE_BOOLEAN, TRUE,
811             "channels", G_TYPE_INT, dvdec->decoder->audio->num_channels,
812             "endianness", G_TYPE_INT, G_LITTLE_ENDIAN
813           ));
814     }
815
816     outbuf = gst_buffer_new ();
817     GST_BUFFER_SIZE (outbuf) = dvdec->decoder->audio->samples_this_frame * 
818                                sizeof (gint16) * dvdec->decoder->audio->num_channels;
819     GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
820
821     a_ptr = (gint16 *) GST_BUFFER_DATA (outbuf);
822
823     for (i = 0; i < dvdec->decoder->audio->samples_this_frame; i++) {
824       for (j = 0; j < dvdec->decoder->audio->num_channels; j++) {
825         *(a_ptr++) = dvdec->audio_buffers[j][i];
826       }
827     }
828     gst_dvdec_push (dvdec, outbuf, dvdec->audiosrcpad, ts);
829   }
830
831   if (GST_PAD_IS_LINKED (dvdec->videosrcpad)) {
832     guint8 *outframe;
833     guint8 *outframe_ptrs[3];
834     gint outframe_pitches[3];
835
836     outbuf = gst_buffer_new_and_alloc ((720 * height) * dvdec->bpp);
837     
838     outframe = GST_BUFFER_DATA (outbuf);
839
840     outframe_ptrs[0] = outframe;
841     outframe_pitches[0] = 720 * dvdec->bpp;
842
843     /* the rest only matters for YUY2 */
844     if (dvdec->bpp < 3) {
845       outframe_ptrs[1] = outframe_ptrs[0] + 720 * height;
846       outframe_ptrs[2] = outframe_ptrs[1] + 360 * height;
847   
848       outframe_pitches[1] = height / 2;
849       outframe_pitches[2] = outframe_pitches[1];
850     }
851
852     dv_decode_full_frame (dvdec->decoder, GST_BUFFER_DATA (buf), 
853                           dvdec->space, outframe_ptrs, outframe_pitches);
854
855     gst_dvdec_push (dvdec, outbuf, dvdec->videosrcpad, ts);
856   }
857
858   if ((dvdec->end_position != -1) && 
859       (dvdec->next_ts >= dvdec->end_position) && 
860       !dvdec->loop) {
861     gst_element_set_eos (GST_ELEMENT (dvdec));
862   }
863
864   if (dvdec->need_discont) {
865     dvdec->need_discont = FALSE;
866   }
867
868   gst_buffer_unref (buf);
869 }
870
871 static GstElementStateReturn
872 gst_dvdec_change_state (GstElement *element)
873 {
874   GstDVDec *dvdec = GST_DVDEC (element);
875             
876   switch (GST_STATE_TRANSITION (element)) {
877     case GST_STATE_NULL_TO_READY:
878       break;
879     case GST_STATE_READY_TO_PAUSED:
880       dvdec->bs = gst_bytestream_new (dvdec->sinkpad);
881       dvdec->decoder = dv_decoder_new (0, dvdec->clamp_luma, dvdec->clamp_chroma);
882       dvdec->decoder->quality = dvdec->quality;
883       break;
884     case GST_STATE_PAUSED_TO_PLAYING:
885       break;
886     case GST_STATE_PLAYING_TO_PAUSED:
887       break;
888     case GST_STATE_PAUSED_TO_READY:
889       dv_decoder_free (dvdec->decoder);
890       dvdec->decoder = NULL;
891       gst_bytestream_destroy (dvdec->bs);
892       break;
893     case GST_STATE_READY_TO_NULL:
894       break;
895     default:
896       break;
897   }
898               
899   parent_class->change_state (element);
900                 
901   return GST_STATE_SUCCESS;
902 }
903
904 /* Arguments are part of the Gtk+ object system, and these functions
905  * enable the element to respond to various arguments.
906  */
907 static void
908 gst_dvdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
909 {
910   GstDVDec *dvdec;
911
912   /* It's not null if we got it, but it might not be ours */
913   g_return_if_fail(GST_IS_DVDEC(object));
914
915   /* Get a pointer of the right type. */
916   dvdec = GST_DVDEC(object);
917
918   /* Check the argument id to see which argument we're setting. */
919   switch (prop_id) {
920     case ARG_CLAMP_LUMA:
921       dvdec->clamp_luma = g_value_get_boolean (value);
922       break;
923     case ARG_CLAMP_CHROMA:
924       dvdec->clamp_chroma = g_value_get_boolean (value);
925       break;
926     case ARG_QUALITY:
927       dvdec->quality = g_value_get_flags (value);
928       break;
929     default:
930       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
931       break;
932   }
933 }
934
935 /* The set function is simply the inverse of the get fuction. */
936 static void
937 gst_dvdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
938 {
939   GstDVDec *dvdec;
940
941   /* It's not null if we got it, but it might not be ours */
942   g_return_if_fail(GST_IS_DVDEC(object));
943   dvdec = GST_DVDEC(object);
944
945   switch (prop_id) {
946     case ARG_CLAMP_LUMA:
947       g_value_set_boolean (value, dvdec->clamp_luma);
948       break;
949     case ARG_CLAMP_CHROMA:
950       g_value_set_boolean (value, dvdec->clamp_chroma);
951       break;
952     case ARG_QUALITY:
953       g_value_set_flags (value, dvdec->quality);
954       break;
955     default:
956       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
957       break;
958   }
959 }
960
961 /* This is the entry into the plugin itself.  When the plugin loads,
962  * this function is called to register everything that the plugin provides.
963  */
964 static gboolean
965 plugin_init (GstPlugin *plugin)
966 {
967   if (!gst_library_load ("gstbytestream"))
968     return FALSE;
969
970   if (!gst_element_register (plugin, "dvdec", GST_RANK_PRIMARY, gst_dvdec_get_type()))
971     return FALSE;
972
973   return TRUE;
974 }
975
976 GST_PLUGIN_DEFINE (
977   GST_VERSION_MAJOR,
978   GST_VERSION_MINOR,
979   "dvdec",
980   "Uses libdv to decode DV video (libdv.sourceforge.net)",
981   plugin_init,
982   VERSION,
983   "LGPL",
984   GST_PACKAGE,
985   GST_ORIGIN
986 );