Second attempt at committing a working dvdec element.
[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 GstPadLinkReturn gst_dvdec_video_link (GstPad          *pad,
190                                               const GstCaps   *caps);
191 static GstCaps*   gst_dvdec_video_getcaps   (GstPad       *pad);
192        
193 static const GstEventMask*
194                         gst_dvdec_get_event_masks       (GstPad *pad);
195 static gboolean         gst_dvdec_handle_src_event      (GstPad *pad, GstEvent *event);
196
197 static void             gst_dvdec_loop                  (GstElement *element);
198
199 static GstElementStateReturn    
200                         gst_dvdec_change_state          (GstElement *element);
201
202 static void             gst_dvdec_set_property          (GObject *object, guint prop_id, 
203                                                          const GValue *value, GParamSpec *pspec);
204 static void             gst_dvdec_get_property          (GObject *object, guint prop_id, 
205                                                          GValue *value, GParamSpec *pspec);
206
207 /* The parent class pointer needs to be kept around for some object
208  * operations.
209  */
210 static GstElementClass *parent_class = NULL;
211
212 /* This array holds the ids of the signals registered for this object.
213  * The array indexes are based on the enum up above.
214  */
215 /*static guint gst_dvdec_signals[LAST_SIGNAL] = { 0 }; */
216
217 /* This function is used to register and subsequently return the type
218  * identifier for this object class.  On first invocation, it will
219  * register the type, providing the name of the class, struct sizes,
220  * and pointers to the various functions that define the class.
221  */
222 GType
223 gst_dvdec_get_type (void)
224 {
225   static GType dvdec_type = 0;
226
227   if (!dvdec_type) {
228     static const GTypeInfo dvdec_info = {
229       sizeof (GstDVDecClass),      
230       gst_dvdec_base_init,      
231       NULL,
232       (GClassInitFunc) gst_dvdec_class_init,
233       NULL,
234       NULL,
235       sizeof (GstDVDec),
236       0,
237       (GInstanceInitFunc) gst_dvdec_init,
238     };
239     dvdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstDVDec", &dvdec_info, 0);
240   }
241   return dvdec_type;
242 }
243
244 static void
245 gst_dvdec_base_init (gpointer g_class)
246 {
247   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
248
249   /* The pad templates can be easily generated from the factories above,
250    * and then added to the list of padtemplates for the elementfactory.
251    * Note that the generated padtemplates are stored in static global
252    * variables, for the gst_dvdec_init function to use later on.
253    */
254   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get(&sink_temp));
255   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get(&video_src_temp));
256   gst_element_class_add_pad_template (element_class, gst_static_pad_template_get(&audio_src_temp));
257
258   gst_element_class_set_details (element_class, &dvdec_details);
259 }
260
261 /* In order to create an instance of an object, the class must be
262  * initialized by this function.  GObject will take care of running
263  * it, based on the pointer to the function provided above.
264  */
265 static void
266 gst_dvdec_class_init (GstDVDecClass *klass)
267 {
268   /* Class pointers are needed to supply pointers to the private
269    * implementations of parent class methods.
270    */
271   GObjectClass *gobject_class;
272   GstElementClass *gstelement_class;
273
274   /* Since the dvdec class contains the parent classes, you can simply
275    * cast the pointer to get access to the parent classes.
276    */
277   gobject_class = (GObjectClass*) klass;
278   gstelement_class = (GstElementClass*) klass;
279
280   /* The parent class is needed for class method overrides. */
281   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
282
283   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CLAMP_LUMA,
284     g_param_spec_boolean ("clamp_luma", "Clamp luma", "Clamp luma",
285                           FALSE, G_PARAM_READWRITE));
286   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CLAMP_CHROMA,
287     g_param_spec_boolean ("clamp_chroma", "Clamp chroma", "Clamp chroma",
288                           FALSE, G_PARAM_READWRITE));
289   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
290     g_param_spec_flags ("quality", "Quality", "Decoding quality",
291                         GST_TYPE_DVDEC_QUALITY, DV_QUALITY_BEST, G_PARAM_READWRITE));
292
293   gobject_class->set_property = gst_dvdec_set_property;
294   gobject_class->get_property = gst_dvdec_get_property;
295   
296   gstelement_class->change_state = gst_dvdec_change_state;
297
298   /* table initialization, only do once */
299   dv_init(0, 0);
300 }
301
302 /* This function is responsible for initializing a specific instance of
303  * the plugin.
304  */
305 static void
306 gst_dvdec_init(GstDVDec *dvdec)
307 {
308   gint i;
309   dvdec->found_header = FALSE;
310
311   dvdec->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&sink_temp), "sink");
312   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->sinkpad);
313   gst_pad_set_query_function (dvdec->sinkpad, NULL);
314   gst_pad_set_convert_function (dvdec->sinkpad, GST_DEBUG_FUNCPTR (gst_dvdec_sink_convert));
315   gst_pad_set_formats_function (dvdec->sinkpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_formats));
316
317   dvdec->videosrcpad = gst_pad_new_from_template (gst_static_pad_template_get (&video_src_temp), "video");
318   gst_pad_set_query_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_query));
319   gst_pad_set_query_type_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_src_query_types));
320   gst_pad_set_event_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_handle_src_event));
321   gst_pad_set_event_mask_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_event_masks));
322   gst_pad_set_convert_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_convert));
323   gst_pad_set_formats_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_formats));
324   gst_pad_set_getcaps_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_video_getcaps));
325   gst_pad_set_link_function (dvdec->videosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_video_link));
326   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->videosrcpad);
327   
328   dvdec->audiosrcpad = gst_pad_new_from_template (gst_static_pad_template_get (&audio_src_temp), "audio");
329   gst_pad_set_query_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_query));
330   gst_pad_set_query_type_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_src_query_types));
331   gst_pad_set_event_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_handle_src_event));
332   gst_pad_set_event_mask_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_event_masks));
333   gst_pad_set_convert_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_src_convert));
334   gst_pad_set_formats_function (dvdec->audiosrcpad, GST_DEBUG_FUNCPTR (gst_dvdec_get_formats));
335   gst_pad_use_explicit_caps (dvdec->audiosrcpad);
336   gst_element_add_pad (GST_ELEMENT(dvdec), dvdec->audiosrcpad);
337
338   gst_element_set_loop_function (GST_ELEMENT (dvdec), gst_dvdec_loop);
339
340   dvdec->length = 0;
341   dvdec->next_ts = 0LL;
342   dvdec->end_position = -1LL;
343   dvdec->need_discont = FALSE;
344   dvdec->framerate = 0;
345   dvdec->height = 0;
346   dvdec->frequency = 0;
347   dvdec->channels = 0;
348   
349   dvdec->clamp_luma = FALSE;
350   dvdec->clamp_chroma = FALSE;
351   dvdec->quality = DV_QUALITY_BEST;
352   dvdec->loop = FALSE;
353   
354   for (i = 0; i <4; i++) {
355     dvdec->audio_buffers[i] = (gint16 *)g_malloc (DV_AUDIO_MAX_SAMPLES * sizeof (gint16));
356   }
357 }
358
359 static const GstFormat*
360 gst_dvdec_get_formats (GstPad *pad)
361 {
362   static const GstFormat src_formats[] = {
363     GST_FORMAT_BYTES,
364     GST_FORMAT_DEFAULT,
365     GST_FORMAT_TIME,
366     0
367   };
368   static const GstFormat sink_formats[] = {
369     GST_FORMAT_BYTES,
370     GST_FORMAT_TIME,
371     0
372   };
373              
374   return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
375
376
377 static gboolean
378 gst_dvdec_src_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
379                        GstFormat *dest_format, gint64 *dest_value)
380 {
381   gboolean res = TRUE;
382   GstDVDec *dvdec;
383   gint scale = 1;
384         
385   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
386
387   if (dvdec->length == 0)
388     return FALSE;
389
390   if (dvdec->decoder == NULL)
391     return FALSE;
392
393   switch (src_format) {
394     case GST_FORMAT_BYTES:
395       switch (*dest_format) {
396         case GST_FORMAT_TIME:
397         default:
398           res = FALSE;
399       }
400       break;
401     case GST_FORMAT_TIME:
402       switch (*dest_format) {
403         case GST_FORMAT_BYTES:
404           if (pad == dvdec->videosrcpad)
405             scale = 720 * dvdec->height * dvdec->bpp;
406           else if (pad == dvdec->audiosrcpad)
407             scale = dvdec->decoder->audio->num_channels * 2;
408           /* fallthrough */
409         case GST_FORMAT_DEFAULT:
410           if (pad == dvdec->videosrcpad)
411             *dest_value = src_value * dvdec->framerate * scale / GST_SECOND;
412           else if (pad == dvdec->audiosrcpad)
413             *dest_value = src_value * dvdec->decoder->audio->frequency * scale / GST_SECOND;
414           break;
415         default:
416           res = FALSE;
417       }
418       break;
419     default:
420       res = FALSE;
421   }
422   return res;
423 }
424
425 static gboolean
426 gst_dvdec_sink_convert (GstPad *pad, GstFormat src_format, gint64 src_value,
427                         GstFormat *dest_format, gint64 *dest_value)
428 {
429   gboolean res = TRUE;
430   GstDVDec *dvdec;
431         
432   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
433
434   if (dvdec->length == 0)
435     return FALSE;
436
437   switch (src_format) {
438     case GST_FORMAT_BYTES:
439       switch (*dest_format) {
440         case GST_FORMAT_TIME:
441         {
442           guint64 frame;
443           /* get frame number */
444           frame = src_value / dvdec->length;
445
446           *dest_value = (frame * GST_SECOND) / dvdec->framerate;
447           break;
448         }
449         default:
450           res = FALSE;
451       }
452       break;
453     case GST_FORMAT_TIME:
454       switch (*dest_format) {
455         case GST_FORMAT_BYTES:
456         {
457           guint64 frame;
458           /* calculate the frame */
459           frame = src_value * dvdec->framerate / GST_SECOND;
460           /* calculate the offset */
461           *dest_value = frame * dvdec->length;
462           break;
463         }
464         default:
465           res = FALSE;
466       }
467       break;
468     default:
469       res = FALSE;
470   }
471   return res;
472 }
473
474 static const GstQueryType*
475 gst_dvdec_get_src_query_types (GstPad *pad)
476 {   
477   static const GstQueryType src_query_types[] = {
478     GST_QUERY_TOTAL,
479     GST_QUERY_POSITION,
480     0
481   };
482   return src_query_types;
483 }
484
485 static gboolean
486 gst_dvdec_src_query (GstPad *pad, GstQueryType type,
487                      GstFormat *format, gint64 *value)
488 {
489   gboolean res = TRUE;
490   GstDVDec *dvdec;
491
492   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
493
494   switch (type) {
495     case GST_QUERY_TOTAL:
496       switch (*format) {
497         default:
498         {
499           guint64 len;
500           GstFormat tmp_format;
501
502           len = gst_bytestream_length (dvdec->bs);
503           tmp_format = GST_FORMAT_TIME;
504           if (len == -1 || !gst_pad_convert (dvdec->sinkpad, 
505                                              GST_FORMAT_BYTES, 
506                                              len, 
507                                              &tmp_format, value)) 
508           {
509             return FALSE;
510           }
511           if (!gst_pad_convert (pad, GST_FORMAT_TIME, *value, format, value)) {
512             return FALSE;
513           }
514           break;
515         }
516       }
517       break;
518     case GST_QUERY_POSITION:
519       switch (*format) {
520         default:
521           res = gst_pad_convert (pad, GST_FORMAT_TIME, dvdec->next_ts, format, value);
522           break;
523       }
524       break;
525     default:
526       res = FALSE;
527       break;
528   }
529   return res;
530
531
532 static const GstEventMask*
533 gst_dvdec_get_event_masks (GstPad *pad)
534
535   static const GstEventMask src_event_masks[] = {
536     { GST_EVENT_SEEK, GST_SEEK_METHOD_SET |
537                       GST_SEEK_FLAG_FLUSH },
538     { 0, }
539   };
540   static const GstEventMask sink_event_masks[] = {
541     { GST_EVENT_EOS, 0 },
542     { GST_EVENT_DISCONTINUOUS, 0 },
543     { GST_EVENT_FLUSH, 0 },
544     { 0, }
545   };
546
547   return (GST_PAD_IS_SRC (pad) ? src_event_masks : sink_event_masks);
548
549
550 static gboolean
551 gst_dvdec_handle_sink_event (GstDVDec *dvdec)
552 {
553   guint32 remaining;
554   GstEvent *event;
555   GstEventType type;
556                 
557   gst_bytestream_get_status (dvdec->bs, &remaining, &event);
558                   
559   type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
560                       
561   switch (type) {
562     case GST_EVENT_FLUSH:
563       break;
564     case GST_EVENT_DISCONTINUOUS:
565     {
566       gint i;
567       gboolean found = FALSE;
568       GstFormat format;
569
570       format = GST_FORMAT_TIME;
571       /* try to get a timestamp from the discont formats */
572       for (i = 0; i < GST_EVENT_DISCONT_OFFSET_LEN(event); i++) {
573         if (gst_pad_convert (dvdec->sinkpad, 
574                              GST_EVENT_DISCONT_OFFSET(event,i).format, 
575                              GST_EVENT_DISCONT_OFFSET(event,i).value,
576                              &format, &dvdec->next_ts)) 
577         {
578           found = TRUE;
579           break;
580         }
581       }
582       /* assume 0 then */
583       if (!found) {
584         dvdec->next_ts = 0LL;
585       }
586       dvdec->need_discont = TRUE;
587       break;
588     }
589     default:
590       return gst_pad_event_default (dvdec->sinkpad, event);
591       break;
592   }
593   gst_event_unref (event);
594   return TRUE;
595 }
596
597 static gboolean
598 gst_dvdec_handle_src_event (GstPad *pad, GstEvent *event)
599 {
600   gboolean res = TRUE;
601   GstDVDec *dvdec;
602
603   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
604  
605   switch (GST_EVENT_TYPE (event)) {
606     case GST_EVENT_SEEK_SEGMENT:
607     {
608       gint64 position;
609       GstFormat format;
610
611       /* first bring the format to time */
612       format = GST_FORMAT_TIME;
613       if (!gst_pad_convert (pad, 
614                             GST_EVENT_SEEK_FORMAT (event), 
615                             GST_EVENT_SEEK_ENDOFFSET (event),
616                             &format, &position)) 
617       {
618         /* could not convert seek format to time offset */
619         res = FALSE;
620         break;
621       }
622
623       dvdec->end_position = position;
624       dvdec->loop = GST_EVENT_SEEK_TYPE (event) & GST_SEEK_FLAG_SEGMENT_LOOP;
625     }
626     case GST_EVENT_SEEK:
627     {
628       gint64 position;
629       GstFormat format;
630
631       /* first bring the format to time */
632       format = GST_FORMAT_TIME;
633       if (!gst_pad_convert (pad, 
634                             GST_EVENT_SEEK_FORMAT (event), 
635                             GST_EVENT_SEEK_OFFSET (event),
636                             &format, &position)) 
637       {
638         /* could not convert seek format to time offset */
639         res = FALSE;
640         break;
641       }
642       dvdec->next_ts = position;
643       /* then try to figure out the byteoffset for this time */
644       format = GST_FORMAT_BYTES;
645       if (!gst_pad_convert (dvdec->sinkpad, GST_FORMAT_TIME, position,
646                         &format, &position)) 
647       {
648         /* could not convert seek format to byte offset */
649         res = FALSE;
650         break;
651       }
652       /* seek to offset */
653       if (!gst_bytestream_seek (dvdec->bs, position, GST_SEEK_METHOD_SET)) {
654         res = FALSE;
655       }
656       if (GST_EVENT_TYPE (event) != GST_EVENT_SEEK_SEGMENT)
657         dvdec->end_position = -1;
658       break;
659     }
660     default:
661       res = FALSE;
662       break;
663   }
664   gst_event_unref (event);
665   return res;
666 }
667
668 static GstCaps*   
669 gst_dvdec_video_getcaps (GstPad *pad)
670 {
671   GstDVDec *dvdec;
672   GstCaps *caps;
673   GstPadTemplate *src_pad_template;
674   
675   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
676   src_pad_template = gst_static_pad_template_get (&video_src_temp);
677   caps = gst_caps_copy(gst_pad_template_get_caps (src_pad_template));
678   
679   if (dvdec->found_header)
680   {
681     int i;
682     
683     /* set the height */
684     for (i = 0; i < gst_caps_get_size (caps); i++)
685     {
686         GstStructure *structure = gst_caps_get_structure (caps, i);
687         gst_structure_set(structure, 
688                 "height", G_TYPE_INT, dvdec->height,
689               "framerate", G_TYPE_DOUBLE, dvdec->framerate, NULL
690               );
691     }
692   }
693   
694   return caps;
695 }
696
697 static GstPadLinkReturn 
698 gst_dvdec_video_link (GstPad *pad, const GstCaps *caps)
699 {
700   GstDVDec *dvdec;
701   GstStructure *structure;
702   guint32 fourcc;
703   gint height;
704   gdouble framerate;
705   
706   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
707
708   /* if we did not find a header yet, return delayed */
709   if (!dvdec->found_header)
710   {
711     return GST_PAD_LINK_DELAYED;
712   }
713  
714   structure = gst_caps_get_structure (caps, 0);
715   
716   /* it worked, try to find what it was again */
717   if (!gst_structure_get_fourcc (structure, "format", &fourcc) ||
718       !gst_structure_get_int (structure, "height", &height) ||
719       !gst_structure_get_double (structure, "framerate", &framerate))
720     return GST_PAD_LINK_REFUSED;
721   
722   if ((height != dvdec->height) || (framerate != dvdec->framerate))
723     return GST_PAD_LINK_REFUSED;
724     
725   if (fourcc == GST_STR_FOURCC ("RGB ")) {
726     gint bpp;
727   
728     gst_structure_get_int (structure, "bpp", &bpp);
729     if (bpp == 24) {
730       dvdec->space = e_dv_color_rgb;
731       dvdec->bpp = 3;
732     }
733     else {
734       dvdec->space = e_dv_color_bgr0;
735       dvdec->bpp = 4;
736     }
737   }
738   else {
739     dvdec->space = e_dv_color_yuv;
740     dvdec->bpp = 2;
741   }
742   
743   return GST_PAD_LINK_OK;
744 }
745
746 static void
747 gst_dvdec_push (GstDVDec *dvdec, GstBuffer *outbuf, GstPad *pad, GstClockTime ts)
748 {   
749   GST_BUFFER_TIMESTAMP (outbuf) = ts;
750
751   if (dvdec->need_discont) {
752     GstEvent *discont;
753
754     discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, ts, NULL);
755     gst_pad_push (pad, GST_DATA (discont));
756   }
757
758   gst_pad_push (pad, GST_DATA (outbuf));
759
760   if ((dvdec->end_position != -1) &&
761       (dvdec->next_ts >= dvdec->end_position)) {
762     if (dvdec->loop) 
763       gst_pad_push (pad, GST_DATA(gst_event_new (GST_EVENT_SEGMENT_DONE)));
764     else
765       gst_pad_push (pad, GST_DATA(gst_event_new (GST_EVENT_EOS)));
766   }
767 }
768
769 static void
770 gst_dvdec_loop (GstElement *element)
771 {   
772   GstDVDec *dvdec;
773   GstBuffer *buf, *outbuf;
774   guint8 *inframe;
775   gint height;
776   guint32 length, got_bytes;
777   GstFormat format;
778   guint64 ts;
779   gdouble fps;
780
781   dvdec = GST_DVDEC (element);
782
783   /* first read enough bytes to parse the header */
784   got_bytes = gst_bytestream_peek_bytes (dvdec->bs, &inframe, header_size);
785   if (got_bytes < header_size) {
786     gst_dvdec_handle_sink_event (dvdec);
787     return;
788   }
789   if (dv_parse_header (dvdec->decoder, inframe) < 0)
790   {
791     GST_ELEMENT_ERROR (dvdec, STREAM, DECODE, (NULL), (NULL));
792     return;
793   }
794   
795   /* after parsing the header we know the length of the data */
796   dvdec->PAL = dv_system_50_fields (dvdec->decoder);
797   dvdec->found_header = TRUE;
798   
799   fps = (dvdec->PAL ? PAL_FRAMERATE : NTSC_FRAMERATE);
800   height = (dvdec->PAL ? PAL_HEIGHT : NTSC_HEIGHT);
801   length = (dvdec->PAL ? PAL_BUFFER : NTSC_BUFFER);
802
803   if ((dvdec->framerate != fps) ||
804       (dvdec->height != height))
805   {
806     dvdec->height = height;
807     dvdec->framerate = fps;
808     
809     if (GST_PAD_LINK_FAILED (gst_pad_renegotiate (dvdec->videosrcpad)))
810     {
811       GST_ELEMENT_ERROR (dvdec, CORE, NEGOTIATION, (NULL), (NULL));
812       return;
813     }
814   }
815   
816   if (length != dvdec->length) {
817     dvdec->length = length;
818     gst_bytestream_size_hint (dvdec->bs, length);
819   }
820
821   /* then read the read data */
822   got_bytes = gst_bytestream_read (dvdec->bs, &buf, length);
823   if (got_bytes < length) {
824     gst_dvdec_handle_sink_event (dvdec);
825     return;
826   }
827
828   format = GST_FORMAT_TIME;
829   gst_pad_query (dvdec->videosrcpad, GST_QUERY_POSITION, &format, &ts);
830
831   dvdec->next_ts += GST_SECOND / dvdec->framerate;
832
833   dv_parse_packs (dvdec->decoder, GST_BUFFER_DATA (buf));
834
835   if (GST_PAD_IS_LINKED (dvdec->audiosrcpad)) {
836     gint16 *a_ptr;
837     gint i, j;
838
839     dv_decode_full_audio (dvdec->decoder, GST_BUFFER_DATA (buf), dvdec->audio_buffers);
840
841     if ((dvdec->decoder->audio->frequency != dvdec->frequency) ||
842         (dvdec->decoder->audio->num_channels != dvdec->channels))
843     {
844       if (!gst_pad_set_explicit_caps (dvdec->audiosrcpad,
845                       gst_caps_new_simple (
846                         "audio/x-raw-int", 
847                         "rate", G_TYPE_INT, dvdec->decoder->audio->frequency,
848                         "depth", G_TYPE_INT,  16,
849                         "width", G_TYPE_INT,  16,
850                         "signed", G_TYPE_BOOLEAN, TRUE,
851                         "channels", G_TYPE_INT, dvdec->decoder->audio->num_channels,
852                         "endianness", G_TYPE_INT, G_LITTLE_ENDIAN,
853                         NULL
854                       )))
855       {
856         gst_buffer_unref (buf);
857         GST_ELEMENT_ERROR (dvdec, CORE, NEGOTIATION, (NULL), (NULL));        
858         return;
859       }
860       
861       dvdec->frequency = dvdec->decoder->audio->frequency;
862       dvdec->channels = dvdec->decoder->audio->num_channels;
863     }
864       
865     outbuf = gst_buffer_new ();
866     GST_BUFFER_SIZE (outbuf) = dvdec->decoder->audio->samples_this_frame * 
867                         sizeof (gint16) * dvdec->decoder->audio->num_channels;
868     GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
869     
870     a_ptr = (gint16 *) GST_BUFFER_DATA (outbuf);
871     
872     for (i = 0; i < dvdec->decoder->audio->samples_this_frame; i++) {
873       for (j = 0; j < dvdec->decoder->audio->num_channels; j++) {
874         *(a_ptr++) = dvdec->audio_buffers[j][i];
875       }
876     }
877     gst_dvdec_push (dvdec, outbuf, dvdec->audiosrcpad, ts);
878   }
879
880   if (GST_PAD_IS_LINKED (dvdec->videosrcpad)) {
881     guint8 *outframe;
882     guint8 *outframe_ptrs[3];
883     gint outframe_pitches[3];
884
885     outbuf = gst_buffer_new_and_alloc ((720 * height) * dvdec->bpp);
886     
887     outframe = GST_BUFFER_DATA (outbuf);
888
889     outframe_ptrs[0] = outframe;
890     outframe_pitches[0] = 720 * dvdec->bpp;
891
892     /* the rest only matters for YUY2 */
893     if (dvdec->bpp < 3) {
894       outframe_ptrs[1] = outframe_ptrs[0] + 720 * height;
895       outframe_ptrs[2] = outframe_ptrs[1] + 360 * height;
896   
897       outframe_pitches[1] = height / 2;
898       outframe_pitches[2] = outframe_pitches[1];
899     }
900
901     dv_decode_full_frame (dvdec->decoder, GST_BUFFER_DATA (buf), 
902                           dvdec->space, outframe_ptrs, outframe_pitches);
903
904     gst_dvdec_push (dvdec, outbuf, dvdec->videosrcpad, ts);
905   }
906
907   if ((dvdec->end_position != -1) && 
908       (dvdec->next_ts >= dvdec->end_position) && 
909       !dvdec->loop) {
910     gst_element_set_eos (GST_ELEMENT (dvdec));
911   }
912
913   if (dvdec->need_discont) {
914     dvdec->need_discont = FALSE;
915   }
916
917   gst_buffer_unref (buf);
918 }
919
920 static GstElementStateReturn
921 gst_dvdec_change_state (GstElement *element)
922 {
923   GstDVDec *dvdec = GST_DVDEC (element);
924             
925   switch (GST_STATE_TRANSITION (element)) {
926     case GST_STATE_NULL_TO_READY:
927       break;
928     case GST_STATE_READY_TO_PAUSED:
929       dvdec->bs = gst_bytestream_new (dvdec->sinkpad);
930       dvdec->decoder = dv_decoder_new (0, dvdec->clamp_luma, dvdec->clamp_chroma);
931       dvdec->decoder->quality = dvdec->quality;
932       break;
933     case GST_STATE_PAUSED_TO_PLAYING:
934       break;
935     case GST_STATE_PLAYING_TO_PAUSED:
936       break;
937     case GST_STATE_PAUSED_TO_READY:
938       dv_decoder_free (dvdec->decoder);
939       dvdec->decoder = NULL;
940       dvdec->found_header = FALSE;
941       gst_bytestream_destroy (dvdec->bs);
942       break;
943     case GST_STATE_READY_TO_NULL:
944       break;
945     default:
946       break;
947   }
948               
949   parent_class->change_state (element);
950                 
951   return GST_STATE_SUCCESS;
952 }
953
954 /* Arguments are part of the Gtk+ object system, and these functions
955  * enable the element to respond to various arguments.
956  */
957 static void
958 gst_dvdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
959 {
960   GstDVDec *dvdec;
961
962   /* It's not null if we got it, but it might not be ours */
963   g_return_if_fail(GST_IS_DVDEC(object));
964
965   /* Get a pointer of the right type. */
966   dvdec = GST_DVDEC(object);
967
968   /* Check the argument id to see which argument we're setting. */
969   switch (prop_id) {
970     case ARG_CLAMP_LUMA:
971       dvdec->clamp_luma = g_value_get_boolean (value);
972       break;
973     case ARG_CLAMP_CHROMA:
974       dvdec->clamp_chroma = g_value_get_boolean (value);
975       break;
976     case ARG_QUALITY:
977       dvdec->quality = g_value_get_flags (value);
978       break;
979     default:
980       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
981       break;
982   }
983 }
984
985 /* The set function is simply the inverse of the get fuction. */
986 static void
987 gst_dvdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
988 {
989   GstDVDec *dvdec;
990
991   /* It's not null if we got it, but it might not be ours */
992   g_return_if_fail(GST_IS_DVDEC(object));
993   dvdec = GST_DVDEC(object);
994
995   switch (prop_id) {
996     case ARG_CLAMP_LUMA:
997       g_value_set_boolean (value, dvdec->clamp_luma);
998       break;
999     case ARG_CLAMP_CHROMA:
1000       g_value_set_boolean (value, dvdec->clamp_chroma);
1001       break;
1002     case ARG_QUALITY:
1003       g_value_set_flags (value, dvdec->quality);
1004       break;
1005     default:
1006       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1007       break;
1008   }
1009 }
1010
1011 /* This is the entry into the plugin itself.  When the plugin loads,
1012  * this function is called to register everything that the plugin provides.
1013  */
1014 static gboolean
1015 plugin_init (GstPlugin *plugin)
1016 {
1017   if (!gst_library_load ("gstbytestream"))
1018     return FALSE;
1019
1020   if (!gst_element_register (plugin, "dvdec", GST_RANK_PRIMARY, gst_dvdec_get_type()))
1021     return FALSE;
1022
1023   return TRUE;
1024 }
1025
1026 GST_PLUGIN_DEFINE (
1027   GST_VERSION_MAJOR,
1028   GST_VERSION_MINOR,
1029   "dvdec",
1030   "Uses libdv to decode DV video (libdv.sourceforge.net)",
1031   plugin_init,
1032   VERSION,
1033   "LGPL",
1034   GST_PACKAGE,
1035   GST_ORIGIN
1036 );