All plugins updated for element state changes.
[platform/upstream/gstreamer.git] / ext / dvdread / dvdreadsrc.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <sys/ioctl.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <errno.h>
37 //#include <linux/cdrom.h>
38 #include <assert.h>
39
40 #include "dvdreadsrc.h"
41 #include "stream_labels.h"
42
43 #include <dvdread/dvd_reader.h>
44 #include <dvdread/ifo_types.h>
45 #include <dvdread/ifo_read.h>
46 #include <dvdread/nav_read.h>
47 #include <dvdread/nav_print.h>
48
49 struct _DVDReadSrcPrivate
50 {
51   /* pads */
52   GstPad *srcpad;
53
54   /* location */
55   gchar *location;
56   gchar *last_uri;
57
58   gboolean new_seek;
59
60   gboolean new_cell;
61
62   int title, chapter, angle;
63   int pgc_id, start_cell, cur_cell, cur_pack;
64   int last_cell;
65   int ttn, pgn, next_cell;
66   dvd_reader_t *dvd;
67   dvd_file_t *dvd_title;
68   ifo_handle_t *vmg_file;
69   tt_srpt_t *tt_srpt;
70   ifo_handle_t *vts_file;
71   vts_ptt_srpt_t *vts_ptt_srpt;
72   pgc_t *cur_pgc;
73
74   /* where we are */
75   gboolean seek_pend, flush_pend;
76   GstFormat seek_pend_fmt;
77 };
78
79 GST_DEBUG_CATEGORY_STATIC (gstdvdreadsrc_debug);
80 #define GST_CAT_DEFAULT (gstdvdreadsrc_debug)
81
82 GstElementDetails dvdreadsrc_details = {
83   "DVD Source",
84   "Source/File/DVD",
85   "Access a DVD title/chapter/angle using libdvdread",
86   "Erik Walthinsen <omega@cse.ogi.edu>",
87 };
88
89
90 /* DVDReadSrc signals and args */
91 enum
92 {
93   /* FILL ME */
94   LAST_SIGNAL
95 };
96
97 enum
98 {
99   ARG_0,
100   ARG_LOCATION,
101   ARG_DEVICE,
102   ARG_TITLE,
103   ARG_CHAPTER,
104   ARG_ANGLE
105 };
106
107 static void dvdreadsrc_base_init (gpointer g_class);
108 static void dvdreadsrc_class_init (DVDReadSrcClass * klass);
109 static void dvdreadsrc_init (DVDReadSrc * dvdreadsrc);
110 static void dvdreadsrc_finalize (GObject * object);
111
112 static void dvdreadsrc_set_property (GObject * object, guint prop_id,
113     const GValue * value, GParamSpec * pspec);
114 static void dvdreadsrc_get_property (GObject * object, guint prop_id,
115     GValue * value, GParamSpec * pspec);
116
117 static const GstEventMask *dvdreadsrc_get_event_mask (GstPad * pad);
118 static const GstQueryType *dvdreadsrc_get_query_types (GstPad * pad);
119 static const GstFormat *dvdreadsrc_get_formats (GstPad * pad);
120 static gboolean dvdreadsrc_srcpad_event (GstPad * pad, GstEvent * event);
121 static gboolean dvdreadsrc_srcpad_query (GstPad * pad, GstQueryType type,
122     GstFormat * format, gint64 * value);
123
124 static GstData *dvdreadsrc_get (GstPad * pad);
125 static GstStateChangeReturn dvdreadsrc_change_state (GstElement * element,
126     GstStateChange transition);
127
128 static void dvdreadsrc_uri_handler_init (gpointer g_iface, gpointer iface_data);
129
130
131 static GstElementClass *parent_class = NULL;
132
133 static GstFormat sector_format, angle_format, title_format, chapter_format;
134
135 /*static guint dvdreadsrc_signals[LAST_SIGNAL] = { 0 }; */
136
137 GType
138 dvdreadsrc_get_type (void)
139 {
140   static GType dvdreadsrc_type = 0;
141
142   if (!dvdreadsrc_type) {
143     static const GTypeInfo dvdreadsrc_info = {
144       sizeof (DVDReadSrcClass),
145       dvdreadsrc_base_init,
146       NULL,
147       (GClassInitFunc) dvdreadsrc_class_init,
148       NULL,
149       NULL,
150       sizeof (DVDReadSrc),
151       0,
152       (GInstanceInitFunc) dvdreadsrc_init,
153     };
154     static const GInterfaceInfo urihandler_info = {
155       dvdreadsrc_uri_handler_init,
156       NULL,
157       NULL
158     };
159
160     sector_format = gst_format_register ("sector", "DVD sector");
161     title_format = gst_format_register ("title", "DVD title");
162     chapter_format = gst_format_register ("chapter", "DVD chapter");
163     angle_format = gst_format_register ("angle", "DVD angle");
164
165     dvdreadsrc_type =
166         g_type_register_static (GST_TYPE_ELEMENT, "DVDReadSrc",
167         &dvdreadsrc_info, 0);
168     g_type_add_interface_static (dvdreadsrc_type,
169         GST_TYPE_URI_HANDLER, &urihandler_info);
170   }
171
172   return dvdreadsrc_type;
173 }
174
175 static void
176 dvdreadsrc_base_init (gpointer g_class)
177 {
178   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
179
180   gst_element_class_set_details (element_class, &dvdreadsrc_details);
181 }
182
183 static void
184 dvdreadsrc_class_init (DVDReadSrcClass * klass)
185 {
186   GObjectClass *gobject_class;
187   GstElementClass *gstelement_class;
188
189   gobject_class = (GObjectClass *) klass;
190   gstelement_class = (GstElementClass *) klass;
191
192   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
193
194   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
195       g_param_spec_string ("location", "Location",
196           "DVD device location (deprecated; use device)",
197           NULL, G_PARAM_READWRITE));
198   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
199       g_param_spec_string ("device", "Device",
200           "DVD device location", NULL, G_PARAM_READWRITE));
201   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TITLE,
202       g_param_spec_int ("title", "title", "title",
203           0, G_MAXINT, 0, G_PARAM_READWRITE));
204   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHAPTER,
205       g_param_spec_int ("chapter", "chapter", "chapter",
206           0, G_MAXINT, 0, G_PARAM_READWRITE));
207   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
208       g_param_spec_int ("angle", "angle", "angle",
209           0, G_MAXINT, 0, G_PARAM_READWRITE));
210
211   gobject_class->set_property = GST_DEBUG_FUNCPTR (dvdreadsrc_set_property);
212   gobject_class->get_property = GST_DEBUG_FUNCPTR (dvdreadsrc_get_property);
213
214   gobject_class->finalize = dvdreadsrc_finalize;
215
216   gstelement_class->change_state = dvdreadsrc_change_state;
217
218   GST_DEBUG_CATEGORY_INIT (gstdvdreadsrc_debug, "dvdreadsrc", 0,
219       "DVD reader element based on dvdreadsrc");
220 }
221
222 static void
223 dvdreadsrc_init (DVDReadSrc * dvdreadsrc)
224 {
225   dvdreadsrc->priv = g_new (DVDReadSrcPrivate, 1);
226   dvdreadsrc->priv->srcpad = gst_pad_new ("src", GST_PAD_SRC);
227   gst_pad_set_get_function (dvdreadsrc->priv->srcpad, dvdreadsrc_get);
228   gst_pad_set_event_function (dvdreadsrc->priv->srcpad,
229       dvdreadsrc_srcpad_event);
230   gst_pad_set_event_mask_function (dvdreadsrc->priv->srcpad,
231       dvdreadsrc_get_event_mask);
232   gst_pad_set_query_function (dvdreadsrc->priv->srcpad,
233       dvdreadsrc_srcpad_query);
234   gst_pad_set_query_type_function (dvdreadsrc->priv->srcpad,
235       dvdreadsrc_get_query_types);
236   gst_pad_set_formats_function (dvdreadsrc->priv->srcpad,
237       dvdreadsrc_get_formats);
238   gst_element_add_pad (GST_ELEMENT (dvdreadsrc), dvdreadsrc->priv->srcpad);
239
240   dvdreadsrc->priv->dvd = NULL;
241   dvdreadsrc->priv->vts_file = NULL;
242   dvdreadsrc->priv->vmg_file = NULL;
243   dvdreadsrc->priv->dvd_title = NULL;
244
245   dvdreadsrc->priv->location = g_strdup ("/dev/dvd");
246   dvdreadsrc->priv->last_uri = NULL;
247   dvdreadsrc->priv->new_seek = TRUE;
248   dvdreadsrc->priv->new_cell = TRUE;
249   dvdreadsrc->priv->title = 0;
250   dvdreadsrc->priv->chapter = 0;
251   dvdreadsrc->priv->angle = 0;
252
253   dvdreadsrc->priv->seek_pend = FALSE;
254   dvdreadsrc->priv->flush_pend = FALSE;
255   dvdreadsrc->priv->seek_pend_fmt = GST_FORMAT_UNDEFINED;
256 }
257
258 static void
259 dvdreadsrc_finalize (GObject * object)
260 {
261   DVDReadSrc *dvdreadsrc = DVDREADSRC (object);
262
263   if (dvdreadsrc->priv) {
264     g_free (dvdreadsrc->priv->location);
265     g_free (dvdreadsrc->priv->last_uri);
266     g_free (dvdreadsrc->priv);
267     dvdreadsrc->priv = NULL;
268   }
269   G_OBJECT_CLASS (parent_class)->finalize (object);
270 }
271
272 static void
273 dvdreadsrc_set_property (GObject * object, guint prop_id, const GValue * value,
274     GParamSpec * pspec)
275 {
276   DVDReadSrc *src;
277   DVDReadSrcPrivate *priv;
278
279   g_return_if_fail (GST_IS_DVDREADSRC (object));
280
281   src = DVDREADSRC (object);
282   priv = src->priv;
283
284   switch (prop_id) {
285     case ARG_LOCATION:
286     case ARG_DEVICE:
287       /* the element must be stopped in order to do this */
288       /*g_return_if_fail(!GST_FLAG_IS_SET(src,GST_STATE_RUNNING)); */
289
290       g_free (priv->location);
291       /* clear the filename if we get a NULL (is that possible?) */
292       if (g_value_get_string (value) == NULL)
293         priv->location = g_strdup ("/dev/dvd");
294       /* otherwise set the new filename */
295       else
296         priv->location = g_strdup (g_value_get_string (value));
297       break;
298     case ARG_TITLE:
299       priv->title = g_value_get_int (value);
300       priv->new_seek = TRUE;
301       break;
302     case ARG_CHAPTER:
303       priv->chapter = g_value_get_int (value);
304       priv->new_seek = TRUE;
305       break;
306     case ARG_ANGLE:
307       priv->angle = g_value_get_int (value);
308       break;
309     default:
310       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
311       break;
312   }
313
314 }
315
316 static void
317 dvdreadsrc_get_property (GObject * object, guint prop_id, GValue * value,
318     GParamSpec * pspec)
319 {
320   DVDReadSrc *src;
321   DVDReadSrcPrivate *priv;
322
323   g_return_if_fail (GST_IS_DVDREADSRC (object));
324
325   src = DVDREADSRC (object);
326   priv = src->priv;
327
328   switch (prop_id) {
329     case ARG_DEVICE:
330     case ARG_LOCATION:
331       g_value_set_string (value, priv->location);
332       break;
333     case ARG_TITLE:
334       g_value_set_int (value, priv->title);
335       break;
336     case ARG_CHAPTER:
337       g_value_set_int (value, priv->chapter);
338       break;
339     case ARG_ANGLE:
340       g_value_set_int (value, priv->angle);
341       break;
342     default:
343       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
344       break;
345   }
346 }
347
348 /*
349  * Querying and seeking.
350  */
351
352 static const GstEventMask *
353 dvdreadsrc_get_event_mask (GstPad * pad)
354 {
355   static const GstEventMask masks[] = {
356     {GST_EVENT_SEEK, GST_SEEK_METHOD_CUR |
357           GST_SEEK_METHOD_SET | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
358     {0, 0}
359   };
360
361   return masks;
362 }
363
364 static const GstQueryType *
365 dvdreadsrc_get_query_types (GstPad * pad)
366 {
367   static const GstQueryType types[] = {
368     GST_QUERY_TOTAL,
369     GST_QUERY_POSITION,
370     0
371   };
372
373   return types;
374 }
375
376 static const GstFormat *
377 dvdreadsrc_get_formats (GstPad * pad)
378 {
379   static GstFormat formats[] = {
380     GST_FORMAT_BYTES,
381     0, 0, 0, 0,                 /* init later */
382     0,
383   };
384   if (formats[1] == 0) {
385     formats[1] = sector_format;
386     formats[2] = angle_format;
387     formats[3] = title_format;
388     formats[4] = chapter_format;
389   }
390
391   return formats;
392 }
393
394 static gboolean
395 dvdreadsrc_srcpad_event (GstPad * pad, GstEvent * event)
396 {
397   DVDReadSrc *dvdreadsrc = DVDREADSRC (gst_pad_get_parent (pad));
398   gboolean res = TRUE;
399
400   switch (GST_EVENT_TYPE (event)) {
401     case GST_EVENT_SEEK:{
402       gint64 new_off, total, cur;
403       GstFormat fmt;
404
405       /* get requested offset */
406       new_off = GST_EVENT_SEEK_OFFSET (event);
407       switch (GST_EVENT_SEEK_FORMAT (event)) {
408         case GST_FORMAT_BYTES:
409           new_off /= DVD_VIDEO_LB_LEN;
410           fmt = sector_format;
411           break;
412         default:
413           fmt = GST_EVENT_SEEK_FORMAT (event);
414           if (fmt == sector_format ||
415               fmt == angle_format ||
416               fmt == title_format || fmt == chapter_format)
417             break;
418           GST_LOG ("Unsupported seek format");
419           return FALSE;
420       }
421
422       /* get current offset and length */
423       gst_pad_query (pad, GST_QUERY_TOTAL, &fmt, &total);
424       gst_pad_query (pad, GST_QUERY_POSITION, &fmt, &cur);
425       if (cur == new_off) {
426         GST_LOG ("We're already at that position!");
427         return TRUE;
428       }
429
430       /* get absolute */
431       switch (GST_EVENT_SEEK_METHOD (event)) {
432         case GST_SEEK_METHOD_SET:
433           /* no-op */
434           break;
435         case GST_SEEK_METHOD_CUR:
436           new_off += cur;
437           break;
438         case GST_SEEK_METHOD_END:
439           new_off = total - new_off;
440           break;
441         default:
442           GST_LOG ("Unsupported seek method");
443           return FALSE;
444       }
445       if (new_off < 0 || new_off >= total) {
446         GST_LOG ("Invalid seek position");
447         return FALSE;
448       }
449
450       GST_LOG ("Seeking to unit %d in format %d", new_off, fmt);
451
452       if (fmt == sector_format || fmt == chapter_format || fmt == title_format) {
453         if (fmt == sector_format) {
454           dvdreadsrc->priv->cur_pack = new_off;
455         } else if (fmt == chapter_format) {
456           dvdreadsrc->priv->cur_pack = 0;
457           dvdreadsrc->priv->chapter = new_off;
458           dvdreadsrc->priv->seek_pend_fmt = fmt;
459         } else if (fmt == title_format) {
460           dvdreadsrc->priv->cur_pack = 0;
461           dvdreadsrc->priv->title = new_off;
462           dvdreadsrc->priv->chapter = 0;
463           dvdreadsrc->priv->seek_pend_fmt = fmt;
464         }
465
466         /* leave for events */
467         dvdreadsrc->priv->seek_pend = TRUE;
468         if (GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH)
469           dvdreadsrc->priv->flush_pend = TRUE;
470       } else if (fmt == angle_format) {
471         dvdreadsrc->priv->angle = new_off;
472       }
473
474       break;
475     }
476     default:
477       res = FALSE;
478       break;
479   }
480
481   gst_event_unref (event);
482
483   return res;
484 }
485
486 static gboolean
487 dvdreadsrc_srcpad_query (GstPad * pad, GstQueryType type,
488     GstFormat * format, gint64 * value)
489 {
490   DVDReadSrc *dvdreadsrc = DVDREADSRC (gst_pad_get_parent (pad));
491   DVDReadSrcPrivate *priv = dvdreadsrc->priv;
492   gboolean res = TRUE;
493
494   if (!GST_FLAG_IS_SET (dvdreadsrc, DVDREADSRC_OPEN))
495     return FALSE;
496
497   switch (type) {
498     case GST_QUERY_TOTAL:
499       switch (*format) {
500         case GST_FORMAT_BYTES:
501           *value = DVDFileSize (priv->dvd_title) * DVD_VIDEO_LB_LEN;
502           break;
503         default:
504           if (*format == sector_format) {
505             *value = DVDFileSize (priv->dvd_title);
506           } else if (*format == title_format) {
507             *value = priv->tt_srpt->nr_of_srpts;
508           } else if (*format == chapter_format) {
509             *value = priv->tt_srpt->title[priv->title].nr_of_ptts;
510           } else if (*format == angle_format) {
511             *value = priv->tt_srpt->title[priv->title].nr_of_angles;
512           } else {
513             GST_LOG ("Unknown format");
514             res = FALSE;
515           }
516           break;
517       }
518       break;
519     case GST_QUERY_POSITION:
520       switch (*format) {
521         case GST_FORMAT_BYTES:
522           *value = priv->cur_pack * DVD_VIDEO_LB_LEN;
523           break;
524         default:
525           if (*format == sector_format) {
526             *value = priv->cur_pack;
527           } else if (*format == title_format) {
528             *value = priv->title;
529           } else if (*format == chapter_format) {
530             *value = priv->chapter;
531           } else if (*format == angle_format) {
532             *value = priv->angle;
533           } else {
534             GST_LOG ("Unknown format");
535             res = FALSE;
536           }
537           break;
538       }
539       break;
540     default:
541       res = FALSE;
542       break;
543   }
544
545   return res;
546 }
547
548 /*
549  * Returns true if the pack is a NAV pack.  This check is clearly insufficient,
550  * and sometimes we incorrectly think that valid other packs are NAV packs.  I
551  * need to make this stronger.
552  */
553 static int
554 is_nav_pack (unsigned char *buffer)
555 {
556   return (buffer[41] == 0xbf && buffer[1027] == 0xbf);
557 }
558
559 static int
560 _close (DVDReadSrcPrivate * priv)
561 {
562   ifoClose (priv->vts_file);
563   priv->vts_file = NULL;
564
565   ifoClose (priv->vmg_file);
566   priv->vmg_file = NULL;
567
568   DVDCloseFile (priv->dvd_title);
569   priv->dvd_title = NULL;
570
571   DVDClose (priv->dvd);
572   priv->dvd = NULL;
573
574   return 0;
575 }
576
577 static int
578 _open (DVDReadSrcPrivate * priv, const gchar * location)
579 {
580   g_return_val_if_fail (priv != NULL, -1);
581   g_return_val_if_fail (location != NULL, -1);
582
583   /*
584    * Open the disc.
585    */
586   priv->dvd = DVDOpen (location);
587   if (!priv->dvd) {
588     GST_ERROR ("Couldn't open DVD: %s", location);
589     return -1;
590   }
591
592
593   /*
594    * Load the video manager to find out the information about the titles on
595    * this disc.
596    */
597   priv->vmg_file = ifoOpen (priv->dvd, 0);
598   if (!priv->vmg_file) {
599     GST_ERROR ("Can't open VMG info");
600     return -1;
601   }
602   priv->tt_srpt = priv->vmg_file->tt_srpt;
603
604   return 0;
605 }
606
607 static int
608 _seek_title (DVDReadSrcPrivate * priv, int title, int angle)
609 {
610   GHashTable *languagelist = NULL;
611
612   /*
613    * Make sure our title number is valid.
614    */
615   GST_LOG ("There are %d titles on this DVD", priv->tt_srpt->nr_of_srpts);
616   if (title < 0 || title >= priv->tt_srpt->nr_of_srpts) {
617     GST_WARNING ("Invalid title %d (only %d available)",
618         title, priv->tt_srpt->nr_of_srpts);
619
620     if (title < 0)
621       title = 0;
622     else
623       title = priv->tt_srpt->nr_of_srpts - 1;
624   }
625
626   GST_LOG ("There are %d chapters in this title",
627       priv->tt_srpt->title[title].nr_of_ptts);
628
629   /*
630    * Make sure the angle number is valid for this title.
631    */
632   GST_LOG ("There are %d angles available in this title",
633       priv->tt_srpt->title[title].nr_of_angles);
634
635   if (angle < 0 || angle >= priv->tt_srpt->title[title].nr_of_angles) {
636     GST_WARNING ("Invalid angle %d (only %d available)",
637         angle, priv->tt_srpt->title[title].nr_of_angles);
638     if (angle < 0)
639       angle = 0;
640     else
641       angle = priv->tt_srpt->title[title].nr_of_angles - 1;
642   }
643
644   /*
645    * Load the VTS information for the title set our title is in.
646    */
647   priv->vts_file =
648       ifoOpen (priv->dvd, priv->tt_srpt->title[title].title_set_nr);
649   if (!priv->vts_file) {
650     GST_ERROR ("Can't open the info file of title %d",
651         priv->tt_srpt->title[title].title_set_nr);
652     _close (priv);
653     return -1;
654   }
655
656   priv->ttn = priv->tt_srpt->title[title].vts_ttn;
657   priv->vts_ptt_srpt = priv->vts_file->vts_ptt_srpt;
658
659   /*
660    * We've got enough info, time to open the title set data.
661    */
662   priv->dvd_title =
663       DVDOpenFile (priv->dvd, priv->tt_srpt->title[title].title_set_nr,
664       DVD_READ_TITLE_VOBS);
665   if (!priv->dvd_title) {
666     GST_ERROR ("Can't open title VOBS (VTS_%02d_1.VOB)",
667         priv->tt_srpt->title[title].title_set_nr);
668     _close (priv);
669     return -1;
670   }
671
672   /* Get stream labels for all audio and subtitle streams */
673   languagelist = dvdreadsrc_init_languagelist ();
674
675   dvdreadsrc_get_audio_stream_labels (priv->vts_file, languagelist);
676   dvdreadsrc_get_subtitle_stream_labels (priv->vts_file, languagelist);
677
678   g_hash_table_destroy (languagelist);
679
680   GST_LOG ("Opened title %d, angle %d", title, angle);
681   priv->title = title;
682   priv->angle = angle;
683
684   return 0;
685 }
686
687 static int
688 _seek_chapter (DVDReadSrcPrivate * priv, int chapter)
689 {
690   int i;
691
692   /*
693    * Make sure the chapter number is valid for this title.
694    */
695   if (chapter < 0 || chapter >= priv->tt_srpt->title[priv->title].nr_of_ptts) {
696     GST_WARNING ("Invalid chapter %d (only %d available)",
697         chapter, priv->tt_srpt->title[priv->title].nr_of_ptts);
698     if (chapter < 0)
699       chapter = 0;
700     chapter = priv->tt_srpt->title[priv->title].nr_of_ptts - 1;
701   }
702
703   /*
704    * Determine which program chain we want to watch.  This is based on the
705    * chapter number.
706    */
707   priv->pgc_id = priv->vts_ptt_srpt->title[priv->ttn - 1].ptt[chapter].pgcn;
708   priv->pgn = priv->vts_ptt_srpt->title[priv->ttn - 1].ptt[chapter].pgn;
709   priv->cur_pgc = priv->vts_file->vts_pgcit->pgci_srp[priv->pgc_id - 1].pgc;
710   priv->start_cell = priv->cur_pgc->program_map[priv->pgn - 1] - 1;
711
712   if (chapter + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
713     priv->last_cell = priv->cur_pgc->nr_of_cells;
714   } else {
715     priv->last_cell =
716         priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
717                 1].ptt[chapter + 1].pgn) - 1] - 1;
718   }
719
720   GST_LOG ("Opened chapter %d - cell %d-%d",
721       chapter, priv->start_cell, priv->last_cell);
722
723   /* retrieve position */
724   priv->cur_pack = 0;
725   for (i = 0; i < chapter; i++) {
726     gint c1, c2;
727
728     c1 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
729                 1].ptt[i].pgn) - 1] - 1;
730     if (i + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
731       c2 = priv->cur_pgc->nr_of_cells;
732     } else {
733       c2 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
734                   1].ptt[i + 1].pgn) - 1] - 1;
735     }
736
737     for (; c1 < c2; c1++) {
738       priv->cur_pack +=
739           priv->cur_pgc->cell_playback[c1].last_sector -
740           priv->cur_pgc->cell_playback[c1].first_sector;
741     }
742   }
743
744   /* prepare reading for new cell */
745   priv->new_cell = TRUE;
746   priv->next_cell = priv->start_cell;
747
748   priv->chapter = chapter;
749   return 0;
750 }
751
752 static int
753 get_next_cell_for (DVDReadSrcPrivate * priv, int cell)
754 {
755   /* Check if we're entering an angle block. */
756   if (priv->cur_pgc->cell_playback[cell].block_type == BLOCK_TYPE_ANGLE_BLOCK) {
757     int i;
758
759     for (i = 0;; ++i) {
760       if (priv->cur_pgc->cell_playback[cell + i].block_mode
761           == BLOCK_MODE_LAST_CELL) {
762         return cell + i + 1;
763       }
764     }
765
766     /* not reached */
767   }
768
769   return cell + 1;
770 }
771
772 /*
773  * Read function.
774  * -1: error, -2: eos, -3: try again, 0: ok.
775  */
776
777 static int
778 _read (DVDReadSrcPrivate * priv, int angle, int new_seek, GstBuffer * buf)
779 {
780   unsigned char *data, static_data[DVD_VIDEO_LB_LEN];
781
782   if (buf) {
783     data = GST_BUFFER_DATA (buf);
784   } else {
785     data = static_data;
786   }
787
788   /*
789    * Playback by cell in this pgc, starting at the cell for our chapter.
790    */
791   if (new_seek)
792     priv->cur_cell = priv->start_cell;
793
794 again:
795
796   if (priv->cur_cell < priv->last_cell) {
797     if (priv->new_cell || new_seek) {
798       if (!new_seek) {
799         priv->cur_cell = priv->next_cell;
800         if (priv->cur_cell >= priv->last_cell) {
801           GST_LOG ("last cell in chapter");
802           goto again;
803         }
804       }
805
806       /* take angle into account */
807       if (priv->cur_pgc->cell_playback[priv->cur_cell].block_type
808           == BLOCK_TYPE_ANGLE_BLOCK)
809         priv->cur_cell += angle;
810
811       /* calculate next cell */
812       priv->next_cell = get_next_cell_for (priv, priv->cur_cell);
813
814       /*
815        * We loop until we're out of this cell.
816        */
817       priv->cur_pack =
818           priv->cur_pgc->cell_playback[priv->cur_cell].first_sector;
819       priv->new_cell = FALSE;
820     }
821
822     if (priv->cur_pack <
823         priv->cur_pgc->cell_playback[priv->cur_cell].last_sector) {
824       dsi_t dsi_pack;
825       unsigned int next_vobu, next_ilvu_start, cur_output_size;
826       int len;
827
828       /*
829        * Read NAV packet.
830        */
831     nav_retry:
832
833       len = DVDReadBlocks (priv->dvd_title, priv->cur_pack, 1, data);
834       if (len == 0) {
835         GST_ERROR ("Read failed for block %d", priv->cur_pack);
836         return -1;
837       }
838
839       if (!is_nav_pack (data)) {
840         priv->cur_pack++;
841         goto nav_retry;
842       }
843
844
845       /*
846        * Parse the contained dsi packet.
847        */
848       navRead_DSI (&dsi_pack, &(data[DSI_START_BYTE]));
849       assert (priv->cur_pack == dsi_pack.dsi_gi.nv_pck_lbn);
850
851
852       /*
853        * Determine where we go next.  These values are the ones we mostly
854        * care about.
855        */
856       next_ilvu_start = priv->cur_pack + dsi_pack.sml_agli.data[angle].address;
857       cur_output_size = dsi_pack.dsi_gi.vobu_ea;
858
859
860       /*
861        * If we're not at the end of this cell, we can determine the next
862        * VOBU to display using the VOBU_SRI information section of the
863        * DSI.  Using this value correctly follows the current angle,
864        * avoiding the doubled scenes in The Matrix, and makes our life
865        * really happy.
866        *
867        * Otherwise, we set our next address past the end of this cell to
868        * force the code above to go to the next cell in the program.
869        */
870       if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
871         next_vobu = priv->cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
872       } else {
873         next_vobu = priv->cur_pack + cur_output_size + 1;
874       }
875
876       assert (cur_output_size < 1024);
877       priv->cur_pack++;
878
879       if (buf) {
880         /*
881          * Read in and output cursize packs.
882          */
883         len =
884             DVDReadBlocks (priv->dvd_title, priv->cur_pack, cur_output_size,
885             data);
886         if (len != cur_output_size) {
887           GST_ERROR ("Read failed for %d blocks at %d",
888               cur_output_size, priv->cur_pack);
889           return -1;
890         }
891
892         GST_BUFFER_SIZE (buf) = cur_output_size * DVD_VIDEO_LB_LEN;
893         //GST_BUFFER_OFFSET (buf) = priv->cur_pack * DVD_VIDEO_LB_LEN;
894       }
895
896       priv->cur_pack = next_vobu;
897
898       GST_LOG ("done reading data - %u sectors", cur_output_size);
899
900       return 0;
901     } else {
902       priv->new_cell = TRUE;
903     }
904   } else {
905     /* swap to next chapter */
906     if (priv->chapter + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
907       GST_LOG ("last chapter done - eos");
908       return -2;
909     }
910
911     GST_LOG ("end-of-chapter, switch to next");
912
913     priv->chapter++;
914     _seek_chapter (priv, priv->chapter);
915   }
916
917   /* again */
918   GST_LOG ("Need another try");
919
920   return -3;
921 }
922
923 static gboolean
924 seek_sector (DVDReadSrcPrivate * priv, int angle)
925 {
926   gint seek_to = priv->cur_pack;
927   gint chapter, sectors, next, cur, i;
928
929   /* retrieve position */
930   priv->cur_pack = 0;
931   for (i = 0; i < priv->tt_srpt->title[priv->title].nr_of_ptts; i++) {
932     gint c1, c2;
933
934     c1 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
935                 1].ptt[i].pgn) - 1] - 1;
936     if (i + 1 == priv->tt_srpt->title[priv->title].nr_of_ptts) {
937       c2 = priv->cur_pgc->nr_of_cells;
938     } else {
939       c2 = priv->cur_pgc->program_map[(priv->vts_ptt_srpt->title[priv->ttn -
940                   1].ptt[i + 1].pgn) - 1] - 1;
941     }
942
943     for (next = cur = c1; cur < c2;) {
944       if (next != cur) {
945         sectors =
946             priv->cur_pgc->cell_playback[cur].last_sector -
947             priv->cur_pgc->cell_playback[cur].first_sector;
948         if (priv->cur_pack + sectors > seek_to) {
949           chapter = i;
950           goto done;
951         }
952         priv->cur_pack += sectors;
953       }
954       cur = next;
955       if (priv->cur_pgc->cell_playback[cur].block_type
956           == BLOCK_TYPE_ANGLE_BLOCK)
957         cur += angle;
958       next = get_next_cell_for (priv, cur);
959     }
960   }
961
962   GST_LOG ("Seek to sector %u failed", seek_to);
963   return FALSE;
964
965 done:
966   /* so chapter $chapter and cell $cur contain our sector
967    * of interest. Let's go there! */
968   GST_LOG ("Seek succeeded, going to chapter %u, cell %u", chapter, cur);
969
970   _seek_chapter (priv, chapter);
971   priv->cur_cell = cur;
972   priv->next_cell = next;
973   priv->new_cell = FALSE;
974   priv->cur_pack = seek_to;
975
976   return TRUE;
977 }
978
979 static GstData *
980 dvdreadsrc_get (GstPad * pad)
981 {
982   gint res;
983   DVDReadSrc *dvdreadsrc;
984   DVDReadSrcPrivate *priv;
985   GstBuffer *buf;
986
987   g_return_val_if_fail (pad != NULL, NULL);
988   g_return_val_if_fail (GST_IS_PAD (pad), NULL);
989
990   dvdreadsrc = DVDREADSRC (gst_pad_get_parent (pad));
991   priv = dvdreadsrc->priv;
992   g_return_val_if_fail (GST_FLAG_IS_SET (dvdreadsrc, DVDREADSRC_OPEN), NULL);
993
994   /* handle vents, if any */
995   if (priv->seek_pend) {
996     if (priv->flush_pend) {
997       priv->flush_pend = FALSE;
998
999       return GST_DATA (gst_event_new (GST_EVENT_FLUSH));
1000     }
1001
1002     priv->seek_pend = FALSE;
1003     if (priv->seek_pend_fmt != GST_FORMAT_UNDEFINED) {
1004       if (priv->seek_pend_fmt == title_format) {
1005         _seek_title (priv, priv->title, priv->angle);
1006       }
1007       _seek_chapter (priv, priv->chapter);
1008
1009       priv->seek_pend_fmt = GST_FORMAT_UNDEFINED;
1010     } else {
1011       if (!seek_sector (priv, priv->angle)) {
1012         gst_element_set_eos (GST_ELEMENT (dvdreadsrc));
1013         return GST_DATA (gst_event_new (GST_EVENT_EOS));
1014       }
1015     }
1016
1017     return GST_DATA (gst_event_new_discontinuous (FALSE,
1018             GST_FORMAT_BYTES, (gint64) (priv->cur_pack * DVD_VIDEO_LB_LEN),
1019             GST_FORMAT_UNDEFINED));
1020   }
1021
1022   /* create the buffer */
1023   /* FIXME: should eventually use a bufferpool for this */
1024   buf = gst_buffer_new_and_alloc (1024 * DVD_VIDEO_LB_LEN);
1025
1026   if (priv->new_seek) {
1027     _seek_title (priv, priv->title, priv->angle);
1028     _seek_chapter (priv, priv->chapter);
1029   }
1030
1031   /* read it in from the file */
1032   while ((res = _read (priv, priv->angle, priv->new_seek, buf)) == -3);
1033   switch (res) {
1034     case -1:
1035       GST_ELEMENT_ERROR (dvdreadsrc, RESOURCE, READ, (NULL), (NULL));
1036       gst_buffer_unref (buf);
1037       return NULL;
1038     case -2:
1039       gst_element_set_eos (GST_ELEMENT (dvdreadsrc));
1040       gst_buffer_unref (buf);
1041       return GST_DATA (gst_event_new (GST_EVENT_EOS));
1042     case 0:
1043       break;
1044     default:
1045       g_assert_not_reached ();
1046   }
1047
1048   priv->new_seek = FALSE;
1049
1050   return GST_DATA (buf);
1051 }
1052
1053 /* open the file, necessary to go to RUNNING state */
1054 static gboolean
1055 dvdreadsrc_open_file (DVDReadSrc * src)
1056 {
1057   g_return_val_if_fail (src != NULL, FALSE);
1058   g_return_val_if_fail (GST_IS_DVDREADSRC (src), FALSE);
1059   g_return_val_if_fail (!GST_FLAG_IS_SET (src, DVDREADSRC_OPEN), FALSE);
1060
1061   if (_open (src->priv, src->priv->location)) {
1062     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), (NULL));
1063     return FALSE;
1064   }
1065   src->priv->seek_pend_fmt = title_format;
1066   src->priv->seek_pend = TRUE;
1067
1068   GST_FLAG_SET (src, DVDREADSRC_OPEN);
1069
1070   return TRUE;
1071 }
1072
1073 /* close the file */
1074 static void
1075 dvdreadsrc_close_file (DVDReadSrc * src)
1076 {
1077   g_return_if_fail (GST_FLAG_IS_SET (src, DVDREADSRC_OPEN));
1078
1079   _close (src->priv);
1080
1081   GST_FLAG_UNSET (src, DVDREADSRC_OPEN);
1082 }
1083
1084 static GstStateChangeReturn
1085 dvdreadsrc_change_state (GstElement * element, GstStateChange transition)
1086 {
1087   DVDReadSrc *dvdreadsrc = DVDREADSRC (element);
1088
1089   g_return_val_if_fail (GST_IS_DVDREADSRC (element), GST_STATE_CHANGE_FAILURE);
1090
1091   GST_DEBUG ("gstdvdreadsrc: state pending %d", GST_STATE_PENDING (element));
1092
1093   /* if going down into NULL state, close the file if it's open */
1094   switch (transition) {
1095     case GST_STATE_CHANGE_NULL_TO_READY:
1096       if (!dvdreadsrc_open_file (DVDREADSRC (element)))
1097         return GST_STATE_CHANGE_FAILURE;
1098       break;
1099     case GST_STATE_CHANGE_PAUSED_TO_READY:
1100       dvdreadsrc->priv->new_cell = TRUE;
1101       dvdreadsrc->priv->new_seek = TRUE;
1102       dvdreadsrc->priv->chapter = 0;
1103       dvdreadsrc->priv->title = 0;
1104       dvdreadsrc->priv->flush_pend = FALSE;
1105       dvdreadsrc->priv->seek_pend = FALSE;
1106       dvdreadsrc->priv->seek_pend_fmt = GST_FORMAT_UNDEFINED;
1107       break;
1108     case GST_STATE_CHANGE_READY_TO_NULL:
1109       dvdreadsrc_close_file (DVDREADSRC (element));
1110       break;
1111     default:
1112       break;
1113   }
1114
1115   /* if we haven't failed already, give the parent class a chance to ;-) */
1116   if (GST_ELEMENT_CLASS (parent_class)->change_state)
1117     return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1118
1119   return GST_STATE_CHANGE_SUCCESS;
1120 }
1121
1122 /*
1123  * URI interface.
1124  */
1125
1126 static GstURIType
1127 dvdreadsrc_uri_get_type (void)
1128 {
1129   return GST_URI_SRC;
1130 }
1131
1132 static gchar **
1133 dvdreadsrc_uri_get_protocols (void)
1134 {
1135   static gchar *protocols[] = { "dvd", NULL };
1136
1137   return protocols;
1138 }
1139
1140 static const gchar *
1141 dvdreadsrc_uri_get_uri (GstURIHandler * handler)
1142 {
1143   DVDReadSrc *dvdreadsrc = DVDREADSRC (handler);
1144
1145   g_free (dvdreadsrc->priv->last_uri);
1146   dvdreadsrc->priv->last_uri =
1147       g_strdup_printf ("dvd://%d,%d,%d", dvdreadsrc->priv->title,
1148       dvdreadsrc->priv->chapter, dvdreadsrc->priv->angle);
1149
1150   return dvdreadsrc->priv->last_uri;
1151 }
1152
1153 static gboolean
1154 dvdreadsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
1155 {
1156   DVDReadSrc *dvdreadsrc = DVDREADSRC (handler);
1157   gboolean ret;
1158   gchar *protocol = gst_uri_get_protocol (uri);
1159
1160   ret = (protocol && !strcmp (protocol, "dvd")) ? TRUE : FALSE;
1161   g_free (protocol);
1162   protocol = NULL;
1163
1164   if (!ret)
1165     return ret;
1166
1167   /*
1168    * Parse out the new t/c/a and seek to them
1169    */
1170   {
1171     gchar *location = NULL;
1172     gchar **strs;
1173     gchar **strcur;
1174     gint pos = 0;
1175
1176     location = gst_uri_get_location (uri);
1177
1178     if (!location)
1179       return ret;
1180
1181     strcur = strs = g_strsplit (location, ",", 0);
1182     while (strcur && *strcur) {
1183       gint val;
1184
1185       if (!sscanf (*strcur, "%d", &val))
1186         break;
1187
1188       switch (pos) {
1189         case 0:
1190           if (val != dvdreadsrc->priv->title) {
1191             dvdreadsrc->priv->title = val;
1192             dvdreadsrc->priv->new_seek = TRUE;
1193           }
1194           break;
1195         case 1:
1196           if (val != dvdreadsrc->priv->chapter) {
1197             dvdreadsrc->priv->chapter = val;
1198             dvdreadsrc->priv->new_seek = TRUE;
1199           }
1200           break;
1201         case 2:
1202           dvdreadsrc->priv->angle = val;
1203           break;
1204       }
1205
1206       strcur++;
1207       pos++;
1208     }
1209
1210     g_strfreev (strs);
1211     g_free (location);
1212   }
1213
1214   return ret;
1215 }
1216
1217 static void
1218 dvdreadsrc_uri_handler_init (gpointer g_iface, gpointer iface_data)
1219 {
1220   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1221
1222   iface->get_type = dvdreadsrc_uri_get_type;
1223   iface->get_protocols = dvdreadsrc_uri_get_protocols;
1224   iface->get_uri = dvdreadsrc_uri_get_uri;
1225   iface->set_uri = dvdreadsrc_uri_set_uri;
1226 }
1227
1228 static gboolean
1229 plugin_init (GstPlugin * plugin)
1230 {
1231   if (!gst_element_register (plugin, "dvdreadsrc", GST_RANK_NONE,
1232           GST_TYPE_DVDREADSRC))
1233     return FALSE;
1234
1235   return TRUE;
1236 }
1237
1238 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1239     GST_VERSION_MINOR,
1240     "dvdreadsrc",
1241     "Access a DVD with dvdread",
1242     plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN)