1 /* GStreamer
2 * Copyright (C) 2002 David I. Lehn <dlehn@users.sourceforge.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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <assert.h>
32 #include <gst/gst-i18n-plugin.h>
33 #include <gst/gst.h>
35 #include <dvdnav/dvdnav.h>
36 #include <dvdnav/nav_print.h>
39 GST_DEBUG_CATEGORY_STATIC (dvdnavsrc_debug);
40 #define GST_CAT_DEFAULT (dvdnavsrc_debug)
42 /* Size of a DVD sector, used for sector-byte format conversions */
43 #define DVD_SECTOR_SIZE 2048
45 #define CLOCK_BASE 9LL
46 #define CLOCK_FREQ CLOCK_BASE * 10000
48 #define MPEGTIME_TO_GSTTIME(time) (((time) * (GST_MSECOND/10)) / CLOCK_BASE)
49 #define GSTTIME_TO_MPEGTIME(time) (((time) * CLOCK_BASE) / (GST_MSECOND/10))
51 /* Call a dvdnav function and, it it fails, report an error an execute
52 the code in the 'action' parameter. */
53 #define DVDNAV_RAWCALL(func, params, elem, action) \
54 if (func params != DVDNAV_STATUS_OK) { \
55 GST_ELEMENT_ERROR (elem, LIBRARY, FAILED, \
56 (_("Error invoking \"%s\": %s."), \
57 #func, dvdnav_err_to_string ((elem)->dvdnav)), \
58 GST_ERROR_SYSTEM); \
59 action \
60 }
62 /* Call a dvdnav function and, it it fails, report an error and return
63 from the current procedure. */
64 #define DVDNAV_CALL(func, params, elem) \
65 DVDNAV_RAWCALL (func, params, elem, return;)
67 /* Call a dvdnav function and, it it fails, report an error and return
68 from the current procedure with the value 'retval'. */
69 #define DVDNAV_CALLVAL(func, params, elem, retval) \
70 DVDNAV_RAWCALL (func, params, elem, return (retval);)
73 /* The maxinum number of audio and SPU streams in a DVD. */
74 #define DVDNAVSRC_MAX_AUDIO_STREAMS 8
75 #define DVDNAVSRC_MAX_SPU_STREAMS 32
77 #define GST_TYPE_DVDNAVSRC \
78 (dvdnavsrc_get_type())
79 #define DVDNAVSRC(obj) \
80 (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_DVDNAVSRC,DVDNavSrc))
81 #define DVDNAVSRC_CLASS(klass) \
82 (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_DVDNAVSRC,DVDNavSrcClass))
83 #define GST_IS_DVDNAVSRC(obj) \
84 (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_DVDNAVSRC))
85 #define GST_IS_DVDNAVSRC_CLASS(obj) \
86 (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_DVDNAVSRC))
88 typedef struct _DVDNavSrc DVDNavSrc;
89 typedef struct _DVDNavSrcClass DVDNavSrcClass;
91 /* The pause modes to handle still frames. */
92 typedef enum
93 {
94 DVDNAVSRC_PAUSE_OFF, /* No pause active. */
95 DVDNAVSRC_PAUSE_LIMITED, /* A time limited pause is active. */
96 DVDNAVSRC_PAUSE_UNLIMITED /* An time unlimited pause is active. */
97 }
98 DVDNavSrcPauseMode;
100 /* Interval of time to sleep during pauses. */
101 #define DVDNAVSRC_PAUSE_INTERVAL (GST_SECOND / 20)
103 /* The DVD domain types. */
104 typedef enum
105 {
106 DVDNAVSRC_DOMAIN_UNKNOWN, /* Unknown domain. */
107 DVDNAVSRC_DOMAIN_FP, /* First Play domain. */
108 DVDNAVSRC_DOMAIN_VMGM, /* Video Management Menu domain */
109 DVDNAVSRC_DOMAIN_VTSM, /* Video Title Menu domain. */
110 DVDNAVSRC_DOMAIN_VTS /* Video Title domain. */
111 }
112 DVDNavSrcDomainType;
114 struct _DVDNavSrc
115 {
116 GstElement element;
118 /* Pads */
119 GstPad *srcpad;
120 GstCaps *streaminfo;
122 /* Location */
123 gchar *location;
125 gboolean did_seek;
126 gboolean need_flush;
127 gboolean need_newmedia;
129 /* Timing */
130 GstClock *clock; /* The clock for this element. */
132 /* Pause handling */
133 DVDNavSrcPauseMode pause_mode; /* The current pause mode. */
134 GstClockTime pause_end; /* The clock time for the end of the
135 pause. */
137 /* Highligh handling */
138 int button; /* The currently highlighted button
139 number (0 if no highlight). */
140 dvdnav_highlight_area_t area; /* The area corresponding to the
141 currently highlighted button. */
143 /* State handling */
144 DVDNavSrcDomainType domain; /* The current DVD domain. */
146 int title, chapter, angle;
148 int audio_phys, audio_log; /* The current audio streams. */
149 int subp_phys, subp_log; /* The current subpicture streams. */
151 dvdnav_t *dvdnav; /* The libdvdnav handle. */
153 GstCaps *buttoninfo;
155 GstBuffer *cur_buf; /* Current output buffer. See
156 dvdnavsrc_get. */
157 };
159 struct _DVDNavSrcClass
160 {
161 GstElementClass parent_class;
163 void (*user_op) (DVDNavSrc * src, int op);
164 };
166 /* elementfactory information */
167 GstElementDetails dvdnavsrc_details = {
168 "DVD Source",
169 "Source/File/DVD",
170 "Access a DVD with navigation features using libdvdnav",
171 "David I. Lehn <dlehn@users.sourceforge.net>",
172 };
175 /* DVDNavSrc signals and args */
176 enum
177 {
178 USER_OP_SIGNAL,
179 LAST_SIGNAL
180 };
182 enum
183 {
184 ARG_0,
185 ARG_LOCATION,
186 ARG_DEVICE,
187 ARG_STREAMINFO,
188 ARG_BUTTONINFO,
189 ARG_TITLE_STRING,
190 ARG_TITLE,
191 ARG_CHAPTER,
192 ARG_ANGLE,
193 ARG_AUDIO_LANGS,
194 ARG_AUDIO_LANG,
195 ARG_SPU_LANGS,
196 ARG_SPU_LANG
197 };
199 typedef enum
200 {
201 DVDNAVSRC_OPEN = GST_ELEMENT_FLAG_LAST,
203 DVDNAVSRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST + 2
204 }
205 DVDNavSrcFlags;
208 GType dvdnavsrc_get_type (void);
209 static void dvdnavsrc_base_init (gpointer g_class);
210 static void dvdnavsrc_class_init (DVDNavSrcClass * klass);
211 static void dvdnavsrc_init (DVDNavSrc * dvdnavsrc);
212 static void dvdnavsrc_finalize (GObject * object);
214 static void dvdnavsrc_set_property (GObject * object,
215 guint prop_id, const GValue * value, GParamSpec * pspec);
216 static void dvdnavsrc_get_property (GObject * object,
217 guint prop_id, GValue * value, GParamSpec * pspec);
219 static void dvdnavsrc_set_clock (GstElement * element, GstClock * clock);
221 static GstEvent *dvdnavsrc_make_dvd_event
222 (DVDNavSrc * src, const gchar * event_name, const gchar * firstfield, ...);
223 static GstEvent *dvdnavsrc_make_dvd_nav_packet_event
224 (DVDNavSrc * src, const pci_t * pci);
225 static GstEvent *dvdnavsrc_make_clut_change_event
226 (DVDNavSrc * src, const guint * clut);
228 static void dvdnavsrc_loop (GstElement * element);
229 static gboolean dvdnavsrc_event (GstPad * pad, GstEvent * event);
230 static const GstEventMask *dvdnavsrc_get_event_mask (GstPad * pad);
231 static const GstFormat *dvdnavsrc_get_formats (GstPad * pad);
232 static gboolean dvdnavsrc_query (GstPad * pad,
233 GstQueryType type, GstFormat * format, gint64 * value);
234 static const GstQueryType *dvdnavsrc_get_query_types (GstPad * pad);
235 static gboolean dvdnavsrc_convert (GstPad * pad,
236 GstFormat src_format, gint64 src_value,
237 GstFormat * dest_format, gint64 * dest_value);
239 static gboolean dvdnavsrc_close (DVDNavSrc * src);
240 static gboolean dvdnavsrc_open (DVDNavSrc * src);
241 static gboolean dvdnavsrc_is_open (DVDNavSrc * src);
243 #ifndef GST_DISABLE_GST_DEBUG
244 static void dvdnavsrc_print_event (DVDNavSrc * src,
245 guint8 * data, int event, int len);
246 #else
247 #define dvdnavsrc_print_event(src, data, event, len) ((void) 0)
248 #endif /* GST_DISABLE_GST_DEBUG */
249 static void dvdnavsrc_update_streaminfo (DVDNavSrc * src);
250 static void dvdnavsrc_set_domain (DVDNavSrc * src);
251 static void dvdnavsrc_update_highlight (DVDNavSrc * src);
252 static void dvdnavsrc_user_op (DVDNavSrc * src, int op);
253 static GstStateChangeReturn dvdnavsrc_change_state (GstElement * element,
254 GstStateChange transition);
256 static void dvdnavsrc_uri_handler_init (gpointer g_iface, gpointer iface_data);
258 static GstElementClass *parent_class = NULL;
259 static guint dvdnavsrc_signals[LAST_SIGNAL] = { 0 };
261 static GstFormat sector_format;
262 static GstFormat title_format;
263 static GstFormat chapter_format;
264 static GstFormat angle_format;
266 GType
267 dvdnavsrc_get_type (void)
268 {
269 static GType dvdnavsrc_type = 0;
271 if (!dvdnavsrc_type) {
272 static const GTypeInfo dvdnavsrc_info = {
273 sizeof (DVDNavSrcClass),
274 dvdnavsrc_base_init,
275 NULL,
276 (GClassInitFunc) dvdnavsrc_class_init,
277 NULL,
278 NULL,
279 sizeof (DVDNavSrc),
280 0,
281 (GInstanceInitFunc) dvdnavsrc_init,
282 };
283 static const GInterfaceInfo urihandler_info = {
284 dvdnavsrc_uri_handler_init,
285 NULL,
286 NULL
287 };
289 dvdnavsrc_type = g_type_register_static (GST_TYPE_ELEMENT,
290 "DVDNavSrc", &dvdnavsrc_info, 0);
291 g_type_add_interface_static (dvdnavsrc_type,
292 GST_TYPE_URI_HANDLER, &urihandler_info);
294 sector_format = gst_format_register ("sector", "DVD sector");
295 title_format = gst_format_register ("title", "DVD title");
296 chapter_format = gst_format_register ("chapter", "DVD chapter");
297 angle_format = gst_format_register ("angle", "DVD angle");
299 GST_DEBUG_CATEGORY_INIT (dvdnavsrc_debug, "dvdnavsrc", 0,
300 "DVD navigation element");
301 }
302 return dvdnavsrc_type;
303 }
305 static void
306 dvdnavsrc_base_init (gpointer g_class)
307 {
308 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
310 gst_element_class_set_details (element_class, &dvdnavsrc_details);
311 }
313 static void
314 dvdnavsrc_class_init (DVDNavSrcClass * klass)
315 {
316 GObjectClass *gobject_class;
317 GstElementClass *gstelement_class;
319 gobject_class = (GObjectClass *) klass;
320 gstelement_class = (GstElementClass *) klass;
322 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
324 dvdnavsrc_signals[USER_OP_SIGNAL] =
325 g_signal_new ("user-op",
326 G_TYPE_FROM_CLASS (klass),
327 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
328 G_STRUCT_OFFSET (DVDNavSrcClass, user_op),
329 NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);
331 gobject_class->finalize = dvdnavsrc_finalize;
333 klass->user_op = dvdnavsrc_user_op;
335 g_object_class_install_property (gobject_class, ARG_LOCATION,
336 g_param_spec_string ("location", "Location",
337 "DVD device location (deprecated; use device)",
338 NULL, G_PARAM_READWRITE));
339 g_object_class_install_property (gobject_class, ARG_DEVICE,
340 g_param_spec_string ("device", "Device",
341 "DVD device location", NULL, G_PARAM_READWRITE));
342 g_object_class_install_property (gobject_class, ARG_TITLE_STRING,
343 g_param_spec_string ("title_string", "title string", "DVD title string",
344 NULL, G_PARAM_READABLE));
345 g_object_class_install_property (gobject_class, ARG_TITLE,
346 g_param_spec_int ("title", "title", "title",
347 0, 99, 1, G_PARAM_READWRITE));
348 g_object_class_install_property (gobject_class, ARG_CHAPTER,
349 g_param_spec_int ("chapter", "chapter", "chapter",
350 1, 99, 1, G_PARAM_READWRITE));
351 g_object_class_install_property (gobject_class, ARG_ANGLE,
352 g_param_spec_int ("angle", "angle", "angle", 1, 9, 1, G_PARAM_READWRITE));
353 g_object_class_install_property (gobject_class, ARG_STREAMINFO,
354 g_param_spec_boxed ("streaminfo", "streaminfo", "streaminfo",
355 GST_TYPE_CAPS, G_PARAM_READABLE));
356 g_object_class_install_property (gobject_class, ARG_BUTTONINFO,
357 g_param_spec_boxed ("buttoninfo", "buttoninfo", "buttoninfo",
358 GST_TYPE_CAPS, G_PARAM_READABLE));
359 g_object_class_install_property (gobject_class, ARG_AUDIO_LANGS,
360 g_param_spec_string ("audio_languages", "audio_languages",
361 "Available audio languages", NULL, G_PARAM_READABLE));
362 g_object_class_install_property (gobject_class, ARG_AUDIO_LANG,
363 g_param_spec_string ("audio_language", "audio_language",
364 "Current audio language", NULL, G_PARAM_READABLE));
365 g_object_class_install_property (gobject_class, ARG_SPU_LANGS,
366 g_param_spec_string ("spu_languages", "spu_languages",
367 "Available SPU languages", NULL, G_PARAM_READABLE));
368 g_object_class_install_property (gobject_class, ARG_SPU_LANG,
369 g_param_spec_string ("spu_language", "spu_language",
370 "Current SPU language", NULL, G_PARAM_READABLE));
372 gobject_class->set_property = GST_DEBUG_FUNCPTR (dvdnavsrc_set_property);
373 gobject_class->get_property = GST_DEBUG_FUNCPTR (dvdnavsrc_get_property);
375 gstelement_class->change_state = dvdnavsrc_change_state;
376 gstelement_class->set_clock = dvdnavsrc_set_clock;
377 }
379 static void
380 dvdnavsrc_init (DVDNavSrc * src)
381 {
382 src->srcpad = gst_pad_new ("src", GST_PAD_SRC);
384 gst_element_set_loop_function (GST_ELEMENT (src), dvdnavsrc_loop);
386 gst_pad_set_event_function (src->srcpad, dvdnavsrc_event);
387 gst_pad_set_event_mask_function (src->srcpad, dvdnavsrc_get_event_mask);
388 gst_pad_set_convert_function (src->srcpad, dvdnavsrc_convert);
389 gst_pad_set_query_function (src->srcpad, dvdnavsrc_query);
390 gst_pad_set_query_type_function (src->srcpad, dvdnavsrc_get_query_types);
391 gst_pad_set_formats_function (src->srcpad, dvdnavsrc_get_formats);
393 gst_element_add_pad (GST_ELEMENT (src), src->srcpad);
395 src->location = g_strdup ("/dev/dvd");
397 src->did_seek = FALSE;
398 src->need_flush = FALSE;
399 src->need_newmedia = TRUE;
401 /* Pause mode is initially inactive. */
402 src->pause_mode = DVDNAVSRC_PAUSE_OFF;
404 /* No highlighted button. */
405 src->button = 0;
407 /* Domain is unknown at the begining. */
408 src->domain = DVDNAVSRC_DOMAIN_UNKNOWN;
410 src->title = 0;
411 src->chapter = 1;
412 src->angle = 1;
413 src->streaminfo = NULL;
414 src->buttoninfo = NULL;
416 src->audio_phys = -1;
417 src->audio_log = -1;
418 src->subp_phys = -1;
419 src->subp_log = -1;
421 /* No current output buffer. */
422 src->cur_buf = NULL;
423 }
425 static void
426 dvdnavsrc_finalize (GObject * object)
427 {
428 DVDNavSrc *src = DVDNAVSRC (object);
430 /* If there's a current output buffer, get rid of it. */
431 if (src->cur_buf != NULL) {
432 gst_buffer_unref (src->cur_buf);
433 }
434 }
436 static gboolean
437 dvdnavsrc_is_open (DVDNavSrc * src)
438 {
439 g_return_val_if_fail (src != NULL, FALSE);
440 g_return_val_if_fail (GST_IS_DVDNAVSRC (src), FALSE);
442 return GST_FLAG_IS_SET (src, DVDNAVSRC_OPEN);
443 }
445 static void
446 dvdnavsrc_set_property (GObject * object, guint prop_id,
447 const GValue * value, GParamSpec * pspec)
448 {
449 DVDNavSrc *src;
451 g_return_if_fail (GST_IS_DVDNAVSRC (object));
453 src = DVDNAVSRC (object);
455 switch (prop_id) {
456 case ARG_LOCATION:
457 case ARG_DEVICE:
458 /* the element must be stopped in order to do this */
459 /*g_return_if_fail(!GST_FLAG_IS_SET(src,GST_STATE_RUNNING)); */
461 g_free (src->location);
462 /* clear the filename if we get a NULL (is that possible?) */
463 if (g_value_get_string (value) == NULL)
464 src->location = g_strdup ("/dev/dvd");
465 /* otherwise set the new filename */
466 else
467 src->location = g_strdup (g_value_get_string (value));
468 break;
469 case ARG_TITLE:
470 src->title = g_value_get_int (value);
471 src->did_seek = TRUE;
472 break;
473 case ARG_CHAPTER:
474 src->chapter = g_value_get_int (value);
475 src->did_seek = TRUE;
476 break;
477 case ARG_ANGLE:
478 src->angle = g_value_get_int (value);
479 break;
480 case ARG_AUDIO_LANG:
481 if (dvdnavsrc_is_open (src)) {
482 const gchar *code = g_value_get_string (value);
484 if (code != NULL) {
485 GST_INFO_OBJECT (src, "setting language %s", code);
486 if (dvdnav_audio_language_select (src->dvdnav, (char *) code) !=
487 DVDNAV_STATUS_OK) {
488 GST_ERROR_OBJECT (src, "setting language: %s",
489 dvdnav_err_to_string (src->dvdnav));
490 }
491 }
492 }
493 break;
494 case ARG_SPU_LANG:
495 if (dvdnavsrc_is_open (src)) {
496 const gchar *code = g_value_get_string (value);
498 if (code != NULL) {
499 dvdnav_spu_language_select (src->dvdnav, (char *) code);
500 }
501 }
502 break;
503 break;
504 default:
505 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
506 break;
507 }
508 }
510 static void
511 dvdnavsrc_get_property (GObject * object, guint prop_id,
512 GValue * value, GParamSpec * pspec)
513 {
514 DVDNavSrc *src;
515 const char *title_string;
517 g_return_if_fail (GST_IS_DVDNAVSRC (object));
519 src = DVDNAVSRC (object);
521 switch (prop_id) {
522 case ARG_LOCATION:
523 case ARG_DEVICE:
524 g_value_set_string (value, src->location);
525 break;
526 case ARG_STREAMINFO:
527 g_value_set_boxed (value, src->streaminfo);
528 break;
529 case ARG_BUTTONINFO:
530 g_value_set_boxed (value, src->buttoninfo);
531 break;
532 case ARG_TITLE_STRING:
533 if (!dvdnavsrc_is_open (src)) {
534 g_value_set_string (value, "");
535 } else if (dvdnav_get_title_string (src->dvdnav, &title_string) !=
536 DVDNAV_STATUS_OK) {
537 g_value_set_string (value, "UNKNOWN");
538 } else {
539 g_value_set_string (value, title_string);
540 }
541 break;
542 case ARG_TITLE:
543 g_value_set_int (value, src->title);
544 break;
545 case ARG_CHAPTER:
546 g_value_set_int (value, src->chapter);
547 break;
548 case ARG_ANGLE:
549 g_value_set_int (value, src->angle);
550 break;
551 case ARG_AUDIO_LANGS:
552 if (!dvdnavsrc_is_open (src)) {
553 g_value_set_string (value, "");
554 } else {
555 uint8_t physical, logical;
556 uint16_t lang_int;
557 gchar langs[DVDNAVSRC_MAX_AUDIO_STREAMS * 3];
558 gchar *lang_ptr = langs;
560 for (physical = 0; physical < DVDNAVSRC_MAX_AUDIO_STREAMS; physical++) {
561 logical = dvdnav_get_audio_logical_stream (src->dvdnav, physical);
562 lang_int = dvdnav_audio_stream_to_lang (src->dvdnav, logical);
563 if (lang_int != 0xffff) {
564 lang_ptr[0] = (lang_int >> 8) & 0xff;
565 lang_ptr[1] = lang_int & 0xff;
566 lang_ptr[2] = ' ';
567 lang_ptr += 3;
568 }
569 }
571 if (lang_ptr > langs) {
572 /* Overwrite the space at the end. */
573 lang_ptr[-1] = '\0';
574 } else {
575 langs[0] = '\0';
576 }
578 g_value_set_string (value, langs);
579 }
580 break;
581 case ARG_AUDIO_LANG:
582 if (!dvdnavsrc_is_open (src)) {
583 g_value_set_string (value, "");
584 } else {
585 uint8_t logical;
586 uint16_t lang_int;
587 gchar lang[3];
589 logical = dvdnav_get_active_audio_stream (src->dvdnav);
590 lang_int = dvdnav_audio_stream_to_lang (src->dvdnav, logical);
591 if (lang_int != 0xffff) {
592 lang[0] = (lang_int >> 8) & 0xff;
593 lang[1] = lang_int & 0xff;
594 lang[2] = '\0';
595 g_value_set_string (value, lang);
596 } else {
597 g_value_set_string (value, "");
598 }
599 }
600 break;
601 case ARG_SPU_LANGS:
602 if (!dvdnavsrc_is_open (src)) {
603 g_value_set_string (value, "");
604 } else {
605 uint8_t physical, logical;
606 uint16_t lang_int;
607 gchar langs[DVDNAVSRC_MAX_SPU_STREAMS * 3];
608 gchar *lang_ptr = langs;
610 for (physical = 0; physical < DVDNAVSRC_MAX_SPU_STREAMS; physical++) {
611 logical = dvdnav_get_spu_logical_stream (src->dvdnav, physical);
612 lang_int = dvdnav_spu_stream_to_lang (src->dvdnav, logical);
613 if (lang_int != 0xffff) {
614 lang_ptr[0] = (lang_int >> 8) & 0xff;
615 lang_ptr[1] = lang_int & 0xff;
616 lang_ptr[2] = ' ';
617 lang_ptr += 3;
618 }
619 }
621 if (lang_ptr > langs) {
622 /* Overwrite the space at the end. */
623 lang_ptr[-1] = '\0';
624 } else {
625 langs[0] = '\0';
626 }
628 g_value_set_string (value, langs);
629 }
630 break;
631 case ARG_SPU_LANG:
632 if (!dvdnavsrc_is_open (src)) {
633 g_value_set_string (value, "");
634 } else {
635 uint8_t logical;
636 uint16_t lang_int;
637 gchar lang[3];
639 logical = dvdnav_get_active_spu_stream (src->dvdnav);
640 lang_int = dvdnav_spu_stream_to_lang (src->dvdnav, logical);
641 if (lang_int != 0xffff) {
642 lang[0] = (lang_int >> 8) & 0xff;
643 lang[1] = lang_int & 0xff;
644 lang[2] = '\0';
645 g_value_set_string (value, lang);
646 } else {
647 g_value_set_string (value, "");
648 }
649 }
650 break;
651 default:
652 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
653 break;
654 }
655 }
657 static void
658 dvdnavsrc_set_clock (GstElement * element, GstClock * clock)
659 {
660 DVDNavSrc *src = DVDNAVSRC (element);
662 src->clock = clock;
663 }
665 static gboolean
666 dvdnavsrc_tca_seek (DVDNavSrc * src, int title, int chapter, int angle)
667 {
668 int titles, programs, curangle, angles;
670 g_return_val_if_fail (src != NULL, FALSE);
671 g_return_val_if_fail (src->dvdnav != NULL, FALSE);
672 g_return_val_if_fail (dvdnavsrc_is_open (src), FALSE);
674 /* Dont try to seek to track 0 - First Play program chain */
675 g_return_val_if_fail (src->title > 0, FALSE);
677 GST_INFO_OBJECT (src, "seeking to %d/%d/%d", title, chapter, angle);
678 /*
679 * Make sure our title number is valid.
680 */
681 if (dvdnav_get_number_of_titles (src->dvdnav, &titles) != DVDNAV_STATUS_OK) {
682 GST_ERROR_OBJECT (src, "dvdnav_get_number_of_titles: %s",
683 dvdnav_err_to_string (src->dvdnav));
684 return FALSE;
685 }
686 GST_INFO_OBJECT (src, "there are %d titles on this DVD", titles);
687 if (title < 1 || title > titles) {
688 GST_ERROR_OBJECT (src, "invalid title %d", title);
689 dvdnavsrc_close (src);
690 return FALSE;
691 }
693 /*
694 * Before we can get the number of chapters (programs) we need to call
695 * dvdnav_title_play so that dvdnav_get_number_of_programs knows which title
696 * to operate on (also needed to get the number of angles)
697 */
698 /* FIXME: This is probably not necessary anymore! */
699 if (dvdnav_title_play (src->dvdnav, title) != DVDNAV_STATUS_OK) {
700 GST_ERROR_OBJECT (src, "dvdnav_title_play: %s",
701 dvdnav_err_to_string (src->dvdnav));
702 return FALSE;
703 }
705 /*
706 * Make sure the chapter number is valid for this title.
707 */
708 if (dvdnav_get_number_of_titles (src->dvdnav, &programs)
709 != DVDNAV_STATUS_OK) {
710 GST_ERROR ("dvdnav_get_number_of_programs: %s",
711 dvdnav_err_to_string (src->dvdnav));
712 return FALSE;
713 }
714 GST_INFO_OBJECT (src, "there are %d chapters in this title", programs);
715 if (chapter < 0 || chapter > programs) {
716 GST_ERROR_OBJECT (src, "invalid chapter %d", chapter);
717 dvdnavsrc_close (src);
718 return FALSE;
719 }
721 /*
722 * Make sure the angle number is valid for this title.
723 */
724 if (dvdnav_get_angle_info (src->dvdnav, &curangle, &angles)
725 != DVDNAV_STATUS_OK) {
726 GST_ERROR_OBJECT (src, "dvdnav_get_angle_info: %s",
727 dvdnav_err_to_string (src->dvdnav));
728 return FALSE;
729 }
730 GST_INFO_OBJECT (src, "there are %d angles in this title", angles);
731 if (angle < 1 || angle > angles) {
732 GST_ERROR_OBJECT (src, "invalid angle %d", angle);
733 dvdnavsrc_close (src);
734 return FALSE;
735 }
737 /*
738 * We've got enough info, time to open the title set data.
739 */
740 if (src->chapter == 0) {
741 if (dvdnav_title_play (src->dvdnav, title) != DVDNAV_STATUS_OK) {
742 GST_ERROR_OBJECT (src, "dvdnav_title_play: %s",
743 dvdnav_err_to_string (src->dvdnav));
744 return FALSE;
745 }
746 } else {
747 if (dvdnav_part_play (src->dvdnav, title, chapter) != DVDNAV_STATUS_OK) {
748 GST_ERROR_OBJECT (src, "dvdnav_part_play: %s",
749 dvdnav_err_to_string (src->dvdnav));
750 return FALSE;
751 }
752 }
753 if (dvdnav_angle_change (src->dvdnav, angle) != DVDNAV_STATUS_OK) {
754 GST_ERROR_OBJECT (src, "dvdnav_angle_change: %s",
755 dvdnav_err_to_string (src->dvdnav));
756 return FALSE;
757 }
759 src->did_seek = TRUE;
761 return TRUE;
762 }
764 static void
765 dvdnavsrc_update_streaminfo (DVDNavSrc * src)
766 {
767 GstCaps *caps;
768 GstStructure *structure;
769 gint64 value;
771 caps = gst_caps_new_empty ();
772 structure = gst_structure_empty_new ("application/x-gst-streaminfo");
773 gst_caps_append_structure (caps, structure);
775 if (dvdnavsrc_query (src->srcpad, GST_QUERY_TOTAL, &title_format, &value)) {
776 gst_caps_set_simple (caps, "titles", G_TYPE_INT, value, NULL);
777 }
778 if (dvdnavsrc_query (src->srcpad, GST_QUERY_POSITION, &title_format, &value)) {
779 gst_caps_set_simple (caps, "title", G_TYPE_INT, value, NULL);
780 }
782 if (dvdnavsrc_query (src->srcpad, GST_QUERY_TOTAL, &chapter_format, &value)) {
783 gst_caps_set_simple (caps, "chapters", G_TYPE_INT, value, NULL);
784 }
785 if (dvdnavsrc_query (src->srcpad, GST_QUERY_POSITION,
786 &chapter_format, &value)) {
787 gst_caps_set_simple (caps, "chapter", G_TYPE_INT, value, NULL);
788 }
790 if (dvdnavsrc_query (src->srcpad, GST_QUERY_TOTAL, &angle_format, &value)) {
791 gst_caps_set_simple (caps, "angles", G_TYPE_INT, value, NULL);
792 }
793 if (dvdnavsrc_query (src->srcpad, GST_QUERY_POSITION, &angle_format, &value)) {
794 gst_caps_set_simple (caps, "angle", G_TYPE_INT, value, NULL);
795 }
797 if (src->streaminfo) {
798 gst_caps_free (src->streaminfo);
799 }
800 src->streaminfo = caps;
801 g_object_notify (G_OBJECT (src), "streaminfo");
802 }
804 /*
805 * Check for a new DVD domain area, and update the structure if
806 * necessary.
807 */
808 static void
809 dvdnavsrc_set_domain (DVDNavSrc * src)
810 {
811 DVDNavSrcDomainType domain;
813 if (dvdnav_is_domain_fp (src->dvdnav)) {
814 domain = DVDNAVSRC_DOMAIN_FP;
815 } else if (dvdnav_is_domain_vmgm (src->dvdnav)) {
816 domain = DVDNAVSRC_DOMAIN_VMGM;
817 } else if (dvdnav_is_domain_vtsm (src->dvdnav)) {
818 domain = DVDNAVSRC_DOMAIN_VTSM;
819 } else if (dvdnav_is_domain_vts (src->dvdnav)) {
820 domain = DVDNAVSRC_DOMAIN_VTS;
821 } else {
822 domain = DVDNAVSRC_DOMAIN_UNKNOWN;
823 }
825 /* FIXME: We may send a signal if we have a new domain. */
826 src->domain = domain;
827 }
829 /*
830 * Check for a new highlighted area, and send an spu highlight event if
831 * necessary.
832 */
833 static void
834 dvdnavsrc_update_highlight (DVDNavSrc * src)
835 {
836 int button = 0;
837 pci_t *pci;
838 dvdnav_highlight_area_t area;
839 GstEvent *event;
841 DVDNAV_CALL (dvdnav_get_current_highlight, (src->dvdnav, &button), src);
843 pci = dvdnav_get_current_nav_pci (src->dvdnav);
844 if ((button > pci->hli.hl_gi.btn_ns) || (button < 0)) {
845 /* button is out of the range of possible buttons. */
846 button = 0;
847 }
849 if (!pci->hli.hl_gi.hli_ss) {
850 /* Not in menu */
851 button = 0;
852 }
854 if (button == 0) {
855 if (src->button != 0) {
856 src->button = 0;
858 event = dvdnavsrc_make_dvd_event (src, "dvd-spu-reset-highlight", NULL);
859 gst_pad_push (src->srcpad, GST_DATA (event));
860 }
861 return;
862 }
864 DVDNAV_CALL (dvdnav_get_highlight_area, (pci, button, 0, &area), src);
866 /* Check if we have a new button number, or a new highlight region. */
867 if (button != src->button ||
868 memcmp (&area, &(src->area), sizeof (dvdnav_highlight_area_t)) != 0) {
869 memcpy (&(src->area), &area, sizeof (dvdnav_highlight_area_t));
871 event = dvdnavsrc_make_dvd_event (src,
872 "dvd-spu-highlight",
873 "button", G_TYPE_INT, (gint) button,
874 "palette", G_TYPE_INT, (gint) area.palette,
875 "sx", G_TYPE_INT, (gint) area.sx,
876 "sy", G_TYPE_INT, (gint) area.sy,
877 "ex", G_TYPE_INT, (gint) area.ex,
878 "ey", G_TYPE_INT, (gint) area.ey, NULL);
880 if (src->button == 0) {
881 /* When setting the button for the first time, take the
882 timestamp into account. */
883 GST_EVENT_TIMESTAMP (event) = MPEGTIME_TO_GSTTIME (area.pts);
884 }
886 src->button = button;
888 GST_DEBUG ("Sending dvd-spu-highlight for button %d", button);
889 gst_pad_push (src->srcpad, GST_DATA (event));
890 }
891 }
893 static void
894 dvdnavsrc_user_op (DVDNavSrc * src, int op)
895 {
896 pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
898 GST_INFO_OBJECT (src, "user operation %d", op);
900 /* Magic user_op ids */
901 switch (op) {
902 case 0: /* None */
903 break;
904 case 1: /* Upper */
905 if (dvdnav_upper_button_select (src->dvdnav, pci) != DVDNAV_STATUS_OK) {
906 goto naverr;
907 }
908 break;
909 case 2: /* Lower */
910 if (dvdnav_lower_button_select (src->dvdnav, pci) != DVDNAV_STATUS_OK) {
911 goto naverr;
912 }
913 break;
914 case 3: /* Left */
915 if (dvdnav_left_button_select (src->dvdnav, pci) != DVDNAV_STATUS_OK) {
916 goto naverr;
917 }
918 break;
919 case 4: /* Right */
920 if (dvdnav_right_button_select (src->dvdnav, pci) != DVDNAV_STATUS_OK) {
921 goto naverr;
922 }
923 break;
924 case 5: /* Activate */
925 if (dvdnav_button_activate (src->dvdnav, pci) != DVDNAV_STATUS_OK) {
926 goto naverr;
927 }
928 break;
929 case 6: /* GoUp */
930 if (dvdnav_go_up (src->dvdnav) != DVDNAV_STATUS_OK) {
931 goto naverr;
932 }
933 break;
934 case 7: /* TopPG */
935 if (dvdnav_top_pg_search (src->dvdnav) != DVDNAV_STATUS_OK) {
936 goto naverr;
937 }
938 break;
939 case 8: /* PrevPG */
940 if (dvdnav_prev_pg_search (src->dvdnav) != DVDNAV_STATUS_OK) {
941 goto naverr;
942 }
943 break;
944 case 9: /* NextPG */
945 if (dvdnav_next_pg_search (src->dvdnav) != DVDNAV_STATUS_OK) {
946 goto naverr;
947 }
948 break;
949 case 10: /* Menu - Title */
950 if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Title) != DVDNAV_STATUS_OK) {
951 goto naverr;
952 }
953 break;
954 case 11: /* Menu - Root */
955 if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Root) != DVDNAV_STATUS_OK) {
956 goto naverr;
957 }
958 break;
959 case 12: /* Menu - Subpicture */
960 if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Subpicture)
961 != DVDNAV_STATUS_OK) {
962 goto naverr;
963 }
964 break;
965 case 13: /* Menu - Audio */
966 if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Audio) != DVDNAV_STATUS_OK) {
967 goto naverr;
968 }
969 break;
970 case 14: /* Menu - Angle */
971 if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Angle) != DVDNAV_STATUS_OK) {
972 goto naverr;
973 }
974 break;
975 case 15: /* Menu - Part */
976 if (dvdnav_menu_call (src->dvdnav, DVD_MENU_Part) != DVDNAV_STATUS_OK) {
977 goto naverr;
978 }
979 break;
980 case 50: /* Select button */
981 {
982 int32_t button;
984 dvdnav_get_current_highlight (src->dvdnav, &button);
985 if (button == 0) {
986 for (button = 1; button <= 36; button++) {
987 if (dvdnav_button_select (src->dvdnav, pci, button) ==
988 DVDNAV_STATUS_OK) {
989 break;
990 }
991 }
992 dvdnav_get_current_highlight (src->dvdnav, &button);
993 }
994 GST_INFO_OBJECT (src, "Selected button: %d", button);
995 }
996 break;
997 }
998 return;
999 naverr:
1000 GST_WARNING_OBJECT (src, "user op %d failure: %s",
1001 op, dvdnav_err_to_string (src->dvdnav));
1002 }
1004 #ifndef GST_DISABLE_GST_DEBUG
1005 static gchar *
1006 dvdnav_get_event_name (int event)
1007 {
1008 switch (event) {
1009 case DVDNAV_BLOCK_OK:
1010 return "DVDNAV_BLOCK_OK";
1011 break;
1012 case DVDNAV_NOP:
1013 return "DVDNAV_NOP";
1014 break;
1015 case DVDNAV_STILL_FRAME:
1016 return "DVDNAV_STILL_FRAME";
1017 break;
1018 case DVDNAV_WAIT:
1019 return "DVDNAV_WAIT";
1020 break;
1021 case DVDNAV_SPU_STREAM_CHANGE:
1022 return "DVDNAV_SPU_STREAM_CHANGE";
1023 break;
1024 case DVDNAV_AUDIO_STREAM_CHANGE:
1025 return "DVDNAV_AUDIO_STREAM_CHANGE";
1026 break;
1027 case DVDNAV_VTS_CHANGE:
1028 return "DVDNAV_VTS_CHANGE";
1029 break;
1030 case DVDNAV_CELL_CHANGE:
1031 return "DVDNAV_CELL_CHANGE";
1032 break;
1033 case DVDNAV_NAV_PACKET:
1034 return "DVDNAV_NAV_PACKET";
1035 break;
1036 case DVDNAV_STOP:
1037 return "DVDNAV_STOP";
1038 break;
1039 case DVDNAV_HIGHLIGHT:
1040 return "DVDNAV_HIGHLIGHT";
1041 break;
1042 case DVDNAV_SPU_CLUT_CHANGE:
1043 return "DVDNAV_SPU_CLUT_CHANGE";
1044 break;
1045 case DVDNAV_HOP_CHANNEL:
1046 return "DVDNAV_HOP_CHANNEL";
1047 break;
1048 }
1049 return "UNKNOWN";
1050 }
1052 static gchar *
1053 dvdnav_get_read_domain_name (dvd_read_domain_t domain)
1054 {
1055 switch (domain) {
1056 case DVD_READ_INFO_FILE:
1057 return "DVD_READ_INFO_FILE";
1058 break;
1059 case DVD_READ_INFO_BACKUP_FILE:
1060 return "DVD_READ_INFO_BACKUP_FILE";
1061 break;
1062 case DVD_READ_MENU_VOBS:
1063 return "DVD_READ_MENU_VOBS";
1064 break;
1065 case DVD_READ_TITLE_VOBS:
1066 return "DVD_READ_TITLE_VOBS";
1067 break;
1068 }
1069 return "UNKNOWN";
1070 }
1072 static void
1073 dvdnavsrc_print_event (DVDNavSrc * src, guint8 * data, int event, int len)
1074 {
1075 g_return_if_fail (src != NULL);
1076 g_return_if_fail (GST_IS_DVDNAVSRC (src));
1078 GST_DEBUG_OBJECT (src, "dvdnavsrc (%p): event: %s", src,
1079 dvdnav_get_event_name (event));
1080 switch (event) {
1081 case DVDNAV_BLOCK_OK:
1082 break;
1083 case DVDNAV_NOP:
1084 break;
1085 case DVDNAV_STILL_FRAME:
1086 {
1087 dvdnav_still_event_t *event = (dvdnav_still_event_t *) data;
1089 GST_DEBUG_OBJECT (src, " still frame: %d seconds", event->length);
1090 }
1091 break;
1092 case DVDNAV_WAIT:
1093 {
1094 }
1095 break;
1096 case DVDNAV_SPU_STREAM_CHANGE:
1097 {
1098 dvdnav_spu_stream_change_event_t *event =
1099 (dvdnav_spu_stream_change_event_t *) data;
1100 GST_DEBUG_OBJECT (src, " physical_wide: %d", event->physical_wide);
1101 GST_DEBUG_OBJECT (src, " physical_letterbox: %d",
1102 event->physical_letterbox);
1103 GST_DEBUG_OBJECT (src, " physical_pan_scan: %d",
1104 event->physical_pan_scan);
1105 GST_DEBUG_OBJECT (src, " logical: %d", event->logical);
1106 }
1107 break;
1108 case DVDNAV_AUDIO_STREAM_CHANGE:
1109 {
1110 dvdnav_audio_stream_change_event_t *event =
1111 (dvdnav_audio_stream_change_event_t *) data;
1112 GST_DEBUG_OBJECT (src, " physical: %d", event->physical);
1113 GST_DEBUG_OBJECT (src, " logical: %d", event->logical);
1114 }
1115 break;
1116 case DVDNAV_VTS_CHANGE:
1117 {
1118 dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t *) data;
1120 GST_DEBUG_OBJECT (src, " old_vtsN: %d", event->old_vtsN);
1121 GST_DEBUG_OBJECT (src, " old_domain: %s",
1122 dvdnav_get_read_domain_name (event->old_domain));
1123 GST_DEBUG_OBJECT (src, " new_vtsN: %d", event->new_vtsN);
1124 GST_DEBUG_OBJECT (src, " new_domain: %s",
1125 dvdnav_get_read_domain_name (event->new_domain));
1126 }
1127 break;
1128 case DVDNAV_CELL_CHANGE:
1129 {
1130 /*dvdnav_cell_change_event_t *event =
1131 (dvdnav_cell_change_event_t *)data; */
1132 /* FIXME: Print something relevant here. */
1133 }
1134 break;
1135 case DVDNAV_NAV_PACKET:
1136 {
1137 /* FIXME: Print something relevant here. */
1138 }
1139 break;
1140 case DVDNAV_STOP:
1141 break;
1142 case DVDNAV_HIGHLIGHT:
1143 {
1144 dvdnav_highlight_event_t *event = (dvdnav_highlight_event_t *) data;
1146 GST_DEBUG_OBJECT (src, " display: %s",
1147 event->display == 0 ?
1148 "hide" : (event->display == 1 ? "show" : "unknown"));
1149 if (event->display == 1) {
1150 GST_DEBUG_OBJECT (src, " palette: %08x", event->palette);
1151 GST_DEBUG_OBJECT (src, " coords (%u, %u) - (%u, %u)",
1152 event->sx, event->sy, event->ex, event->ey);
1153 GST_DEBUG_OBJECT (src, " pts: %u", event->pts);
1154 GST_DEBUG_OBJECT (src, " button: %u", event->buttonN);
1155 }
1156 }
1157 break;
1158 case DVDNAV_SPU_CLUT_CHANGE:
1159 break;
1160 case DVDNAV_HOP_CHANNEL:
1161 break;
1162 default:
1163 GST_DEBUG_OBJECT (src, " event id: %d", event);
1164 break;
1165 }
1166 }
1167 #endif /* GST_DISABLE_GST_DEBUG */
1169 static GstEvent *
1170 dvdnavsrc_make_dvd_event (DVDNavSrc * src, const gchar * event_name,
1171 const gchar * firstfield, ...)
1172 {
1173 GstEvent *event;
1174 GstStructure *structure;
1175 va_list varargs;
1177 g_return_val_if_fail (event_name != NULL, NULL);
1179 /* Create a structure with the given fields. */
1180 va_start (varargs, firstfield);
1181 structure = gst_structure_new ("application/x-gst-dvd",
1182 "event", G_TYPE_STRING, event_name, NULL);
1183 gst_structure_set_valist (structure, firstfield, varargs);
1184 va_end (varargs);
1186 /* Create the DVD event and put the structure into it. */
1187 event = gst_event_new (GST_EVENT_ANY);
1188 event->event_data.structure.structure = structure;
1190 #ifndef GST_DISABLE_GST_DEBUG
1191 {
1192 gchar *text = gst_structure_to_string (structure);
1194 GST_LOG_OBJECT (src, "creating event \"%s\"", text);
1195 g_free (text);
1196 }
1197 #endif
1199 return event;
1200 }
1202 static GstEvent *
1203 dvdnavsrc_make_dvd_nav_packet_event (DVDNavSrc * src, const pci_t * pci)
1204 {
1205 GstEvent *event;
1206 GstStructure *structure;
1207 GValue start_ptm = { 0 }, end_ptm = {
1208 0};
1210 /* Store the time values in GValues. */
1211 g_value_init (&start_ptm, G_TYPE_UINT64);
1212 g_value_set_uint64 (&start_ptm, MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm));
1213 g_value_init (&end_ptm, G_TYPE_UINT64);
1214 g_value_set_uint64 (&end_ptm, MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm));
1216 /* Add the values to a new structure. */
1217 structure = gst_structure_new ("application/x-gst-dvd",
1218 "event", G_TYPE_STRING, "dvd-nav-packet", NULL);
1219 gst_structure_set_value (structure, "start_ptm", &start_ptm);
1220 gst_structure_set_value (structure, "end_ptm", &end_ptm);
1222 /* Create the DVD event and put the structure into it. */
1223 event = gst_event_new (GST_EVENT_ANY);
1224 event->event_data.structure.structure = structure;
1226 #ifndef GST_DISABLE_GST_DEBUG
1227 {
1228 gchar *text = gst_structure_to_string (structure);
1230 GST_LOG_OBJECT (src, "creating event \"%s\"", text);
1231 g_free (text);
1232 }
1233 #endif
1235 return event;
1236 }
1238 static GstEvent *
1239 dvdnavsrc_make_clut_change_event (DVDNavSrc * src, const guint * clut)
1240 {
1241 GstEvent *event;
1242 GstStructure *structure;
1243 guchar name[16];
1244 int i;
1246 structure = gst_structure_new ("application/x-gst-dvd",
1247 "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
1249 /* Create a separate field for each value in the table. */
1250 for (i = 0; i < 16; i++) {
1251 sprintf (name, "clut%02d", i);
1252 gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
1253 }
1255 /* Create the DVD event and put the structure into it. */
1256 event = gst_event_new (GST_EVENT_ANY);
1257 event->event_data.structure.structure = structure;
1259 #ifndef GST_DISABLE_GST_DEBUG
1260 {
1261 gchar *text = gst_structure_to_string (structure);
1263 GST_LOG_OBJECT (src, "creating event \"%s\"", text);
1264 g_free (text);
1265 }
1266 #endif
1268 return event;
1269 }
1271 static void
1272 dvdnavsrc_loop (GstElement * element)
1273 {
1274 DVDNavSrc *src = DVDNAVSRC (element);
1275 int event, len;
1276 guint8 *data;
1277 GstData *send_data;
1279 g_return_if_fail (dvdnavsrc_is_open (src));
1281 if (src->did_seek || src->need_newmedia) {
1282 GstEvent *event;
1284 src->did_seek = FALSE;
1285 GST_INFO_OBJECT (src, "sending discont");
1287 event = gst_event_new_discontinuous (src->need_newmedia, 0);
1289 src->need_flush = FALSE;
1290 src->need_newmedia = FALSE;
1291 gst_pad_push (src->srcpad, GST_DATA (event));
1292 return;
1293 }
1295 if (src->need_flush) {
1296 src->need_flush = FALSE;
1297 GST_INFO_OBJECT (src, "sending flush");
1298 gst_pad_push (src->srcpad, GST_DATA (gst_event_new_flush ()));
1299 return;
1300 }
1302 /* Loop processing blocks until there is data to send. */
1303 send_data = NULL;
1304 while (send_data == NULL) {
1305 if (src->cur_buf == NULL) {
1306 src->cur_buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN);
1307 if (src->cur_buf == NULL) {
1308 GST_ELEMENT_ERROR (src, CORE, TOO_LAZY, (NULL),
1309 ("Failed to create a new GstBuffer"));
1310 return;
1311 }
1312 }
1313 data = GST_BUFFER_DATA (src->cur_buf);
1315 DVDNAV_CALL (dvdnav_get_next_block, (src->dvdnav, data, &event, &len), src);
1317 switch (event) {
1318 case DVDNAV_NOP:
1319 break;
1321 case DVDNAV_BLOCK_OK:
1322 send_data = GST_DATA (src->cur_buf);
1323 src->cur_buf = NULL;
1324 break;
1326 case DVDNAV_STILL_FRAME:
1327 {
1328 dvdnav_still_event_t *info = (dvdnav_still_event_t *) data;
1329 GstClockTime current_time = gst_element_get_time (GST_ELEMENT (src));
1331 if (src->pause_mode == DVDNAVSRC_PAUSE_OFF) {
1332 dvdnavsrc_print_event (src, data, event, len);
1334 /* We just saw a still frame. Start a pause now. */
1335 if (info->length == 0xff) {
1336 GST_INFO_OBJECT (src, "starting unlimited pause");
1337 src->pause_mode = DVDNAVSRC_PAUSE_UNLIMITED;
1338 } else {
1339 src->pause_mode = DVDNAVSRC_PAUSE_LIMITED;
1340 src->pause_end = current_time + info->length * GST_SECOND;
1341 GST_INFO_OBJECT (src,
1342 "starting limited pause: %d seconds at %llu until %llu",
1343 info->length, current_time, src->pause_end);
1344 }
1346 /* For the moment, send the first empty event to let
1347 everyone know that we are displaying a still frame.
1348 Subsequent calls to this function will take care of
1349 the rest of the pause. */
1350 GST_DEBUG_OBJECT (src, "sending still frame event");
1351 send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
1352 "dvd-spu-still-frame", NULL));
1353 break;
1354 }
1356 if (src->pause_mode == DVDNAVSRC_PAUSE_UNLIMITED ||
1357 current_time < src->pause_end) {
1358 GstEvent *event;
1360 /* We are in pause mode. Make this element sleep for a
1361 fraction of a second. */
1362 if (current_time + DVDNAVSRC_PAUSE_INTERVAL > src->pause_end) {
1363 gst_element_wait (GST_ELEMENT (src), src->pause_end);
1364 } else {
1365 gst_element_wait (GST_ELEMENT (src),
1366 current_time + DVDNAVSRC_PAUSE_INTERVAL);
1367 }
1368 /* Send an empty event to keep the pipeline going. */
1369 /* FIXME: Use an interrupt/filler event here. */
1370 event = gst_event_new (GST_EVENT_EMPTY);
1371 send_data = GST_DATA (event);
1372 GST_EVENT_TIMESTAMP (event) =
1373 gst_element_get_time (GST_ELEMENT (src));
1375 break;
1376 } else {
1377 /* We reached the end of the pause. */
1378 src->pause_mode = DVDNAVSRC_PAUSE_OFF;
1379 DVDNAV_CALL (dvdnav_still_skip, (src->dvdnav), src);
1380 }
1381 }
1382 break;
1384 case DVDNAV_WAIT:
1385 /* FIXME: We should really wait here until the fifos are
1386 empty, but I have no idea how to do that. In the mean time,
1387 just clean the wait state. */
1388 GST_INFO_OBJECT (src, "sending wait");
1389 DVDNAV_CALL (dvdnav_wait_skip, (src->dvdnav), src);
1390 break;
1392 case DVDNAV_STOP:
1393 GST_INFO_OBJECT (src, "sending eos");
1394 gst_element_set_eos (GST_ELEMENT (src));
1395 dvdnavsrc_close (src);
1397 send_data = GST_DATA (gst_event_new (GST_EVENT_EOS));
1398 break;
1400 case DVDNAV_CELL_CHANGE:
1401 dvdnavsrc_update_streaminfo (src);
1402 break;
1404 case DVDNAV_NAV_PACKET:
1405 {
1406 pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
1408 /* Check for forced buttons. */
1409 if (pci->hli.hl_gi.hli_ss == 1) {
1410 GST_LOG_OBJECT (src, "menu ahead");
1411 if (pci->hli.hl_gi.fosl_btnn > 0) {
1412 GST_DEBUG_OBJECT (src, "forced button");
1413 dvdnav_button_select (src->dvdnav, pci, pci->hli.hl_gi.fosl_btnn);
1414 }
1415 }
1417 dvdnavsrc_update_highlight (src);
1419 /* Send a dvd nav packet event. */
1420 send_data = GST_DATA (dvdnavsrc_make_dvd_nav_packet_event (src, pci));
1421 }
1422 break;
1424 case DVDNAV_SPU_CLUT_CHANGE:
1425 send_data = GST_DATA (dvdnavsrc_make_clut_change_event (src,
1426 (guint *) data));
1427 break;
1429 case DVDNAV_VTS_CHANGE:
1430 {
1431 dvdnavsrc_set_domain (src);
1433 send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
1434 "dvd-vts-change", "domain",
1435 G_TYPE_INT, (gint) src->domain, NULL));
1436 }
1437 break;
1439 case DVDNAV_AUDIO_STREAM_CHANGE:
1440 {
1441 dvdnav_audio_stream_change_event_t *info =
1442 (dvdnav_audio_stream_change_event_t *) data;
1443 int phys = info->physical;
1445 dvdnavsrc_print_event (src, data, event, len);
1447 if (phys < 0 || phys > DVDNAVSRC_MAX_AUDIO_STREAMS) {
1448 phys = -1;
1449 }
1451 if (phys == src->audio_phys &&
1452 dvdnav_get_active_audio_stream (src->dvdnav) == src->audio_log) {
1453 /* Audio state hasn't changed. */
1454 break;
1455 }
1457 src->audio_phys = phys;
1458 src->audio_log = dvdnav_get_active_audio_stream (src->dvdnav);
1459 send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
1460 "dvd-audio-stream-change",
1461 "physical", G_TYPE_INT, (gint) src->audio_phys,
1462 "logical", G_TYPE_INT, (gint) src->audio_log, NULL));
1463 }
1464 break;
1466 case DVDNAV_SPU_STREAM_CHANGE:
1467 {
1468 dvdnav_spu_stream_change_event_t *info =
1469 (dvdnav_spu_stream_change_event_t *) data;
1470 /* FIXME: Which type of physical stream to use here should
1471 be configurable through a property. We take widescreen
1472 for the moment. */
1473 int phys = info->physical_wide;
1475 dvdnavsrc_print_event (src, data, event, len);
1477 if (phys < 0 || phys > DVDNAVSRC_MAX_SPU_STREAMS) {
1478 phys = -1;
1479 }
1481 if (phys == src->subp_phys &&
1482 dvdnav_get_active_spu_stream (src->dvdnav) == src->subp_log) {
1483 /* Subpicture state hasn't changed. */
1484 break;
1485 }
1487 src->subp_phys = phys;
1488 src->subp_log = dvdnav_get_active_spu_stream (src->dvdnav);
1489 send_data = GST_DATA (dvdnavsrc_make_dvd_event (src,
1490 "dvd-spu-stream-change",
1491 "physical", G_TYPE_INT, (gint) phys,
1492 "logical", G_TYPE_INT,
1493 (gint) dvdnav_get_active_spu_stream (src->dvdnav), NULL));
1494 }
1495 break;
1497 case DVDNAV_HIGHLIGHT:
1498 dvdnavsrc_print_event (src, data, event, len);
1500 dvdnavsrc_update_highlight (src);
1501 break;
1503 case DVDNAV_HOP_CHANNEL:
1504 dvdnavsrc_print_event (src, data, event, len);
1506 src->button = 0;
1507 src->pause_mode = DVDNAVSRC_PAUSE_OFF;
1508 send_data = GST_DATA (gst_event_new_flush ());
1509 break;
1511 default:
1512 g_error ("dvdnavsrc: Unknown dvdnav event %d", event);
1513 break;
1514 }
1515 }
1517 gst_pad_push (src->srcpad, send_data);
1518 }
1520 /* open the file, necessary to go to RUNNING state */
1521 static gboolean
1522 dvdnavsrc_open (DVDNavSrc * src)
1523 {
1524 g_return_val_if_fail (src != NULL, FALSE);
1525 g_return_val_if_fail (GST_IS_DVDNAVSRC (src), FALSE);
1526 g_return_val_if_fail (!dvdnavsrc_is_open (src), FALSE);
1527 g_return_val_if_fail (src->location != NULL, FALSE);
1529 if (dvdnav_open (&src->dvdnav, (char *) src->location) != DVDNAV_STATUS_OK) {
1530 GST_ELEMENT_ERROR (src, LIBRARY, FAILED,
1531 (_("Failed to open DVD device '%s'."), src->location),
1532 GST_ERROR_SYSTEM);
1533 return FALSE;
1534 }
1536 GST_FLAG_SET (src, DVDNAVSRC_OPEN);
1538 /* Read the first block before seeking to force a libdvdnav internal
1539 * call to vm_start, otherwise it ignores our seek position.
1540 * This happens because vm_start sets the domain to the first-play (FP)
1541 * domain, overriding any other title that has been set.
1542 * Track/chapter setting used to work, but libdvdnav has delayed the call
1543 * to vm_start from _open, to _get_block.
1544 * FIXME: But, doing it this way has problems too, as there is no way to
1545 * get back to the FP domain.
1546 * Maybe we could title==0 to mean FP domain, and not do this read & seek.
1547 * If title subsequently gets set to 0, we would need to dvdnav_close
1548 * followed by dvdnav_open to get back to the FP domain.
1549 * Since we dont currently support seeking by setting the title/chapter/angle
1550 * after opening, we'll forget about close/open for now, and just do the
1551 * title==0 thing.
1552 */
1554 if (src->title > 0) {
1555 unsigned char buf[2048];
1556 int event, buflen = sizeof (buf);
1558 DVDNAV_CALLVAL (dvdnav_get_next_block,
1559 (src->dvdnav, buf, &event, &buflen), src, FALSE);
1560 dvdnavsrc_print_event (src, buf, event, buflen);
1562 if (!dvdnavsrc_tca_seek (src, src->title, src->chapter, src->angle)) {
1563 return FALSE;
1564 }
1565 }
1567 return TRUE;
1568 }
1570 /* close the file */
1571 static gboolean
1572 dvdnavsrc_close (DVDNavSrc * src)
1573 {
1574 g_return_val_if_fail (src != NULL, FALSE);
1575 g_return_val_if_fail (GST_IS_DVDNAVSRC (src), FALSE);
1576 g_return_val_if_fail (dvdnavsrc_is_open (src), FALSE);
1577 g_return_val_if_fail (src->dvdnav != NULL, FALSE);
1579 DVDNAV_CALLVAL (dvdnav_close, (src->dvdnav), src, FALSE);
1581 GST_FLAG_UNSET (src, DVDNAVSRC_OPEN);
1583 return TRUE;
1584 }
1586 static GstStateChangeReturn
1587 dvdnavsrc_change_state (GstElement * element, GstStateChange transition)
1588 {
1589 DVDNavSrc *src;
1591 g_return_val_if_fail (GST_IS_DVDNAVSRC (element), GST_STATE_CHANGE_FAILURE);
1593 src = DVDNAVSRC (element);
1595 switch (transition) {
1596 case GST_STATE_CHANGE_NULL_TO_READY:
1597 break;
1598 case GST_STATE_CHANGE_READY_TO_PAUSED:
1599 if (!dvdnavsrc_is_open (src)) {
1600 if (!dvdnavsrc_open (src)) {
1601 return GST_STATE_CHANGE_FAILURE;
1602 }
1603 }
1604 src->streaminfo = NULL;
1605 src->need_newmedia = TRUE;
1606 break;
1607 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1608 break;
1609 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1610 break;
1611 case GST_STATE_CHANGE_PAUSED_TO_READY:
1612 if (dvdnavsrc_is_open (src)) {
1613 if (!dvdnavsrc_close (src)) {
1614 return GST_STATE_CHANGE_FAILURE;
1615 }
1616 }
1617 break;
1618 case GST_STATE_CHANGE_READY_TO_NULL:
1619 break;
1620 }
1622 /* if we haven't failed already, give the parent class a chance to ;-) */
1623 if (GST_ELEMENT_CLASS (parent_class)->change_state)
1624 return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1626 return GST_STATE_CHANGE_SUCCESS;
1627 }
1629 static const GstEventMask *
1630 dvdnavsrc_get_event_mask (GstPad * pad)
1631 {
1632 static const GstEventMask masks[] = {
1633 {GST_EVENT_SEEK, GST_SEEK_METHOD_SET |
1634 GST_SEEK_METHOD_CUR | GST_SEEK_METHOD_END | GST_SEEK_FLAG_FLUSH},
1635 {GST_EVENT_FLUSH, 0},
1636 {GST_EVENT_NAVIGATION, GST_EVENT_FLAG_NONE},
1637 {0,}
1638 };
1640 return masks;
1641 }
1643 static gboolean
1644 dvdnav_handle_navigation_event (DVDNavSrc * src, GstEvent * event)
1645 {
1646 GstStructure *structure = event->event_data.structure.structure;
1647 const char *event_type = gst_structure_get_string (structure, "event");
1649 g_return_val_if_fail (event != NULL, FALSE);
1651 if (strcmp (event_type, "key-press") == 0) {
1652 const char *key = gst_structure_get_string (structure, "key");
1654 g_assert (key != NULL);
1655 GST_DEBUG ("dvdnavsrc got a keypress: %s", key);
1656 } else if (strcmp (event_type, "mouse-move") == 0) {
1657 double x, y;
1659 gst_structure_get_double (structure, "pointer_x", &x);
1660 gst_structure_get_double (structure, "pointer_y", &y);
1662 dvdnav_mouse_select (src->dvdnav,
1663 dvdnav_get_current_nav_pci (src->dvdnav), (int) x, (int) y);
1665 dvdnavsrc_update_highlight (src);
1666 } else if (strcmp (event_type, "mouse-button-release") == 0) {
1667 double x, y;
1669 gst_structure_get_double (structure, "pointer_x", &x);
1670 gst_structure_get_double (structure, "pointer_y", &y);
1672 dvdnav_mouse_activate (src->dvdnav,
1673 dvdnav_get_current_nav_pci (src->dvdnav), (int) x, (int) y);
1674 }
1676 return TRUE;
1677 }
1679 static gboolean
1680 dvdnavsrc_event (GstPad * pad, GstEvent * event)
1681 {
1682 DVDNavSrc *src;
1683 gboolean res = TRUE;
1685 src = DVDNAVSRC (gst_pad_get_parent (pad));
1687 if (!GST_FLAG_IS_SET (src, DVDNAVSRC_OPEN))
1688 goto error;
1690 switch (GST_EVENT_TYPE (event)) {
1691 case GST_EVENT_SEEK:
1692 {
1693 gint64 offset;
1694 gint format;
1695 int titles, title, new_title;
1696 int parts, part, new_part;
1697 int angles, angle, new_angle;
1698 int origin;
1700 format = GST_EVENT_SEEK_FORMAT (event);
1701 offset = GST_EVENT_SEEK_OFFSET (event);
1703 switch (format) {
1704 case GST_FORMAT_BYTES:
1705 switch (GST_EVENT_SEEK_METHOD (event)) {
1706 case GST_SEEK_METHOD_SET:
1707 origin = SEEK_SET;
1708 break;
1709 case GST_SEEK_METHOD_CUR:
1710 origin = SEEK_CUR;
1711 break;
1712 case GST_SEEK_METHOD_END:
1713 origin = SEEK_END;
1714 break;
1715 default:
1716 goto error;
1717 }
1718 if (dvdnav_sector_search (src->dvdnav, (offset / DVD_SECTOR_SIZE),
1719 origin) != DVDNAV_STATUS_OK) {
1720 goto error;
1721 }
1722 default:
1723 if (format == sector_format) {
1724 switch (GST_EVENT_SEEK_METHOD (event)) {
1725 case GST_SEEK_METHOD_SET:
1726 origin = SEEK_SET;
1727 break;
1728 case GST_SEEK_METHOD_CUR:
1729 origin = SEEK_CUR;
1730 break;
1731 case GST_SEEK_METHOD_END:
1732 origin = SEEK_END;
1733 break;
1734 default:
1735 goto error;
1736 }
1737 if (dvdnav_sector_search (src->dvdnav, offset, origin) !=
1738 DVDNAV_STATUS_OK) {
1739 goto error;
1740 }
1741 } else if (format == title_format) {
1742 if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
1743 DVDNAV_STATUS_OK) {
1744 goto error;
1745 }
1746 switch (GST_EVENT_SEEK_METHOD (event)) {
1747 case GST_SEEK_METHOD_SET:
1748 new_title = offset;
1749 break;
1750 case GST_SEEK_METHOD_CUR:
1751 new_title = title + offset;
1752 break;
1753 case GST_SEEK_METHOD_END:
1754 if (dvdnav_get_number_of_titles (src->dvdnav, &titles) !=
1755 DVDNAV_STATUS_OK) {
1756 goto error;
1757 }
1758 new_title = titles + offset;
1759 break;
1760 default:
1761 goto error;
1762 }
1763 if (dvdnav_title_play (src->dvdnav, new_title) != DVDNAV_STATUS_OK) {
1764 goto error;
1765 }
1766 } else if (format == chapter_format) {
1767 if (dvdnav_current_title_info (src->dvdnav, &title, &part) !=
1768 DVDNAV_STATUS_OK) {
1769 goto error;
1770 }
1771 switch (GST_EVENT_SEEK_METHOD (event)) {
1772 case GST_SEEK_METHOD_SET:
1773 new_part = offset;
1774 break;
1775 case GST_SEEK_METHOD_CUR:
1776 new_part = part + offset;
1777 break;
1778 case GST_SEEK_METHOD_END:
1779 if (dvdnav_get_number_of_titles (src->dvdnav, &parts) !=
1780 DVDNAV_STATUS_OK) {
1781 goto error;
1782 }
1783 new_part = parts + offset;
1784 break;
1785 default:
1786 goto error;
1787 }
1788 /*if (dvdnav_part_search(src->dvdnav, new_part) != */
1789 if (dvdnav_part_play (src->dvdnav, title, new_part) !=
1790 DVDNAV_STATUS_OK) {
1791 goto error;
1792 }
1793 } else if (format == angle_format) {
1794 if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles) !=
1795 DVDNAV_STATUS_OK) {
1796 goto error;
1797 }
1798 switch (GST_EVENT_SEEK_METHOD (event)) {
1799 case GST_SEEK_METHOD_SET:
1800 new_angle = offset;
1801 break;
1802 case GST_SEEK_METHOD_CUR:
1803 new_angle = angle + offset;
1804 break;
1805 case GST_SEEK_METHOD_END:
1806 new_angle = angles + offset;
1807 break;
1808 default:
1809 goto error;
1810 }
1811 if (dvdnav_angle_change (src->dvdnav, new_angle) !=
1812 DVDNAV_STATUS_OK) {
1813 goto error;
1814 }
1815 } else {
1816 goto error;
1817 }
1818 }
1819 src->did_seek = TRUE;
1820 src->need_flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
1821 break;
1822 }
1823 case GST_EVENT_NAVIGATION:
1824 res = dvdnav_handle_navigation_event (src, event);
1825 break;
1826 case GST_EVENT_FLUSH:
1827 src->need_flush = TRUE;
1828 break;
1829 default:
1830 goto error;
1831 }
1833 if (FALSE) {
1834 error:
1835 res = FALSE;
1836 }
1837 gst_event_unref (event);
1839 return res;
1840 }
1842 static const GstFormat *
1843 dvdnavsrc_get_formats (GstPad * pad)
1844 {
1845 int i;
1846 static GstFormat formats[] = {
1847 GST_FORMAT_BYTES,
1848 /*
1849 GST_FORMAT_TIME,
1850 GST_FORMAT_DEFAULT,
1851 */
1852 0, /* filled later */
1853 0, /* filled later */
1854 0, /* filled later */
1855 0, /* filled later */
1856 0
1857 };
1858 static gboolean format_initialized = FALSE;
1860 if (!format_initialized) {
1861 for (i = 0; formats[i] != 0; i++) {
1862 }
1863 formats[i++] = sector_format;
1864 formats[i++] = title_format;
1865 formats[i++] = chapter_format;
1866 formats[i++] = angle_format;
1867 format_initialized = TRUE;
1868 }
1870 return formats;
1871 }
1873 static gboolean
1874 dvdnavsrc_convert (GstPad * pad,
1875 GstFormat src_format, gint64 src_value,
1876 GstFormat * dest_format, gint64 * dest_value)
1877 {
1878 DVDNavSrc *src;
1880 src = DVDNAVSRC (gst_pad_get_parent (pad));
1882 if (!GST_FLAG_IS_SET (src, DVDNAVSRC_OPEN))
1883 return FALSE;
1885 switch (src_format) {
1886 case GST_FORMAT_BYTES:
1887 if (*dest_format == sector_format) {
1888 *dest_value = src_value / DVD_SECTOR_SIZE;
1889 } else
1890 return FALSE;
1891 default:
1892 if ((src_format == sector_format) && (*dest_format == GST_FORMAT_BYTES)) {
1893 *dest_value = src_value * DVD_SECTOR_SIZE;
1894 } else
1895 return FALSE;
1896 }
1898 return TRUE;
1899 }
1901 static const GstQueryType *
1902 dvdnavsrc_get_query_types (GstPad * pad)
1903 {
1904 static const GstQueryType src_query_types[] = {
1905 GST_QUERY_TOTAL,
1906 GST_QUERY_POSITION,
1907 0
1908 };
1910 return src_query_types;
1911 }
1913 static gboolean
1914 dvdnavsrc_query (GstPad * pad, GstQueryType type,
1915 GstFormat * format, gint64 * value)
1916 {
1917 gboolean res = TRUE;
1918 DVDNavSrc *src;
1919 int titles, title;
1920 int parts, part;
1921 int angles, angle;
1922 unsigned int pos, len;
1924 src = DVDNAVSRC (gst_pad_get_parent (pad));
1926 if (!GST_FLAG_IS_SET (src, DVDNAVSRC_OPEN))
1927 return FALSE;
1929 switch (type) {
1930 case GST_QUERY_TOTAL:
1931 if (*format == sector_format) {
1932 if (dvdnav_get_position (src->dvdnav, &pos, &len)
1933 != DVDNAV_STATUS_OK) {
1934 res = FALSE;
1935 }
1936 *value = len;
1937 } else if (*format == GST_FORMAT_BYTES) {
1938 if (dvdnav_get_position (src->dvdnav, &pos, &len) != DVDNAV_STATUS_OK) {
1939 res = FALSE;
1940 }
1941 *value = len * DVD_SECTOR_SIZE;
1942 } else if (*format == title_format) {
1943 if (dvdnav_get_number_of_titles (src->dvdnav, &titles)
1944 != DVDNAV_STATUS_OK) {
1945 res = FALSE;
1946 }
1947 *value = titles;
1948 } else if (*format == chapter_format) {
1949 if (dvdnav_get_number_of_titles (src->dvdnav, &parts)
1950 != DVDNAV_STATUS_OK) {
1951 res = FALSE;
1952 }
1953 *value = parts;
1954 } else if (*format == angle_format) {
1955 if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles)
1956 != DVDNAV_STATUS_OK) {
1957 res = FALSE;
1958 }
1959 *value = angles;
1960 } else {
1961 res = FALSE;
1962 }
1963 break;
1964 case GST_QUERY_POSITION:
1965 if (*format == sector_format) {
1966 if (dvdnav_get_position (src->dvdnav, &pos, &len)
1967 != DVDNAV_STATUS_OK) {
1968 res = FALSE;
1969 }
1970 *value = pos;
1971 } else if (*format == title_format) {
1972 if (dvdnav_current_title_info (src->dvdnav, &title, &part)
1973 != DVDNAV_STATUS_OK) {
1974 res = FALSE;
1975 }
1976 *value = title;
1977 } else if (*format == chapter_format) {
1978 if (dvdnav_current_title_info (src->dvdnav, &title, &part)
1979 != DVDNAV_STATUS_OK) {
1980 res = FALSE;
1981 }
1982 *value = part;
1983 } else if (*format == angle_format) {
1984 if (dvdnav_get_angle_info (src->dvdnav, &angle, &angles)
1985 != DVDNAV_STATUS_OK) {
1986 res = FALSE;
1987 }
1988 *value = angle;
1989 } else {
1990 res = FALSE;
1991 }
1992 break;
1993 default:
1994 res = FALSE;
1995 break;
1996 }
1997 return res;
1998 }
2000 /*
2001 * URI interface.
2002 */
2004 static guint
2005 dvdnavsrc_uri_get_type (void)
2006 {
2007 return GST_URI_SRC;
2008 }
2010 static gchar **
2011 dvdnavsrc_uri_get_protocols (void)
2012 {
2013 static gchar *protocols[] = { "dvdnav", NULL };
2015 return protocols;
2016 }
2018 static const gchar *
2019 dvdnavsrc_uri_get_uri (GstURIHandler * handler)
2020 {
2021 return "dvdnav://";
2022 }
2024 static gboolean
2025 dvdnavsrc_uri_set_uri (GstURIHandler * handler, const gchar * uri)
2026 {
2027 gboolean ret;
2028 gchar *protocol = gst_uri_get_protocol (uri);
2030 ret = (protocol && !strcmp (protocol, "dvdnav")) ? TRUE : FALSE;
2031 g_free (protocol);
2033 return ret;
2034 }
2036 static void
2037 dvdnavsrc_uri_handler_init (gpointer g_iface, gpointer iface_data)
2038 {
2039 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
2041 iface->get_type = dvdnavsrc_uri_get_type;
2042 iface->get_protocols = dvdnavsrc_uri_get_protocols;
2043 iface->get_uri = dvdnavsrc_uri_get_uri;
2044 iface->set_uri = dvdnavsrc_uri_set_uri;
2045 }
2047 static gboolean
2048 plugin_init (GstPlugin * plugin)
2049 {
2050 if (!gst_element_register (plugin, "dvdnavsrc", GST_RANK_NONE,
2051 GST_TYPE_DVDNAVSRC))
2052 return FALSE;
2054 return TRUE;
2055 }
2057 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2058 GST_VERSION_MINOR,
2059 "dvdnavsrc",
2060 "Access a DVD with navigation features using libdvdnav",
2061 plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN)