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, ×, &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