Also allow PAL dv implement total time
[platform/upstream/gstreamer.git] / ext / dv / gstdvdec.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <string.h>
21
22 /* First, include the header file for the plugin, to bring in the
23  * object definition and other useful things.
24  */
25 #include "gstdvdec.h"
26
27 #define NTSC_HEIGHT 480
28 #define NTSC_BUFFER 120000
29 #define PAL_HEIGHT 576
30 #define PAL_BUFFER 144000
31
32 /* The ElementDetails structure gives a human-readable description
33  * of the plugin, as well as author and version data.
34  */
35 static GstElementDetails dvdec_details = {
36   "DV (smpte314) decoder plugin",
37   "Decoder/DV",
38   "Uses libdv to decode DV video (libdv.sourceforge.net)",
39   VERSION,
40   "Erik Walthinsen <omega@cse.ogi.edu>\n"
41   "Wim Taymans <wim.taymans@tvd.be>",
42   "(C) 2001-2002",
43 };
44
45
46 /* These are the signals that this element can fire.  They are zero-
47  * based because the numbers themselves are private to the object.
48  * LAST_SIGNAL is used for initialization of the signal array.
49  */
50 enum {
51   ASDF,
52   /* FILL ME */
53   LAST_SIGNAL
54 };
55
56 /* Arguments are identified the same way, but cannot be zero, so you
57  * must leave the ARG_0 entry in as a placeholder.
58  */
59 enum {
60   ARG_0,
61   /* FILL ME */
62 };
63
64 /* The PadFactory structures describe what pads the element has or
65  * can have.  They can be quite complex, but for this dvdec plugin
66  * they are rather simple.
67  */
68 GST_PAD_TEMPLATE_FACTORY (sink_temp,
69   "sink",
70   GST_PAD_SINK,
71   GST_PAD_ALWAYS,
72   GST_CAPS_NEW (
73     "dv_dec_sink",
74     "video/dv",
75     "format",   GST_PROPS_LIST (
76                   GST_PROPS_STRING ("PAL"),
77                   GST_PROPS_STRING ("NTSC")
78                )
79   )
80 )
81
82
83 GST_PAD_TEMPLATE_FACTORY (video_src_temp,
84   "video",
85   GST_PAD_SRC,
86   GST_PAD_ALWAYS,
87   GST_CAPS_NEW (
88     "dv_dec_src",
89     "video/raw",
90     "format",           GST_PROPS_FOURCC (GST_MAKE_FOURCC ('Y','U','Y','2')),
91     "width",            GST_PROPS_INT (720),
92     "height",           GST_PROPS_INT_RANGE (NTSC_HEIGHT, PAL_HEIGHT)
93   ),
94   GST_CAPS_NEW (
95     "dv_dec_src",
96     "video/raw",
97     "format",           GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')),
98     "bpp",              GST_PROPS_INT(24),
99     "depth",            GST_PROPS_INT(24),
100     "red_mask",         GST_PROPS_INT(0x0000ff),
101     "green_mask",       GST_PROPS_INT(0x00ff00),
102     "blue_mask",        GST_PROPS_INT(0xff0000),
103     "width",            GST_PROPS_INT (720),
104     "height",           GST_PROPS_INT_RANGE (NTSC_HEIGHT, PAL_HEIGHT)
105   )
106 )
107
108 GST_PAD_TEMPLATE_FACTORY ( audio_src_temp,
109   "audio",
110   GST_PAD_SRC,
111   GST_PAD_ALWAYS,
112   GST_CAPS_NEW (
113     "arts_sample",
114     "audio/raw",
115     "format",           GST_PROPS_STRING ("int"),
116     "law",              GST_PROPS_INT (0),
117     "depth",            GST_PROPS_INT (16),
118     "width",            GST_PROPS_INT (16),
119     "signed",           GST_PROPS_BOOLEAN (TRUE),
120     "channels",         GST_PROPS_INT (2),
121     "endianness",       GST_PROPS_INT (G_LITTLE_ENDIAN)
122   )
123 )
124
125 /* typefind stuff */
126 static GstCaps*
127 dv_type_find (GstBuffer *buf, gpointer private)
128 {
129   gulong head = GULONG_FROM_BE(*((gulong *)GST_BUFFER_DATA(buf)));
130   GstCaps *new = NULL;
131
132   /* check for DIF  and DV flag */
133   if ((head & 0xffffff00) == 0x1f070000 && !(GST_BUFFER_DATA(buf)[4] & 0x01)) {
134     gchar *format;
135
136     if ((head & 0x000000ff) & 0x80)
137       format = "PAL";
138     else
139       format = "NTSC";
140     
141     new = GST_CAPS_NEW ("dv_type_find",
142                         "video/dv",
143                           "format",   GST_PROPS_STRING (format)
144                        );
145   }
146   return new;
147 }
148
149 static GstTypeDefinition dv_definition = {
150   "dv_video/dv", "video/dv", ".dv", dv_type_find 
151 };
152
153 /* A number of functon prototypes are given so we can refer to them later. */
154 static void             gst_dvdec_class_init    (GstDVDecClass *klass);
155 static void             gst_dvdec_init          (GstDVDec *dvdec);
156
157 static gboolean         gst_dvdec_src_query     (GstPad *pad, GstPadQueryType type,
158                                                  GstFormat *format, gint64 *value);
159
160 static void             gst_dvdec_loop          (GstElement *element);
161
162 static GstElementStateReturn    
163                         gst_dvdec_change_state  (GstElement *element);
164
165 static void             gst_dvdec_set_property  (GObject *object, guint prop_id, 
166                                                  const GValue *value, GParamSpec *pspec);
167 static void             gst_dvdec_get_property  (GObject *object, guint prop_id, 
168                                                  GValue *value, GParamSpec *pspec);
169
170 /* The parent class pointer needs to be kept around for some object
171  * operations.
172  */
173 static GstElementClass *parent_class = NULL;
174
175 /* This array holds the ids of the signals registered for this object.
176  * The array indexes are based on the enum up above.
177  */
178 /*static guint gst_dvdec_signals[LAST_SIGNAL] = { 0 }; */
179
180 /* This function is used to register and subsequently return the type
181  * identifier for this object class.  On first invocation, it will
182  * register the type, providing the name of the class, struct sizes,
183  * and pointers to the various functions that define the class.
184  */
185 GType
186 gst_dvdec_get_type (void)
187 {
188   static GType dvdec_type = 0;
189
190   if (!dvdec_type) {
191     static const GTypeInfo dvdec_info = {
192       sizeof (GstDVDecClass),      
193       NULL,      
194       NULL,
195       (GClassInitFunc) gst_dvdec_class_init,
196       NULL,
197       NULL,
198       sizeof (GstDVDec),
199       0,
200       (GInstanceInitFunc) gst_dvdec_init,
201     };
202     dvdec_type = g_type_register_static (GST_TYPE_ELEMENT, "GstDVDec", &dvdec_info, 0);
203   }
204   return dvdec_type;
205 }
206
207 /* In order to create an instance of an object, the class must be
208  * initialized by this function.  GObject will take care of running
209  * it, based on the pointer to the function provided above.
210  */
211 static void
212 gst_dvdec_class_init (GstDVDecClass *klass)
213 {
214   /* Class pointers are needed to supply pointers to the private
215    * implementations of parent class methods.
216    */
217   GObjectClass *gobject_class;
218   GstElementClass *gstelement_class;
219
220   /* Since the dvdec class contains the parent classes, you can simply
221    * cast the pointer to get access to the parent classes.
222    */
223   gobject_class = (GObjectClass*) klass;
224   gstelement_class = (GstElementClass*) klass;
225
226   /* The parent class is needed for class method overrides. */
227   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
228
229   gobject_class->set_property = gst_dvdec_set_property;
230   gobject_class->get_property = gst_dvdec_get_property;
231
232   gstelement_class->change_state = gst_dvdec_change_state;
233
234   /* table initialization, only do once */
235   dv_init(0, 0);
236 }
237
238 /* This function is responsible for initializing a specific instance of
239  * the plugin.
240  */
241 static void
242 gst_dvdec_init(GstDVDec *dvdec)
243 {
244   gint i;
245
246   dvdec->sinkpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (sink_temp), "sink");
247   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->sinkpad);
248   gst_pad_set_query_function (dvdec->sinkpad, NULL);
249
250   dvdec->videosrcpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (video_src_temp), "video");
251   gst_element_add_pad (GST_ELEMENT (dvdec), dvdec->videosrcpad);
252   gst_pad_set_query_function (dvdec->videosrcpad, gst_dvdec_src_query);
253
254   dvdec->audiosrcpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET(audio_src_temp), "audio");
255   gst_element_add_pad(GST_ELEMENT(dvdec),dvdec->audiosrcpad);
256   gst_pad_set_query_function (dvdec->audiosrcpad, gst_dvdec_src_query);
257
258   gst_element_set_loop_function (GST_ELEMENT (dvdec), gst_dvdec_loop);
259
260   dvdec->decoder = dv_decoder_new (0, 0, 0);
261   dvdec->decoder->quality = DV_QUALITY_BEST;
262   dvdec->pool = NULL;
263   dvdec->length = 0;
264   dvdec->next_ts = 0LL;
265
266   for (i = 0; i <4; i++) {
267     dvdec->audio_buffers[i] = (gint16 *)g_malloc (DV_AUDIO_MAX_SAMPLES * sizeof (gint16));
268   }
269 }
270
271 static gboolean
272 gst_dvdec_src_query (GstPad *pad, GstPadQueryType type,
273                      GstFormat *format, gint64 *value)
274 {
275   gboolean res = TRUE;
276   GstDVDec *dvdec;
277
278   dvdec = GST_DVDEC (gst_pad_get_parent (pad));
279
280   switch (type) {
281     case GST_PAD_QUERY_TOTAL:
282       switch (*format) {
283         case GST_FORMAT_DEFAULT:
284           *format = GST_FORMAT_TIME;
285         case GST_FORMAT_TIME:
286         {
287           guint64 len;
288
289           len = gst_bytestream_length (dvdec->bs);
290           if (len != -1 && dvdec->length) {
291             *value = (len * GST_SECOND) / (dvdec->length * 25);
292           }
293           else 
294             return FALSE;
295           break;
296         }
297         default:
298           res = FALSE;
299           break;
300       }
301       break;
302     case GST_PAD_QUERY_POSITION:
303       switch (*format) {
304         case GST_FORMAT_DEFAULT:
305           *format = GST_FORMAT_TIME;
306         default:
307           *value = dvdec->next_ts;
308           break;
309       }
310       break;
311     default:
312       res = FALSE;
313       break;
314   }
315   return res;
316
317
318 static gboolean
319 gst_dvdec_handle_event (GstDVDec *dvdec)
320 {
321   guint32 remaining;
322   GstEvent *event;
323   GstEventType type;
324                 
325   gst_bytestream_get_status (dvdec->bs, &remaining, &event);
326                   
327   type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
328                       
329   switch (type) {
330     case GST_EVENT_EOS:
331       gst_pad_event_default (dvdec->sinkpad, event);
332       break;
333     case GST_EVENT_SEEK:
334       g_warning ("seek event\n");
335       gst_event_free (event);
336       break;
337     case GST_EVENT_FLUSH:
338       g_warning ("flush event\n");
339       gst_event_free (event);
340       break;
341     case GST_EVENT_DISCONTINUOUS:
342       g_warning ("discont event\n");
343       gst_event_free (event);
344       break;
345     default:
346       g_warning ("unhandled event %d\n", type);
347       gst_event_free (event);
348       break;
349   }
350   return TRUE;
351 }
352
353
354 static void
355 gst_dvdec_loop (GstElement *element)
356 {   
357   GstDVDec *dvdec;
358   GstBuffer *buf, *outbuf;
359   guint8 *inframe;
360   gboolean PAL;
361   gint height;
362   guint32 length, got_bytes;
363   GstFormat format;
364   guint64 ts;
365
366   dvdec = GST_DVDEC (element);
367
368   /* first read enough bytes to parse the header */
369   got_bytes = gst_bytestream_peek_bytes (dvdec->bs, &inframe, header_size);
370   if (got_bytes < header_size) {
371     gst_dvdec_handle_event (dvdec);
372     return;
373   }
374   dv_parse_header (dvdec->decoder, inframe);
375   /* after parsing the header we know the size of the data */
376   PAL = dv_system_50_fields (dvdec->decoder);
377
378   height = (PAL ? PAL_HEIGHT : NTSC_HEIGHT);
379   length = (PAL ? PAL_BUFFER : NTSC_BUFFER);
380
381   if (length != dvdec->length) {
382     dvdec->length = length;
383     gst_bytestream_size_hint (dvdec->bs, length);
384   }
385
386   /* then read the read data */
387   got_bytes = gst_bytestream_read (dvdec->bs, &buf, length);
388   if (got_bytes < length) {
389     gst_dvdec_handle_event (dvdec);
390     return;
391   }
392   
393   /* if we did not negotiate yet, do it now */
394   if (!GST_PAD_CAPS (dvdec->videosrcpad)) {
395     GstCaps *allowed;
396     GstCaps *trylist;
397     
398     /* we what we are allowed to do */
399     allowed = gst_pad_get_allowed_caps (dvdec->videosrcpad);
400
401     /* try to fix our height */
402     trylist = gst_caps_intersect (allowed,
403                                     GST_CAPS_NEW (
404                                       "dvdec_negotiate",
405                                       "video/raw",
406                                         "height",       GST_PROPS_INT (height)
407                                     ));
408     
409     /* prepare for looping */
410     trylist = gst_caps_normalize (trylist);
411
412     while (trylist) {
413       GstCaps *to_try = gst_caps_copy_1 (trylist);
414
415       /* try each format */
416       if (gst_pad_try_set_caps (dvdec->videosrcpad, to_try)) {
417         guint32 fourcc;
418
419         /* it worked, try to find what it was again */
420         gst_caps_get_fourcc_int (to_try, "format", &fourcc);
421
422         if (fourcc == GST_STR_FOURCC ("RGB ")) {
423           dvdec->space = e_dv_color_rgb;
424           dvdec->bpp = 3;
425         }
426         else {
427           dvdec->space = e_dv_color_yuv;
428           dvdec->bpp = 2;
429         }
430         break;
431       }
432       trylist = trylist->next;
433     }
434     /* oops list exhausted an nothing was found... */
435     if (!trylist) {
436       gst_element_error (element, "could not negotiate");
437       return;
438     }
439   }
440
441   /* if we did not negotiate yet, do it now */
442   if (!GST_PAD_CAPS (dvdec->audiosrcpad)) {
443     gst_pad_try_set_caps (dvdec->audiosrcpad,
444                           GST_CAPS_NEW (
445                                   "dvdec_audio_caps",
446                                   "audio/raw",
447                                     "format",           GST_PROPS_STRING ("int"),
448                                     "rate",             GST_PROPS_INT (dvdec->decoder->audio->frequency),
449                                     "law",              GST_PROPS_INT (0),
450                                     "depth",            GST_PROPS_INT (16),
451                                     "width",            GST_PROPS_INT (16),
452                                     "signed",           GST_PROPS_BOOLEAN (TRUE),
453                                     "channels",         GST_PROPS_INT (dvdec->decoder->audio->num_channels),
454                                     "endianness",       GST_PROPS_INT (G_LITTLE_ENDIAN)
455                           ));
456   }
457   
458   format = GST_FORMAT_TIME;
459   gst_pad_query (dvdec->videosrcpad, GST_PAD_QUERY_POSITION, &format, &ts);
460
461   if (GST_PAD_IS_CONNECTED (dvdec->audiosrcpad)) {
462     gint16 *a_ptr;
463     gint i, j;
464
465     dv_decode_full_audio (dvdec->decoder, GST_BUFFER_DATA (buf), dvdec->audio_buffers);
466
467     outbuf = gst_buffer_new ();
468     GST_BUFFER_SIZE (outbuf) = dvdec->decoder->audio->samples_this_frame * sizeof (gint16) * dvdec->decoder->audio->num_channels;
469     GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
470
471     a_ptr = (gint16 *) GST_BUFFER_DATA (outbuf);
472
473     for (i = 0; i < dvdec->decoder->audio->samples_this_frame; i++) {
474       for (j = 0; j < dvdec->decoder->audio->num_channels; j++) {
475         *(a_ptr++) = dvdec->audio_buffers[j][i];
476       }
477     }
478     GST_BUFFER_TIMESTAMP (outbuf) = ts;
479
480     gst_pad_push (dvdec->audiosrcpad, outbuf);
481   }
482
483   if (GST_PAD_IS_CONNECTED (dvdec->videosrcpad)) {
484     guint8 *outframe;
485     guint8 *outframe_ptrs[3];
486     gint outframe_pitches[3];
487
488     /* try to grab a pool */
489     if (!dvdec->pool) {
490       dvdec->pool = gst_pad_get_bufferpool (dvdec->videosrcpad);
491     }
492
493     outbuf = NULL;
494     /* try to get a buffer from the pool if we have one */
495     if (dvdec->pool) {
496       outbuf = gst_buffer_new_from_pool (dvdec->pool, 0, 0);
497     }
498     /* no buffer from pool, allocate one ourselves */
499     if (!outbuf) {
500       outbuf = gst_buffer_new ();
501     
502       GST_BUFFER_SIZE (outbuf) = (720 * height) * dvdec->bpp;
503       GST_BUFFER_DATA (outbuf) = g_malloc (GST_BUFFER_SIZE (outbuf));
504     }
505     
506     outframe = GST_BUFFER_DATA (outbuf);
507
508     outframe_ptrs[0] = outframe;
509     outframe_ptrs[1] = outframe_ptrs[0] + 720 * height;
510     outframe_ptrs[2] = outframe_ptrs[1] + 360 * height;
511   
512     outframe_pitches[0] = 720 * dvdec->bpp;
513     outframe_pitches[1] = height / 2;
514     outframe_pitches[2] = height / 2;
515
516     dv_decode_full_frame (dvdec->decoder, GST_BUFFER_DATA (buf), 
517                           dvdec->space, outframe_ptrs, outframe_pitches);
518
519     GST_BUFFER_TIMESTAMP (outbuf) = ts;
520
521     gst_pad_push (dvdec->videosrcpad, outbuf);
522   }
523
524   /* FIXME this is inaccurate for NTSC */
525   dvdec->next_ts += GST_SECOND / (PAL ? 25 : 30);
526
527   gst_buffer_unref (buf);
528 }
529
530 static GstElementStateReturn
531 gst_dvdec_change_state (GstElement *element)
532 {
533   GstDVDec *dvdec = GST_DVDEC (element);
534             
535   switch (GST_STATE_TRANSITION (element)) {
536     case GST_STATE_NULL_TO_READY:
537       break;
538     case GST_STATE_READY_TO_PAUSED:
539       dvdec->bs = gst_bytestream_new (dvdec->sinkpad);
540       break;
541     case GST_STATE_PAUSED_TO_PLAYING:
542       break;
543     case GST_STATE_PLAYING_TO_PAUSED:
544       dvdec->pool = NULL;
545       break;
546     case GST_STATE_PAUSED_TO_READY:
547       gst_bytestream_destroy (dvdec->bs);
548       break;
549     case GST_STATE_READY_TO_NULL:
550       break;
551     default:
552       break;
553   }
554               
555   parent_class->change_state (element);
556                 
557   return GST_STATE_SUCCESS;
558 }
559
560 /* Arguments are part of the Gtk+ object system, and these functions
561  * enable the element to respond to various arguments.
562  */
563 static void
564 gst_dvdec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
565 {
566   GstDVDec *dvdec;
567
568   /* It's not null if we got it, but it might not be ours */
569   g_return_if_fail(GST_IS_DVDEC(object));
570
571   /* Get a pointer of the right type. */
572   dvdec = GST_DVDEC(object);
573
574   /* Check the argument id to see which argument we're setting. */
575   switch (prop_id) {
576     default:
577       break;
578   }
579 }
580
581 /* The set function is simply the inverse of the get fuction. */
582 static void
583 gst_dvdec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
584 {
585   GstDVDec *dvdec;
586
587   /* It's not null if we got it, but it might not be ours */
588   g_return_if_fail(GST_IS_DVDEC(object));
589   dvdec = GST_DVDEC(object);
590
591   switch (prop_id) {
592     default:
593       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
594       break;
595   }
596 }
597
598 /* This is the entry into the plugin itself.  When the plugin loads,
599  * this function is called to register everything that the plugin provides.
600  */
601 static gboolean
602 plugin_init (GModule *module, GstPlugin *plugin)
603 {
604   GstElementFactory *factory;
605   GstTypeFactory *type;
606
607   if (!gst_library_load ("gstbytestream")) {
608     gst_info("dvdec: could not load support library: 'gstbytestream'\n");
609     return FALSE;
610   }
611
612   /* We need to create an ElementFactory for each element we provide.
613    * This consists of the name of the element, the GType identifier,
614    * and a pointer to the details structure at the top of the file.
615    */
616   factory = gst_element_factory_new("dvdec", GST_TYPE_DVDEC, &dvdec_details);
617   g_return_val_if_fail(factory != NULL, FALSE);
618
619   gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_PRIMARY);
620   /* The pad templates can be easily generated from the factories above,
621    * and then added to the list of padtemplates for the elementfactory.
622    * Note that the generated padtemplates are stored in static global
623    * variables, for the gst_dvdec_init function to use later on.
624    */
625   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(sink_temp));
626   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(video_src_temp));
627   gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET(audio_src_temp));
628
629   /* The very last thing is to register the elementfactory with the plugin. */
630   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
631
632   type = gst_type_factory_new (&dv_definition);
633   gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
634
635   return TRUE;
636 }
637
638 GstPluginDesc plugin_desc = {
639   GST_VERSION_MAJOR,
640   GST_VERSION_MINOR,
641   "dvdec",
642   plugin_init
643 };
644