1 /* GStreamer
2  * Copyright (C) 2008-2009 Jan Schmidt <thaytan@noraisin.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 
28 #include <gmodule.h>
29 #include <gst/gst.h>
30 #include <gst/glib-compat-private.h>
31 #include <gst/gst-i18n-plugin.h>
32 #include <gst/video/video.h>
33 #include <gst/video/navigation.h>
34 #include <gst/tag/tag.h>
35 
36 #include "resindvdsrc.h"
37 
38 GST_DEBUG_CATEGORY_STATIC (rsndvdsrc_debug);
39 #define GST_CAT_DEFAULT rsndvdsrc_debug
40 
41 #define DEFAULT_DEVICE "/dev/cd0"
42 #define DEFAULT_FASTSTART TRUE
43 #define DEFAULT_LANG "en"
44 
45 #define GST_FLOW_WOULD_BLOCK GST_FLOW_CUSTOM_SUCCESS
46 
47 #define CLOCK_BASE 9LL
48 #define CLOCK_FREQ CLOCK_BASE * 10000
49 
50 #define MPEGTIME_TO_GSTTIME(time) (gst_util_uint64_scale ((time), \
51             GST_MSECOND/10, CLOCK_BASE))
52 #define GSTTIME_TO_MPEGTIME(time) (gst_util_uint64_scale ((time), \
53             CLOCK_BASE, GST_MSECOND/10))
54 
55 typedef enum
56 {
57   RSN_NAV_RESULT_NONE,
58   RSN_NAV_RESULT_HIGHLIGHT,
59   RSN_NAV_RESULT_BRANCH,
60   RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT
61 } RsnNavResult;
62 
63 typedef enum
64 {
65   RSN_BTN_NONE = 0x00,
66   RSN_BTN_LEFT = 0x01,
67   RSN_BTN_RIGHT = 0x02,
68   RSN_BTN_UP = 0x04,
69   RSN_BTN_DOWN = 0x08
70 } RsnBtnMask;
71 
72 enum
73 {
74   /* FILL ME */
75   LAST_SIGNAL
76 };
77 
78 enum
79 {
80   ARG_0,
81   ARG_DEVICE,
82   ARG_FASTSTART
83 };
84 
85 typedef struct
86 {
87   GstBuffer *buffer;
88   GstClockTime ts;
89   GstClockTime running_ts;
90 } RsnDvdPendingNav;
91 
92 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
93     GST_PAD_SRC,
94     GST_PAD_ALWAYS,
95     // GST_STATIC_CAPS ("video/mpeg,mpegversion=2,systemstream=true")
96     GST_STATIC_CAPS ("application/x-resin-dvd")
97     );
98 
99 /* Private seek format for private flushing */
100 static GstFormat rsndvd_format;
101 /* Title/chapter formats */
102 static GstFormat title_format;
103 static GstFormat chapter_format;
104 
105 static void rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type);
106 
107 #define rsn_dvdsrc_parent_class parent_class
108 G_DEFINE_TYPE_EXTENDED (resinDvdSrc, rsn_dvdsrc, GST_TYPE_BASE_SRC,
109     0, rsn_dvdsrc_register_extra (g_define_type_id));
110 
111 static gboolean read_vts_info (resinDvdSrc * src);
112 
113 static void rsn_dvdsrc_set_property (GObject * object, guint prop_id,
114     const GValue * value, GParamSpec * pspec);
115 static void rsn_dvdsrc_get_property (GObject * object, guint prop_id,
116     GValue * value, GParamSpec * pspec);
117 
118 static void rsn_dvdsrc_finalize (GObject * object);
119 
120 static gboolean rsn_dvdsrc_start (GstBaseSrc * bsrc);
121 static gboolean rsn_dvdsrc_stop (GstBaseSrc * bsrc);
122 static gboolean rsn_dvdsrc_unlock (GstBaseSrc * bsrc);
123 static gboolean rsn_dvdsrc_unlock_stop (GstBaseSrc * bsrc);
124 
125 static gboolean rsn_dvdsrc_is_seekable (GstBaseSrc * bsrc);
126 static gboolean rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc,
127     GstEvent * event, GstSegment * segment);
128 static gboolean rsn_dvdsrc_do_seek (GstBaseSrc * bsrc, GstSegment * segment);
129 static GstStateChangeReturn rsn_dvdsrc_change_state (GstElement * element,
130     GstStateChange transition);
131 
132 static void rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src,
133     guint8 logical_stream, guint8 phys_stream, gboolean forced_only);
134 static void rsn_dvdsrc_prepare_audio_stream_event (resinDvdSrc * src,
135     guint8 logical_stream, guint8 phys_stream);
136 static gboolean rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src);
137 static void rsn_dvdsrc_prepare_clut_change_event (resinDvdSrc * src,
138     const guint32 * clut);
139 static void rsn_dvdsrc_update_highlight (resinDvdSrc * src);
140 
141 static void rsn_dvdsrc_enqueue_nav_block (resinDvdSrc * src,
142     GstBuffer * nav_buf, GstClockTime ts);
143 static void rsn_dvdsrc_activate_nav_block (resinDvdSrc * src,
144     GstBuffer * nav_buf);
145 static void rsn_dvdsrc_clear_nav_blocks (resinDvdSrc * src);
146 static void rsn_dvdsrc_check_nav_blocks (resinDvdSrc * src);
147 static void rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src,
148     RsnDvdPendingNav * next_nav);
149 
150 static GstFlowReturn rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset,
151     guint length, GstBuffer ** buf);
152 static gboolean rsn_dvdsrc_src_event (GstBaseSrc * basesrc, GstEvent * event);
153 static gboolean rsn_dvdsrc_src_query (GstBaseSrc * basesrc, GstQuery * query);
154 
155 static GstClockTime ifotime_to_gsttime (dvd_time_t * ifo_time);
156 static void rsn_dvdsrc_send_commands_changed (resinDvdSrc * src);
157 
158 static GstClockTime
ifotime_to_gsttime(dvd_time_t * ifo_time)159 ifotime_to_gsttime (dvd_time_t * ifo_time)
160 {
161   GstClockTime ts;
162   guint frames;
163 
164   ts = 36000 * GST_SECOND * ((ifo_time->hour & 0xf0) >> 4);
165   ts += 3600 * GST_SECOND * (ifo_time->hour & 0x0f);
166   ts += 600 * GST_SECOND * ((ifo_time->minute & 0xf0) >> 4);
167   ts += 60 * GST_SECOND * (ifo_time->minute & 0x0f);
168   ts += 10 * GST_SECOND * ((ifo_time->second & 0xf0) >> 4);
169   ts += GST_SECOND * (ifo_time->second & 0x0f);
170 
171   frames = ((ifo_time->frame_u >> 4) & 0x3) * 10;
172   frames += (ifo_time->frame_u & 0xf);
173 
174   if (ifo_time->frame_u & 0x80)
175     ts += GST_SECOND * frames / 30;
176   else
177     ts += GST_SECOND * frames / 25;
178 
179   return ts;
180 }
181 
182 static void
rsn_dvdsrc_register_extra(GType rsn_dvdsrc_type)183 rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type)
184 {
185   GST_DEBUG_CATEGORY_INIT (rsndvdsrc_debug, "rsndvdsrc", 0,
186       "Resin DVD source element based on libdvdnav");
187 
188   rsndvd_format = gst_format_register ("rsndvdsrc-internal",
189       "private Resin DVD src format");
190 
191   title_format = gst_format_register ("title", "DVD title format");
192   chapter_format = gst_format_register ("chapter", "DVD chapter format");
193 }
194 
195 static void
rsn_dvdsrc_class_init(resinDvdSrcClass * klass)196 rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
197 {
198   GObjectClass *gobject_class;
199   GstElementClass *gstelement_class;
200   GstBaseSrcClass *gstbasesrc_class;
201 
202   gobject_class = (GObjectClass *) klass;
203   gstelement_class = (GstElementClass *) klass;
204   gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
205 
206   gobject_class->finalize = rsn_dvdsrc_finalize;
207   gobject_class->set_property = rsn_dvdsrc_set_property;
208   gobject_class->get_property = rsn_dvdsrc_get_property;
209 
210   gstelement_class->change_state = rsn_dvdsrc_change_state;
211 
212   gstbasesrc_class->start = GST_DEBUG_FUNCPTR (rsn_dvdsrc_start);
213   gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (rsn_dvdsrc_stop);
214   gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (rsn_dvdsrc_unlock);
215   gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (rsn_dvdsrc_unlock_stop);
216   gstbasesrc_class->event = GST_DEBUG_FUNCPTR (rsn_dvdsrc_src_event);
217   gstbasesrc_class->query = GST_DEBUG_FUNCPTR (rsn_dvdsrc_src_query);
218   gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (rsn_dvdsrc_is_seekable);
219   gstbasesrc_class->prepare_seek_segment =
220       GST_DEBUG_FUNCPTR (rsn_dvdsrc_prepare_seek);
221   gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (rsn_dvdsrc_do_seek);
222 
223   gstbasesrc_class->create = GST_DEBUG_FUNCPTR (rsn_dvdsrc_create);
224 
225   g_object_class_install_property (gobject_class, ARG_DEVICE,
226       g_param_spec_string ("device", "Device", "DVD device location",
227           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
228 
229   g_object_class_install_property (gobject_class, ARG_FASTSTART,
230       g_param_spec_boolean ("fast-start", "Fast start",
231           "Skip straight to the DVD menu on start", DEFAULT_FASTSTART,
232           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
233 
234   gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
235   gst_element_class_set_static_metadata (gstelement_class, "Resin DVD Src",
236       "Source/DVD", "DVD source element", "Jan Schmidt <thaytan@noraisin.net>");
237 }
238 
239 static void
rsn_dvdsrc_init(resinDvdSrc * rsndvdsrc)240 rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc)
241 {
242   const gchar *envvar;
243 
244   envvar = g_getenv ("DVDFASTSTART");
245   if (envvar)
246     rsndvdsrc->faststart = (strcmp (envvar, "0") && strcmp (envvar, "no"));
247   else
248     rsndvdsrc->faststart = DEFAULT_FASTSTART;
249 
250   rsndvdsrc->device = g_strdup (DEFAULT_DEVICE);
251   g_mutex_init (&rsndvdsrc->dvd_lock);
252   g_mutex_init (&rsndvdsrc->branch_lock);
253   rsndvdsrc->branching = FALSE;
254   g_cond_init (&rsndvdsrc->still_cond);
255 
256   gst_base_src_set_format (GST_BASE_SRC (rsndvdsrc), GST_FORMAT_TIME);
257 }
258 
259 static void
rsn_dvdsrc_finalize(GObject * object)260 rsn_dvdsrc_finalize (GObject * object)
261 {
262   resinDvdSrc *src = RESINDVDSRC (object);
263 
264   g_mutex_clear (&src->dvd_lock);
265   g_mutex_clear (&src->branch_lock);
266   g_cond_clear (&src->still_cond);
267   g_free (src->device);
268 
269   gst_buffer_replace (&src->alloc_buf, NULL);
270   gst_buffer_replace (&src->next_buf, NULL);
271 
272   G_OBJECT_CLASS (parent_class)->finalize (object);
273 }
274 
275 static gboolean
rsn_dvdsrc_unlock(GstBaseSrc * bsrc)276 rsn_dvdsrc_unlock (GstBaseSrc * bsrc)
277 {
278   resinDvdSrc *src = RESINDVDSRC (bsrc);
279 
280   g_mutex_lock (&src->branch_lock);
281   src->branching = TRUE;
282   g_cond_broadcast (&src->still_cond);
283   g_mutex_unlock (&src->branch_lock);
284 
285   return TRUE;
286 }
287 
288 static gboolean
rsn_dvdsrc_unlock_stop(GstBaseSrc * bsrc)289 rsn_dvdsrc_unlock_stop (GstBaseSrc * bsrc)
290 {
291   resinDvdSrc *src = RESINDVDSRC (bsrc);
292 
293   g_mutex_lock (&src->branch_lock);
294   src->branching = FALSE;
295   g_mutex_unlock (&src->branch_lock);
296 
297   return TRUE;
298 }
299 
300 static void
rsn_dvdsrc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)301 rsn_dvdsrc_set_property (GObject * object, guint prop_id,
302     const GValue * value, GParamSpec * pspec)
303 {
304   resinDvdSrc *src = RESINDVDSRC (object);
305 
306   switch (prop_id) {
307     case ARG_DEVICE:
308       GST_OBJECT_LOCK (src);
309       g_free (src->device);
310       if (g_value_get_string (value) == NULL)
311         src->device = g_strdup (DEFAULT_DEVICE);
312       else
313         src->device = g_value_dup_string (value);
314       GST_OBJECT_UNLOCK (src);
315       break;
316     case ARG_FASTSTART:
317       GST_OBJECT_LOCK (src);
318       src->faststart = g_value_get_boolean (value);
319       GST_OBJECT_UNLOCK (src);
320       break;
321     default:
322       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
323       break;
324   }
325 }
326 
327 static void
rsn_dvdsrc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)328 rsn_dvdsrc_get_property (GObject * object, guint prop_id,
329     GValue * value, GParamSpec * pspec)
330 {
331   resinDvdSrc *src = RESINDVDSRC (object);
332 
333   switch (prop_id) {
334     case ARG_DEVICE:
335       GST_OBJECT_LOCK (src);
336       g_value_set_string (value, src->device);
337       GST_OBJECT_UNLOCK (src);
338       break;
339     case ARG_FASTSTART:
340       GST_OBJECT_LOCK (src);
341       g_value_set_boolean (value, src->faststart);
342       GST_OBJECT_UNLOCK (src);
343       break;
344     default:
345       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
346       break;
347   }
348 }
349 
350 static gboolean
rsn_dvdsrc_start(GstBaseSrc * bsrc)351 rsn_dvdsrc_start (GstBaseSrc * bsrc)
352 {
353   resinDvdSrc *src = RESINDVDSRC (bsrc);
354   const gchar *const *langs, *const *cur;
355   const char *disc_name;
356   gchar lang[8];
357 
358   g_mutex_lock (&src->dvd_lock);
359   if (!read_vts_info (src)) {
360     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
361         (_("Could not read title information for DVD.")), GST_ERROR_SYSTEM);
362     goto fail;
363   }
364 
365   if (dvdnav_open (&src->dvdnav, src->device) != DVDNAV_STATUS_OK) {
366     GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL),
367         (_("Failed to open DVD device '%s'."), src->device));
368     goto fail;
369   }
370 
371   if (dvdnav_set_PGC_positioning_flag (src->dvdnav, 1) != DVDNAV_STATUS_OK) {
372     GST_ELEMENT_ERROR (src, LIBRARY, FAILED,
373         (_("Failed to set PGC based seeking.")), GST_ERROR_SYSTEM);
374     goto fail;
375   }
376 
377   /* Attempt to set DVD menu, audio and spu languages */
378   langs = g_get_language_names ();
379   strncpy (lang, DEFAULT_LANG, 8);
380   for (cur = langs; *cur != NULL; cur++) {
381     /* Look for a 2 char iso-639 lang */
382     if (strlen (*cur) == 2) {
383       strncpy (lang, *cur, 8);
384       break;
385     }
386   }
387   /* Set the user's preferred language */
388   dvdnav_menu_language_select (src->dvdnav, lang);
389   dvdnav_audio_language_select (src->dvdnav, lang);
390   dvdnav_spu_language_select (src->dvdnav, lang);
391 
392   if (src->faststart) {
393     if (dvdnav_title_play (src->dvdnav, 1) != DVDNAV_STATUS_OK ||
394         (dvdnav_menu_call (src->dvdnav, DVD_MENU_Title) != DVDNAV_STATUS_OK &&
395             dvdnav_menu_call (src->dvdnav,
396                 DVD_MENU_Root) != DVDNAV_STATUS_OK)) {
397       /* Fast start failed. Do normal start */
398       dvdnav_reset (src->dvdnav);
399     }
400   }
401 
402   /* Get disc name and convert to UTF-8 */
403   g_free (src->disc_name);
404   dvdnav_get_title_string (src->dvdnav, &disc_name);
405   if (disc_name != NULL && *disc_name != '\0')
406     src->disc_name = gst_tag_freeform_string_to_utf8 (disc_name, -1, NULL);
407   else
408     src->disc_name = NULL;
409 
410   src->first_seek = TRUE;
411   src->running = TRUE;
412   src->branching = FALSE;
413   src->discont = TRUE;
414   src->need_segment = TRUE;
415   src->need_tag_update = TRUE;
416 
417   src->cur_position = GST_CLOCK_TIME_NONE;
418   src->pgc_duration = GST_CLOCK_TIME_NONE;
419   src->cur_start_ts = GST_CLOCK_TIME_NONE;
420   src->cur_end_ts = GST_CLOCK_TIME_NONE;
421   src->cur_vobu_base_ts = GST_CLOCK_TIME_NONE;
422 
423   src->vts_n = 0;
424   src->in_menu = FALSE;
425   src->title_n = -1;
426   src->part_n = -1;
427 
428   src->active_button = -1;
429   src->cur_btn_mask = RSN_BTN_NONE;
430 
431   src->angles_changed = FALSE;
432   src->n_angles = 0;
433   src->cur_angle = 0;
434 
435   src->commands_changed = TRUE;
436 
437   src->cur_spu_phys_stream = -1;
438   src->cur_spu_forced_only = FALSE;
439   memset (src->cur_clut, 0, sizeof (guint32) * 16);
440   src->cur_audio_phys_stream = -1;
441 
442   g_mutex_unlock (&src->dvd_lock);
443 
444   return TRUE;
445 
446 fail:
447   if (src->dvdnav) {
448     dvdnav_close (src->dvdnav);
449     src->dvdnav = NULL;
450   }
451   g_mutex_unlock (&src->dvd_lock);
452   return FALSE;
453 }
454 
455 /* Use libdvdread to read and cache info from the IFO file about
456  * streams in each VTS */
457 static gboolean
read_vts_info(resinDvdSrc * src)458 read_vts_info (resinDvdSrc * src)
459 {
460   gint n_vts;
461 
462   if (src->vts_attrs) {
463     g_array_free (src->vts_attrs, TRUE);
464     src->vts_attrs = NULL;
465   }
466 
467   if (src->dvdread)
468     DVDClose (src->dvdread);
469 
470   src->dvdread = DVDOpen (src->device);
471   if (src->dvdread == NULL)
472     return FALSE;
473 
474   if (!(src->vmg_file = ifoOpen (src->dvdread, 0))) {
475     GST_ERROR ("Can't open VMG ifo");
476     return FALSE;
477   }
478   if (!src->vmg_file->vts_atrt) {
479     GST_INFO ("No vts_atrt - odd, but apparently OK");
480     g_array_set_size (src->vts_attrs, 0);
481     src->vts_attrs = NULL;
482     return TRUE;
483   }
484   n_vts = src->vmg_file->vts_atrt->nr_of_vtss;
485   memcpy (&src->vmgm_attr, src->vmg_file->vmgi_mat, sizeof (vmgi_mat_t));
486 
487   GST_DEBUG ("Reading IFO info for %d VTSs", n_vts);
488   src->vts_attrs =
489       g_array_sized_new (FALSE, TRUE, sizeof (vtsi_mat_t), n_vts + 1);
490   if (!src->vts_attrs)
491     return FALSE;
492   g_array_set_size (src->vts_attrs, n_vts + 1);
493 
494   return TRUE;
495 }
496 
497 static vtsi_mat_t *
get_vts_attr(resinDvdSrc * src,gint n)498 get_vts_attr (resinDvdSrc * src, gint n)
499 {
500   vtsi_mat_t *vts_attr;
501 
502   if (src->vts_attrs == NULL || n >= src->vts_attrs->len) {
503     if (src->vts_attrs)
504       GST_ERROR_OBJECT (src, "No stream info for VTS %d (have %d)", n,
505           src->vts_attrs->len);
506     else
507       GST_ERROR_OBJECT (src, "No stream info");
508     return NULL;
509   }
510 
511   vts_attr = &g_array_index (src->vts_attrs, vtsi_mat_t, src->vts_n);
512 
513   /* Check if we have read this VTS ifo yet */
514   if (vts_attr->vtsm_vobs == 0) {
515     ifo_handle_t *ifo = ifoOpen (src->dvdread, n);
516 
517     if (!ifo) {
518       GST_ERROR ("Can't open VTS %d", n);
519       return NULL;
520     }
521 
522     GST_DEBUG ("VTS %d, Menu has %d audio %d subpictures. "
523         "Title has %d and %d", n,
524         ifo->vtsi_mat->nr_of_vtsm_audio_streams,
525         ifo->vtsi_mat->nr_of_vtsm_subp_streams,
526         ifo->vtsi_mat->nr_of_vts_audio_streams,
527         ifo->vtsi_mat->nr_of_vts_subp_streams);
528 
529     memcpy (&g_array_index (src->vts_attrs, vtsi_mat_t, n),
530         ifo->vtsi_mat, sizeof (vtsi_mat_t));
531 
532     ifoClose (ifo);
533   };
534 
535   return vts_attr;
536 }
537 
538 static gboolean
rsn_dvdsrc_stop(GstBaseSrc * bsrc)539 rsn_dvdsrc_stop (GstBaseSrc * bsrc)
540 {
541   resinDvdSrc *src = RESINDVDSRC (bsrc);
542   gboolean ret = TRUE;
543   GstMessage *mouse_over_msg = NULL;
544 
545   g_mutex_lock (&src->dvd_lock);
546 
547   if (src->nav_clock_id) {
548     gst_clock_id_unschedule (src->nav_clock_id);
549     gst_clock_id_unref (src->nav_clock_id);
550     src->nav_clock_id = NULL;
551   }
552   rsn_dvdsrc_clear_nav_blocks (src);
553   src->have_pci = FALSE;
554 
555   if (src->was_mouse_over) {
556     mouse_over_msg =
557         gst_navigation_message_new_mouse_over ((GstObject *) src, FALSE);
558     src->was_mouse_over = FALSE;
559   }
560 
561   /* Clear any allocated output buffer */
562   gst_buffer_replace (&src->alloc_buf, NULL);
563   gst_buffer_replace (&src->next_buf, NULL);
564   src->running = FALSE;
565 
566   if (src->streams_event) {
567     gst_event_unref (src->streams_event);
568     src->streams_event = NULL;
569   }
570   if (src->clut_event) {
571     gst_event_unref (src->clut_event);
572     src->clut_event = NULL;
573   }
574   if (src->spu_select_event) {
575     gst_event_unref (src->spu_select_event);
576     src->spu_select_event = NULL;
577   }
578   if (src->audio_select_event) {
579     gst_event_unref (src->audio_select_event);
580     src->audio_select_event = NULL;
581   }
582   if (src->highlight_event) {
583     gst_event_unref (src->highlight_event);
584     src->highlight_event = NULL;
585   }
586 
587   g_free (src->disc_name);
588   src->disc_name = NULL;
589 
590   if (src->dvdnav) {
591     if (dvdnav_close (src->dvdnav) != DVDNAV_STATUS_OK) {
592       GST_ELEMENT_ERROR (src, RESOURCE, CLOSE, (NULL),
593           ("dvdnav_close failed: %s", dvdnav_err_to_string (src->dvdnav)));
594       ret = FALSE;
595     }
596     src->dvdnav = NULL;
597   }
598 
599   if (src->vmg_file) {
600     ifoClose (src->vmg_file);
601     src->vmg_file = NULL;
602   }
603   if (src->vts_file) {
604     ifoClose (src->vts_file);
605     src->vts_file = NULL;
606   }
607   if (src->dvdread) {
608     DVDClose (src->dvdread);
609     src->dvdread = NULL;
610   }
611 
612   g_mutex_unlock (&src->dvd_lock);
613 
614   if (mouse_over_msg)
615     gst_element_post_message (GST_ELEMENT_CAST (src), mouse_over_msg);
616   return ret;
617 }
618 
619 /* handle still events. Call with dvd_lock */
620 static gboolean
rsn_dvdsrc_do_still(resinDvdSrc * src,int duration)621 rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
622 {
623   GstEvent *still_event;
624   GstEvent *hl_event;
625   gboolean cmds_changed;
626   GstEvent *seg_event;
627   GstSegment *segment = &(GST_BASE_SRC (src)->segment);
628 
629   if (src->in_still_state == FALSE) {
630     GST_DEBUG_OBJECT (src, "**** Start STILL FRAME. Duration %d ****",
631         duration);
632 
633     if (duration == 255)
634       src->still_time_remaining = GST_CLOCK_TIME_NONE;
635     else
636       src->still_time_remaining = GST_SECOND * duration;
637 
638     /* Send a close-segment event, and a still-frame start
639      * event, then sleep */
640     still_event = gst_video_event_new_still_frame (TRUE);
641 
642     segment->stop = segment->position = src->cur_end_ts;
643     GST_LOG_OBJECT (src, "Segment position now %" GST_TIME_FORMAT,
644         GST_TIME_ARGS (segment->position));
645 
646     seg_event = gst_event_new_segment (segment);
647 
648     /* Grab any pending highlight event to send too */
649     hl_event = src->highlight_event;
650     src->highlight_event = NULL;
651     cmds_changed = src->commands_changed;
652     src->commands_changed = FALSE;
653 
654     /* Now, send the events. We need to drop the dvd lock while doing so,
655      * and then check after if we got flushed */
656     g_mutex_unlock (&src->dvd_lock);
657     gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event);
658     gst_pad_push_event (GST_BASE_SRC_PAD (src), seg_event);
659     if (hl_event) {
660       GST_LOG_OBJECT (src, "Sending highlight event before still");
661       gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
662     }
663     if (cmds_changed)
664       rsn_dvdsrc_send_commands_changed (src);
665 
666     g_mutex_lock (&src->dvd_lock);
667 
668     g_mutex_lock (&src->branch_lock);
669 
670     src->in_still_state = TRUE;
671   } else {
672     GST_DEBUG_OBJECT (src,
673         "Re-entering still wait with %" GST_TIME_FORMAT " remaining",
674         GST_TIME_ARGS (src->still_time_remaining));
675     g_mutex_lock (&src->branch_lock);
676   }
677 
678   if (src->branching) {
679     GST_INFO_OBJECT (src, "Branching - aborting still");
680     g_mutex_unlock (&src->branch_lock);
681     return TRUE;
682   }
683 
684   if (duration == 255) {
685     /*
686      * The only way to get woken from this still is by a flushing
687      * seek or a user action. Either one will clear the still, so
688      * don't skip it
689      */
690     src->need_segment = TRUE;
691 
692     g_mutex_unlock (&src->dvd_lock);
693     GST_LOG_OBJECT (src, "Entering cond_wait still");
694     g_cond_wait (&src->still_cond, &src->branch_lock);
695     GST_LOG_OBJECT (src, "cond_wait still over, branching = %d",
696         src->branching);
697 
698     if (src->branching) {
699       g_mutex_unlock (&src->branch_lock);
700       g_mutex_lock (&src->dvd_lock);
701       return TRUE;
702     }
703     src->in_still_state = FALSE;
704 
705     g_mutex_unlock (&src->branch_lock);
706     g_mutex_lock (&src->dvd_lock);
707   } else {
708     gboolean was_signalled;
709 
710     if (src->still_time_remaining > 0) {
711       gint64 end_time;
712 
713       end_time =
714           g_get_monotonic_time () + src->still_time_remaining / GST_USECOND;
715 
716       /* Implement timed stills by sleeping, possibly
717        * in multiple steps if we get paused/unpaused */
718       g_mutex_unlock (&src->dvd_lock);
719       GST_LOG_OBJECT (src, "cond_timed_wait still for %d sec", duration);
720       was_signalled =
721           g_cond_wait_until (&src->still_cond, &src->branch_lock, end_time);
722       was_signalled |= src->branching;
723 
724       g_mutex_unlock (&src->branch_lock);
725       g_mutex_lock (&src->dvd_lock);
726 
727       if (was_signalled) {
728         /* Signalled - must be flushing */
729         gint64 cur_time;
730         GstClockTimeDiff remain;
731 
732         cur_time = g_get_monotonic_time ();
733         remain = (end_time - cur_time) * GST_USECOND;
734         if (remain < 0)
735           src->still_time_remaining = 0;
736         else
737           src->still_time_remaining = remain;
738 
739         GST_LOG_OBJECT (src,
740             "cond_timed_wait still aborted by signal with %" GST_TIME_FORMAT
741             " remaining. branching = %d",
742             GST_TIME_ARGS (src->still_time_remaining), src->branching);
743 
744         return TRUE;
745       }
746     }
747 
748     /* Else timed out, end the still */
749     GST_DEBUG_OBJECT (src,
750         "Timed still of %d secs over, calling dvdnav_still_skip", duration);
751 
752     if (dvdnav_still_skip (src->dvdnav) != DVDNAV_STATUS_OK) {
753       return FALSE;
754     }
755 
756     /* Tell downstream the still is over.
757      * We only do this if the still isn't interrupted: */
758     still_event = gst_video_event_new_still_frame (FALSE);
759 
760     /* If the segment was too short in a timed still, it may need extending */
761     if (segment->position < segment->start + GST_SECOND * duration) {
762       segment->position = segment->start + (GST_SECOND * duration);
763       if (segment->stop != -1 && segment->position > segment->stop)
764         segment->stop = segment->position;
765 
766       GST_LOG_OBJECT (src, "Extended segment position to %" GST_TIME_FORMAT,
767           GST_TIME_ARGS (segment->position));
768     }
769 
770     g_mutex_unlock (&src->dvd_lock);
771     gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event);
772     g_mutex_lock (&src->dvd_lock);
773   }
774 
775   return TRUE;
776 }
777 
778 static pgc_t *
get_current_pgc(resinDvdSrc * src)779 get_current_pgc (resinDvdSrc * src)
780 {
781   gint title, part, pgc_n;
782   gint32 vts_ttn;
783   pgc_t *pgc;
784 
785   if (dvdnav_is_domain_fp (src->dvdnav)) {
786     return src->vmg_file->first_play_pgc;
787   }
788 
789   if (src->vts_n == 0 || src->in_menu) {
790     /* FIXME: look up current menu PGC */
791     return NULL;
792   }
793 
794   if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
795       DVDNAV_STATUS_OK)
796     return NULL;
797 
798   /* To find the right PGC, we need the title number within this VTS (vts_ttn)
799    * from the VMG tt_srpt table... */
800   if (title < 1 || title > src->vmg_file->tt_srpt->nr_of_srpts)
801     return NULL;
802 
803   /* We must be in the correct VTS for any of this to succeed... */
804   if (src->vts_n != src->vmg_file->tt_srpt->title[title - 1].title_set_nr)
805     return NULL;
806 
807   /* We must also be in the VTS domain to use the tmap table */
808   if (src->vts_n == 0)
809     return NULL;
810 
811   vts_ttn = src->vmg_file->tt_srpt->title[title - 1].vts_ttn;
812 
813   if (vts_ttn < 1 || vts_ttn > src->vts_file->vts_ptt_srpt->nr_of_srpts)
814     return NULL;
815 
816   if (src->vts_file->vts_ptt_srpt->title[vts_ttn - 1].nr_of_ptts == 0)
817     return NULL;
818 
819   pgc_n = src->vts_file->vts_ptt_srpt->title[vts_ttn - 1].ptt[0].pgcn;
820   if (pgc_n > src->vts_file->vts_pgcit->nr_of_pgci_srp)
821     return NULL;
822 
823   pgc = src->vts_file->vts_pgcit->pgci_srp[pgc_n - 1].pgc;
824 
825   return pgc;
826 }
827 
828 static GstTagList *
update_title_info(resinDvdSrc * src,gboolean force)829 update_title_info (resinDvdSrc * src, gboolean force)
830 {
831   gint n_angles, cur_agl;
832   gint title_n, part_n;
833 
834   if (dvdnav_get_angle_info (src->dvdnav, &cur_agl,
835           &n_angles) == DVDNAV_STATUS_OK && src->n_angles != n_angles) {
836     /* Make sure we send an angles-changed message soon */
837     src->angles_changed = TRUE;
838   }
839 
840   if (dvdnav_current_title_info (src->dvdnav, &title_n,
841           &part_n) != DVDNAV_STATUS_OK) {
842     if (!src->in_menu)
843       return NULL;              /* Can't update now */
844     /* Must be in the first play sequence */
845     title_n = -1;
846     part_n = 0;
847   }
848 
849   if (title_n != src->title_n || part_n != src->part_n ||
850       src->n_angles != n_angles || src->cur_angle != cur_agl || force) {
851     gchar *title_str = NULL;
852 
853     src->title_n = title_n;
854     src->part_n = part_n;
855     src->n_angles = n_angles;
856     src->cur_angle = cur_agl;
857 
858     if (title_n == 0) {
859       /* In a menu */
860       title_str = g_strdup ("DVD Menu");
861     } else if (title_n > 0) {
862       /* In a title */
863       if (n_angles > 1) {
864         title_str = g_strdup_printf ("Title %i, Chapter %i, Angle %i of %i",
865             title_n, part_n, cur_agl, n_angles);
866 
867       } else {
868         title_str = g_strdup_printf ("Title %i, Chapter %i", title_n, part_n);
869       }
870     }
871 
872     if (src->disc_name && src->disc_name[0]) {
873       /* We have a name for this disc, publish it */
874       if (title_str) {
875         gchar *new_title_str =
876             g_strdup_printf ("%s, %s", title_str, src->disc_name);
877         g_free (title_str);
878         title_str = new_title_str;
879       } else {
880         title_str = g_strdup (src->disc_name);
881       }
882     }
883     if (title_str) {
884       GstTagList *tags = gst_tag_list_new (GST_TAG_TITLE, title_str, NULL);
885       g_free (title_str);
886       return tags;
887     }
888   }
889 
890   return NULL;
891 }
892 
893 /* we don't cache the result on purpose */
894 static gboolean
rsn_descrambler_available(void)895 rsn_descrambler_available (void)
896 {
897   GModule *module;
898   gpointer sym;
899   gsize res;
900 
901   module = g_module_open ("libdvdcss", 0);
902   if (module != NULL) {
903     res = g_module_symbol (module, "dvdcss_open", &sym);
904     g_module_close (module);
905   } else {
906     res = FALSE;
907   }
908 
909   return res;
910 }
911 
912 static GstFlowReturn
rsn_dvdsrc_step(resinDvdSrc * src,gboolean have_dvd_lock)913 rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
914 {
915   GstFlowReturn ret = GST_FLOW_OK;
916   dvdnav_status_t dvdnav_ret;
917   gint event, len;
918   GstMapInfo mmap;
919 
920   /* Allocate an output buffer if there isn't a pending one */
921   if (src->alloc_buf == NULL)
922     src->alloc_buf = gst_buffer_new_allocate (NULL, DVD_VIDEO_LB_LEN, NULL);
923 
924   gst_buffer_map (src->alloc_buf, &mmap, GST_MAP_WRITE);
925 
926   len = DVD_VIDEO_LB_LEN;
927 
928   dvdnav_ret = dvdnav_get_next_block (src->dvdnav, mmap.data, &event, &len);
929   if (dvdnav_ret != DVDNAV_STATUS_OK)
930     goto read_error;
931   g_mutex_lock (&src->branch_lock);
932   if (src->branching)
933     goto branching;
934   g_mutex_unlock (&src->branch_lock);
935 
936   switch (event) {
937     case DVDNAV_BLOCK_OK:
938       /* Data block that needs outputting */
939       gst_buffer_unmap (src->alloc_buf, &mmap);
940       src->next_buf = src->alloc_buf;
941       src->alloc_buf = NULL;
942 
943       src->next_is_nav_block = FALSE;
944       src->next_nav_ts = GST_CLOCK_TIME_NONE;
945       src->in_still_state = FALSE;
946       break;
947     case DVDNAV_NAV_PACKET:
948     {
949       pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
950       GstClockTime new_start_ptm = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm);
951       GstClockTime new_end_ptm = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm);
952       GstClockTimeDiff new_base_time = ifotime_to_gsttime (&pci->pci_gi.e_eltm);
953       gboolean discont = FALSE;
954 
955       src->in_still_state = FALSE;
956 
957       if (new_start_ptm != src->cur_end_ts) {
958         /* Hack because libdvdnav seems to lose a NAV packet during
959          * angle block changes, triggering a false discont */
960         GstClockTimeDiff diff = GST_CLOCK_DIFF (src->cur_end_ts, new_start_ptm);
961         if (src->cur_end_ts == GST_CLOCK_TIME_NONE || diff > 2 * GST_SECOND ||
962             diff < 0) {
963           discont = TRUE;
964           GST_DEBUG_OBJECT (src, "Discont NAV packet start TS %" GST_TIME_FORMAT
965               " != end TS %" GST_TIME_FORMAT,
966               GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (src->cur_end_ts));
967         }
968       }
969 
970       GST_LOG_OBJECT (src, "NAV packet start TS %" GST_TIME_FORMAT
971           " end TS %" GST_TIME_FORMAT " base %" GST_STIME_FORMAT " %s",
972           GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_end_ptm),
973           GST_STIME_ARGS (new_base_time), discont ? "discont" : "");
974 
975 #if 0
976       g_print ("NAV packet start TS %" GST_TIME_FORMAT
977           " end TS %" GST_TIME_FORMAT " base %" G_GINT64_FORMAT " %s\n",
978           GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_end_ptm),
979           new_base_time, discont ? "discont" : "");
980 #endif
981 
982       if (discont) {
983         GST_DEBUG_OBJECT (src,
984             "NAV packet discont: cur_end_ts %" GST_TIME_FORMAT " != "
985             " vobu_start_ptm: %" GST_TIME_FORMAT " base %" GST_TIME_FORMAT,
986             GST_TIME_ARGS (src->cur_end_ts),
987             GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_base_time));
988         src->need_segment = TRUE;
989       }
990 
991       src->cur_start_ts = new_start_ptm;
992       src->cur_end_ts = new_end_ptm;
993       src->cur_vobu_base_ts = new_base_time;
994 
995       /* NAV packet is also a data block that needs sending */
996       gst_buffer_unmap (src->alloc_buf, &mmap);
997       src->next_buf = src->alloc_buf;
998       src->alloc_buf = NULL;
999 
1000       if (!src->have_pci || pci->hli.hl_gi.hli_ss != 2) {
1001         /* Store the nav packet for activation at the right moment
1002          * if we don't have a packet yet or the info has changed (hli_ss != 2)
1003          */
1004         if (pci->hli.hl_gi.hli_s_ptm != 0)
1005           new_start_ptm = MPEGTIME_TO_GSTTIME (pci->hli.hl_gi.hli_s_ptm);
1006 
1007         src->next_is_nav_block = TRUE;
1008         src->next_nav_ts = new_start_ptm;
1009         GST_LOG_OBJECT (src, "Storing NAV pack with TS %" GST_TIME_FORMAT,
1010             GST_TIME_ARGS (src->next_nav_ts));
1011       } else {
1012         src->next_is_nav_block = FALSE;
1013         src->next_nav_ts = GST_CLOCK_TIME_NONE;
1014       }
1015       break;
1016     }
1017     case DVDNAV_STOP:
1018       /* End of the disc. EOS */
1019       dvdnav_reset (src->dvdnav);
1020       ret = GST_FLOW_EOS;
1021       break;
1022     case DVDNAV_STILL_FRAME:
1023     {
1024       dvdnav_still_event_t *info = (dvdnav_still_event_t *) mmap.data;
1025 
1026       if (!have_dvd_lock) {
1027         /* At a still frame but can't block, handle it later */
1028         return GST_FLOW_WOULD_BLOCK;
1029       }
1030 
1031       if (!rsn_dvdsrc_do_still (src, info->length))
1032         goto internal_error;
1033 
1034       g_mutex_lock (&src->branch_lock);
1035       if (src->branching)
1036         goto branching;
1037       g_mutex_unlock (&src->branch_lock);
1038       break;
1039     }
1040     case DVDNAV_WAIT:
1041       /* Drain out the queues so that the info on the screen matches
1042        * the VM state */
1043       if (have_dvd_lock) {
1044         /* FIXME: Drain out the queues, by sleeping on the clock or something */
1045         GST_LOG_OBJECT (src, "****** FIXME: WAIT for queues to drain *****");
1046       }
1047       if (dvdnav_wait_skip (src->dvdnav) != DVDNAV_STATUS_OK)
1048         goto internal_error;
1049       break;
1050     case DVDNAV_CELL_CHANGE:{
1051       dvdnav_cell_change_event_t *event =
1052           (dvdnav_cell_change_event_t *) mmap.data;
1053       GstMessage *message;
1054 
1055       src->pgc_duration = MPEGTIME_TO_GSTTIME (event->pgc_length);
1056       /* event->cell_start has the wrong time - it doesn't handle
1057        * multi-angle correctly (as of libdvdnav 4.1.3). The current_time()
1058        * calculates it correctly. */
1059       src->cur_position =
1060           MPEGTIME_TO_GSTTIME (dvdnav_get_current_time (src->dvdnav));
1061 
1062       GST_DEBUG_OBJECT (src,
1063           "CELL change dur now %" GST_TIME_FORMAT " position now %"
1064           GST_TIME_FORMAT, GST_TIME_ARGS (src->pgc_duration),
1065           GST_TIME_ARGS (src->cur_position));
1066 
1067       message = gst_message_new_duration_changed (GST_OBJECT (src));
1068       gst_element_post_message (GST_ELEMENT (src), message);
1069 
1070       rsn_dvdsrc_prepare_streamsinfo_event (src);
1071       src->need_tag_update = TRUE;
1072 
1073       break;
1074     }
1075     case DVDNAV_SPU_CLUT_CHANGE:
1076       rsn_dvdsrc_prepare_clut_change_event (src, (const guint32 *) mmap.data);
1077       break;
1078     case DVDNAV_VTS_CHANGE:{
1079       dvdnav_vts_change_event_t *event =
1080           (dvdnav_vts_change_event_t *) mmap.data;
1081 
1082       if (dvdnav_is_domain_vmgm (src->dvdnav)) {
1083         src->vts_n = 0;
1084       } else {
1085         src->vts_n = event->new_vtsN;
1086         if (src->vts_file) {
1087           ifoClose (src->vts_file);
1088           src->vts_file = NULL;
1089         }
1090         src->vts_file = ifoOpen (src->dvdread, src->vts_n);
1091       }
1092 
1093       src->in_menu = !dvdnav_is_domain_vts (src->dvdnav);
1094 
1095       break;
1096     }
1097     case DVDNAV_AUDIO_STREAM_CHANGE:{
1098       dvdnav_audio_stream_change_event_t *event =
1099           (dvdnav_audio_stream_change_event_t *) mmap.data;
1100 
1101       rsn_dvdsrc_prepare_audio_stream_event (src,
1102           event->logical, event->physical);
1103       GST_DEBUG_OBJECT (src, "  physical: %d", event->physical);
1104       GST_DEBUG_OBJECT (src, "  logical: %d", event->logical);
1105 
1106       break;
1107     }
1108     case DVDNAV_SPU_STREAM_CHANGE:{
1109       dvdnav_spu_stream_change_event_t *event =
1110           (dvdnav_spu_stream_change_event_t *) mmap.data;
1111       gint phys_track = event->physical_wide & 0x1f;
1112       gboolean forced_only = (event->physical_wide & 0x80) ? TRUE : FALSE;
1113 
1114       rsn_dvdsrc_prepare_spu_stream_event (src, event->logical, phys_track,
1115           forced_only);
1116 
1117       GST_DEBUG_OBJECT (src, "  physical_wide: %x", event->physical_wide);
1118       GST_DEBUG_OBJECT (src, "  physical_letterbox: %x",
1119           event->physical_letterbox);
1120       GST_DEBUG_OBJECT (src, "  physical_pan_scan: %x",
1121           event->physical_pan_scan);
1122       GST_DEBUG_OBJECT (src, "  logical: %x", event->logical);
1123       break;
1124     }
1125     case DVDNAV_HIGHLIGHT:{
1126       GST_DEBUG_OBJECT (src, "highlight change event, button %d",
1127           ((dvdnav_highlight_event_t *) mmap.data)->buttonN);
1128       rsn_dvdsrc_update_highlight (src);
1129       break;
1130     }
1131     case DVDNAV_HOP_CHANNEL:
1132       GST_DEBUG_OBJECT (src, "Channel hop - User action");
1133       src->need_segment = TRUE;
1134       break;
1135     case DVDNAV_NOP:
1136       break;
1137     default:
1138       GST_WARNING_OBJECT (src, "Unknown dvdnav event %d", event);
1139       break;
1140   }
1141   if (src->alloc_buf) {
1142     gst_buffer_unmap (src->alloc_buf, &mmap);
1143   }
1144 
1145   if (src->highlight_event && have_dvd_lock && src->in_playing) {
1146     GstEvent *hl_event = src->highlight_event;
1147 
1148     src->highlight_event = NULL;
1149     g_mutex_unlock (&src->dvd_lock);
1150     GST_DEBUG_OBJECT (src, "Sending highlight event - button %d",
1151         src->active_button);
1152     gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
1153     g_mutex_lock (&src->dvd_lock);
1154   }
1155 
1156   return ret;
1157 
1158 /* ERRORS */
1159 read_error:
1160   {
1161     gst_buffer_unmap (src->alloc_buf, &mmap);
1162     if (!rsn_descrambler_available ()) {
1163       GST_ELEMENT_ERROR (src, RESOURCE, READ,
1164           (_("Could not read DVD. This may be because the DVD is encrypted "
1165                   "and a DVD decryption library is not installed.")),
1166           ("Failed to read next DVD block. Error: %s",
1167               dvdnav_err_to_string (src->dvdnav)));
1168     } else {
1169       GST_ELEMENT_ERROR (src, RESOURCE, READ, (_("Could not read DVD.")),
1170           ("Failed to read next DVD block. Error: %s",
1171               dvdnav_err_to_string (src->dvdnav)));
1172     }
1173     return GST_FLOW_ERROR;
1174   }
1175 internal_error:
1176   {
1177     gst_buffer_unmap (src->alloc_buf, &mmap);
1178     GST_ELEMENT_ERROR (src, RESOURCE, READ, (_("Could not read DVD.")),
1179         ("Internal error processing DVD commands. Error: %s",
1180             dvdnav_err_to_string (src->dvdnav)));
1181     return GST_FLOW_ERROR;
1182   }
1183 branching:
1184   {
1185     g_mutex_unlock (&src->branch_lock);
1186     gst_buffer_unmap (src->alloc_buf, &mmap);
1187     return GST_FLOW_FLUSHING;
1188   }
1189 }
1190 
1191 /* Send app a bus message that the available commands have changed */
1192 static void
rsn_dvdsrc_send_commands_changed(resinDvdSrc * src)1193 rsn_dvdsrc_send_commands_changed (resinDvdSrc * src)
1194 {
1195   GstMessage *cmds_msg =
1196       gst_navigation_message_new_commands_changed (GST_OBJECT_CAST (src));
1197   gst_element_post_message (GST_ELEMENT_CAST (src), cmds_msg);
1198 }
1199 
1200 static gboolean
rsn_dvdsrc_handle_cmds_query(resinDvdSrc * src,GstQuery * query)1201 rsn_dvdsrc_handle_cmds_query (resinDvdSrc * src, GstQuery * query)
1202 {
1203   /* Expand this array if we have more commands in the future: */
1204   GstNavigationCommand cmds[16];
1205   gint n_cmds = 0;
1206 
1207   /* Fill out the standard set of commands we support */
1208   cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DVD_MENU;
1209   cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DVD_TITLE_MENU;
1210   cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DVD_ROOT_MENU;
1211   cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DVD_SUBPICTURE_MENU;
1212   cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DVD_AUDIO_MENU;
1213   cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DVD_ANGLE_MENU;
1214   cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DVD_CHAPTER_MENU;
1215 
1216   g_mutex_lock (&src->dvd_lock);
1217 
1218   /* Multiple angles available? */
1219   if (src->n_angles > 1) {
1220     cmds[n_cmds++] = GST_NAVIGATION_COMMAND_PREV_ANGLE;
1221     cmds[n_cmds++] = GST_NAVIGATION_COMMAND_NEXT_ANGLE;
1222   }
1223 
1224   /* Add button selection commands if we have them */
1225   if (src->active_button > 0) {
1226     /* We have a valid current button */
1227     cmds[n_cmds++] = GST_NAVIGATION_COMMAND_ACTIVATE;
1228   }
1229   /* Check for buttons in each direction */
1230   if (src->cur_btn_mask & RSN_BTN_LEFT)
1231     cmds[n_cmds++] = GST_NAVIGATION_COMMAND_LEFT;
1232   if (src->cur_btn_mask & RSN_BTN_RIGHT)
1233     cmds[n_cmds++] = GST_NAVIGATION_COMMAND_RIGHT;
1234   if (src->cur_btn_mask & RSN_BTN_UP)
1235     cmds[n_cmds++] = GST_NAVIGATION_COMMAND_UP;
1236   if (src->cur_btn_mask & RSN_BTN_DOWN)
1237     cmds[n_cmds++] = GST_NAVIGATION_COMMAND_DOWN;
1238   g_mutex_unlock (&src->dvd_lock);
1239 
1240   gst_navigation_query_set_commandsv (query, n_cmds, cmds);
1241 
1242   return TRUE;
1243 }
1244 
1245 static gboolean
rsn_dvdsrc_handle_angles_query(resinDvdSrc * src,GstQuery * query)1246 rsn_dvdsrc_handle_angles_query (resinDvdSrc * src, GstQuery * query)
1247 {
1248   gint cur_agl, n_angles;
1249   gboolean res = FALSE;
1250 
1251   g_mutex_lock (&src->dvd_lock);
1252   if (dvdnav_get_angle_info (src->dvdnav, &cur_agl,
1253           &n_angles) == DVDNAV_STATUS_OK) {
1254     gst_navigation_query_set_angles (query, cur_agl, n_angles);
1255     res = TRUE;
1256   }
1257   g_mutex_unlock (&src->dvd_lock);
1258 
1259   return res;
1260 }
1261 
1262 static gboolean
rsn_dvdsrc_handle_navigation_query(resinDvdSrc * src,GstNavigationQueryType nq_type,GstQuery * query)1263 rsn_dvdsrc_handle_navigation_query (resinDvdSrc * src,
1264     GstNavigationQueryType nq_type, GstQuery * query)
1265 {
1266   gboolean res;
1267 
1268   GST_LOG_OBJECT (src, "Have Navigation query of type %d", nq_type);
1269 
1270   switch (nq_type) {
1271     case GST_NAVIGATION_QUERY_COMMANDS:
1272       res = rsn_dvdsrc_handle_cmds_query (src, query);
1273       break;
1274     case GST_NAVIGATION_QUERY_ANGLES:
1275       res = rsn_dvdsrc_handle_angles_query (src, query);
1276       break;
1277     default:
1278       res = FALSE;
1279   }
1280 
1281   return res;
1282 }
1283 
1284 static GstFlowReturn
rsn_dvdsrc_prepare_next_block(resinDvdSrc * src,gboolean have_dvd_lock)1285 rsn_dvdsrc_prepare_next_block (resinDvdSrc * src, gboolean have_dvd_lock)
1286 {
1287   GstFlowReturn ret;
1288 
1289   /* If buffer already ready, return */
1290   if (src->next_buf)
1291     return GST_FLOW_OK;
1292 
1293   do {
1294     ret = rsn_dvdsrc_step (src, have_dvd_lock);
1295   }
1296   while (ret == GST_FLOW_OK && src->next_buf == NULL);
1297 
1298   if (ret == GST_FLOW_WOULD_BLOCK)
1299     ret = GST_FLOW_OK;
1300 
1301   return ret;
1302 }
1303 
1304 static GstFlowReturn
rsn_dvdsrc_create(GstBaseSrc * bsrc,guint64 offset,guint length,GstBuffer ** outbuf)1305 rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset,
1306     guint length, GstBuffer ** outbuf)
1307 {
1308   resinDvdSrc *src = RESINDVDSRC (bsrc);
1309   GstSegment *segment = &(GST_BASE_SRC (src)->segment);
1310   GstFlowReturn ret;
1311   GstEvent *streams_event = NULL;
1312   GstEvent *clut_event = NULL;
1313   GstEvent *spu_select_event = NULL;
1314   GstEvent *audio_select_event = NULL;
1315   GstEvent *highlight_event = NULL;
1316   GstMessage *angles_msg = NULL;
1317   GstTagList *tags = NULL;
1318   gboolean cmds_changed = FALSE;
1319 
1320   *outbuf = NULL;
1321 
1322   g_mutex_lock (&src->dvd_lock);
1323   ret = rsn_dvdsrc_prepare_next_block (src, TRUE);
1324   if (ret != GST_FLOW_OK) {
1325     g_mutex_unlock (&src->dvd_lock);
1326     return ret;
1327   }
1328 
1329   streams_event = src->streams_event;
1330   src->streams_event = NULL;
1331 
1332   spu_select_event = src->spu_select_event;
1333   src->spu_select_event = NULL;
1334 
1335   audio_select_event = src->audio_select_event;
1336   src->audio_select_event = NULL;
1337 
1338   clut_event = src->clut_event;
1339   src->clut_event = NULL;
1340 
1341   if (src->angles_changed) {
1342     gint cur, agls;
1343     if (dvdnav_get_angle_info (src->dvdnav, &cur, &agls) == DVDNAV_STATUS_OK) {
1344 
1345       angles_msg =
1346           gst_navigation_message_new_angles_changed (GST_OBJECT_CAST (src),
1347           cur, agls);
1348     }
1349     src->angles_changed = FALSE;
1350   }
1351 
1352   cmds_changed = src->commands_changed;
1353   src->commands_changed = FALSE;
1354 
1355   if (src->need_tag_update) {
1356     tags = update_title_info (src, FALSE);
1357     src->need_tag_update = FALSE;
1358   }
1359 
1360   g_mutex_unlock (&src->dvd_lock);
1361 
1362   /* Push in-band events now that we've dropped the dvd_lock, before
1363    * we change segment */
1364   if (streams_event) {
1365     GST_LOG_OBJECT (src, "Pushing stream layout event");
1366     gst_pad_push_event (GST_BASE_SRC_PAD (src), streams_event);
1367   }
1368   if (clut_event) {
1369     GST_LOG_OBJECT (src, "Pushing clut event");
1370     gst_pad_push_event (GST_BASE_SRC_PAD (src), clut_event);
1371   }
1372   /* Out of band events */
1373   if (spu_select_event) {
1374     GST_LOG_OBJECT (src, "Pushing spu_select event");
1375     gst_pad_push_event (GST_BASE_SRC_PAD (src), spu_select_event);
1376   }
1377   if (audio_select_event) {
1378     GST_LOG_OBJECT (src, "Pushing audio_select event");
1379     gst_pad_push_event (GST_BASE_SRC_PAD (src), audio_select_event);
1380   }
1381 
1382   if (src->need_segment) {
1383     /* Seamless segment update */
1384     GstClockTime elapsed_time = 0;
1385 
1386     if (src->cur_position != GST_CLOCK_TIME_NONE)
1387       elapsed_time += src->cur_position;
1388     if (src->cur_vobu_base_ts != GST_CLOCK_TIME_NONE)
1389       elapsed_time += src->cur_vobu_base_ts;
1390 
1391     GST_DEBUG_OBJECT (src,
1392         "Starting seamless segment update to %" GST_TIME_FORMAT " -> %"
1393         GST_TIME_FORMAT " VOBU %" GST_TIME_FORMAT " time %" GST_TIME_FORMAT,
1394         GST_TIME_ARGS (src->cur_start_ts), GST_TIME_ARGS (src->cur_end_ts),
1395         GST_TIME_ARGS (src->cur_vobu_base_ts), GST_TIME_ARGS (elapsed_time));
1396 
1397     gst_base_src_new_seamless_segment (GST_BASE_SRC (src),
1398         src->cur_start_ts, -1, elapsed_time);
1399 
1400     src->need_segment = FALSE;
1401   }
1402 
1403   if (src->cur_end_ts != GST_CLOCK_TIME_NONE) {
1404     segment->position = src->cur_end_ts;
1405     if (segment->stop != -1 && segment->position > segment->stop)
1406       segment->stop = segment->position;
1407 
1408     GST_LOG_OBJECT (src, "Segment position now %" GST_TIME_FORMAT,
1409         GST_TIME_ARGS (segment->position));
1410   }
1411 
1412   if (tags) {
1413     GstEvent *tag_event = gst_event_new_tag (tags);
1414     gst_pad_push_event (GST_BASE_SRC_PAD (src), tag_event);
1415     tags = NULL;
1416   }
1417   g_mutex_lock (&src->dvd_lock);
1418 
1419   if (src->next_buf != NULL) {
1420     /* Now that we're in the new segment, we can enqueue any nav packet
1421      * correctly */
1422     if (src->next_is_nav_block) {
1423       rsn_dvdsrc_enqueue_nav_block (src, src->next_buf, src->next_nav_ts);
1424       src->next_is_nav_block = FALSE;
1425     }
1426 
1427     *outbuf = src->next_buf;
1428     src->next_buf = NULL;
1429 
1430     if (src->discont) {
1431       GST_LOG_OBJECT (src, "Marking discont buffer");
1432       GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_DISCONT);
1433       src->discont = FALSE;
1434     }
1435   }
1436 
1437   if (src->in_playing) {
1438     highlight_event = src->highlight_event;
1439     src->highlight_event = NULL;
1440   } else {
1441     highlight_event = NULL;
1442   }
1443 
1444   /* Schedule a clock callback for the any pending nav packet */
1445   rsn_dvdsrc_check_nav_blocks (src);
1446 
1447   g_mutex_unlock (&src->dvd_lock);
1448 
1449   if (highlight_event) {
1450     GST_LOG_OBJECT (src, "Pushing highlight event with TS %" GST_TIME_FORMAT,
1451         GST_TIME_ARGS (GST_EVENT_TIMESTAMP (highlight_event)));
1452     gst_pad_push_event (GST_BASE_SRC_PAD (src), highlight_event);
1453   }
1454 
1455   if (angles_msg) {
1456     gst_element_post_message (GST_ELEMENT_CAST (src), angles_msg);
1457   }
1458 
1459   if (cmds_changed)
1460     rsn_dvdsrc_send_commands_changed (src);
1461 
1462   return ret;
1463 }
1464 
1465 static RsnNavResult
rsn_dvdsrc_perform_button_action(resinDvdSrc * src,GstNavigationCommand action)1466 rsn_dvdsrc_perform_button_action (resinDvdSrc * src,
1467     GstNavigationCommand action)
1468 {
1469   pci_t *pci;
1470   RsnNavResult result = RSN_NAV_RESULT_NONE;
1471   int button = 0;
1472   btni_t *btn_info;
1473 
1474   if (!src->have_pci)
1475     return RSN_NAV_RESULT_NONE;
1476   pci = &src->cur_pci;
1477 
1478   if (pci->hli.hl_gi.hli_ss == 0)
1479     return RSN_NAV_RESULT_NONE; /* No buttons at the moment */
1480 
1481   dvdnav_get_current_highlight (src->dvdnav, &button);
1482 
1483   if (button > pci->hli.hl_gi.btn_ns || button < 1)
1484     return RSN_NAV_RESULT_NONE; /* No valid button */
1485 
1486   btn_info = pci->hli.btnit + button - 1;
1487 
1488   switch (action) {
1489     case GST_NAVIGATION_COMMAND_ACTIVATE:
1490       if (dvdnav_button_activate (src->dvdnav, pci) == DVDNAV_STATUS_OK)
1491         result = RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT;
1492       break;
1493     case GST_NAVIGATION_COMMAND_LEFT:
1494       if (dvdnav_left_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
1495         if (btn_info->left &&
1496             pci->hli.btnit[btn_info->left - 1].auto_action_mode)
1497           result = RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT;
1498         else
1499           result = RSN_NAV_RESULT_HIGHLIGHT;
1500       }
1501       break;
1502     case GST_NAVIGATION_COMMAND_RIGHT:
1503       if (dvdnav_right_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
1504         if (btn_info->right &&
1505             pci->hli.btnit[btn_info->right - 1].auto_action_mode)
1506           result = RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT;
1507         else
1508           result = RSN_NAV_RESULT_HIGHLIGHT;
1509       }
1510       break;
1511     case GST_NAVIGATION_COMMAND_DOWN:
1512       if (dvdnav_lower_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
1513         if (btn_info->down &&
1514             pci->hli.btnit[btn_info->down - 1].auto_action_mode)
1515           result = RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT;
1516         else
1517           result = RSN_NAV_RESULT_HIGHLIGHT;
1518       }
1519       break;
1520     case GST_NAVIGATION_COMMAND_UP:
1521       if (dvdnav_upper_button_select (src->dvdnav, pci) == DVDNAV_STATUS_OK) {
1522         if (btn_info->up && pci->hli.btnit[btn_info->up - 1].auto_action_mode)
1523           result = RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT;
1524         else
1525           result = RSN_NAV_RESULT_HIGHLIGHT;
1526       }
1527       break;
1528     default:
1529       break;
1530   }
1531 
1532   if (result == RSN_NAV_RESULT_HIGHLIGHT) {
1533     /* If we're *only* changing the highlight, wake up the still condition.
1534      * If we're branching, that will happen anyway */
1535     g_cond_broadcast (&src->still_cond);
1536   }
1537 
1538   return result;
1539 }
1540 
1541 static RsnNavResult
rsn_dvdsrc_do_command(resinDvdSrc * src,GstNavigationCommand command)1542 rsn_dvdsrc_do_command (resinDvdSrc * src, GstNavigationCommand command)
1543 {
1544   RsnNavResult result = RSN_NAV_RESULT_NONE;
1545 
1546   switch (command) {
1547     case GST_NAVIGATION_COMMAND_DVD_MENU:
1548       if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Escape) == DVDNAV_STATUS_OK)
1549         result = RSN_NAV_RESULT_BRANCH;
1550       break;
1551     case GST_NAVIGATION_COMMAND_DVD_TITLE_MENU:
1552       if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Title) == DVDNAV_STATUS_OK)
1553         result = RSN_NAV_RESULT_BRANCH;
1554       break;
1555     case GST_NAVIGATION_COMMAND_DVD_ROOT_MENU:
1556       if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Root) == DVDNAV_STATUS_OK)
1557         result = RSN_NAV_RESULT_BRANCH;
1558       break;
1559     case GST_NAVIGATION_COMMAND_DVD_SUBPICTURE_MENU:
1560       if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Subpicture) ==
1561           DVDNAV_STATUS_OK)
1562         result = RSN_NAV_RESULT_BRANCH;
1563       break;
1564     case GST_NAVIGATION_COMMAND_DVD_AUDIO_MENU:
1565       if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Audio) == DVDNAV_STATUS_OK)
1566         result = RSN_NAV_RESULT_BRANCH;
1567       break;
1568     case GST_NAVIGATION_COMMAND_DVD_ANGLE_MENU:
1569       if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Angle) == DVDNAV_STATUS_OK)
1570         result = RSN_NAV_RESULT_BRANCH;
1571       break;
1572     case GST_NAVIGATION_COMMAND_DVD_CHAPTER_MENU:
1573       if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Part) == DVDNAV_STATUS_OK)
1574         result = RSN_NAV_RESULT_BRANCH;
1575       break;
1576     case GST_NAVIGATION_COMMAND_LEFT:
1577     case GST_NAVIGATION_COMMAND_RIGHT:
1578     case GST_NAVIGATION_COMMAND_UP:
1579     case GST_NAVIGATION_COMMAND_DOWN:
1580     case GST_NAVIGATION_COMMAND_ACTIVATE:
1581       return rsn_dvdsrc_perform_button_action (src, command);
1582 
1583     case GST_NAVIGATION_COMMAND_PREV_ANGLE:{
1584       gint32 cur, agls;
1585       gint new_angle = 0;
1586       if (dvdnav_get_angle_info (src->dvdnav, &cur, &agls) == DVDNAV_STATUS_OK) {
1587         if (cur > 0 &&
1588             dvdnav_angle_change (src->dvdnav, cur - 1) == DVDNAV_STATUS_OK) {
1589           new_angle = cur - 1;
1590         } else if (cur == 1 &&
1591             dvdnav_angle_change (src->dvdnav, agls) == DVDNAV_STATUS_OK) {
1592           new_angle = agls;
1593         }
1594         /* Angle switches are seamless and involve no branching */
1595         if (new_angle) {
1596           src->angles_changed = TRUE;
1597           GST_INFO_OBJECT (src, "Switched to angle %d", new_angle);
1598         }
1599       }
1600       break;
1601     }
1602     case GST_NAVIGATION_COMMAND_NEXT_ANGLE:{
1603       gint32 cur, agls;
1604       gint new_angle = 0;
1605       if (dvdnav_get_angle_info (src->dvdnav, &cur, &agls) == DVDNAV_STATUS_OK) {
1606         if (cur < agls
1607             && dvdnav_angle_change (src->dvdnav, cur + 1) == DVDNAV_STATUS_OK) {
1608           new_angle = cur + 1;
1609         } else if (cur == agls
1610             && dvdnav_angle_change (src->dvdnav, 1) == DVDNAV_STATUS_OK) {
1611           new_angle = 1;
1612         }
1613         /* Angle switches are seamless and involve no branching */
1614         if (new_angle) {
1615           src->angles_changed = TRUE;
1616           GST_INFO_OBJECT (src, "Switched to angle %d", new_angle);
1617         }
1618       }
1619       break;
1620     }
1621     default:
1622       break;
1623   }
1624 
1625   return result;
1626 }
1627 
1628 static gboolean
rsn_dvdsrc_handle_navigation_event(resinDvdSrc * src,GstEvent * event)1629 rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
1630 {
1631   gboolean have_lock = FALSE;
1632   GstEvent *hl_event = NULL;
1633   RsnNavResult nav_res = RSN_NAV_RESULT_NONE;
1634   GstNavigationEventType etype = gst_navigation_event_get_type (event);
1635   GstMessage *mouse_over_msg = NULL;
1636   GstMessage *angles_msg = NULL;
1637 
1638   switch (etype) {
1639     case GST_NAVIGATION_EVENT_KEY_PRESS:{
1640       const gchar *key;
1641       if (!gst_navigation_event_parse_key_event (event, &key))
1642         return FALSE;
1643 
1644       GST_DEBUG ("dvdnavsrc got a keypress: %s", key);
1645 
1646       g_mutex_lock (&src->dvd_lock);
1647       have_lock = TRUE;
1648       if (!src->running)
1649         goto not_running;
1650 
1651       if (g_str_equal (key, "Return")) {
1652         nav_res = rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_ACTIVATE);
1653       } else if (g_str_equal (key, "Left")) {
1654         nav_res = rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_LEFT);
1655       } else if (g_str_equal (key, "Right")) {
1656         nav_res = rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_RIGHT);
1657       } else if (g_str_equal (key, "Up")) {
1658         nav_res = rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_UP);
1659       } else if (g_str_equal (key, "Down")) {
1660         nav_res = rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_DOWN);
1661       } else if (g_str_equal (key, "m")) {
1662         nav_res = rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_DVD_MENU);
1663       } else if (g_str_equal (key, "t")) {
1664         nav_res =
1665             rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_DVD_TITLE_MENU);
1666       } else if (g_str_equal (key, "r")) {
1667         nav_res =
1668             rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_DVD_ROOT_MENU);
1669       } else if (g_str_equal (key, "comma")) {
1670         gint title = 0;
1671         gint part = 0;
1672 
1673         if (dvdnav_current_title_info (src->dvdnav, &title, &part)) {
1674           if (title > 0 && part > 1) {
1675             dvdnav_prev_pg_search (src->dvdnav);
1676             nav_res = RSN_NAV_RESULT_BRANCH;
1677           }
1678         }
1679       } else if (g_str_equal (key, "period")) {
1680         dvdnav_next_pg_search (src->dvdnav);
1681         nav_res = RSN_NAV_RESULT_BRANCH;
1682       } else if (g_str_equal (key, "bracketleft")) {
1683         nav_res =
1684             rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_PREV_ANGLE);
1685       } else if (g_str_equal (key, "bracketright")) {
1686         nav_res =
1687             rsn_dvdsrc_do_command (src, GST_NAVIGATION_COMMAND_NEXT_ANGLE);
1688       } else if (key && key[0] >= '1' && key[0] <= '8') {
1689         gint new_stream = key[0] - '1';
1690         GST_INFO_OBJECT (src, "Selecting audio stream %d", new_stream);
1691         rsn_dvdsrc_prepare_audio_stream_event (src, new_stream, new_stream);
1692       }
1693       break;
1694     }
1695     case GST_NAVIGATION_EVENT_MOUSE_MOVE:{
1696       gdouble x, y;
1697 
1698       if (!gst_navigation_event_parse_mouse_move_event (event, &x, &y))
1699         return FALSE;
1700 
1701       g_mutex_lock (&src->dvd_lock);
1702       have_lock = TRUE;
1703       if (!src->running)
1704         goto not_running;
1705 
1706       if (src->have_pci &&
1707           dvdnav_mouse_select (src->dvdnav, &src->cur_pci, (int) x, (int) y) ==
1708           DVDNAV_STATUS_OK) {
1709         nav_res = RSN_NAV_RESULT_HIGHLIGHT;
1710         if (!src->was_mouse_over) {
1711           GST_DEBUG_OBJECT (src, "Mouse moved onto a button");
1712           mouse_over_msg =
1713               gst_navigation_message_new_mouse_over ((GstObject *) src, TRUE);
1714           src->was_mouse_over = TRUE;
1715         }
1716       } else if (src->was_mouse_over) {
1717         GST_DEBUG_OBJECT (src, "Mouse moved out of a button");
1718         mouse_over_msg =
1719             gst_navigation_message_new_mouse_over ((GstObject *) src, FALSE);
1720         src->was_mouse_over = FALSE;
1721       }
1722       break;
1723     }
1724     case GST_NAVIGATION_EVENT_MOUSE_BUTTON_RELEASE:{
1725       gdouble x, y;
1726       gint button;
1727 
1728       if (!gst_navigation_event_parse_mouse_button_event (event, &button, &x,
1729               &y))
1730         return FALSE;
1731       if (button != 1)
1732         return FALSE;
1733 
1734       GST_DEBUG_OBJECT (src, "Got click at %g, %g", x, y);
1735 
1736       g_mutex_lock (&src->dvd_lock);
1737       have_lock = TRUE;
1738       if (!src->running)
1739         goto not_running;
1740 
1741       if (src->have_pci && dvdnav_mouse_activate (src->dvdnav, &src->cur_pci,
1742               (int) x, (int) y) == DVDNAV_STATUS_OK) {
1743         nav_res = RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT;
1744       }
1745       break;
1746     }
1747     case GST_NAVIGATION_EVENT_COMMAND:{
1748       GstNavigationCommand command;
1749 
1750       if (!gst_navigation_event_parse_command (event, &command))
1751         return FALSE;
1752       if (command == GST_NAVIGATION_COMMAND_INVALID)
1753         return FALSE;
1754 
1755       g_mutex_lock (&src->dvd_lock);
1756       have_lock = TRUE;
1757       if (!src->running)
1758         goto not_running;
1759 
1760       GST_LOG_OBJECT (src, "handling navigation command %d", command);
1761       nav_res = rsn_dvdsrc_do_command (src, command);
1762       break;
1763     }
1764     default:
1765       return TRUE;
1766   }
1767 
1768   if (have_lock) {
1769     gboolean channel_hop = FALSE;
1770     gboolean cmds_changed;
1771 
1772     if (nav_res != RSN_NAV_RESULT_NONE) {
1773       if (nav_res == RSN_NAV_RESULT_BRANCH) {
1774         channel_hop = TRUE;
1775       } else if (nav_res == RSN_NAV_RESULT_BRANCH_AND_HIGHLIGHT) {
1776         src->active_highlight = TRUE;
1777         channel_hop = TRUE;
1778       }
1779 
1780       rsn_dvdsrc_update_highlight (src);
1781     }
1782 
1783     if (channel_hop) {
1784       GstEvent *seek;
1785 
1786       GST_DEBUG_OBJECT (src, "Processing flush and jump");
1787       g_mutex_lock (&src->branch_lock);
1788       src->branching = TRUE;
1789       g_cond_broadcast (&src->still_cond);
1790       g_mutex_unlock (&src->branch_lock);
1791 
1792       hl_event = src->highlight_event;
1793       src->highlight_event = NULL;
1794       src->active_highlight = FALSE;
1795 
1796       g_mutex_unlock (&src->dvd_lock);
1797 
1798       if (hl_event) {
1799         GST_DEBUG_OBJECT (src, "Sending highlight change event - button: %d",
1800             src->active_button);
1801         gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
1802       }
1803 
1804       /* Send ourselves a seek event to wake everything up and flush */
1805       seek = gst_event_new_seek (1.0, rsndvd_format, GST_SEEK_FLAG_FLUSH,
1806           GST_SEEK_TYPE_NONE, -1, GST_SEEK_TYPE_NONE, -1);
1807       src->flushing_seek = TRUE;
1808       gst_element_send_event (GST_ELEMENT (src), seek);
1809 
1810       g_mutex_lock (&src->dvd_lock);
1811 
1812       rsn_dvdsrc_update_highlight (src);
1813     }
1814 
1815     hl_event = src->highlight_event;
1816     src->highlight_event = NULL;
1817 
1818     if (src->angles_changed) {
1819       gint cur, agls;
1820       if (dvdnav_get_angle_info (src->dvdnav, &cur, &agls) == DVDNAV_STATUS_OK) {
1821 
1822         angles_msg =
1823             gst_navigation_message_new_angles_changed (GST_OBJECT_CAST (src),
1824             cur, agls);
1825       }
1826       src->angles_changed = FALSE;
1827 
1828       src->need_tag_update = TRUE;
1829     }
1830 
1831     cmds_changed = src->commands_changed;
1832     src->commands_changed = FALSE;
1833 
1834     g_mutex_unlock (&src->dvd_lock);
1835 
1836     if (hl_event) {
1837       GST_DEBUG_OBJECT (src, "Sending highlight change event - button: %d",
1838           src->active_button);
1839       gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
1840     }
1841 
1842     if (cmds_changed)
1843       rsn_dvdsrc_send_commands_changed (src);
1844   }
1845 
1846   if (mouse_over_msg) {
1847     gst_element_post_message (GST_ELEMENT_CAST (src), mouse_over_msg);
1848   }
1849 
1850   if (angles_msg) {
1851     gst_element_post_message (GST_ELEMENT_CAST (src), angles_msg);
1852   }
1853 
1854   return TRUE;
1855 not_running:
1856   if (have_lock)
1857     g_mutex_unlock (&src->dvd_lock);
1858   GST_DEBUG_OBJECT (src, "Element not started. Ignoring navigation event");
1859   return FALSE;
1860 }
1861 
1862 static void
rsn_dvdsrc_prepare_audio_stream_event(resinDvdSrc * src,guint8 logical_stream,guint8 phys_stream)1863 rsn_dvdsrc_prepare_audio_stream_event (resinDvdSrc * src, guint8 logical_stream,
1864     guint8 phys_stream)
1865 {
1866   GstStructure *s;
1867   GstEvent *e;
1868 
1869   if (phys_stream == src->cur_audio_phys_stream)
1870     return;
1871   src->cur_audio_phys_stream = phys_stream;
1872 
1873   GST_DEBUG_OBJECT (src, "Preparing audio change, phys %d", phys_stream);
1874 
1875   s = gst_structure_new ("application/x-gst-dvd",
1876       "event", G_TYPE_STRING, "dvd-set-audio-track",
1877       "logical-id", G_TYPE_INT, (gint) logical_stream,
1878       "physical-id", G_TYPE_INT, (gint) phys_stream, NULL);
1879 
1880   e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
1881 
1882   if (src->audio_select_event)
1883     gst_event_unref (src->audio_select_event);
1884   src->audio_select_event = e;
1885 }
1886 
1887 static void
rsn_dvdsrc_prepare_spu_stream_event(resinDvdSrc * src,guint8 logical_stream,guint8 phys_stream,gboolean forced_only)1888 rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src, guint8 logical_stream,
1889     guint8 phys_stream, gboolean forced_only)
1890 {
1891   GstStructure *s;
1892   GstEvent *e;
1893 
1894   if (phys_stream == src->cur_spu_phys_stream &&
1895       forced_only == src->cur_spu_forced_only) {
1896     return;
1897   }
1898   src->cur_spu_phys_stream = phys_stream;
1899   src->cur_spu_forced_only = forced_only;
1900 
1901   GST_DEBUG_OBJECT (src, "Preparing SPU change, log %d phys %d forced %d",
1902       logical_stream, phys_stream, forced_only);
1903 
1904   s = gst_structure_new ("application/x-gst-dvd",
1905       "event", G_TYPE_STRING, "dvd-set-subpicture-track",
1906       "logical-id", G_TYPE_INT, (gint) logical_stream,
1907       "physical-id", G_TYPE_INT, (gint) phys_stream,
1908       "forced-only", G_TYPE_BOOLEAN, forced_only, NULL);
1909 
1910   e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
1911 
1912   if (src->spu_select_event)
1913     gst_event_unref (src->spu_select_event);
1914   src->spu_select_event = e;
1915 }
1916 
1917 static gboolean
rsn_dvdsrc_prepare_streamsinfo_event(resinDvdSrc * src)1918 rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src)
1919 {
1920   vtsi_mat_t *vts_attr;
1921   video_attr_t *v_attr;
1922   audio_attr_t *a_attrs;
1923   subp_attr_t *s_attrs;
1924   gint n_audio, n_subp;
1925   int8_t cur_audio;
1926   GstStructure *s;
1927   GstEvent *e;
1928   gint i;
1929   gchar lang_code[3] = { '\0', '\0', '\0' };
1930   gchar *t;
1931   gboolean is_widescreen;
1932   gboolean have_audio;
1933   gboolean have_subp;
1934 
1935   if (src->vts_n == 0 || src->vts_attrs == NULL) {
1936     /* VMGM info */
1937     vts_attr = NULL;
1938     v_attr = &src->vmgm_attr.vmgm_video_attr;
1939     a_attrs = &src->vmgm_attr.vmgm_audio_attr;
1940     n_audio = MIN (1, src->vmgm_attr.nr_of_vmgm_audio_streams);
1941     s_attrs = &src->vmgm_attr.vmgm_subp_attr;
1942     n_subp = MIN (1, src->vmgm_attr.nr_of_vmgm_subp_streams);
1943   } else if (src->in_menu) {
1944     /* VTSM attrs */
1945     vts_attr = get_vts_attr (src, src->vts_n);
1946     v_attr = &vts_attr->vtsm_video_attr;
1947     a_attrs = &vts_attr->vtsm_audio_attr;
1948     n_audio = MAX (1, vts_attr->nr_of_vtsm_audio_streams);
1949     s_attrs = &vts_attr->vtsm_subp_attr;
1950     n_subp = MAX (1, vts_attr->nr_of_vtsm_subp_streams);
1951   } else {
1952     /* VTS domain */
1953     vts_attr = get_vts_attr (src, src->vts_n);
1954     v_attr = &vts_attr->vts_video_attr;
1955     a_attrs = vts_attr->vts_audio_attr;
1956     n_audio = vts_attr->nr_of_vts_audio_streams;
1957     s_attrs = vts_attr->vts_subp_attr;
1958     n_subp = vts_attr->nr_of_vts_subp_streams;
1959   }
1960 
1961   if (src->vts_n > 0 && vts_attr == NULL)
1962     return FALSE;
1963 
1964   GST_DEBUG_OBJECT (src, "Preparing streamsinfo for %d audio and "
1965       "%d subpicture streams", n_audio, n_subp);
1966 
1967   /* build event */
1968   s = gst_structure_new ("application/x-gst-dvd",
1969       "event", G_TYPE_STRING, "dvd-lang-codes", NULL);
1970   e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
1971 
1972   /* video */
1973   is_widescreen = (v_attr->display_aspect_ratio != 0);
1974   gst_structure_set (s, "video-pal-format", G_TYPE_BOOLEAN,
1975       (v_attr->video_format != 0), NULL);
1976   gst_structure_set (s, "video-widescreen", G_TYPE_BOOLEAN, is_widescreen,
1977       NULL);
1978 
1979   /* audio */
1980   cur_audio = dvdnav_get_active_audio_stream (src->dvdnav);
1981 
1982   have_audio = FALSE;
1983   for (i = 0; i < n_audio; i++) {
1984     const audio_attr_t *a = a_attrs + i;
1985     gint phys_id = dvdnav_get_audio_logical_stream (src->dvdnav, (guint) i);
1986 
1987     if (phys_id == -1) {
1988       GST_DEBUG_OBJECT (src, "No substream ID in map for audio %d. Skipping.",
1989           i);
1990       continue;
1991     }
1992 
1993     GST_DEBUG_OBJECT (src, "mapped logical audio %d to MPEG substream %d",
1994         i, phys_id);
1995     /* Force audio stream reselection in case format changed ... */
1996     if (i == cur_audio) {
1997       src->cur_audio_phys_stream = -1;
1998       rsn_dvdsrc_prepare_audio_stream_event (src, i, phys_id);
1999     }
2000 #if 0
2001     /* Old test code: Only output A52 streams */
2002     if (a->audio_format != 0) {
2003       GST_DEBUG_OBJECT (src, "Ignoring non-A52 stream %d, format %d", i,
2004           (int) a->audio_format);
2005       continue;
2006     }
2007     if (a->audio_format == 0)
2008       have_audio = TRUE;
2009 #else
2010     have_audio = TRUE;
2011 #endif
2012 
2013     GST_DEBUG_OBJECT (src, "Audio stream %d is format %d, substream %d", i,
2014         (int) a->audio_format, phys_id);
2015 
2016     t = g_strdup_printf ("audio-%d-stream", i);
2017     gst_structure_set (s, t, G_TYPE_INT, phys_id, NULL);
2018     g_free (t);
2019 
2020     t = g_strdup_printf ("audio-%d-format", i);
2021     gst_structure_set (s, t, G_TYPE_INT, (int) a->audio_format, NULL);
2022     g_free (t);
2023 
2024     /* Check that the language code is flagged and at least somewhat valid
2025      * before putting it in the output structure */
2026     if (a->lang_type && a->lang_code > 0x100) {
2027       t = g_strdup_printf ("audio-%d-language", i);
2028       lang_code[0] = (a->lang_code >> 8) & 0xff;
2029       lang_code[1] = a->lang_code & 0xff;
2030       gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
2031       g_free (t);
2032 
2033       GST_DEBUG_OBJECT (src, "Audio stream %d is language %s", i, lang_code);
2034     } else
2035       GST_DEBUG_OBJECT (src, "Audio stream %d - no language", i);
2036   }
2037 
2038   if (have_audio == FALSE) {
2039     /* Always create at least one audio stream of the required type */
2040     gst_structure_set (s, "audio-0-format", G_TYPE_INT, (int) 0,
2041         "audio-0-stream", G_TYPE_INT, (int) 0, NULL);
2042   }
2043 
2044   /* subpictures */
2045   have_subp = FALSE;
2046   for (i = 0; i < n_subp; i++) {
2047     const subp_attr_t *u = s_attrs + i;
2048     gint phys_id = dvdnav_get_spu_logical_stream (src->dvdnav, (guint) i);
2049 
2050     if (phys_id == -1) {
2051       GST_DEBUG_OBJECT (src, "No substream ID in map for subpicture %d. "
2052           "Skipping", i);
2053       continue;
2054     }
2055     have_subp = TRUE;
2056 
2057     GST_DEBUG_OBJECT (src, "mapped logical subpicture %d to MPEG substream %d",
2058         i, phys_id);
2059 
2060     t = g_strdup_printf ("subpicture-%d-stream", i);
2061     gst_structure_set (s, t, G_TYPE_INT, (int) phys_id, NULL);
2062     g_free (t);
2063 
2064     t = g_strdup_printf ("subpicture-%d-format", i);
2065     gst_structure_set (s, t, G_TYPE_INT, (int) 0, NULL);
2066     g_free (t);
2067 
2068     t = g_strdup_printf ("subpicture-%d-language", i);
2069     if (u->type && u->lang_code > 0x100) {
2070       lang_code[0] = (u->lang_code >> 8) & 0xff;
2071       lang_code[1] = u->lang_code & 0xff;
2072       gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
2073     } else {
2074       gst_structure_set (s, t, G_TYPE_STRING, "MENU", NULL);
2075     }
2076     g_free (t);
2077 
2078     GST_DEBUG_OBJECT (src, "Subpicture stream %d is language %s", i,
2079         lang_code[0] ? lang_code : "NONE");
2080   }
2081   if (!have_subp) {
2082     /* Always create at least one subpicture stream */
2083     gst_structure_set (s, "subpicture-0-format", G_TYPE_INT, (int) 0,
2084         "subpicture-0-language", G_TYPE_STRING, "MENU",
2085         "subpicture-0-stream", G_TYPE_INT, (int) 0, NULL);
2086   }
2087 
2088   if (src->streams_event)
2089     gst_event_unref (src->streams_event);
2090   src->streams_event = e;
2091 
2092   return TRUE;
2093 }
2094 
2095 static void
rsn_dvdsrc_prepare_clut_change_event(resinDvdSrc * src,const guint32 * clut)2096 rsn_dvdsrc_prepare_clut_change_event (resinDvdSrc * src, const guint32 * clut)
2097 {
2098   GstEvent *event;
2099   GstStructure *structure;
2100   gchar name[16];
2101   int i;
2102 
2103   if (memcmp (src->cur_clut, clut, sizeof (guint32) * 16) == 0)
2104     return;
2105   memcpy (src->cur_clut, clut, sizeof (guint32) * 16);
2106 
2107   structure = gst_structure_new ("application/x-gst-dvd",
2108       "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
2109 
2110   /* Create a separate field for each value in the table. */
2111   for (i = 0; i < 16; i++) {
2112     sprintf (name, "clut%02d", i);
2113     gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
2114   }
2115 
2116   /* Create the DVD event and put the structure into it. */
2117   event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
2118 
2119   GST_LOG_OBJECT (src, "preparing clut change event %" GST_PTR_FORMAT, event);
2120 
2121   if (src->clut_event)
2122     gst_event_unref (src->clut_event);
2123   src->clut_event = event;
2124 }
2125 
2126 /*
2127  * Check for a new highlighted area, and prepare an spu highlight event if
2128  * necessary.
2129  */
2130 static void
rsn_dvdsrc_update_highlight(resinDvdSrc * src)2131 rsn_dvdsrc_update_highlight (resinDvdSrc * src)
2132 {
2133   int button = 0;
2134   pci_t *pci = &src->cur_pci;
2135   dvdnav_highlight_area_t area;
2136   int mode = src->active_highlight ? 1 : 0;
2137   GstEvent *event = NULL;
2138   GstStructure *s;
2139 
2140   if (src->have_pci) {
2141     if (dvdnav_get_current_highlight (src->dvdnav, &button) == DVDNAV_STATUS_OK) {
2142       GST_LOG_OBJECT (src, "current dvdnav button is %d, we have %d",
2143           button, src->active_button);
2144     }
2145 
2146     if (pci->hli.hl_gi.hli_ss == 0 || button < 0) {
2147       button = 0;
2148     } else if (button > pci->hli.hl_gi.btn_ns) {
2149       /* button is out of the range of possible buttons. */
2150       button = pci->hli.hl_gi.btn_ns;
2151       dvdnav_button_select (src->dvdnav, &src->cur_pci, button);
2152     }
2153 
2154     if (button > 0 && dvdnav_get_highlight_area (pci, button, mode,
2155             &area) != DVDNAV_STATUS_OK) {
2156       button = 0;
2157     }
2158   }
2159 
2160   if (button == 0) {
2161     /* No highlight available, or no button selected - clear the SPU */
2162     if (src->active_button != 0) {
2163       src->active_button = 0;
2164 
2165       s = gst_structure_new ("application/x-gst-dvd", "event",
2166           G_TYPE_STRING, "dvd-spu-reset-highlight", NULL);
2167       event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
2168       if (src->highlight_event)
2169         gst_event_unref (src->highlight_event);
2170       src->highlight_event = event;
2171       if (src->cur_btn_mask != RSN_BTN_NONE) {
2172         src->cur_btn_mask = RSN_BTN_NONE;
2173         src->commands_changed = TRUE;
2174       }
2175     }
2176     return;
2177   }
2178 
2179   /* Check if we have a new button number, or a new highlight region. */
2180   if (button != src->active_button ||
2181       area.sx != src->area.sx || area.sy != src->area.sy ||
2182       area.ex != src->area.ex || area.ey != src->area.ey ||
2183       area.palette != src->area.palette) {
2184     btni_t *btn_info = pci->hli.btnit + button - 1;
2185     guint32 btn_mask;
2186 
2187     GST_DEBUG_OBJECT (src, "Setting highlight. Button %d @ %d,%d,%d,%d "
2188         "active %d palette 0x%x (from button %d @ %d,%d,%d,%d palette 0x%x)",
2189         button, area.sx, area.sy, area.ex, area.ey,
2190         mode, area.palette,
2191         src->active_button, src->area.sx, src->area.sy, src->area.ex,
2192         src->area.ey, src->area.palette);
2193 
2194     memcpy (&(src->area), &area, sizeof (dvdnav_highlight_area_t));
2195 
2196     s = gst_structure_new ("application/x-gst-dvd", "event",
2197         G_TYPE_STRING, "dvd-spu-highlight",
2198         "button", G_TYPE_INT, (gint) button,
2199         "palette", G_TYPE_INT, (gint) area.palette,
2200         "sx", G_TYPE_INT, (gint) area.sx,
2201         "sy", G_TYPE_INT, (gint) area.sy,
2202         "ex", G_TYPE_INT, (gint) area.ex,
2203         "ey", G_TYPE_INT, (gint) area.ey, NULL);
2204 
2205     event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
2206 
2207     if (src->active_button < 1) {
2208       /* When setting the button for the first time, take the
2209          timestamp into account. */
2210       gst_structure_set (s, "ts", GST_TYPE_CLOCK_TIME,
2211           MPEGTIME_TO_GSTTIME (area.pts), NULL);
2212     }
2213 
2214     src->active_button = button;
2215 
2216     if (src->highlight_event)
2217       gst_event_unref (src->highlight_event);
2218     src->highlight_event = event;
2219 
2220     /* Calculate whether the available set of button motions is changed */
2221     btn_mask = 0;
2222     if (btn_info->left && btn_info->left != button)
2223       btn_mask |= RSN_BTN_LEFT;
2224     if (btn_info->right && btn_info->right != button)
2225       btn_mask |= RSN_BTN_RIGHT;
2226     if (btn_info->up && btn_info->up != button)
2227       btn_mask |= RSN_BTN_UP;
2228     if (btn_info->down && btn_info->down != button)
2229       btn_mask |= RSN_BTN_DOWN;
2230 
2231     if (btn_mask != src->cur_btn_mask) {
2232       src->cur_btn_mask = btn_mask;
2233       src->commands_changed = TRUE;
2234     }
2235   }
2236 }
2237 
2238 static void
rsn_dvdsrc_enqueue_nav_block(resinDvdSrc * src,GstBuffer * nav_buf,GstClockTime ts)2239 rsn_dvdsrc_enqueue_nav_block (resinDvdSrc * src, GstBuffer * nav_buf,
2240     GstClockTime ts)
2241 {
2242   RsnDvdPendingNav *pend_nav = g_new0 (RsnDvdPendingNav, 1);
2243   GstSegment *seg = &(GST_BASE_SRC (src)->segment);
2244 
2245   pend_nav->buffer = gst_buffer_ref (nav_buf);
2246   pend_nav->ts = ts;
2247   pend_nav->running_ts = gst_segment_to_running_time (seg, GST_FORMAT_TIME, ts);
2248 
2249   if (src->pending_nav_blocks == NULL) {
2250     src->pending_nav_blocks = src->pending_nav_blocks_end =
2251         g_slist_append (src->pending_nav_blocks_end, pend_nav);
2252   } else {
2253     src->pending_nav_blocks_end =
2254         g_slist_append (src->pending_nav_blocks_end, pend_nav);
2255     src->pending_nav_blocks_end = g_slist_next (src->pending_nav_blocks_end);
2256   }
2257 
2258   GST_LOG_OBJECT (src, "Enqueued nav with TS %" GST_TIME_FORMAT
2259       " with run ts %" GST_TIME_FORMAT ". %d packs pending",
2260       GST_TIME_ARGS (ts), GST_TIME_ARGS (pend_nav->running_ts),
2261       g_slist_length (src->pending_nav_blocks));
2262 }
2263 
2264 static void
rsn_dvdsrc_activate_nav_block(resinDvdSrc * src,GstBuffer * nav_buf)2265 rsn_dvdsrc_activate_nav_block (resinDvdSrc * src, GstBuffer * nav_buf)
2266 {
2267   int32_t forced_button;
2268 
2269   {
2270     GstMapInfo mmap;
2271     gst_buffer_map (nav_buf, &mmap, GST_MAP_READ);
2272 
2273     navRead_PCI (&src->cur_pci, mmap.data + 0x2d);
2274 
2275     gst_buffer_unmap (nav_buf, &mmap);
2276   }
2277 
2278   src->have_pci = TRUE;
2279 
2280   forced_button = src->cur_pci.hli.hl_gi.fosl_btnn & 0x3f;
2281   if (forced_button != 0) {
2282     GST_DEBUG_OBJECT (src, "Selecting button %d based on nav packet command",
2283         forced_button);
2284     dvdnav_button_select (src->dvdnav, &src->cur_pci, forced_button);
2285   }
2286   /* highlight might change, let's check */
2287   rsn_dvdsrc_update_highlight (src);
2288 
2289   if (src->highlight_event && src->in_still_state) {
2290     GST_LOG_OBJECT (src, "Signalling still condition due to highlight change");
2291     g_cond_broadcast (&src->still_cond);
2292   }
2293 }
2294 
2295 static void
rsn_dvdsrc_clear_nav_blocks(resinDvdSrc * src)2296 rsn_dvdsrc_clear_nav_blocks (resinDvdSrc * src)
2297 {
2298   GST_DEBUG_OBJECT (src, "Clearing %d pending navpacks",
2299       g_slist_length (src->pending_nav_blocks));
2300 
2301   while (src->pending_nav_blocks) {
2302     RsnDvdPendingNav *cur = (RsnDvdPendingNav *) src->pending_nav_blocks->data;
2303 
2304     gst_buffer_unref (cur->buffer);
2305     g_free (cur);
2306 
2307     src->pending_nav_blocks =
2308         g_slist_delete_link (src->pending_nav_blocks, src->pending_nav_blocks);
2309   }
2310 
2311   src->pending_nav_blocks_end = NULL;
2312 }
2313 
2314 static gboolean
rsn_dvdsrc_nav_clock_cb(GstClock * clock,GstClockTime time,GstClockID id,gpointer user_data)2315 rsn_dvdsrc_nav_clock_cb (GstClock * clock, GstClockTime time, GstClockID id,
2316     gpointer user_data)
2317 {
2318   resinDvdSrc *src = (resinDvdSrc *) user_data;
2319   GstClockTime base_time = gst_element_get_base_time (GST_ELEMENT (src));
2320 
2321   GST_LOG_OBJECT (src, "NAV pack callback for TS %" GST_TIME_FORMAT " at ts %"
2322       GST_TIME_FORMAT, GST_TIME_ARGS (time),
2323       GST_TIME_ARGS (gst_clock_get_time (clock) - base_time));
2324 
2325   g_mutex_lock (&src->dvd_lock);
2326 
2327   /* Destroy the clock id that caused this callback */
2328   if (src->nav_clock_id) {
2329     gst_clock_id_unref (src->nav_clock_id);
2330     src->nav_clock_id = NULL;
2331   }
2332 
2333   while (src->pending_nav_blocks) {
2334     RsnDvdPendingNav *cur = (RsnDvdPendingNav *) src->pending_nav_blocks->data;
2335 
2336     if (time < base_time + cur->running_ts)
2337       break;                    /* Next NAV is in the future */
2338 
2339     GST_DEBUG_OBJECT (src, "Activating nav pack with TS %" GST_TIME_FORMAT
2340         " at running TS %" GST_TIME_FORMAT, GST_TIME_ARGS (cur->ts),
2341         GST_TIME_ARGS (cur->running_ts));
2342     rsn_dvdsrc_activate_nav_block (src, cur->buffer);
2343 
2344     gst_buffer_unref (cur->buffer);
2345     g_free (cur);
2346 
2347     src->pending_nav_blocks =
2348         g_slist_delete_link (src->pending_nav_blocks, src->pending_nav_blocks);
2349   }
2350 
2351   if (src->pending_nav_blocks == NULL)
2352     src->pending_nav_blocks_end = NULL;
2353   else {
2354     /* Schedule a next packet, if any */
2355     RsnDvdPendingNav *next_nav =
2356         (RsnDvdPendingNav *) src->pending_nav_blocks->data;
2357     rsn_dvdsrc_schedule_nav_cb (src, next_nav);
2358   }
2359 
2360   g_mutex_unlock (&src->dvd_lock);
2361 
2362   return TRUE;
2363 }
2364 
2365 /* Called with dvd_lock held. NOTE: Releases dvd_lock briefly */
2366 static void
rsn_dvdsrc_schedule_nav_cb(resinDvdSrc * src,RsnDvdPendingNav * next_nav)2367 rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src, RsnDvdPendingNav * next_nav)
2368 {
2369   GstClock *clock;
2370   GstClockTime base_ts;
2371 
2372   if (!src->in_playing) {
2373     GST_LOG_OBJECT (src, "Not scheduling NAV block - state != PLAYING");
2374     return;                     /* Not in playing state yet */
2375   }
2376 
2377   GST_OBJECT_LOCK (src);
2378   clock = GST_ELEMENT_CLOCK (src);
2379   base_ts = GST_ELEMENT (src)->base_time;
2380 
2381   if (clock == NULL) {
2382     GST_LOG_OBJECT (src, "Not scheduling NAV block - no clock yet");
2383     GST_OBJECT_UNLOCK (src);
2384     return;
2385   }
2386   gst_object_ref (clock);
2387 
2388   src->nav_clock_id = gst_clock_new_single_shot_id (clock,
2389       base_ts + next_nav->running_ts);
2390 
2391   GST_OBJECT_UNLOCK (src);
2392 
2393   GST_LOG_OBJECT (src, "Schedule nav pack for running TS %" GST_TIME_FORMAT,
2394       GST_TIME_ARGS (next_nav->running_ts));
2395 
2396   g_mutex_unlock (&src->dvd_lock);
2397   gst_clock_id_wait_async (src->nav_clock_id, rsn_dvdsrc_nav_clock_cb, src,
2398       NULL);
2399   gst_object_unref (clock);
2400   g_mutex_lock (&src->dvd_lock);
2401 }
2402 
2403 /* Called with dvd_lock held */
2404 static void
rsn_dvdsrc_check_nav_blocks(resinDvdSrc * src)2405 rsn_dvdsrc_check_nav_blocks (resinDvdSrc * src)
2406 {
2407   RsnDvdPendingNav *next_nav;
2408 
2409   /* Make sure a callback is scheduled for the first nav packet */
2410   if (src->nav_clock_id != NULL) {
2411     return;                     /* Something already scheduled */
2412   }
2413   if (src->pending_nav_blocks == NULL) {
2414     return;                     /* No nav blocks available yet */
2415   }
2416   if (!src->in_playing)
2417     return;                     /* Not in playing state yet */
2418 
2419   GST_LOG_OBJECT (src, "Installing NAV callback");
2420   next_nav = (RsnDvdPendingNav *) src->pending_nav_blocks->data;
2421 
2422   rsn_dvdsrc_schedule_nav_cb (src, next_nav);
2423 }
2424 
2425 static gboolean
rsn_dvdsrc_src_event(GstBaseSrc * basesrc,GstEvent * event)2426 rsn_dvdsrc_src_event (GstBaseSrc * basesrc, GstEvent * event)
2427 {
2428   resinDvdSrc *src = RESINDVDSRC (basesrc);
2429   gboolean res;
2430 
2431   switch (GST_EVENT_TYPE (event)) {
2432     case GST_EVENT_NAVIGATION:
2433       res = rsn_dvdsrc_handle_navigation_event (src, event);
2434       break;
2435     case GST_EVENT_SEEK:{
2436       GstSeekFlags flags;
2437 
2438       GST_LOG_OBJECT (src, "handling seek event");
2439 
2440       gst_event_parse_seek (event, NULL, NULL, &flags, NULL, NULL, NULL, NULL);
2441       src->flushing_seek = ! !(flags & GST_SEEK_FLAG_FLUSH);
2442       GST_DEBUG_OBJECT (src, "%s seek event",
2443           src->flushing_seek ? "flushing" : "non-flushing");
2444 
2445       res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
2446       break;
2447     }
2448     default:
2449       GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
2450 
2451       res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
2452       break;
2453   }
2454 
2455   return res;
2456 }
2457 
2458 static void
rsn_dvdsrc_post_title_info(GstElement * element)2459 rsn_dvdsrc_post_title_info (GstElement * element)
2460 {
2461   resinDvdSrc *src = RESINDVDSRC (element);
2462   GstMessage *message;
2463   GstStructure *s;
2464   int32_t n, ntitles;
2465   int res;
2466   GValue array = { 0 };
2467 
2468   res = dvdnav_get_number_of_titles (src->dvdnav, &ntitles);
2469   if (res != DVDNAV_STATUS_OK) {
2470     GST_WARNING_OBJECT (src, "Failed to get number of titles: %d", res);
2471     return;
2472   }
2473 
2474   g_value_init (&array, GST_TYPE_ARRAY);
2475 
2476   s = gst_structure_new ("application/x-gst-dvd", "event",
2477       G_TYPE_STRING, "dvd-title-info", NULL);
2478 
2479   for (n = 0; n < ntitles; ++n) {
2480     uint64_t *times, duration;
2481     uint32_t nchapters;
2482     GValue item = { 0 };
2483 
2484     g_value_init (&item, G_TYPE_UINT64);
2485 
2486     nchapters =
2487         dvdnav_describe_title_chapters (src->dvdnav, n, &times, &duration);
2488     if (nchapters == 0) {
2489       GST_WARNING_OBJECT (src, "Failed to get title %d info", n);
2490       g_value_set_uint64 (&item, GST_CLOCK_TIME_NONE);
2491     } else {
2492       g_value_set_uint64 (&item, gst_util_uint64_scale (duration, GST_SECOND,
2493               90000));
2494       free (times);
2495     }
2496     gst_value_array_append_value (&array, &item);
2497     g_value_unset (&item);
2498   }
2499   gst_structure_set_value (s, "title-durations", &array);
2500   g_value_unset (&array);
2501 
2502   message = gst_message_new_element (GST_OBJECT (src), s);
2503   gst_element_post_message (GST_ELEMENT_CAST (src), message);
2504 }
2505 
2506 static GstStateChangeReturn
rsn_dvdsrc_change_state(GstElement * element,GstStateChange transition)2507 rsn_dvdsrc_change_state (GstElement * element, GstStateChange transition)
2508 {
2509   GstStateChangeReturn ret;
2510   resinDvdSrc *src = RESINDVDSRC (element);
2511 
2512   switch (transition) {
2513     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2514       GST_DEBUG_OBJECT (element, "Switching to PAUSED");
2515       /* Unschedule any NAV packet callback */
2516       g_mutex_lock (&src->dvd_lock);
2517       src->in_playing = FALSE;
2518       if (src->nav_clock_id) {
2519         gst_clock_id_unschedule (src->nav_clock_id);
2520         gst_clock_id_unref (src->nav_clock_id);
2521         src->nav_clock_id = NULL;
2522       }
2523       g_mutex_unlock (&src->dvd_lock);
2524       break;
2525     default:
2526       break;
2527   }
2528 
2529   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2530   if (ret == GST_STATE_CHANGE_FAILURE)
2531     return ret;
2532 
2533   switch (transition) {
2534     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2535       GST_DEBUG_OBJECT (element, "Switching to PLAYING");
2536       /* Kick off the NAV packet callback if needed */
2537       g_mutex_lock (&src->dvd_lock);
2538       src->in_playing = TRUE;
2539       rsn_dvdsrc_check_nav_blocks (src);
2540       g_mutex_unlock (&src->dvd_lock);
2541       break;
2542     case GST_STATE_CHANGE_READY_TO_PAUSED:
2543       rsn_dvdsrc_post_title_info (element);
2544       break;
2545     default:
2546       break;
2547   }
2548 
2549   return ret;
2550 }
2551 
2552 static gboolean
rsn_dvdsrc_src_query(GstBaseSrc * basesrc,GstQuery * query)2553 rsn_dvdsrc_src_query (GstBaseSrc * basesrc, GstQuery * query)
2554 {
2555   resinDvdSrc *src = RESINDVDSRC (basesrc);
2556   gboolean res = FALSE;
2557   GstFormat format;
2558   gint64 val;
2559 
2560   switch (GST_QUERY_TYPE (query)) {
2561     case GST_QUERY_DURATION:
2562       gst_query_parse_duration (query, &format, NULL);
2563       g_mutex_lock (&src->dvd_lock);
2564       if (!src->running) {
2565         g_mutex_unlock (&src->dvd_lock);
2566         break;
2567       }
2568 
2569       if (format == GST_FORMAT_TIME) {
2570         if (src->pgc_duration != GST_CLOCK_TIME_NONE) {
2571           val = src->pgc_duration;
2572 
2573           GST_DEBUG_OBJECT (src, "duration : %" GST_TIME_FORMAT,
2574               GST_TIME_ARGS (val));
2575           gst_query_set_duration (query, format, val);
2576           res = TRUE;
2577         }
2578       } else if (format == title_format) {
2579         gint32 titles;
2580 
2581         if (dvdnav_get_number_of_titles (src->dvdnav,
2582                 &titles) == DVDNAV_STATUS_OK) {
2583           val = titles;
2584           gst_query_set_duration (query, format, val);
2585           res = TRUE;
2586         }
2587       } else if (format == chapter_format) {
2588         gint32 title, chapters, x;
2589 
2590         if (dvdnav_current_title_info (src->dvdnav, &title,
2591                 &x) == DVDNAV_STATUS_OK) {
2592           if (dvdnav_get_number_of_parts (src->dvdnav, title,
2593                   &chapters) == DVDNAV_STATUS_OK) {
2594             val = chapters;
2595             gst_query_set_duration (query, format, val);
2596             res = TRUE;
2597           }
2598         }
2599       }
2600       g_mutex_unlock (&src->dvd_lock);
2601       break;
2602     case GST_QUERY_POSITION:
2603       gst_query_parse_position (query, &format, NULL);
2604 
2605       g_mutex_lock (&src->dvd_lock);
2606       if (!src->running) {
2607         g_mutex_unlock (&src->dvd_lock);
2608         break;
2609       }
2610       if (format == title_format) {
2611         gint32 title, chapter;
2612 
2613         if (dvdnav_current_title_info (src->dvdnav, &title,
2614                 &chapter) == DVDNAV_STATUS_OK) {
2615           val = title;
2616           gst_query_set_position (query, format, val);
2617           res = TRUE;
2618         }
2619       } else if (format == chapter_format) {
2620         gint32 title, chapter = -1;
2621 
2622         if (dvdnav_current_title_info (src->dvdnav, &title,
2623                 &chapter) == DVDNAV_STATUS_OK) {
2624           val = chapter;
2625           gst_query_set_position (query, format, val);
2626           res = TRUE;
2627         }
2628       }
2629       g_mutex_unlock (&src->dvd_lock);
2630       break;
2631     case GST_QUERY_CUSTOM:
2632     {
2633       GstNavigationQueryType nq_type = gst_navigation_query_get_type (query);
2634       if (nq_type != GST_NAVIGATION_QUERY_INVALID)
2635         res = rsn_dvdsrc_handle_navigation_query (src, nq_type, query);
2636       else
2637         res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
2638       break;
2639     }
2640     case GST_QUERY_SCHEDULING:
2641     {
2642       /* Make sure we operate in pull mode */
2643       gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL, 1, -1,
2644           0);
2645       gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH);
2646 
2647       res = TRUE;
2648       break;
2649     }
2650     default:
2651       res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
2652       break;
2653   }
2654 
2655   return res;
2656 }
2657 
2658 static gboolean
rsn_dvdsrc_is_seekable(GstBaseSrc * bsrc)2659 rsn_dvdsrc_is_seekable (GstBaseSrc * bsrc)
2660 {
2661   return TRUE;
2662 }
2663 
2664 static gboolean
rsn_dvdsrc_prepare_seek(GstBaseSrc * bsrc,GstEvent * event,GstSegment * segment)2665 rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc, GstEvent * event,
2666     GstSegment * segment)
2667 {
2668   GstSeekType cur_type, stop_type;
2669   gint64 cur, stop;
2670   GstSeekFlags flags;
2671   GstFormat seek_format;
2672   gdouble rate;
2673   gboolean update;
2674   gboolean ret;
2675 
2676   gst_event_parse_seek (event, &rate, &seek_format, &flags,
2677       &cur_type, &cur, &stop_type, &stop);
2678 
2679   /* Don't allow bytes seeks - angle, time, chapter, title only is the plan */
2680   if (seek_format == GST_FORMAT_BYTES)
2681     return FALSE;
2682 
2683   if (seek_format == rsndvd_format || seek_format == title_format ||
2684       seek_format == chapter_format) {
2685     /* Seeks in our internal formats are passed directly through to the do_seek
2686      * method. */
2687     gst_segment_init (segment, seek_format);
2688     gst_segment_do_seek (segment, rate, seek_format, flags, cur_type, cur,
2689         stop_type, stop, &update);
2690 
2691     return TRUE;
2692   }
2693 
2694   /* Let basesrc handle other formats */
2695   ret = GST_BASE_SRC_CLASS (parent_class)->prepare_seek_segment (bsrc,
2696       event, segment);
2697 
2698   return ret;
2699 }
2700 
2701 /* Find sector from time using time map if available */
2702 static gint
rsn_dvdsrc_get_sector_from_time_tmap(resinDvdSrc * src,GstClockTime ts)2703 rsn_dvdsrc_get_sector_from_time_tmap (resinDvdSrc * src, GstClockTime ts)
2704 {
2705   vts_tmapt_t *vts_tmapt;
2706   vts_tmap_t *title_tmap;
2707   gint32 title, part, vts_ttn;
2708   guint32 entry, sector, logical_sector;
2709   gint cell_n;
2710   pgc_t *pgc;
2711 
2712   if (ts == 0)
2713     return 0;
2714 
2715   if (src->vts_file == NULL)
2716     return -1;
2717 
2718   if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
2719       DVDNAV_STATUS_OK)
2720     return -1;
2721 
2722   vts_tmapt = src->vts_file->vts_tmapt;
2723   if (vts_tmapt == NULL)
2724     return -1;
2725 
2726   /* To find the right tmap, we need the title number within this VTS (vts_ttn)
2727    * from the VMG tt_srpt table... */
2728   if (title < 1 || title > src->vmg_file->tt_srpt->nr_of_srpts)
2729     return -1;
2730 
2731   /* We must be in the correct VTS for any of this to succeed... */
2732   if (src->vts_n != src->vmg_file->tt_srpt->title[title - 1].title_set_nr)
2733     return -1;
2734 
2735   /* We must also be in the VTS domain to use the tmap table */
2736   if (src->vts_n == 0 || src->in_menu)
2737     return -1;
2738 
2739   vts_ttn = src->vmg_file->tt_srpt->title[title - 1].vts_ttn;
2740 
2741   GST_DEBUG_OBJECT (src, "Seek to time %" GST_TIME_FORMAT
2742       " in VTS %d title %d (vts_ttn %d of %d)",
2743       GST_TIME_ARGS (ts), src->vts_n, title, vts_ttn, vts_tmapt->nr_of_tmaps);
2744 
2745   if (vts_ttn < 1 || vts_ttn > vts_tmapt->nr_of_tmaps)
2746     return -1;
2747 
2748   pgc = get_current_pgc (src);
2749   if (pgc == NULL)
2750     return -1;
2751 
2752   /* Get the time map */
2753   title_tmap = vts_tmapt->tmap + vts_ttn - 1;
2754   if (title_tmap->tmu == 0)
2755     return -1;
2756 
2757   entry = ts / (title_tmap->tmu * GST_SECOND);
2758   if (entry == 0)
2759     return 0;
2760 
2761   if (entry < 1 || entry > title_tmap->nr_of_entries)
2762     return -1;
2763 
2764   sector = title_tmap->map_ent[entry - 1] & 0x7fffffff;
2765 
2766   GST_LOG_OBJECT (src, "Got sector %u for time seek (entry %d of %d)",
2767       sector, entry, title_tmap->nr_of_entries);
2768 
2769   /* Sector is now an absolute sector within the current VTS, but
2770    * dvdnav_sector_search expects a logical sector within the current PGC...
2771    * which means iterating over the cells of the current PGC until we find
2772    * the cell that contains the time and sector we want, accumulating
2773    * the logical sector offsets until we find it
2774    */
2775   logical_sector = 0;
2776   for (cell_n = 0; cell_n < pgc->nr_of_cells; cell_n++) {
2777     cell_playback_t *cell = pgc->cell_playback + cell_n;
2778 
2779     /* This matches how libdvdnav calculates the logical sector
2780      * in dvdnav_sector_search(): */
2781 
2782     if (sector >= cell->first_sector && sector <= cell->last_sector) {
2783       logical_sector += sector - cell->first_sector;
2784       break;
2785     }
2786 
2787     if (cell->block_type == BLOCK_TYPE_ANGLE_BLOCK &&
2788         cell->block_mode != BLOCK_MODE_FIRST_CELL)
2789       continue;
2790 
2791     logical_sector += (cell->last_sector - cell->first_sector + 1);
2792   }
2793 
2794   GST_DEBUG_OBJECT (src, "Mapped sector %u onto PGC relative sector %u",
2795       sector, logical_sector);
2796 
2797   return logical_sector;
2798 }
2799 
2800 /* call with DVD lock held */
2801 static gboolean
rsn_dvdsrc_seek_to_time(resinDvdSrc * src,GstClockTime ts)2802 rsn_dvdsrc_seek_to_time (resinDvdSrc * src, GstClockTime ts)
2803 {
2804   gint sector;
2805   dvdnav_status_t res;
2806 
2807   GST_DEBUG_OBJECT (src, "Time seek requested to ts %" GST_TIME_FORMAT,
2808       GST_TIME_ARGS (ts));
2809 
2810   sector = rsn_dvdsrc_get_sector_from_time_tmap (src, ts);
2811   if (sector < 0)
2812     return FALSE;
2813 
2814   src->discont = TRUE;
2815   res = dvdnav_sector_search (src->dvdnav, sector, SEEK_SET);
2816 
2817   if (res != DVDNAV_STATUS_OK)
2818     return FALSE;
2819 
2820   return TRUE;
2821 }
2822 
2823 static gboolean
rsn_dvdsrc_do_seek(GstBaseSrc * bsrc,GstSegment * segment)2824 rsn_dvdsrc_do_seek (GstBaseSrc * bsrc, GstSegment * segment)
2825 {
2826   resinDvdSrc *src = RESINDVDSRC (bsrc);
2827   gboolean ret = FALSE;
2828 
2829   if (segment->format == rsndvd_format || src->first_seek) {
2830     /* The internal format has alread served its purpose of waking
2831      * everything up and flushing, we just need to step to the next
2832      * data block (below) so we know our new position */
2833     ret = TRUE;
2834     /* HACK to make initial seek work: */
2835     src->first_seek = FALSE;
2836   } else {
2837     /* Handle other formats: Time, title, chapter, angle */
2838     if (segment->format == GST_FORMAT_TIME) {
2839       g_mutex_lock (&src->dvd_lock);
2840       src->discont = TRUE;
2841       ret = rsn_dvdsrc_seek_to_time (src, segment->start);
2842       g_mutex_unlock (&src->dvd_lock);
2843     } else if (segment->format == title_format) {
2844       gint titles;
2845 
2846       g_mutex_lock (&src->dvd_lock);
2847       if (src->running &&
2848           dvdnav_get_number_of_titles (src->dvdnav,
2849               &titles) == DVDNAV_STATUS_OK) {
2850         if (segment->start > 0 && segment->start <= titles) {
2851           dvdnav_title_play (src->dvdnav, segment->start);
2852           ret = TRUE;
2853           src->discont = TRUE;
2854         }
2855       }
2856       g_mutex_unlock (&src->dvd_lock);
2857     } else if (segment->format == chapter_format) {
2858       g_mutex_lock (&src->dvd_lock);
2859       if (src->running) {
2860         gint32 title, chapters, x;
2861         if (dvdnav_current_title_info (src->dvdnav, &title, &x) ==
2862             DVDNAV_STATUS_OK) {
2863           if (segment->start + 1 == x) {
2864             /* if already on the first part, don't try to get before it */
2865             if (segment->start == 0) {
2866               dvdnav_part_play (src->dvdnav, title, 1);
2867             } else {
2868               dvdnav_prev_pg_search (src->dvdnav);
2869             }
2870             ret = TRUE;
2871             src->discont = TRUE;
2872           } else if (segment->start == x + 1) {
2873             dvdnav_next_pg_search (src->dvdnav);
2874             ret = TRUE;
2875             src->discont = TRUE;
2876           } else if (dvdnav_get_number_of_parts (src->dvdnav, title,
2877                   &chapters) == DVDNAV_STATUS_OK) {
2878             if (segment->start > 0 && segment->start <= chapters) {
2879               dvdnav_part_play (src->dvdnav, title, segment->start);
2880               ret = TRUE;
2881               src->discont = TRUE;
2882             }
2883           }
2884         }
2885       }
2886       g_mutex_unlock (&src->dvd_lock);
2887     }
2888   }
2889 
2890   if (ret) {
2891     /* Force a highlight update */
2892     src->active_button = -1;
2893 
2894     if (src->flushing_seek) {
2895       GstMessage *mouse_over_msg = NULL;
2896       g_mutex_lock (&src->dvd_lock);
2897       src->flushing_seek = FALSE;
2898 
2899       gst_buffer_replace (&src->next_buf, NULL);
2900       src->cur_start_ts = GST_CLOCK_TIME_NONE;
2901       src->cur_end_ts = GST_CLOCK_TIME_NONE;
2902       src->cur_vobu_base_ts = GST_CLOCK_TIME_NONE;
2903       src->have_pci = FALSE;
2904       if (src->nav_clock_id) {
2905         gst_clock_id_unschedule (src->nav_clock_id);
2906         gst_clock_id_unref (src->nav_clock_id);
2907         src->nav_clock_id = NULL;
2908       }
2909       rsn_dvdsrc_clear_nav_blocks (src);
2910       if (src->was_mouse_over) {
2911         mouse_over_msg =
2912             gst_navigation_message_new_mouse_over ((GstObject *) src, FALSE);
2913         src->was_mouse_over = FALSE;
2914       }
2915       g_mutex_unlock (&src->dvd_lock);
2916 
2917       if (mouse_over_msg)
2918         gst_element_post_message (GST_ELEMENT_CAST (src), mouse_over_msg);
2919     }
2920 
2921     GST_LOG_OBJECT (src, "Entering prepare_next_block after seek."
2922         " Flushing = %d", src->flushing_seek);
2923     while (src->cur_start_ts == GST_CLOCK_TIME_NONE) {
2924       if (rsn_dvdsrc_prepare_next_block (src, FALSE) != GST_FLOW_OK)
2925         goto fail;
2926       if (src->cur_start_ts == GST_CLOCK_TIME_NONE)
2927         gst_buffer_replace (&src->next_buf, NULL);
2928     }
2929     GST_LOG_OBJECT (src, "prepare_next_block after seek done");
2930 
2931     segment->format = GST_FORMAT_TIME;
2932     /* The first TS output: */
2933     segment->position = segment->start = src->cur_start_ts;
2934     GST_LOG_OBJECT (src, "Segment position now %" GST_TIME_FORMAT,
2935         GST_TIME_ARGS (segment->position));
2936 
2937     /* time field = position is the 'logical' stream time here: */
2938     segment->time = 0;
2939     if (src->cur_position != GST_CLOCK_TIME_NONE)
2940       segment->time += src->cur_position;
2941     if (src->cur_vobu_base_ts != GST_CLOCK_TIME_NONE)
2942       segment->time += src->cur_vobu_base_ts;
2943 
2944     segment->stop = -1;
2945     segment->duration = -1;
2946 
2947     GST_DEBUG_OBJECT (src, "seek completed. New start TS %" GST_TIME_FORMAT
2948         " pos %" GST_TIME_FORMAT " (offset %" GST_TIME_FORMAT ")",
2949         GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->time),
2950         GST_TIME_ARGS ((GstClockTimeDiff) (segment->start - segment->time)));
2951 
2952     src->need_segment = FALSE;
2953   }
2954 
2955   return ret;
2956 fail:
2957   GST_DEBUG_OBJECT (src, "Seek in format %d failed", segment->format);
2958   return FALSE;
2959 }
2960