2 * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
3 * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
33 #include "dvdreadsrc.h"
35 /* #include <gst/gst-i18n-plugin.h> */
36 /* FIXME: remove once GETTEXT_PACKAGE etc. is set */
39 GST_DEBUG_CATEGORY_STATIC (gstgst_dvd_read_src_debug);
40 #define GST_CAT_DEFAULT (gstgst_dvd_read_src_debug)
42 static void gst_dvd_read_src_do_init (GType dvdreadsrc_type);
53 static GstElementDetails gst_dvd_read_src_details = {
56 "Access a DVD title/chapter/angle using libdvdread",
57 "Erik Walthinsen <omega@cse.ogi.edu>",
60 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
63 GST_STATIC_CAPS ("video/mpeg, mpegversion=2, systemstream=(boolean)true"));
65 static GstFormat title_format;
66 static GstFormat angle_format;
67 static GstFormat sector_format;
68 static GstFormat chapter_format;
70 static gboolean gst_dvd_read_src_start (GstBaseSrc * basesrc);
71 static gboolean gst_dvd_read_src_stop (GstBaseSrc * basesrc);
72 static GstFlowReturn gst_dvd_read_src_create (GstPushSrc * pushsrc,
74 static gboolean gst_dvd_read_src_src_query (GstBaseSrc * basesrc,
76 static gboolean gst_dvd_read_src_src_event (GstBaseSrc * basesrc,
78 static gboolean gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title,
80 static gboolean gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src,
82 static gboolean gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, gint angle);
83 static void gst_dvd_read_src_set_property (GObject * object, guint prop_id,
84 const GValue * value, GParamSpec * pspec);
85 static void gst_dvd_read_src_get_property (GObject * object, guint prop_id,
86 GValue * value, GParamSpec * pspec);
87 static GstEvent *gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
89 static gboolean gst_dvd_read_src_get_size (GstDvdReadSrc * src, gint64 * size);
90 static gboolean gst_dvd_read_src_do_seek (GstBaseSrc * src, GstSegment * s);
92 GST_BOILERPLATE_FULL (GstDvdReadSrc, gst_dvd_read_src, GstPushSrc,
93 GST_TYPE_PUSH_SRC, gst_dvd_read_src_do_init);
96 gst_dvd_read_src_base_init (gpointer g_class)
98 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
100 gst_element_class_add_pad_template (element_class,
101 gst_static_pad_template_get (&srctemplate));
103 gst_element_class_set_details (element_class, &gst_dvd_read_src_details);
107 gst_dvd_read_src_finalize (GObject * object)
109 GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
111 g_free (src->location);
112 g_free (src->last_uri);
114 G_OBJECT_CLASS (parent_class)->finalize (object);
118 gst_dvd_read_src_init (GstDvdReadSrc * src, GstDvdReadSrcClass * klass)
121 src->vts_file = NULL;
122 src->vmg_file = NULL;
123 src->dvd_title = NULL;
125 src->location = g_strdup ("/dev/dvd");
126 src->last_uri = NULL;
127 src->new_seek = TRUE;
128 src->new_cell = TRUE;
129 src->change_cell = FALSE;
131 src->uri_chapter = 1;
134 src->title_lang_event_pending = NULL;
135 src->pending_clut_event = NULL;
137 gst_pad_use_fixed_caps (GST_BASE_SRC_PAD (src));
138 gst_pad_set_caps (GST_BASE_SRC_PAD (src),
139 gst_static_pad_template_get_caps (&srctemplate));
143 gst_dvd_read_src_is_seekable (GstBaseSrc * src)
149 gst_dvd_read_src_class_init (GstDvdReadSrcClass * klass)
151 GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
152 GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
153 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
155 gobject_class->finalize = gst_dvd_read_src_finalize;
156 gobject_class->set_property = gst_dvd_read_src_set_property;
157 gobject_class->get_property = gst_dvd_read_src_get_property;
159 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
160 g_param_spec_string ("device", "Device",
161 "DVD device location", NULL, G_PARAM_READWRITE));
162 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TITLE,
163 g_param_spec_int ("title", "title", "title",
164 1, 999, 1, G_PARAM_READWRITE));
165 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHAPTER,
166 g_param_spec_int ("chapter", "chapter", "chapter",
167 1, 999, 1, G_PARAM_READWRITE));
168 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
169 g_param_spec_int ("angle", "angle", "angle",
170 1, 999, 1, G_PARAM_READWRITE));
172 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dvd_read_src_start);
173 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dvd_read_src_stop);
174 gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_query);
175 gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_event);
176 gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_dvd_read_src_do_seek);
177 gstbasesrc_class->is_seekable =
178 GST_DEBUG_FUNCPTR (gst_dvd_read_src_is_seekable);
180 gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_dvd_read_src_create);
184 gst_dvd_read_src_start (GstBaseSrc * basesrc)
186 GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
188 g_return_val_if_fail (src->location != NULL, FALSE);
190 GST_DEBUG_OBJECT (src, "Opening DVD '%s'", src->location);
192 if ((src->dvd = DVDOpen (src->location)) == NULL)
195 /* Load the video manager to find out the information about the titles */
196 GST_DEBUG_OBJECT (src, "Loading VMG info");
198 if (!(src->vmg_file = ifoOpen (src->dvd, 0)))
199 goto ifo_open_failed;
201 src->tt_srpt = src->vmg_file->tt_srpt;
203 src->title = src->uri_title - 1;
204 src->chapter = src->uri_chapter - 1;
205 src->angle = src->uri_angle - 1;
207 if (!gst_dvd_read_src_goto_title (src, src->title, src->angle))
208 goto title_open_failed;
210 if (!gst_dvd_read_src_goto_chapter (src, src->chapter))
211 goto chapter_open_failed;
213 src->new_seek = FALSE;
214 src->change_cell = TRUE;
221 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
222 (_("Could not open DVD")),
223 ("DVDOpen(%s) failed: %s", src->location, g_strerror (errno)));
228 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
229 (_("Could not open DVD")),
230 ("ifoOpen() failed: %s", g_strerror (errno)));
235 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
236 (_("Could not open DVD title %d"), src->uri_title), (NULL));
241 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
242 (_("Failed to go to chapter %d of DVD title %d"),
243 src->uri_chapter, src->uri_title), (NULL));
249 gst_dvd_read_src_stop (GstBaseSrc * basesrc)
251 GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
254 ifoClose (src->vts_file);
255 src->vts_file = NULL;
258 ifoClose (src->vmg_file);
259 src->vmg_file = NULL;
261 if (src->dvd_title) {
262 DVDCloseFile (src->dvd_title);
263 src->dvd_title = NULL;
269 src->new_cell = TRUE;
270 src->new_seek = TRUE;
271 src->change_cell = FALSE;
274 src->need_newsegment = TRUE;
275 if (src->title_lang_event_pending) {
276 gst_event_unref (src->title_lang_event_pending);
277 src->title_lang_event_pending = NULL;
279 if (src->pending_clut_event) {
280 gst_event_unref (src->pending_clut_event);
281 src->pending_clut_event = NULL;
284 GST_LOG_OBJECT (src, "closed DVD");
290 cur_title_get_chapter_pgc (GstDvdReadSrc * src, gint chapter, gint * p_pgn,
291 gint * p_pgc_id, pgc_t ** p_pgc)
296 g_assert (chapter >= 0 && chapter < src->num_chapters);
298 pgc_id = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgcn;
299 pgn = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgn;
300 pgc = src->vts_file->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
308 cur_title_get_chapter_bounds (GstDvdReadSrc * src, gint chapter,
309 gint * p_first_cell, gint * p_last_cell)
312 gint pgn, pgc_id, pgn_next_ch;
314 g_assert (chapter >= 0 && chapter < src->num_chapters);
316 cur_title_get_chapter_pgc (src, chapter, &pgn, &pgc_id, &pgc);
318 *p_first_cell = pgc->program_map[pgn - 1] - 1;
320 if (chapter == (src->num_chapters - 1)) {
321 *p_last_cell = pgc->nr_of_cells;
323 pgn_next_ch = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter + 1].pgn;
324 *p_last_cell = pgc->program_map[pgn_next_ch - 1] - 1;
329 gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src, gint chapter)
333 /* make sure the chapter number is valid for this title */
334 if (chapter < 0 || chapter >= src->num_chapters) {
335 GST_WARNING_OBJECT (src, "invalid chapter %d (only %d available)",
336 chapter, src->num_chapters);
337 chapter = CLAMP (chapter, 0, src->num_chapters - 1);
340 /* determine which program chain we want to watch. This is
341 * based on the chapter number */
342 cur_title_get_chapter_pgc (src, chapter, &src->pgn, &src->pgc_id,
344 cur_title_get_chapter_bounds (src, chapter, &src->start_cell,
347 GST_LOG_OBJECT (src, "Opened chapter %d - cell %d-%d", chapter,
348 src->start_cell, src->last_cell);
350 /* retrieve position */
352 for (i = 0; i < chapter; i++) {
355 cur_title_get_chapter_bounds (src, i, &c1, &c2);
359 src->cur_pgc->cell_playback[c1].last_sector -
360 src->cur_pgc->cell_playback[c1].first_sector;
365 /* prepare reading for new cell */
366 src->new_cell = TRUE;
367 src->next_cell = src->start_cell;
369 src->chapter = chapter;
371 if (src->pending_clut_event)
372 gst_event_unref (src->pending_clut_event);
374 src->pending_clut_event =
375 gst_dvd_read_src_make_clut_change_event (src, src->cur_pgc->palette);
381 gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title, gint angle)
384 gchar lang_code[3] = { '\0', '\0', '\0' }, *t;
389 /* make sure our title number is valid */
390 num_titles = src->tt_srpt->nr_of_srpts;
391 GST_INFO_OBJECT (src, "There are %d titles on this DVD", num_titles);
392 if (title < 0 || title >= num_titles)
395 src->num_chapters = src->tt_srpt->title[title].nr_of_ptts;
396 GST_INFO_OBJECT (src, "Title %d has %d chapters", title, src->num_chapters);
398 /* make sure the angle number is valid for this title */
399 src->num_angles = src->tt_srpt->title[title].nr_of_angles;
400 GST_LOG_OBJECT (src, "Title %d has %d angles", title, src->num_angles);
401 if (angle < 0 || angle >= src->num_angles) {
402 GST_WARNING_OBJECT (src, "Invalid angle %d (only %d available)",
403 angle, src->num_angles);
404 angle = CLAMP (angle, 0, src->num_angles - 1);
407 /* load the VTS information for the title set our title is in */
408 title_set_nr = src->tt_srpt->title[title].title_set_nr;
409 src->vts_file = ifoOpen (src->dvd, title_set_nr);
410 if (src->vts_file == NULL)
411 goto ifo_open_failed;
413 src->ttn = src->tt_srpt->title[title].vts_ttn;
414 src->vts_ptt_srpt = src->vts_file->vts_ptt_srpt;
416 /* we've got enough info, time to open the title set data */
417 src->dvd_title = DVDOpenFile (src->dvd, title_set_nr, DVD_READ_TITLE_VOBS);
418 if (src->dvd_title == NULL)
419 goto title_open_failed;
421 GST_INFO_OBJECT (src, "Opened title %d, angle %d", title, angle);
427 if (src->title_lang_event_pending) {
428 gst_event_unref (src->title_lang_event_pending);
429 src->title_lang_event_pending = NULL;
432 s = gst_structure_new ("application/x-gst-dvd",
433 "event", G_TYPE_STRING, "dvd-lang-codes", NULL);
436 for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_audio_streams; i++) {
437 const audio_attr_t *a = &src->vts_file->vtsi_mat->vts_audio_attr[i];
439 t = g_strdup_printf ("audio-%d-format", i);
440 gst_structure_set (s, t, G_TYPE_INT, (int) a->audio_format, NULL);
444 t = g_strdup_printf ("audio-%d-language", i);
445 lang_code[0] = (a->lang_code >> 8) & 0xff;
446 lang_code[1] = a->lang_code & 0xff;
447 gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
453 GST_INFO_OBJECT (src, "[%02d] Audio %02d: lang='%s', format=%d",
454 src->title, i, lang_code, (gint) a->audio_format);
458 for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_subp_streams; i++) {
459 const subp_attr_t *u = &src->vts_file->vtsi_mat->vts_subp_attr[i];
462 t = g_strdup_printf ("subtitle-%d-language", i);
463 lang_code[0] = (u->lang_code >> 8) & 0xff;
464 lang_code[1] = u->lang_code & 0xff;
465 gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
471 GST_INFO_OBJECT (src, "[%02d] Subtitle %02d: lang='%s', format=%d",
472 src->title, i, lang_code);
475 src->title_lang_event_pending =
476 gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
483 GST_WARNING_OBJECT (src, "Invalid title %d (only %d available)",
489 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
490 (_("Could not open DVD title %d"), title_set_nr),
491 ("ifoOpen(%d) failed: %s", title_set_nr, g_strerror (errno)));
496 GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
497 (_("Could not open DVD title %d"), title_set_nr),
498 ("Can't open title VOBS (VTS_%02d_1.VOB)", title_set_nr));
503 /* FIXME: double-check this function, compare against original */
505 gst_dvd_read_src_get_next_cell_for (GstDvdReadSrc * src, gint cell)
507 /* Check if we're entering an angle block. */
508 if (src->cur_pgc->cell_playback[cell].block_type != BLOCK_TYPE_ANGLE_BLOCK)
511 while (src->cur_pgc->cell_playback[cell].block_mode == BLOCK_MODE_LAST_CELL)
514 return cell + 1; /* really +1? (tpm) */
517 /* Returns true if the pack is a NAV pack */
519 gst_dvd_read_src_is_nav_pack (const guint8 * data)
521 if (GST_READ_UINT32_BE (data + 0x26) != 0x000001BF)
524 /* Check that this is substream 0 (PCI) */
528 if (GST_READ_UINT32_BE (data + 0x400) != 0x000001BF)
531 /* Check that this is substream 1 (DSI) */
532 if (data[0x406] != 1)
535 /* Check sizes of PCI and DSI packets */
536 if (GST_READ_UINT16_BE (data + 0x2a) != 0x03d4)
539 if (GST_READ_UINT16_BE (data + 0x404) != 0x03fa)
548 GST_DVD_READ_ERROR = -1,
549 GST_DVD_READ_EOS = -2,
550 GST_DVD_READ_AGAIN = -3
553 static GstDvdReadReturn
554 gst_dvd_read_src_read (GstDvdReadSrc * src, gint angle, gint new_seek,
558 guint8 oneblock[DVD_VIDEO_LB_LEN];
560 guint next_vobu, next_ilvu_start, cur_output_size;
563 /* playback by cell in this pgc, starting at the cell for our chapter */
565 src->cur_cell = src->start_cell;
569 if (src->cur_cell >= src->last_cell) {
570 /* advance to next chapter */
571 if (src->chapter == (src->num_chapters - 1))
574 GST_INFO_OBJECT (src, "end of chapter %d, switch to next", src->chapter);
577 gst_dvd_read_src_goto_chapter (src, src->chapter);
579 return GST_DVD_READ_AGAIN;
582 if (src->new_cell || new_seek) {
584 src->cur_cell = src->next_cell;
585 if (src->cur_cell >= src->last_cell) {
586 GST_LOG_OBJECT (src, "last cell in chapter");
591 /* take angle into account */
592 if (src->cur_pgc->cell_playback[src->cur_cell].block_type
593 == BLOCK_TYPE_ANGLE_BLOCK)
594 src->cur_cell += angle;
596 /* calculate next cell */
597 src->next_cell = gst_dvd_read_src_get_next_cell_for (src, src->cur_cell);
599 /* we loop until we're out of this cell */
600 src->cur_pack = src->cur_pgc->cell_playback[src->cur_cell].first_sector;
601 src->new_cell = FALSE;
604 if (src->cur_pack >= src->cur_pgc->cell_playback[src->cur_cell].last_sector) {
605 src->new_cell = TRUE;
606 GST_LOG_OBJECT (src, "Beyond last sector, go to next cell");
607 return GST_DVD_READ_AGAIN;
610 /* read NAV packet */
613 len = DVDReadBlocks (src->dvd_title, src->cur_pack, 1, oneblock);
617 if (!gst_dvd_read_src_is_nav_pack (oneblock)) {
622 /* parse the contained dsi packet */
623 navRead_DSI (&dsi_pack, &oneblock[DSI_START_BYTE]);
624 g_assert (src->cur_pack == dsi_pack.dsi_gi.nv_pck_lbn);
626 /* determine where we go next. These values are the ones we
627 * mostly care about */
628 next_ilvu_start = src->cur_pack + dsi_pack.sml_agli.data[angle].address;
629 cur_output_size = dsi_pack.dsi_gi.vobu_ea;
631 /* If we're not at the end of this cell, we can determine the next
632 * VOBU to display using the VOBU_SRI information section of the
633 * DSI. Using this value correctly follows the current angle,
634 * avoiding the doubled scenes in The Matrix, and makes our life
637 * Otherwise, we set our next address past the end of this cell to
638 * force the code above to go to the next cell in the program. */
639 if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
640 next_vobu = src->cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
642 next_vobu = src->cur_pack + cur_output_size + 1;
645 g_assert (cur_output_size < 1024);
648 /* create the buffer (TODO: use buffer pool?) */
649 buf = gst_buffer_new_and_alloc (cur_output_size * DVD_VIDEO_LB_LEN);
651 /* read in and output cursize packs */
652 len = DVDReadBlocks (src->dvd_title, src->cur_pack, cur_output_size,
653 GST_BUFFER_DATA (buf));
655 if (len != cur_output_size)
656 goto block_read_error;
658 GST_BUFFER_SIZE (buf) = cur_output_size * DVD_VIDEO_LB_LEN;
659 /* GST_BUFFER_OFFSET (buf) = priv->cur_pack * DVD_VIDEO_LB_LEN; */
661 gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
665 src->cur_pack = next_vobu;
667 GST_LOG_OBJECT (src, "Read %u sectors", cur_output_size);
669 return GST_DVD_READ_OK;
674 GST_INFO_OBJECT (src, "last chapter done - EOS");
675 return GST_DVD_READ_EOS;
679 GST_ERROR_OBJECT (src, "Read failed for block %d", src->cur_pack);
680 return GST_DVD_READ_ERROR;
684 GST_ERROR_OBJECT (src, "Read failed for %d blocks at %d",
685 cur_output_size, src->cur_pack);
686 gst_buffer_unref (buf);
687 return GST_DVD_READ_ERROR;
692 gst_dvd_read_src_create (GstPushSrc * pushsrc, GstBuffer ** p_buf)
694 GstDvdReadSrc *src = GST_DVD_READ_SRC (pushsrc);
698 g_return_val_if_fail (src->dvd != NULL, GST_FLOW_ERROR);
700 srcpad = GST_BASE_SRC (src)->srcpad;
702 if (src->need_newsegment) {
703 gst_pad_push_event (srcpad,
704 gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
705 src->cur_pack * DVD_VIDEO_LB_LEN, -1, 0));
706 src->need_newsegment = FALSE;
710 gst_dvd_read_src_goto_title (src, src->title, src->angle);
711 gst_dvd_read_src_goto_chapter (src, src->chapter);
713 src->new_seek = FALSE;
714 src->change_cell = TRUE;
717 if (src->title_lang_event_pending) {
718 gst_pad_push_event (srcpad, src->title_lang_event_pending);
719 src->title_lang_event_pending = NULL;
722 if (src->pending_clut_event) {
723 gst_pad_push_event (srcpad, src->pending_clut_event);
724 src->pending_clut_event = NULL;
729 res = gst_dvd_read_src_read (src, src->angle, src->change_cell, p_buf);
730 } while (res == GST_DVD_READ_AGAIN);
733 case GST_DVD_READ_ERROR:{
734 GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), (NULL));
735 return GST_FLOW_ERROR;
737 case GST_DVD_READ_EOS:{
738 GST_INFO_OBJECT (src, "Reached EOS");
739 return GST_FLOW_UNEXPECTED;
741 case GST_DVD_READ_OK:{
742 src->change_cell = FALSE;
749 g_return_val_if_reached (GST_FLOW_UNEXPECTED);
753 gst_dvd_read_src_set_property (GObject * object, guint prop_id,
754 const GValue * value, GParamSpec * pspec)
756 GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
759 GST_OBJECT_LOCK (src);
760 started = GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED);
765 g_warning ("%s: property '%s' needs to be set before the device is "
766 "opened", GST_ELEMENT_NAME (src), pspec->name);
770 g_free (src->location);
771 /* clear the filename if we get a NULL (is that possible?) */
772 if (g_value_get_string (value) == NULL) {
773 src->location = g_strdup ("/dev/dvd");
775 src->location = g_strdup (g_value_get_string (value));
780 src->uri_title = g_value_get_int (value);
782 src->title = src->uri_title - 1;
783 src->new_seek = TRUE;
787 src->uri_chapter = g_value_get_int (value);
789 src->chapter = src->uri_chapter - 1;
790 src->new_seek = TRUE;
794 src->uri_angle = g_value_get_int (value);
796 src->angle = src->uri_angle - 1;
800 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
804 GST_OBJECT_UNLOCK (src);
808 gst_dvd_read_src_get_property (GObject * object, guint prop_id, GValue * value,
811 GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
813 GST_OBJECT_LOCK (src);
817 g_value_set_string (value, src->location);
820 g_value_set_int (value, src->uri_title);
823 g_value_set_int (value, src->uri_chapter);
826 g_value_set_int (value, src->uri_angle);
829 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
833 GST_OBJECT_UNLOCK (src);
837 gst_dvd_read_src_get_size (GstDvdReadSrc * src, gint64 * size)
839 gboolean ret = FALSE;
841 if (src->dvd_title) {
844 blocks = DVDFileSize (src->dvd_title);
846 *size = (gint64) blocks *DVD_VIDEO_LB_LEN;
850 GST_WARNING_OBJECT (src, "DVDFileSize(%p) failed!", src->dvd_title);
857 /*** Querying and seeking ***/
860 gst_dvd_read_src_handle_seek_event (GstDvdReadSrc * src, GstEvent * event)
863 GstSeekType cur_type, end_type;
864 gint64 new_off, total;
870 gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &new_off,
874 GST_DEBUG_OBJECT (src, "cannot do backwards playback yet");
878 if ((flags & GST_SEEK_FLAG_SEGMENT) != 0) {
879 GST_DEBUG_OBJECT (src, "segment seek not supported");
883 if ((flags & GST_SEEK_FLAG_FLUSH) == 0) {
884 GST_DEBUG_OBJECT (src, "can only do flushing seeks at the moment");
888 if (end_type != GST_SEEK_TYPE_NONE) {
889 GST_DEBUG_OBJECT (src, "end seek type not supported");
893 if (cur_type != GST_SEEK_TYPE_SET) {
894 GST_DEBUG_OBJECT (src, "only SEEK_TYPE_SET is supported");
898 if (format == angle_format) {
899 GST_OBJECT_LOCK (src);
900 if (new_off < 0 || new_off >= src->num_angles) {
901 GST_OBJECT_UNLOCK (src);
902 GST_DEBUG_OBJECT (src, "invalid angle %d, only %d available",
906 src->angle = (gint) new_off;
907 GST_OBJECT_UNLOCK (src);
908 GST_DEBUG_OBJECT (src, "switched to angle %d", (gint) new_off);
912 if (format != chapter_format && format != title_format &&
913 format != GST_FORMAT_BYTES) {
914 GST_DEBUG_OBJECT (src, "unsupported seek format %d (%s)", format,
915 gst_format_get_name (format));
919 if (format == GST_FORMAT_BYTES) {
920 GST_DEBUG_OBJECT (src, "Requested seek to byte %" G_GUINT64_FORMAT,
922 } else if (format == GST_FORMAT_TIME) {
923 GST_DEBUG_OBJECT (src, "Requested seek to time %" GST_TIME_FORMAT,
924 GST_TIME_ARGS (new_off));
927 srcpad = GST_BASE_SRC_PAD (src);
929 /* check whether the seek looks reasonable (ie within possible range) */
930 if (format == GST_FORMAT_BYTES) {
931 GST_OBJECT_LOCK (src);
932 query_ok = gst_dvd_read_src_get_size (src, &total);
933 GST_OBJECT_UNLOCK (src);
935 query_ok = gst_pad_query_duration (srcpad, &format, &total);
939 GST_DEBUG_OBJECT (src, "Failed to query duration in format %s",
940 gst_format_get_name (format));
944 GST_DEBUG_OBJECT (src, "Total %s: %12" G_GINT64_FORMAT,
945 gst_format_get_name (format), total);
946 GST_DEBUG_OBJECT (src, "Seek to %s: %12" G_GINT64_FORMAT,
947 gst_format_get_name (format), new_off);
949 if (new_off >= total) {
950 GST_DEBUG_OBJECT (src, "Seek position out of range");
954 /* set segment to seek format; this allows us to use the do_seek
955 * virtual function and let the base source handle all the tricky
956 * stuff for us. We don't use the segment internally anyway */
957 /* FIXME: can't take the stream lock here - what to do? */
958 GST_OBJECT_LOCK (src);
959 GST_BASE_SRC (src)->segment.format = format;
960 GST_BASE_SRC (src)->segment.start = 0;
961 GST_BASE_SRC (src)->segment.stop = total;
962 GST_BASE_SRC (src)->segment.duration = total;
963 GST_OBJECT_UNLOCK (src);
965 return GST_BASE_SRC_CLASS (parent_class)->event (GST_BASE_SRC (src), event);
969 gst_dvd_read_src_do_seek (GstBaseSrc * basesrc, GstSegment * s)
973 src = GST_DVD_READ_SRC (basesrc);
975 GST_DEBUG_OBJECT (src, "Seeking to %s: %12" G_GINT64_FORMAT,
976 gst_format_get_name (s->format), s->last_stop);
978 if (s->format == sector_format || s->format == GST_FORMAT_BYTES) {
983 if (s->format == sector_format) {
984 src->cur_pack = s->last_stop;
987 src->cur_pack = s->last_stop / DVD_VIDEO_LB_LEN;
988 if ((src->cur_pack * DVD_VIDEO_LB_LEN) != s->last_stop) {
989 GST_LOG_OBJECT (src, "rounded down offset %" G_GINT64_FORMAT " => %"
990 G_GINT64_FORMAT, s->last_stop,
991 (gint64) src->cur_pack * DVD_VIDEO_LB_LEN);
995 if (!gst_dvd_read_src_goto_sector (src, src->angle)) {
996 GST_DEBUG_OBJECT (src, "seek to sector 0x%08x failed", src->cur_pack);
1001 GST_LOG_OBJECT (src, "seek to sector 0x%08x ok", src->cur_pack);
1002 } else if (s->format == chapter_format) {
1003 if (!gst_dvd_read_src_goto_chapter (src, (gint) s->last_stop)) {
1004 GST_DEBUG_OBJECT (src, "seek to chapter %d failed", (gint) s->last_stop);
1007 GST_INFO_OBJECT (src, "seek to chapter %d ok", (gint) s->last_stop);
1008 src->chapter = s->last_stop;
1009 } else if (s->format == title_format) {
1010 if (!gst_dvd_read_src_goto_title (src, (gint) s->last_stop, src->angle) ||
1011 !gst_dvd_read_src_goto_chapter (src, 0)) {
1012 GST_DEBUG_OBJECT (src, "seek to title %d failed", (gint) s->last_stop);
1015 src->title = (gint) s->last_stop;
1017 GST_INFO_OBJECT (src, "seek to title %d ok", src->title);
1019 g_return_val_if_reached (FALSE);
1022 src->need_newsegment = TRUE;
1027 gst_dvd_read_src_src_event (GstBaseSrc * basesrc, GstEvent * event)
1029 GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
1032 GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
1034 switch (GST_EVENT_TYPE (event)) {
1035 case GST_EVENT_SEEK:
1036 res = gst_dvd_read_src_handle_seek_event (src, event);
1039 res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
1047 gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
1050 GstStructure *structure;
1054 structure = gst_structure_new ("application/x-gst-dvd",
1055 "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
1057 /* Create a separate field for each value in the table. */
1058 for (i = 0; i < 16; i++) {
1059 g_snprintf (name, sizeof (name), "clut%02d", i);
1060 gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
1063 /* Create the DVD event and put the structure into it. */
1064 return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
1068 gst_dvd_read_src_convert_timecode (dvd_time_t * time)
1071 const gint64 one_hour = 3600 * GST_SECOND;
1072 const gint64 one_min = 60 * GST_SECOND;
1074 g_return_val_if_fail ((time->hour >> 4) < 0xa
1075 && (time->hour & 0xf) < 0xa, -1);
1076 g_return_val_if_fail ((time->minute >> 4) < 0x7
1077 && (time->minute & 0xf) < 0xa, -1);
1078 g_return_val_if_fail ((time->second >> 4) < 0x7
1079 && (time->second & 0xf) < 0xa, -1);
1081 ret_time = ((time->hour >> 4) * 10 + (time->hour & 0xf)) * one_hour;
1082 ret_time += ((time->minute >> 4) * 10 + (time->minute & 0xf)) * one_min;
1083 ret_time += ((time->second >> 4) * 10 + (time->second & 0xf)) * GST_SECOND;
1089 gst_dvd_read_src_do_duration_query (GstDvdReadSrc * src, GstQuery * query)
1094 gst_query_parse_duration (query, &format, NULL);
1097 case GST_FORMAT_TIME:{
1098 if (src->cur_pgc == NULL)
1100 val = gst_dvd_read_src_convert_timecode (&src->cur_pgc->playback_time);
1105 case GST_FORMAT_BYTES:{
1106 if (!gst_dvd_read_src_get_size (src, &val))
1111 if (format == sector_format) {
1112 val = DVDFileSize (src->dvd_title);
1113 } else if (format == title_format) {
1114 val = src->tt_srpt->nr_of_srpts;
1115 } else if (format == chapter_format) {
1116 val = src->num_chapters;
1117 } else if (format == angle_format) {
1118 val = src->tt_srpt->title[src->title].nr_of_angles;
1120 GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
1121 format, gst_format_get_name (format));
1128 GST_LOG_OBJECT (src, "duration = %" G_GINT64_FORMAT " %s", val,
1129 gst_format_get_name (format));
1131 gst_query_set_duration (query, format, val);
1136 gst_dvd_read_src_do_position_query (GstDvdReadSrc * src, GstQuery * query)
1141 gst_query_parse_position (query, &format, NULL);
1144 case GST_FORMAT_BYTES:{
1145 val = src->cur_pack * DVD_VIDEO_LB_LEN;
1149 if (format == sector_format) {
1150 val = src->cur_pack;
1151 } else if (format == title_format) {
1153 } else if (format == chapter_format) {
1155 } else if (format == angle_format) {
1158 GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
1159 format, gst_format_get_name (format));
1166 GST_LOG_OBJECT (src, "position = %" G_GINT64_FORMAT " %s", val,
1167 gst_format_get_name (format));
1169 gst_query_set_position (query, format, val);
1174 gst_dvd_read_src_src_query (GstBaseSrc * basesrc, GstQuery * query)
1176 GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
1178 gboolean res = TRUE;
1180 GST_LOG_OBJECT (src, "handling %s query",
1181 gst_query_type_get_name (GST_QUERY_TYPE (query)));
1183 GST_OBJECT_LOCK (src);
1184 started = (GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED));
1185 GST_OBJECT_UNLOCK (src);
1188 GST_DEBUG_OBJECT (src, "query failed: not started");
1192 switch (GST_QUERY_TYPE (query)) {
1193 case GST_QUERY_DURATION:
1194 GST_OBJECT_LOCK (src);
1195 res = gst_dvd_read_src_do_duration_query (src, query);
1196 GST_OBJECT_UNLOCK (src);
1198 case GST_QUERY_POSITION:
1199 GST_OBJECT_LOCK (src);
1200 res = gst_dvd_read_src_do_position_query (src, query);
1201 GST_OBJECT_UNLOCK (src);
1204 res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
1212 gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, int angle)
1214 gint seek_to = src->cur_pack;
1215 gint chapter, sectors, next, cur, i;
1217 /* retrieve position */
1219 for (i = 0; i < src->num_chapters; i++) {
1222 cur_title_get_chapter_bounds (src, i, &c1, &c2);
1224 for (next = cur = c1; cur < c2;) {
1227 src->cur_pgc->cell_playback[cur].last_sector -
1228 src->cur_pgc->cell_playback[cur].first_sector;
1229 if (src->cur_pack + sectors > seek_to) {
1233 src->cur_pack += sectors;
1236 if (src->cur_pgc->cell_playback[cur].block_type == BLOCK_TYPE_ANGLE_BLOCK)
1238 next = gst_dvd_read_src_get_next_cell_for (src, cur);
1242 GST_DEBUG_OBJECT (src, "Seek to sector %u failed", seek_to);
1248 /* so chapter $chapter and cell $cur contain our sector
1249 * of interest. Let's go there! */
1250 GST_INFO_OBJECT (src, "Seek succeeded, going to chapter %u, cell %u",
1253 gst_dvd_read_src_goto_chapter (src, chapter);
1254 src->cur_cell = cur;
1255 src->next_cell = next;
1256 src->new_cell = FALSE;
1257 src->cur_pack = seek_to;
1264 /*** URI interface ***/
1267 gst_dvd_read_src_uri_get_type (void)
1273 gst_dvd_read_src_uri_get_protocols (void)
1275 static gchar *protocols[] = { "dvd", NULL };
1280 static const gchar *
1281 gst_dvd_read_src_uri_get_uri (GstURIHandler * handler)
1283 GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
1285 GST_OBJECT_LOCK (src);
1287 g_free (src->last_uri);
1288 src->last_uri = g_strdup_printf ("dvd://%d,%d,%d", src->uri_title,
1289 src->uri_chapter, src->uri_angle);
1291 GST_OBJECT_UNLOCK (src);
1293 return src->last_uri;
1297 gst_dvd_read_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
1299 GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
1303 protocol = gst_uri_get_protocol (uri);
1304 ret = (protocol != NULL && g_str_equal (protocol, "dvd"));
1311 /* parse out the new t/c/a and seek to them */
1313 gchar *location = NULL;
1318 location = gst_uri_get_location (uri);
1323 GST_OBJECT_LOCK (src);
1326 src->uri_chapter = 1;
1329 strcur = strs = g_strsplit (location, ",", 0);
1330 while (strcur && *strcur) {
1333 if (!sscanf (*strcur, "%d", &val))
1337 g_warning ("Invalid value %d in URI '%s'. Must be 1 or greater",
1344 src->uri_title = val;
1347 src->uri_chapter = val;
1350 src->uri_angle = val;
1358 if (pos > 0 && GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)) {
1359 src->title = src->uri_title - 1;
1360 src->chapter = src->uri_chapter - 1;
1361 src->angle = src->uri_angle - 1;
1362 src->new_seek = TRUE;
1365 GST_OBJECT_UNLOCK (src);
1375 gst_dvd_read_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
1377 GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
1379 iface->get_type = gst_dvd_read_src_uri_get_type;
1380 iface->get_protocols = gst_dvd_read_src_uri_get_protocols;
1381 iface->get_uri = gst_dvd_read_src_uri_get_uri;
1382 iface->set_uri = gst_dvd_read_src_uri_set_uri;
1386 gst_dvd_read_src_do_init (GType dvdreadsrc_type)
1388 static const GInterfaceInfo urihandler_info = {
1389 gst_dvd_read_src_uri_handler_init,
1394 g_type_add_interface_static (dvdreadsrc_type, GST_TYPE_URI_HANDLER,
1397 title_format = gst_format_register ("title", "DVD title");
1398 angle_format = gst_format_register ("angle", "DVD angle");
1399 sector_format = gst_format_register ("sector", "DVD sector");
1400 chapter_format = gst_format_register ("chapter", "DVD chapter");
1404 plugin_init (GstPlugin * plugin)
1406 GST_DEBUG_CATEGORY_INIT (gstgst_dvd_read_src_debug, "dvdreadsrc", 0,
1407 "DVD reader element based on dvdreadsrc");
1409 if (!gst_element_register (plugin, "dvdreadsrc", GST_RANK_SECONDARY,
1410 GST_TYPE_DVD_READ_SRC)) {
1417 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1420 "Access a DVD with dvdread",
1421 plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);