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