Merge branch 'tizen_gst_1.16.2' into tizen
[platform/upstream/gstreamer.git] / ext / dvdread / dvdreadsrc.c
1 /* GStreamer DVD title source
2  * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
4  * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #ifdef HAVE_STDINT_H
27 #include <stdint.h>
28 #endif
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <errno.h>
34
35 #include "dvdreadsrc.h"
36
37 #include <gmodule.h>
38
39 #include <gst/gst-i18n-plugin.h>
40
41 GST_DEBUG_CATEGORY_STATIC (gstgst_dvd_read_src_debug);
42 #define GST_CAT_DEFAULT (gstgst_dvd_read_src_debug)
43
44 enum
45 {
46   ARG_0,
47   ARG_DEVICE,
48   ARG_TITLE,
49   ARG_CHAPTER,
50   ARG_ANGLE
51 };
52
53 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
54     GST_PAD_SRC,
55     GST_PAD_ALWAYS,
56     GST_STATIC_CAPS ("video/mpeg, mpegversion=2, systemstream=(boolean)true"));
57
58 static GstFormat title_format;
59 static GstFormat angle_format;
60 static GstFormat sector_format;
61 static GstFormat chapter_format;
62
63 static gboolean gst_dvd_read_src_start (GstBaseSrc * basesrc);
64 static gboolean gst_dvd_read_src_stop (GstBaseSrc * basesrc);
65 static GstFlowReturn gst_dvd_read_src_create (GstPushSrc * pushsrc,
66     GstBuffer ** buf);
67 static gboolean gst_dvd_read_src_src_query (GstBaseSrc * basesrc,
68     GstQuery * query);
69 static gboolean gst_dvd_read_src_src_event (GstBaseSrc * basesrc,
70     GstEvent * event);
71 static gboolean gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title,
72     gint angle);
73 static gboolean gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src,
74     gint chapter);
75 static gboolean gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, gint angle);
76 static void gst_dvd_read_src_set_property (GObject * object, guint prop_id,
77     const GValue * value, GParamSpec * pspec);
78 static void gst_dvd_read_src_get_property (GObject * object, guint prop_id,
79     GValue * value, GParamSpec * pspec);
80 static GstEvent *gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
81     const guint32 * clut);
82 static gboolean gst_dvd_read_src_get_size (GstDvdReadSrc * src, gint64 * size);
83 static gboolean gst_dvd_read_src_do_seek (GstBaseSrc * src, GstSegment * s);
84 static gint64 gst_dvd_read_src_convert_timecode (dvd_time_t * time);
85 static gint gst_dvd_read_src_get_next_cell (GstDvdReadSrc * src,
86     pgc_t * pgc, gint cell);
87 static GstClockTime gst_dvd_read_src_get_time_for_sector (GstDvdReadSrc * src,
88     guint sector);
89 static gint gst_dvd_read_src_get_sector_from_time (GstDvdReadSrc * src,
90     GstClockTime ts);
91
92 static void gst_dvd_read_src_uri_handler_init (gpointer g_iface,
93     gpointer iface_data);
94
95 #define gst_dvd_read_src_parent_class parent_class
96 G_DEFINE_TYPE_WITH_CODE (GstDvdReadSrc, gst_dvd_read_src, GST_TYPE_PUSH_SRC,
97     G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER,
98         gst_dvd_read_src_uri_handler_init));
99
100 static void
101 gst_dvd_read_src_finalize (GObject * object)
102 {
103   GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
104
105   g_free (src->location);
106
107   G_OBJECT_CLASS (parent_class)->finalize (object);
108 }
109
110 static void
111 gst_dvd_read_src_init (GstDvdReadSrc * src)
112 {
113   GstCaps *src_caps = gst_static_pad_template_get_caps (&srctemplate);
114   src->dvd = NULL;
115   src->vts_file = NULL;
116   src->vmg_file = NULL;
117   src->dvd_title = NULL;
118
119   src->location = g_strdup ("/dev/dvd");
120   src->first_seek = TRUE;
121   src->new_seek = TRUE;
122   src->new_cell = TRUE;
123   src->change_cell = FALSE;
124   src->uri_title = 1;
125   src->uri_chapter = 1;
126   src->uri_angle = 1;
127
128   src->title_lang_event_pending = NULL;
129   src->pending_clut_event = NULL;
130
131   gst_pad_use_fixed_caps (GST_BASE_SRC_PAD (src));
132   gst_pad_set_caps (GST_BASE_SRC_PAD (src), src_caps);
133   gst_caps_unref (src_caps);
134 }
135
136 static gboolean
137 gst_dvd_read_src_is_seekable (GstBaseSrc * src)
138 {
139   return TRUE;
140 }
141
142 static void
143 gst_dvd_read_src_class_init (GstDvdReadSrcClass * klass)
144 {
145   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
146   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
147   GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
148   GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
149
150   gobject_class->finalize = gst_dvd_read_src_finalize;
151   gobject_class->set_property = gst_dvd_read_src_set_property;
152   gobject_class->get_property = gst_dvd_read_src_get_property;
153
154   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
155       g_param_spec_string ("device", "Device",
156           "DVD device location", NULL,
157           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
158   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TITLE,
159       g_param_spec_int ("title", "title", "title",
160           1, 999, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
161   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHAPTER,
162       g_param_spec_int ("chapter", "chapter", "chapter",
163           1, 999, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
164   g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
165       g_param_spec_int ("angle", "angle", "angle",
166           1, 999, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
167
168   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
169
170   gst_element_class_set_static_metadata (gstelement_class, "DVD Source",
171       "Source/File/DVD",
172       "Access a DVD title/chapter/angle using libdvdread",
173       "Erik Walthinsen <omega@cse.ogi.edu>");
174
175   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dvd_read_src_start);
176   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dvd_read_src_stop);
177   gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_query);
178   gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_event);
179   gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_dvd_read_src_do_seek);
180   gstbasesrc_class->is_seekable =
181       GST_DEBUG_FUNCPTR (gst_dvd_read_src_is_seekable);
182
183   gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_dvd_read_src_create);
184
185   title_format = gst_format_register ("title", "DVD title");
186   angle_format = gst_format_register ("angle", "DVD angle");
187   sector_format = gst_format_register ("sector", "DVD sector");
188   chapter_format = gst_format_register ("chapter", "DVD chapter");
189 }
190
191 static gboolean
192 gst_dvd_read_src_start (GstBaseSrc * basesrc)
193 {
194   GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
195
196   g_return_val_if_fail (src->location != NULL, FALSE);
197
198   GST_DEBUG_OBJECT (src, "Opening DVD '%s'", src->location);
199
200   if ((src->dvd = DVDOpen (src->location)) == NULL)
201     goto open_failed;
202
203   /* Load the video manager to find out the information about the titles */
204   GST_DEBUG_OBJECT (src, "Loading VMG info");
205
206   if (!(src->vmg_file = ifoOpen (src->dvd, 0)))
207     goto ifo_open_failed;
208
209   src->tt_srpt = src->vmg_file->tt_srpt;
210
211   src->title = src->uri_title - 1;
212   src->chapter = src->uri_chapter - 1;
213   src->angle = src->uri_angle - 1;
214
215   if (!gst_dvd_read_src_goto_title (src, src->title, src->angle))
216     goto title_open_failed;
217
218   if (!gst_dvd_read_src_goto_chapter (src, src->chapter))
219     goto chapter_open_failed;
220
221   src->new_seek = FALSE;
222   src->change_cell = TRUE;
223
224   src->first_seek = TRUE;
225
226   return TRUE;
227
228   /* ERRORS */
229 open_failed:
230   {
231     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
232         (_("Could not open DVD")),
233         ("DVDOpen(%s) failed: %s", src->location, g_strerror (errno)));
234     return FALSE;
235   }
236 ifo_open_failed:
237   {
238     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
239         (_("Could not open DVD")),
240         ("ifoOpen() failed: %s", g_strerror (errno)));
241     return FALSE;
242   }
243 title_open_failed:
244   {
245     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
246         (_("Could not open DVD title %d"), src->uri_title), (NULL));
247     return FALSE;
248   }
249 chapter_open_failed:
250   {
251     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
252         (_("Failed to go to chapter %d of DVD title %d"),
253             src->uri_chapter, src->uri_title), (NULL));
254     return FALSE;
255   }
256 }
257
258 static gboolean
259 gst_dvd_read_src_stop (GstBaseSrc * basesrc)
260 {
261   GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
262
263   if (src->vts_file) {
264     ifoClose (src->vts_file);
265     src->vts_file = NULL;
266   }
267   if (src->vmg_file) {
268     ifoClose (src->vmg_file);
269     src->vmg_file = NULL;
270   }
271   if (src->dvd_title) {
272     DVDCloseFile (src->dvd_title);
273     src->dvd_title = NULL;
274   }
275   if (src->dvd) {
276     DVDClose (src->dvd);
277     src->dvd = NULL;
278   }
279   src->new_cell = TRUE;
280   src->new_seek = TRUE;
281   src->change_cell = FALSE;
282   src->chapter = 0;
283   src->title = 0;
284   src->need_newsegment = TRUE;
285   src->vts_tmapt = NULL;
286   if (src->title_lang_event_pending) {
287     gst_event_unref (src->title_lang_event_pending);
288     src->title_lang_event_pending = NULL;
289   }
290   if (src->pending_clut_event) {
291     gst_event_unref (src->pending_clut_event);
292     src->pending_clut_event = NULL;
293   }
294   if (src->chapter_starts) {
295     g_free (src->chapter_starts);
296     src->chapter_starts = NULL;
297   }
298
299   GST_LOG_OBJECT (src, "closed DVD");
300
301   return TRUE;
302 }
303
304 static void
305 cur_title_get_chapter_pgc (GstDvdReadSrc * src, gint chapter, gint * p_pgn,
306     gint * p_pgc_id, pgc_t ** p_pgc)
307 {
308   pgc_t *pgc;
309   gint pgn, pgc_id;
310
311   g_assert (chapter >= 0 && chapter < src->num_chapters);
312
313   pgc_id = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgcn;
314   pgn = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgn;
315   pgc = src->vts_file->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
316
317   *p_pgn = pgn;
318   *p_pgc_id = pgc_id;
319   *p_pgc = pgc;
320 }
321
322 static void
323 cur_title_get_chapter_bounds (GstDvdReadSrc * src, gint chapter,
324     gint * p_first_cell, gint * p_last_cell)
325 {
326   pgc_t *pgc;
327   gint pgn, pgc_id, pgn_next_ch;
328
329   g_assert (chapter >= 0 && chapter < src->num_chapters);
330
331   cur_title_get_chapter_pgc (src, chapter, &pgn, &pgc_id, &pgc);
332
333   *p_first_cell = pgc->program_map[pgn - 1] - 1;
334
335   /* last cell is used as a 'up to boundary', not 'up to and including',
336    * i.e. it is the first cell not included in the chapter range */
337   if (chapter == (src->num_chapters - 1)) {
338     *p_last_cell = pgc->nr_of_cells;
339   } else {
340     pgn_next_ch = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter + 1].pgn;
341     *p_last_cell = pgc->program_map[pgn_next_ch - 1] - 1;
342   }
343
344   GST_DEBUG_OBJECT (src, "Chapter %d bounds: %d %d (within %d cells)",
345       chapter, *p_first_cell, *p_last_cell, pgc->nr_of_cells);
346 }
347
348 static gboolean
349 gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src, gint chapter)
350 {
351   gint i;
352   const guint8 *palette;
353
354   /* make sure the chapter number is valid for this title */
355   if (chapter < 0 || chapter >= src->num_chapters) {
356     GST_WARNING_OBJECT (src, "invalid chapter %d (only %d available)",
357         chapter, src->num_chapters);
358     chapter = CLAMP (chapter, 0, src->num_chapters - 1);
359   }
360
361   /* determine which program chain we want to watch. This is
362    * based on the chapter number */
363   cur_title_get_chapter_pgc (src, chapter, &src->pgn, &src->pgc_id,
364       &src->cur_pgc);
365   cur_title_get_chapter_bounds (src, chapter, &src->start_cell,
366       &src->last_cell);
367
368   GST_LOG_OBJECT (src, "Opened chapter %d - cell %d-%d", chapter + 1,
369       src->start_cell, src->last_cell);
370
371   /* retrieve position */
372   src->cur_pack = 0;
373   for (i = 0; i < chapter; i++) {
374     gint c1, c2;
375
376     cur_title_get_chapter_bounds (src, i, &c1, &c2);
377
378     while (c1 < c2) {
379       src->cur_pack +=
380           src->cur_pgc->cell_playback[c1].last_sector -
381           src->cur_pgc->cell_playback[c1].first_sector;
382       ++c1;
383     }
384   }
385
386   /* prepare reading for new cell */
387   src->new_cell = TRUE;
388   src->next_cell = src->start_cell;
389
390   src->chapter = chapter;
391
392   if (src->pending_clut_event)
393     gst_event_unref (src->pending_clut_event);
394
395   /* Work around GCC 9 compiler warning here about taking address of packed
396    * member, which may result in an unaligned pointer access */
397   palette = (const guint8 *) src->cur_pgc->palette;
398   src->pending_clut_event =
399       gst_dvd_read_src_make_clut_change_event (src, (const guint32 *) palette);
400
401   return TRUE;
402 }
403
404 static void
405 gst_dvd_read_src_get_chapter_starts (GstDvdReadSrc * src)
406 {
407   GstClockTime uptohere;
408   guint c;
409
410   g_free (src->chapter_starts);
411   src->chapter_starts = g_new (GstClockTime, src->num_chapters);
412
413   uptohere = (GstClockTime) 0;
414   for (c = 0; c < src->num_chapters; ++c) {
415     GstClockTime chapter_duration = 0;
416     gint cell_start, cell_end, cell;
417     gint pgn, pgc_id;
418     pgc_t *pgc;
419
420     cur_title_get_chapter_pgc (src, c, &pgn, &pgc_id, &pgc);
421     cur_title_get_chapter_bounds (src, c, &cell_start, &cell_end);
422
423     cell = cell_start;
424     while (cell < cell_end) {
425       dvd_time_t *cell_duration;
426
427       cell_duration = &pgc->cell_playback[cell].playback_time;
428       chapter_duration += gst_dvd_read_src_convert_timecode (cell_duration);
429       cell = gst_dvd_read_src_get_next_cell (src, pgc, cell);
430     }
431
432     src->chapter_starts[c] = uptohere;
433
434     GST_INFO_OBJECT (src, "[%02u] Chapter %02u starts at %" GST_TIME_FORMAT
435         ", dur = %" GST_TIME_FORMAT ", cells %d-%d", src->title + 1, c + 1,
436         GST_TIME_ARGS (uptohere), GST_TIME_ARGS (chapter_duration),
437         cell_start, cell_end);
438
439     uptohere += chapter_duration;
440   }
441 }
442
443 static gboolean
444 gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title, gint angle)
445 {
446   GstStructure *s;
447   gchar lang_code[3] = { '\0', '\0', '\0' }, *t;
448   pgc_t *pgc0;
449   gint title_set_nr;
450   gint num_titles;
451   gint pgn0, pgc0_id;
452   gint i;
453
454   /* make sure our title number is valid */
455   num_titles = src->tt_srpt->nr_of_srpts;
456   GST_INFO_OBJECT (src, "There are %d titles on this DVD", num_titles);
457   if (title < 0 || title >= num_titles)
458     goto invalid_title;
459
460   src->num_chapters = src->tt_srpt->title[title].nr_of_ptts;
461   GST_INFO_OBJECT (src, "Title %d has %d chapters", title + 1,
462       src->num_chapters);
463
464   /* make sure the angle number is valid for this title */
465   src->num_angles = src->tt_srpt->title[title].nr_of_angles;
466   GST_LOG_OBJECT (src, "Title %d has %d angles", title + 1, src->num_angles);
467   if (angle < 0 || angle >= src->num_angles) {
468     GST_WARNING_OBJECT (src, "Invalid angle %d (only %d available)",
469         angle, src->num_angles);
470     angle = CLAMP (angle, 0, src->num_angles - 1);
471   }
472
473   /* load the VTS information for the title set our title is in */
474   title_set_nr = src->tt_srpt->title[title].title_set_nr;
475   src->vts_file = ifoOpen (src->dvd, title_set_nr);
476   if (src->vts_file == NULL)
477     goto ifo_open_failed;
478
479   src->ttn = src->tt_srpt->title[title].vts_ttn;
480   src->vts_ptt_srpt = src->vts_file->vts_ptt_srpt;
481
482   /* interactive title? */
483   if (src->num_chapters > 0 &&
484       src->vts_ptt_srpt->title[src->ttn - 1].ptt[0].pgn == 0) {
485     goto commands_only_pgc;
486   }
487
488   /* we've got enough info, time to open the title set data */
489   src->dvd_title = DVDOpenFile (src->dvd, title_set_nr, DVD_READ_TITLE_VOBS);
490   if (src->dvd_title == NULL)
491     goto title_open_failed;
492
493   GST_INFO_OBJECT (src, "Opened title %d, angle %d", title + 1, angle);
494   src->title = title;
495   src->angle = angle;
496
497   /* build event */
498
499   if (src->title_lang_event_pending) {
500     gst_event_unref (src->title_lang_event_pending);
501     src->title_lang_event_pending = NULL;
502   }
503
504   s = gst_structure_new ("application/x-gst-dvd",
505       "event", G_TYPE_STRING, "dvd-lang-codes", NULL);
506
507   /* so we can filter out invalid/unused streams (same for all chapters) */
508   cur_title_get_chapter_pgc (src, 0, &pgn0, &pgc0_id, &pgc0);
509
510   /* audio */
511   for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_audio_streams; i++) {
512     const audio_attr_t *a;
513
514     /* audio stream present? */
515     if (pgc0 != NULL && (pgc0->audio_control[i] & 0x8000) == 0)
516       continue;
517
518     a = &src->vts_file->vtsi_mat->vts_audio_attr[i];
519
520     t = g_strdup_printf ("audio-%d-format", i);
521     gst_structure_set (s, t, G_TYPE_INT, (int) a->audio_format, NULL);
522     g_free (t);
523     t = g_strdup_printf ("audio-%d-stream", i);
524     gst_structure_set (s, t, G_TYPE_INT, (int) i, NULL);
525     g_free (t);
526
527     if (a->lang_type) {
528       t = g_strdup_printf ("audio-%d-language", i);
529       lang_code[0] = (a->lang_code >> 8) & 0xff;
530       lang_code[1] = a->lang_code & 0xff;
531       gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
532       g_free (t);
533     } else {
534       lang_code[0] = '\0';
535     }
536
537     GST_INFO_OBJECT (src, "[%02d] Audio    %02d: lang='%s', format=%d",
538         src->title + 1, i, lang_code, (gint) a->audio_format);
539   }
540
541   /* subtitle */
542   for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_subp_streams; i++) {
543     const subp_attr_t *u;
544     const video_attr_t *v;
545     gint sid;
546
547     /* subpicture stream present? */
548     if (pgc0 != NULL && (pgc0->subp_control[i] & 0x80000000) == 0)
549       continue;
550
551     u = &src->vts_file->vtsi_mat->vts_subp_attr[i];
552     v = &src->vts_file->vtsi_mat->vts_video_attr;
553
554     sid = i;
555     if (pgc0 != NULL) {
556       if (v->display_aspect_ratio == 0) /* 4:3 */
557         sid = (pgc0->subp_control[i] >> 24) & 0x1f;
558       else if (v->display_aspect_ratio == 3)    /* 16:9 */
559         sid = (pgc0->subp_control[i] >> 8) & 0x1f;
560     }
561
562     if (u->type) {
563       t = g_strdup_printf ("subpicture-%d-language", i);
564       lang_code[0] = (u->lang_code >> 8) & 0xff;
565       lang_code[1] = u->lang_code & 0xff;
566       gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
567       g_free (t);
568       t = g_strdup_printf ("subpicture-%d-stream", i);
569       gst_structure_set (s, t, G_TYPE_INT, (int) sid, NULL);
570       g_free (t);
571       t = g_strdup_printf ("subpicture-%d-format", i);
572       gst_structure_set (s, t, G_TYPE_INT, (int) 0, NULL);
573       g_free (t);
574     } else {
575       lang_code[0] = '\0';
576     }
577
578     GST_INFO_OBJECT (src, "[%02d] Subtitle %02d: lang='%s', type=%d",
579         src->title + 1, sid, lang_code, u->type);
580   }
581
582   src->title_lang_event_pending =
583       gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
584
585   /* dump seek tables */
586   src->vts_tmapt = src->vts_file->vts_tmapt;
587   if (src->vts_tmapt) {
588     gint i, j;
589
590     GST_LOG_OBJECT (src, "nr_of_tmaps = %d", src->vts_tmapt->nr_of_tmaps);
591     for (i = 0; i < src->vts_tmapt->nr_of_tmaps; ++i) {
592       GST_LOG_OBJECT (src, "======= Table %d ===================", i);
593       GST_LOG_OBJECT (src, "Offset relative to VTS_TMAPTI: %d",
594           src->vts_tmapt->tmap_offset[i]);
595       GST_LOG_OBJECT (src, "Time unit (seconds)          : %d",
596           src->vts_tmapt->tmap[i].tmu);
597       GST_LOG_OBJECT (src, "Number of entries            : %d",
598           src->vts_tmapt->tmap[i].nr_of_entries);
599       for (j = 0; j < src->vts_tmapt->tmap[i].nr_of_entries; j++) {
600         guint64 time;
601
602         time = (guint64) src->vts_tmapt->tmap[i].tmu * (j + 1) * GST_SECOND;
603         GST_LOG_OBJECT (src, "Time: %" GST_TIME_FORMAT " VOBU "
604             "Sector: 0x%08x %s", GST_TIME_ARGS (time),
605             src->vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff,
606             (src->vts_tmapt->tmap[i].map_ent[j] >> 31) ? "discontinuity" : "");
607       }
608     }
609   } else {
610     GST_WARNING_OBJECT (src, "no vts_tmapt - seeking will suck");
611   }
612
613   gst_dvd_read_src_get_chapter_starts (src);
614
615   return TRUE;
616
617   /* ERRORS */
618 invalid_title:
619   {
620     GST_WARNING_OBJECT (src, "Invalid title %d (only %d available)",
621         title, num_titles);
622     return FALSE;
623   }
624 ifo_open_failed:
625   {
626     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
627         (_("Could not open DVD title %d"), title_set_nr),
628         ("ifoOpen(%d) failed: %s", title_set_nr, g_strerror (errno)));
629     return FALSE;
630   }
631 title_open_failed:
632   {
633     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
634         (_("Could not open DVD title %d"), title_set_nr),
635         ("Can't open title VOBS (VTS_%02d_1.VOB)", title_set_nr));
636     return FALSE;
637   }
638 commands_only_pgc:
639   {
640     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
641         (_("Could not open DVD title %d. Interactive titles are not supported "
642                 "by this element"), title_set_nr),
643         ("Commands-only PGC, not supported, use rsndvdbin"));
644     return FALSE;
645   }
646 }
647
648 /* FIXME: double-check this function, compare against original */
649 static gint
650 gst_dvd_read_src_get_next_cell (GstDvdReadSrc * src, pgc_t * pgc, gint cell)
651 {
652   /* Check if we're entering an angle block. */
653   if (pgc->cell_playback[cell].block_type != BLOCK_TYPE_ANGLE_BLOCK)
654     return (cell + 1);
655
656   while (pgc->cell_playback[cell].block_mode != BLOCK_MODE_LAST_CELL)
657     ++cell;
658
659   return cell + 1;
660 }
661
662 /* Returns true if the pack is a NAV pack */
663 static gboolean
664 gst_dvd_read_src_is_nav_pack (const guint8 * data, gint lbn, dsi_t * dsi_pack)
665 {
666   if (GST_READ_UINT32_BE (data + 0x26) != 0x000001BF)
667     return FALSE;
668
669   /* Check that this is substream 0 (PCI) */
670   if (data[0x2c] != 0)
671     return FALSE;
672
673   if (GST_READ_UINT32_BE (data + 0x400) != 0x000001BF)
674     return FALSE;
675
676   /* Check that this is substream 1 (DSI) */
677   if (data[0x406] != 1)
678     return FALSE;
679
680   /* Check sizes of PCI and DSI packets */
681   if (GST_READ_UINT16_BE (data + 0x2a) != 0x03d4)
682     return FALSE;
683
684   if (GST_READ_UINT16_BE (data + 0x404) != 0x03fa)
685     return FALSE;
686
687   /* Read the DSI packet into the provided struct and check it */
688   navRead_DSI (dsi_pack, (unsigned char *) data + DSI_START_BYTE);
689   if (lbn != dsi_pack->dsi_gi.nv_pck_lbn)
690     return FALSE;
691
692   return TRUE;
693 }
694
695 /* find time for sector from index, returns NONE if there is no exact match */
696 static GstClockTime
697 gst_dvd_read_src_get_time_for_sector (GstDvdReadSrc * src, guint sector)
698 {
699   gint i, j;
700
701   if (src->vts_tmapt == NULL || src->vts_tmapt->nr_of_tmaps == 0)
702     return GST_CLOCK_TIME_NONE;
703
704   for (i = 0; i < src->vts_tmapt->nr_of_tmaps; ++i) {
705     for (j = 0; j < src->vts_tmapt->tmap[i].nr_of_entries; ++j) {
706       if ((src->vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff) == sector)
707         return (guint64) src->vts_tmapt->tmap[i].tmu * (j + 1) * GST_SECOND;
708     }
709   }
710
711   if (sector == 0)
712     return (GstClockTime) 0;
713
714   return GST_CLOCK_TIME_NONE;
715 }
716
717 /* returns the sector in the index at (or before) the given time, or -1 */
718 static gint
719 gst_dvd_read_src_get_sector_from_time (GstDvdReadSrc * src, GstClockTime ts)
720 {
721   gint sector, j;
722
723   if (src->vts_tmapt == NULL || src->vts_tmapt->nr_of_tmaps < src->ttn)
724     return -1;
725
726   sector = src->vts_tmapt->tmap[src->ttn - 1].map_ent[0] & 0x7fffffff;
727
728   for (j = 0; j < src->vts_tmapt->tmap[src->ttn - 1].nr_of_entries; ++j) {
729     GstClockTime entry_time;
730
731     entry_time =
732         (guint64) src->vts_tmapt->tmap[src->ttn - 1].tmu * (j + 1) * GST_SECOND;
733     if (entry_time <= ts) {
734       sector = src->vts_tmapt->tmap[src->ttn - 1].map_ent[j] & 0x7fffffff;
735     }
736     if (entry_time >= ts) {
737       return sector;
738     }
739   }
740
741   if (ts == 0)
742     return 0;
743
744   return -1;
745 }
746
747 typedef enum
748 {
749   GST_DVD_READ_OK = 0,
750   GST_DVD_READ_ERROR = -1,
751   GST_DVD_READ_EOS = -2,
752   GST_DVD_READ_AGAIN = -3
753 } GstDvdReadReturn;
754
755 static GstDvdReadReturn
756 gst_dvd_read_src_read (GstDvdReadSrc * src, gint angle, gint new_seek,
757     GstBuffer ** p_buf)
758 {
759   GstBuffer *buf;
760   GstSegment *seg;
761   guint8 oneblock[DVD_VIDEO_LB_LEN];
762   dsi_t dsi_pack;
763   guint next_vobu, cur_output_size;
764   gint len;
765   gint retries;
766   gint64 next_time;
767   GstMapInfo map;
768
769   seg = &(GST_BASE_SRC (src)->segment);
770
771   /* playback by cell in this pgc, starting at the cell for our chapter */
772   if (new_seek)
773     src->cur_cell = src->start_cell;
774
775 again:
776
777   if (src->cur_cell >= src->last_cell) {
778     /* advance to next chapter */
779     if (src->chapter == (src->num_chapters - 1) ||
780         (seg->format == chapter_format && seg->stop != -1 &&
781             src->chapter == (seg->stop - 1))) {
782       GST_DEBUG_OBJECT (src, "end of chapter segment");
783       goto eos;
784     }
785
786     GST_INFO_OBJECT (src, "end of chapter %d, switch to next",
787         src->chapter + 1);
788
789     ++src->chapter;
790     gst_dvd_read_src_goto_chapter (src, src->chapter);
791
792     return GST_DVD_READ_AGAIN;
793   }
794
795   if (src->new_cell || new_seek) {
796     if (!new_seek) {
797       src->cur_cell = src->next_cell;
798       if (src->cur_cell >= src->last_cell) {
799         GST_LOG_OBJECT (src, "last cell in chapter");
800         goto again;
801       }
802     }
803
804     /* take angle into account */
805     if (src->cur_pgc->cell_playback[src->cur_cell].block_type
806         == BLOCK_TYPE_ANGLE_BLOCK)
807       src->cur_cell += angle;
808
809     /* calculate next cell */
810     src->next_cell =
811         gst_dvd_read_src_get_next_cell (src, src->cur_pgc, src->cur_cell);
812
813     /* we loop until we're out of this cell */
814     src->cur_pack = src->cur_pgc->cell_playback[src->cur_cell].first_sector;
815     src->new_cell = FALSE;
816     GST_DEBUG_OBJECT (src, "Starting new cell %d @ pack %d", src->cur_cell,
817         src->cur_pack);
818   }
819
820   if (src->cur_pack >= src->cur_pgc->cell_playback[src->cur_cell].last_sector) {
821     src->new_cell = TRUE;
822     GST_LOG_OBJECT (src, "Beyond last sector for cell %d, going to next cell",
823         src->cur_cell);
824     return GST_DVD_READ_AGAIN;
825   }
826
827   /* read NAV packet */
828   retries = 0;
829 nav_retry:
830   retries++;
831
832   len = DVDReadBlocks (src->dvd_title, src->cur_pack, 1, oneblock);
833   if (len != 1)
834     goto read_error;
835
836   if (!gst_dvd_read_src_is_nav_pack (oneblock, src->cur_pack, &dsi_pack)) {
837     GST_LOG_OBJECT (src, "Skipping nav packet @ pack %d", src->cur_pack);
838     src->cur_pack++;
839
840     if (retries < 2000) {
841       goto nav_retry;
842     } else {
843       GST_LOG_OBJECT (src, "No nav packet @ pack %d after 2000 blocks",
844           src->cur_pack);
845       goto read_error;
846     }
847   }
848
849   /* determine where we go next. These values are the ones we
850    * mostly care about */
851   cur_output_size = dsi_pack.dsi_gi.vobu_ea + 1;
852
853   /* If we're not at the end of this cell, we can determine the next
854    * VOBU to display using the VOBU_SRI information section of the
855    * DSI.  Using this value correctly follows the current angle,
856    * avoiding the doubled scenes in The Matrix, and makes our life
857    * really happy.
858    *
859    * Otherwise, we set our next address past the end of this cell to
860    * force the code above to go to the next cell in the program. */
861   if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
862     next_vobu = src->cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
863   } else {
864     next_vobu = src->cur_pgc->cell_playback[src->cur_cell].last_sector + 1;
865   }
866
867   g_assert (cur_output_size < 1024);
868
869   /* create the buffer (TODO: use buffer pool?) */
870   buf =
871       gst_buffer_new_allocate (NULL, cur_output_size * DVD_VIDEO_LB_LEN, NULL);
872
873   GST_LOG_OBJECT (src, "Going to read %u sectors @ pack %d", cur_output_size,
874       src->cur_pack);
875
876   gst_buffer_map (buf, &map, GST_MAP_WRITE);
877   /* read in and output cursize packs */
878   len =
879       DVDReadBlocks (src->dvd_title, src->cur_pack, cur_output_size, map.data);
880
881   if (len != cur_output_size)
882     goto block_read_error;
883
884   gst_buffer_unmap (buf, &map);
885   gst_buffer_resize (buf, 0, cur_output_size * DVD_VIDEO_LB_LEN);
886   /* GST_BUFFER_OFFSET (buf) = priv->cur_pack * DVD_VIDEO_LB_LEN; */
887   GST_BUFFER_TIMESTAMP (buf) =
888       gst_dvd_read_src_get_time_for_sector (src, src->cur_pack);
889
890   *p_buf = buf;
891
892   GST_LOG_OBJECT (src, "Read %u sectors", cur_output_size);
893
894   src->cur_pack = next_vobu;
895
896   next_time = GST_BUFFER_TIMESTAMP (buf);
897   if (GST_CLOCK_TIME_IS_VALID (next_time) && seg->format == GST_FORMAT_TIME &&
898       GST_CLOCK_TIME_IS_VALID (seg->stop) &&
899       next_time > seg->stop + 5 * GST_SECOND) {
900     GST_DEBUG_OBJECT (src, "end of TIME segment");
901     goto eos;
902   }
903
904   return GST_DVD_READ_OK;
905
906   /* ERRORS */
907 eos:
908   {
909     GST_INFO_OBJECT (src, "Reached end-of-segment/stream - EOS");
910     return GST_DVD_READ_EOS;
911   }
912 read_error:
913   {
914     GST_ERROR_OBJECT (src, "Read failed for block %d", src->cur_pack);
915     return GST_DVD_READ_ERROR;
916   }
917 block_read_error:
918   {
919     GST_ERROR_OBJECT (src, "Read failed for %d blocks at %d",
920         cur_output_size, src->cur_pack);
921     gst_buffer_unmap (buf, &map);
922     gst_buffer_unref (buf);
923     return GST_DVD_READ_ERROR;
924   }
925 }
926
927 /* we don't cache the result on purpose */
928 static gboolean
929 gst_dvd_read_descrambler_available (void)
930 {
931   GModule *module;
932   gpointer sym;
933   gsize res;
934
935   module = g_module_open ("libdvdcss", 0);
936   if (module != NULL) {
937     res = g_module_symbol (module, "dvdcss_open", &sym);
938     g_module_close (module);
939   } else {
940     res = FALSE;
941   }
942
943   return res;
944 }
945
946 static GstFlowReturn
947 gst_dvd_read_src_create (GstPushSrc * pushsrc, GstBuffer ** p_buf)
948 {
949   GstDvdReadSrc *src = GST_DVD_READ_SRC (pushsrc);
950   GstPad *srcpad;
951   gint res;
952
953   g_return_val_if_fail (src->dvd != NULL, GST_FLOW_ERROR);
954
955   srcpad = GST_BASE_SRC (src)->srcpad;
956
957   if (src->need_newsegment) {
958     GstSegment seg;
959
960     gst_segment_init (&seg, GST_FORMAT_BYTES);
961     seg.start = src->cur_pack * DVD_VIDEO_LB_LEN;
962     seg.stop = -1;
963     seg.time = 0;
964     gst_pad_push_event (srcpad, gst_event_new_segment (&seg));
965     src->need_newsegment = FALSE;
966   }
967
968   if (src->new_seek) {
969     gst_dvd_read_src_goto_title (src, src->title, src->angle);
970     gst_dvd_read_src_goto_chapter (src, src->chapter);
971
972     src->new_seek = FALSE;
973     src->change_cell = TRUE;
974   }
975
976   if (src->title_lang_event_pending) {
977     gst_pad_push_event (srcpad, src->title_lang_event_pending);
978     src->title_lang_event_pending = NULL;
979   }
980
981   if (src->pending_clut_event) {
982     gst_pad_push_event (srcpad, src->pending_clut_event);
983     src->pending_clut_event = NULL;
984   }
985
986   /* read it in */
987   do {
988     res = gst_dvd_read_src_read (src, src->angle, src->change_cell, p_buf);
989   } while (res == GST_DVD_READ_AGAIN);
990
991   switch (res) {
992     case GST_DVD_READ_ERROR:{
993       /* FIXME: figure out a way to detect if scrambling is the problem */
994       if (!gst_dvd_read_descrambler_available ()) {
995         GST_ELEMENT_ERROR (src, RESOURCE, READ,
996             (_("Could not read DVD. This may be because the DVD is encrypted "
997                     "and a DVD decryption library is not installed.")), (NULL));
998       } else {
999         GST_ELEMENT_ERROR (src, RESOURCE, READ, (_("Could not read DVD.")),
1000             (NULL));
1001       }
1002       return GST_FLOW_ERROR;
1003     }
1004     case GST_DVD_READ_EOS:{
1005       return GST_FLOW_EOS;
1006     }
1007     case GST_DVD_READ_OK:{
1008       src->change_cell = FALSE;
1009       return GST_FLOW_OK;
1010     }
1011     default:
1012       break;
1013   }
1014
1015   g_return_val_if_reached (GST_FLOW_EOS);
1016 }
1017
1018 static void
1019 gst_dvd_read_src_set_property (GObject * object, guint prop_id,
1020     const GValue * value, GParamSpec * pspec)
1021 {
1022   GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
1023   gboolean started;
1024
1025   GST_OBJECT_LOCK (src);
1026   started = GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_FLAG_STARTED);
1027
1028   switch (prop_id) {
1029     case ARG_DEVICE:{
1030       if (started) {
1031         g_warning ("%s: property '%s' needs to be set before the device is "
1032             "opened", GST_ELEMENT_NAME (src), pspec->name);
1033         break;
1034       }
1035
1036       g_free (src->location);
1037       /* clear the filename if we get a NULL (is that possible?) */
1038       if (g_value_get_string (value) == NULL) {
1039         src->location = g_strdup ("/dev/dvd");
1040       } else {
1041         src->location = g_strdup (g_value_get_string (value));
1042       }
1043       break;
1044     }
1045     case ARG_TITLE:
1046       src->uri_title = g_value_get_int (value);
1047       if (started) {
1048         src->title = src->uri_title - 1;
1049         src->new_seek = TRUE;
1050       }
1051       break;
1052     case ARG_CHAPTER:
1053       src->uri_chapter = g_value_get_int (value);
1054       if (started) {
1055         src->chapter = src->uri_chapter - 1;
1056         src->new_seek = TRUE;
1057       }
1058       break;
1059     case ARG_ANGLE:
1060       src->uri_angle = g_value_get_int (value);
1061       if (started) {
1062         src->angle = src->uri_angle - 1;
1063       }
1064       break;
1065     default:
1066       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1067       break;
1068   }
1069
1070   GST_OBJECT_UNLOCK (src);
1071 }
1072
1073 static void
1074 gst_dvd_read_src_get_property (GObject * object, guint prop_id, GValue * value,
1075     GParamSpec * pspec)
1076 {
1077   GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
1078
1079   GST_OBJECT_LOCK (src);
1080
1081   switch (prop_id) {
1082     case ARG_DEVICE:
1083       g_value_set_string (value, src->location);
1084       break;
1085     case ARG_TITLE:
1086       g_value_set_int (value, src->uri_title);
1087       break;
1088     case ARG_CHAPTER:
1089       g_value_set_int (value, src->uri_chapter);
1090       break;
1091     case ARG_ANGLE:
1092       g_value_set_int (value, src->uri_angle);
1093       break;
1094     default:
1095       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1096       break;
1097   }
1098
1099   GST_OBJECT_UNLOCK (src);
1100 }
1101
1102 static gboolean
1103 gst_dvd_read_src_get_size (GstDvdReadSrc * src, gint64 * size)
1104 {
1105   gboolean ret = FALSE;
1106
1107   if (src->dvd_title) {
1108     gssize blocks;
1109
1110     blocks = DVDFileSize (src->dvd_title);
1111     if (blocks >= 0) {
1112       *size = (gint64) blocks *DVD_VIDEO_LB_LEN;
1113
1114       ret = TRUE;
1115     } else {
1116       GST_WARNING_OBJECT (src, "DVDFileSize(%p) failed!", src->dvd_title);
1117     }
1118   }
1119
1120   return ret;
1121 }
1122
1123 /*** Querying and seeking ***/
1124
1125 static gboolean
1126 gst_dvd_read_src_handle_seek_event (GstDvdReadSrc * src, GstEvent * event)
1127 {
1128   GstSeekFlags flags;
1129   GstSeekType cur_type, end_type;
1130   gint64 new_off, total;
1131   GstFormat format;
1132   GstPad *srcpad;
1133   gboolean query_ok;
1134   gdouble rate;
1135
1136   gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &new_off,
1137       &end_type, NULL);
1138
1139   if (rate <= 0.0) {
1140     GST_DEBUG_OBJECT (src, "cannot do backwards playback yet");
1141     return FALSE;
1142   }
1143
1144   if (end_type != GST_SEEK_TYPE_NONE) {
1145     if ((format != chapter_format && format != GST_FORMAT_TIME) ||
1146         end_type != GST_SEEK_TYPE_SET) {
1147       GST_DEBUG_OBJECT (src, "end seek type not supported");
1148       return FALSE;
1149     }
1150   }
1151
1152   if (cur_type != GST_SEEK_TYPE_SET) {
1153     GST_DEBUG_OBJECT (src, "only SEEK_TYPE_SET is supported");
1154     return FALSE;
1155   }
1156
1157   if (format == angle_format) {
1158     GST_OBJECT_LOCK (src);
1159     if (new_off < 0 || new_off >= src->num_angles) {
1160       GST_OBJECT_UNLOCK (src);
1161       GST_DEBUG_OBJECT (src, "invalid angle %d, only %d available",
1162           src->num_angles, src->num_angles);
1163       return FALSE;
1164     }
1165     src->angle = (gint) new_off;
1166     GST_OBJECT_UNLOCK (src);
1167     GST_DEBUG_OBJECT (src, "switched to angle %d", (gint) new_off + 1);
1168     return TRUE;
1169   }
1170
1171   if (format != chapter_format && format != title_format &&
1172       format != GST_FORMAT_BYTES && format != GST_FORMAT_TIME) {
1173     GST_DEBUG_OBJECT (src, "unsupported seek format %d (%s)", format,
1174         gst_format_get_name (format));
1175     return FALSE;
1176   }
1177
1178   if (format == GST_FORMAT_BYTES) {
1179     GST_DEBUG_OBJECT (src, "Requested seek to byte %" G_GUINT64_FORMAT,
1180         new_off);
1181   } else if (format == GST_FORMAT_TIME) {
1182     GST_DEBUG_OBJECT (src, "Requested seek to time %" GST_TIME_FORMAT,
1183         GST_TIME_ARGS (new_off));
1184     if (gst_dvd_read_src_get_sector_from_time (src, new_off) < 0) {
1185       GST_DEBUG_OBJECT (src, "Can't find sector for requested time");
1186       return FALSE;
1187     }
1188   }
1189
1190   srcpad = GST_BASE_SRC_PAD (src);
1191
1192   /* check whether the seek looks reasonable (ie within possible range) */
1193   if (format == GST_FORMAT_BYTES) {
1194     GST_OBJECT_LOCK (src);
1195     query_ok = gst_dvd_read_src_get_size (src, &total);
1196     GST_OBJECT_UNLOCK (src);
1197   } else {
1198     query_ok = gst_pad_query_duration (srcpad, format, &total);
1199   }
1200
1201   if (!query_ok) {
1202     GST_DEBUG_OBJECT (src, "Failed to query duration in format %s",
1203         gst_format_get_name (format));
1204     return FALSE;
1205   }
1206
1207   GST_DEBUG_OBJECT (src, "Total      %s: %12" G_GINT64_FORMAT,
1208       gst_format_get_name (format), total);
1209   GST_DEBUG_OBJECT (src, "Seek to    %s: %12" G_GINT64_FORMAT,
1210       gst_format_get_name (format), new_off);
1211
1212   if (new_off >= total) {
1213     GST_DEBUG_OBJECT (src, "Seek position out of range");
1214     return FALSE;
1215   }
1216
1217   /* set segment to seek format; this allows us to use the do_seek
1218    * virtual function and let the base source handle all the tricky
1219    * stuff for us. We don't use the segment internally anyway */
1220   /* FIXME: can't take the stream lock here - what to do? */
1221   GST_OBJECT_LOCK (src);
1222   GST_BASE_SRC (src)->segment.format = format;
1223   GST_BASE_SRC (src)->segment.start = 0;
1224   GST_BASE_SRC (src)->segment.stop = total;
1225   GST_BASE_SRC (src)->segment.duration = total;
1226   GST_OBJECT_UNLOCK (src);
1227
1228   return GST_BASE_SRC_CLASS (parent_class)->event (GST_BASE_SRC (src), event);
1229 }
1230
1231 static void
1232 gst_dvd_read_src_get_sector_bounds (GstDvdReadSrc * src, gint * first,
1233     gint * last)
1234 {
1235   gint c1, c2, tmp;
1236   cur_title_get_chapter_bounds (src, 0, &c1, &tmp);
1237   cur_title_get_chapter_bounds (src, src->num_chapters - 1, &tmp, &c2);
1238   *first = src->cur_pgc->cell_playback[c1].first_sector;
1239   *last = src->cur_pgc->cell_playback[c2].last_sector;
1240 }
1241
1242 static gboolean
1243 gst_dvd_read_src_do_seek (GstBaseSrc * basesrc, GstSegment * s)
1244 {
1245   GstDvdReadSrc *src;
1246
1247   src = GST_DVD_READ_SRC (basesrc);
1248
1249   GST_DEBUG_OBJECT (src, "Seeking to %s: %12" G_GINT64_FORMAT,
1250       gst_format_get_name (s->format), s->position);
1251
1252   /* Ignore the first seek to 0, as it breaks starting playback
1253    * from another chapter by seeking back to sector 0 */
1254   if (src->first_seek && s->format == GST_FORMAT_BYTES && s->start == 0) {
1255     src->first_seek = FALSE;
1256     return TRUE;
1257   }
1258
1259   if (s->format == sector_format || s->format == GST_FORMAT_BYTES
1260       || s->format == GST_FORMAT_TIME) {
1261     guint old;
1262
1263     old = src->cur_pack;
1264
1265     if (s->format == sector_format) {
1266       gint first, last;
1267       gst_dvd_read_src_get_sector_bounds (src, &first, &last);
1268       GST_DEBUG_OBJECT (src, "Format is sector, seeking to %" G_GINT64_FORMAT,
1269           s->position);
1270       src->cur_pack = s->position;
1271       if (src->cur_pack < first)
1272         src->cur_pack = first;
1273       if (src->cur_pack > last)
1274         src->cur_pack = last;
1275     } else if (s->format == GST_FORMAT_TIME) {
1276       gint sector;
1277       GST_DEBUG_OBJECT (src, "Format is time");
1278
1279       sector = gst_dvd_read_src_get_sector_from_time (src, s->position);
1280
1281       GST_DEBUG_OBJECT (src, "Time %" GST_TIME_FORMAT " => sector %d",
1282           GST_TIME_ARGS (s->position), sector);
1283
1284       /* really shouldn't happen, we've checked this earlier ... */
1285       g_return_val_if_fail (sector >= 0, FALSE);
1286
1287       src->cur_pack = sector;
1288     } else {
1289       /* byte format */
1290       gint first, last;
1291       gst_dvd_read_src_get_sector_bounds (src, &first, &last);
1292       GST_DEBUG_OBJECT (src, "Format is byte");
1293       src->cur_pack = s->position / DVD_VIDEO_LB_LEN;
1294       if (((gint64) src->cur_pack * DVD_VIDEO_LB_LEN) != s->position) {
1295         GST_LOG_OBJECT (src, "rounded down offset %" G_GINT64_FORMAT " => %"
1296             G_GINT64_FORMAT, s->position,
1297             (gint64) src->cur_pack * DVD_VIDEO_LB_LEN);
1298       }
1299       src->cur_pack += first;
1300     }
1301
1302     if (!gst_dvd_read_src_goto_sector (src, src->angle)) {
1303       GST_DEBUG_OBJECT (src, "seek to sector 0x%08x failed", src->cur_pack);
1304       src->cur_pack = old;
1305       return FALSE;
1306     }
1307
1308     GST_LOG_OBJECT (src, "seek to sector 0x%08x ok", src->cur_pack);
1309   } else if (s->format == chapter_format) {
1310     if (!gst_dvd_read_src_goto_chapter (src, (gint) s->position)) {
1311       GST_DEBUG_OBJECT (src, "seek to chapter %d failed",
1312           (gint) s->position + 1);
1313       return FALSE;
1314     }
1315     GST_INFO_OBJECT (src, "seek to chapter %d ok", (gint) s->position + 1);
1316     src->chapter = s->position;
1317   } else if (s->format == title_format) {
1318     if (!gst_dvd_read_src_goto_title (src, (gint) s->position, src->angle) ||
1319         !gst_dvd_read_src_goto_chapter (src, 0)) {
1320       GST_DEBUG_OBJECT (src, "seek to title %d failed", (gint) s->position);
1321       return FALSE;
1322     }
1323     src->title = (gint) s->position;
1324     src->chapter = 0;
1325     GST_INFO_OBJECT (src, "seek to title %d ok", src->title + 1);
1326   } else {
1327     g_return_val_if_reached (FALSE);
1328   }
1329
1330   src->need_newsegment = TRUE;
1331   return TRUE;
1332 }
1333
1334 static gboolean
1335 gst_dvd_read_src_src_event (GstBaseSrc * basesrc, GstEvent * event)
1336 {
1337   GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
1338   gboolean res;
1339
1340   GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
1341
1342   switch (GST_EVENT_TYPE (event)) {
1343     case GST_EVENT_SEEK:
1344       res = gst_dvd_read_src_handle_seek_event (src, event);
1345       break;
1346     default:
1347       res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
1348       break;
1349   }
1350
1351   return res;
1352 }
1353
1354 static GstEvent *
1355 gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
1356     const guint32 * clut)
1357 {
1358   GstStructure *structure;
1359   gchar name[16];
1360   gint i;
1361
1362   structure = gst_structure_new ("application/x-gst-dvd",
1363       "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
1364
1365   /* Create a separate field for each value in the table. */
1366   for (i = 0; i < 16; i++) {
1367     g_snprintf (name, sizeof (name), "clut%02d", i);
1368     gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
1369   }
1370
1371   /* Create the DVD event and put the structure into it. */
1372   return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY, structure);
1373 }
1374
1375 static gint64
1376 gst_dvd_read_src_convert_timecode (dvd_time_t * time)
1377 {
1378   gint64 ret_time;
1379   const gint64 one_hour = 3600 * GST_SECOND;
1380   const gint64 one_min = 60 * GST_SECOND;
1381
1382   g_return_val_if_fail ((time->hour >> 4) < 0xa
1383       && (time->hour & 0xf) < 0xa, -1);
1384   g_return_val_if_fail ((time->minute >> 4) < 0x7
1385       && (time->minute & 0xf) < 0xa, -1);
1386   g_return_val_if_fail ((time->second >> 4) < 0x7
1387       && (time->second & 0xf) < 0xa, -1);
1388
1389   ret_time = ((time->hour >> 4) * 10 + (time->hour & 0xf)) * one_hour;
1390   ret_time += ((time->minute >> 4) * 10 + (time->minute & 0xf)) * one_min;
1391   ret_time += ((time->second >> 4) * 10 + (time->second & 0xf)) * GST_SECOND;
1392
1393   return ret_time;
1394 }
1395
1396 static gboolean
1397 gst_dvd_read_src_do_duration_query (GstDvdReadSrc * src, GstQuery * query)
1398 {
1399   GstFormat format;
1400   gint64 val;
1401
1402   gst_query_parse_duration (query, &format, NULL);
1403
1404   switch (format) {
1405     case GST_FORMAT_TIME:{
1406       if (src->cur_pgc == NULL)
1407         return FALSE;
1408       val = gst_dvd_read_src_convert_timecode (&src->cur_pgc->playback_time);
1409       if (val < 0)
1410         return FALSE;
1411       break;
1412     }
1413     case GST_FORMAT_BYTES:{
1414       if (!gst_dvd_read_src_get_size (src, &val))
1415         return FALSE;
1416       break;
1417     }
1418     default:{
1419       if (format == sector_format) {
1420         val = DVDFileSize (src->dvd_title);
1421       } else if (format == title_format) {
1422         val = src->tt_srpt->nr_of_srpts;
1423       } else if (format == chapter_format) {
1424         val = src->num_chapters;
1425       } else if (format == angle_format) {
1426         val = src->tt_srpt->title[src->title].nr_of_angles;
1427       } else {
1428         GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
1429             format, gst_format_get_name (format));
1430         return FALSE;
1431       }
1432       break;
1433     }
1434   }
1435
1436   GST_LOG_OBJECT (src, "duration = %" G_GINT64_FORMAT " %s", val,
1437       gst_format_get_name (format));
1438
1439   gst_query_set_duration (query, format, val);
1440   return TRUE;
1441 }
1442
1443 static gboolean
1444 gst_dvd_read_src_do_position_query (GstDvdReadSrc * src, GstQuery * query)
1445 {
1446   GstFormat format;
1447   gint64 val;
1448
1449   gst_query_parse_position (query, &format, NULL);
1450
1451   switch (format) {
1452     case GST_FORMAT_BYTES:{
1453       val = (gint64) src->cur_pack * DVD_VIDEO_LB_LEN;
1454       break;
1455     }
1456     default:{
1457       if (format == sector_format) {
1458         val = src->cur_pack;
1459       } else if (format == title_format) {
1460         val = src->title;
1461       } else if (format == chapter_format) {
1462         val = src->chapter;
1463       } else if (format == angle_format) {
1464         val = src->angle;
1465       } else {
1466         GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
1467             format, gst_format_get_name (format));
1468         return FALSE;
1469       }
1470       break;
1471     }
1472   }
1473
1474   GST_LOG_OBJECT (src, "position = %" G_GINT64_FORMAT " %s", val,
1475       gst_format_get_name (format));
1476
1477   gst_query_set_position (query, format, val);
1478   return TRUE;
1479 }
1480
1481 static gboolean
1482 gst_dvd_read_src_do_convert_query (GstDvdReadSrc * src, GstQuery * query)
1483 {
1484   GstFormat src_format, dest_format;
1485   gboolean ret = FALSE;
1486   gint64 src_val, dest_val = -1;
1487
1488   gst_query_parse_convert (query, &src_format, &src_val, &dest_format, NULL);
1489
1490   if (src_format == dest_format) {
1491     dest_val = src_val;
1492     ret = TRUE;
1493     goto done;
1494   }
1495
1496   /* Formats to consider: TIME, DEFAULT, BYTES, title, chapter, sector.
1497    * Note: title and chapter are counted as starting from 0 here, just like
1498    * in the context of seek events. Another note: DEFAULT format is undefined */
1499
1500   if (src_format == GST_FORMAT_BYTES) {
1501     src_format = sector_format;
1502     src_val /= DVD_VIDEO_LB_LEN;
1503   }
1504
1505   if (src_format == sector_format) {
1506     /* SECTOR => xyz */
1507     if (dest_format == GST_FORMAT_TIME && src_val < G_MAXUINT) {
1508       dest_val = gst_dvd_read_src_get_time_for_sector (src, (guint) src_val);
1509       ret = (dest_val >= 0);
1510     } else if (dest_format == GST_FORMAT_BYTES) {
1511       dest_val = src_val * DVD_VIDEO_LB_LEN;
1512       ret = TRUE;
1513     } else {
1514       ret = FALSE;
1515     }
1516   } else if (src_format == title_format) {
1517     /* TITLE => xyz */
1518     if (dest_format == GST_FORMAT_TIME) {
1519       /* not really true, but we use this to trick the base source into
1520        * handling seeks in title-format for us (the source won't know that
1521        * we changed the title in this case) (changing titles should really
1522        * be done with an interface rather than a seek, but for now we're
1523        * stuck with this mechanism. Fix in 0.11) */
1524       dest_val = (GstClockTime) 0;
1525       ret = TRUE;
1526     } else {
1527       ret = FALSE;
1528     }
1529   } else if (src_format == chapter_format) {
1530     /* CHAPTER => xyz */
1531     if (dest_format == GST_FORMAT_TIME) {
1532       if (src->num_chapters >= 0 && src_val < src->num_chapters) {
1533         dest_val = src->chapter_starts[src_val];
1534         ret = TRUE;
1535       }
1536     } else if (dest_format == sector_format) {
1537     } else {
1538       ret = FALSE;
1539     }
1540   } else if (src_format == GST_FORMAT_TIME) {
1541     /* TIME => xyz */
1542     if (dest_format == sector_format || dest_format == GST_FORMAT_BYTES) {
1543       dest_val = gst_dvd_read_src_get_sector_from_time (src, src_val);
1544       ret = (dest_val >= 0);
1545       if (dest_format == GST_FORMAT_BYTES)
1546         dest_val *= DVD_VIDEO_LB_LEN;
1547     } else if (dest_format == chapter_format) {
1548       if (src->chapter_starts != NULL) {
1549         gint i;
1550
1551         for (i = src->num_chapters - 1; i >= 0; --i) {
1552           if (src->chapter_starts && src->chapter_starts[i] >= src_val) {
1553             dest_val = i;
1554             ret = TRUE;
1555             break;
1556           }
1557         }
1558       } else {
1559         ret = FALSE;
1560       }
1561     } else {
1562       ret = FALSE;
1563     }
1564   } else {
1565     ret = FALSE;
1566   }
1567
1568 done:
1569
1570   if (ret) {
1571     gst_query_set_convert (query, src_format, src_val, dest_format, dest_val);
1572   }
1573
1574   return ret;
1575 }
1576
1577 static gboolean
1578 gst_dvd_read_src_src_query (GstBaseSrc * basesrc, GstQuery * query)
1579 {
1580   GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
1581   gboolean res = TRUE;
1582
1583   GST_LOG_OBJECT (src, "handling %s query", GST_QUERY_TYPE_NAME (query));
1584
1585   switch (GST_QUERY_TYPE (query)) {
1586     case GST_QUERY_DURATION:
1587       GST_OBJECT_LOCK (src);
1588       if (GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_FLAG_STARTED)) {
1589         res = gst_dvd_read_src_do_duration_query (src, query);
1590       } else {
1591         GST_DEBUG_OBJECT (src, "query failed: not started");
1592         res = FALSE;
1593       }
1594       GST_OBJECT_UNLOCK (src);
1595       break;
1596     case GST_QUERY_POSITION:
1597       GST_OBJECT_LOCK (src);
1598       if (GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_FLAG_STARTED)) {
1599         res = gst_dvd_read_src_do_position_query (src, query);
1600       } else {
1601         GST_DEBUG_OBJECT (src, "query failed: not started");
1602         res = FALSE;
1603       }
1604       GST_OBJECT_UNLOCK (src);
1605       break;
1606     case GST_QUERY_CONVERT:
1607       GST_OBJECT_LOCK (src);
1608       if (GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_FLAG_STARTED)) {
1609         res = gst_dvd_read_src_do_convert_query (src, query);
1610       } else {
1611         GST_DEBUG_OBJECT (src, "query failed: not started");
1612         res = FALSE;
1613       }
1614       GST_OBJECT_UNLOCK (src);
1615       break;
1616     default:
1617       res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
1618       break;
1619   }
1620
1621   return res;
1622 }
1623
1624 static gboolean
1625 gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, int angle)
1626 {
1627   gint seek_to = src->cur_pack;
1628   gint chapter, next, cur, i;
1629
1630   /* retrieve position */
1631   src->cur_pack = 0;
1632   GST_DEBUG_OBJECT (src, "Goto sector %d, angle %d, within %d chapters",
1633       seek_to, angle, src->num_chapters);
1634
1635   for (i = 0; i < src->num_chapters; i++) {
1636     gint c1, c2;
1637
1638     cur_title_get_chapter_bounds (src, i, &c1, &c2);
1639     GST_DEBUG_OBJECT (src, " Looking in chapter %d, bounds: %d %d", i, c1, c2);
1640
1641     for (next = cur = c1; cur < c2;) {
1642       gint first = src->cur_pgc->cell_playback[cur].first_sector;
1643       gint last = src->cur_pgc->cell_playback[cur].last_sector;
1644       GST_DEBUG_OBJECT (src, "Cell %d sector bounds: %d %d", cur, first, last);
1645       cur = next;
1646       if (src->cur_pgc->cell_playback[cur].block_type == BLOCK_TYPE_ANGLE_BLOCK)
1647         cur += angle;
1648       next = gst_dvd_read_src_get_next_cell (src, src->cur_pgc, cur);
1649       /* seeking to 0 should end up at first chapter in any case */
1650       if ((seek_to >= first && seek_to <= last) || (seek_to == 0 && i == 0)) {
1651         GST_DEBUG_OBJECT (src, "Seek target found in chapter %d", i);
1652         chapter = i;
1653         goto done;
1654       }
1655     }
1656   }
1657
1658   GST_DEBUG_OBJECT (src, "Seek to sector %u failed", seek_to);
1659
1660   return FALSE;
1661
1662 done:
1663   {
1664     /* so chapter $chapter and cell $cur contain our sector
1665      * of interest. Let's go there! */
1666     GST_INFO_OBJECT (src, "Seek succeeded, going to chapter %u, cell %u",
1667         chapter + 1, cur);
1668
1669     gst_dvd_read_src_goto_chapter (src, chapter);
1670     src->cur_cell = cur;
1671     src->next_cell = next;
1672     src->new_cell = FALSE;
1673     src->cur_pack = seek_to;
1674
1675     return TRUE;
1676   }
1677 }
1678
1679
1680 /*** URI interface ***/
1681
1682 static GstURIType
1683 gst_dvd_read_src_uri_get_type (GType type)
1684 {
1685   return GST_URI_SRC;
1686 }
1687
1688 static const gchar *const *
1689 gst_dvd_read_src_uri_get_protocols (GType type)
1690 {
1691   static const gchar *protocols[] = { "dvd", NULL };
1692
1693   return protocols;
1694 }
1695
1696 static gchar *
1697 gst_dvd_read_src_uri_get_uri (GstURIHandler * handler)
1698 {
1699   GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
1700   gchar *uri;
1701
1702   GST_OBJECT_LOCK (src);
1703   uri = g_strdup_printf ("dvd://%d,%d,%d", src->uri_title, src->uri_chapter,
1704       src->uri_angle);
1705   GST_OBJECT_UNLOCK (src);
1706
1707   return uri;
1708 }
1709
1710 static gboolean
1711 gst_dvd_read_src_uri_set_uri (GstURIHandler * handler, const gchar * uri,
1712     GError ** error)
1713 {
1714   GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
1715
1716   /* parse out the new t/c/a and seek to them */
1717   {
1718     gchar *location = NULL;
1719     gchar **strs;
1720     gchar **strcur;
1721     gint pos = 0;
1722
1723     location = gst_uri_get_location (uri);
1724
1725     GST_OBJECT_LOCK (src);
1726
1727     src->uri_title = 1;
1728     src->uri_chapter = 1;
1729     src->uri_angle = 1;
1730
1731     if (!location)
1732       goto empty_location;
1733
1734     strcur = strs = g_strsplit (location, ",", 0);
1735     while (strcur && *strcur) {
1736       gint val;
1737
1738       if (!sscanf (*strcur, "%d", &val))
1739         break;
1740
1741       if (val <= 0) {
1742         g_warning ("Invalid value %d in URI '%s'. Must be 1 or greater",
1743             val, location);
1744         break;
1745       }
1746
1747       switch (pos) {
1748         case 0:
1749           src->uri_title = val;
1750           break;
1751         case 1:
1752           src->uri_chapter = val;
1753           break;
1754         case 2:
1755           src->uri_angle = val;
1756           break;
1757       }
1758
1759       strcur++;
1760       pos++;
1761     }
1762
1763     if (pos > 0 && GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_FLAG_STARTED)) {
1764       src->title = src->uri_title - 1;
1765       src->chapter = src->uri_chapter - 1;
1766       src->angle = src->uri_angle - 1;
1767       src->new_seek = TRUE;
1768     }
1769
1770     g_strfreev (strs);
1771     g_free (location);
1772
1773   empty_location:
1774
1775     GST_OBJECT_UNLOCK (src);
1776   }
1777
1778   return TRUE;
1779 }
1780
1781 static void
1782 gst_dvd_read_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1783 {
1784   GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1785
1786   iface->get_type = gst_dvd_read_src_uri_get_type;
1787   iface->get_protocols = gst_dvd_read_src_uri_get_protocols;
1788   iface->get_uri = gst_dvd_read_src_uri_get_uri;
1789   iface->set_uri = gst_dvd_read_src_uri_set_uri;
1790 }
1791
1792 static gboolean
1793 plugin_init (GstPlugin * plugin)
1794 {
1795   GST_DEBUG_CATEGORY_INIT (gstgst_dvd_read_src_debug, "dvdreadsrc", 0,
1796       "DVD reader element based on dvdreadsrc");
1797
1798 #ifdef ENABLE_NLS
1799   GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
1800       LOCALEDIR);
1801   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
1802   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1803 #endif /* ENABLE_NLS */
1804
1805   if (!gst_element_register (plugin, "dvdreadsrc", GST_RANK_NONE,
1806           GST_TYPE_DVD_READ_SRC)) {
1807     return FALSE;
1808   }
1809
1810   return TRUE;
1811 }
1812
1813 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1814     GST_VERSION_MINOR,
1815     dvdread,
1816     "Access a DVD with dvdread",
1817     plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);