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