index 705ba3e5070692fa6f63158c1a744f63077de276..a53019f9fd41dcdede6b7e670281c04c88423d94 100644 (file)
--- a/ext/dvdread/dvdreadsrc.c
+++ b/ext/dvdread/dvdreadsrc.c
-/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+/* GStreamer DVD title source
+ * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
+ * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* Boston, MA 02111-1307, USA.
*/
-/**
- * Copyright (C) 2001 Billy Biggs <vektor@dumbterm.net>.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#include "_stdint.h"
+
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <time.h>
#include <unistd.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.h>
+#include <string.h>
#include <errno.h>
-//#include <linux/cdrom.h>
-#include <assert.h>
-
-#include <dvdreadsrc.h>
-
-#include <dvdread/dvd_reader.h>
-#include <dvdread/ifo_types.h>
-#include <dvdread/ifo_read.h>
-#include <dvdread/nav_read.h>
-#include <dvdread/nav_print.h>
-
-struct _DVDReadSrcPrivate
-{
- /* pads */
- GstPad *srcpad;
- /* location */
- gchar *location;
+#include "dvdreadsrc.h"
- gboolean new_seek;
+#include <gst/gst-i18n-plugin.h>
- gboolean new_cell;
+GST_DEBUG_CATEGORY_STATIC (gstgst_dvd_read_src_debug);
+#define GST_CAT_DEFAULT (gstgst_dvd_read_src_debug)
- int title, chapter, angle;
- int pgc_id, start_cell, cur_cell, cur_pack;
- int ttn, pgn, next_cell;
- dvd_reader_t *dvd;
- dvd_file_t *dvd_title;
- ifo_handle_t *vmg_file;
- tt_srpt_t *tt_srpt;
- ifo_handle_t *vts_file;
- vts_ptt_srpt_t *vts_ptt_srpt;
- pgc_t *cur_pgc;
-};
-
-GST_DEBUG_CATEGORY_STATIC (gstdvdreadsrc_debug);
-#define GST_CAT_DEFAULT (gstdvdreadsrc_debug)
-
-GstElementDetails dvdreadsrc_details = {
- "DVD Source",
- "Source/File/DVD",
- "Access a DVD title/chapter/angle using libdvdread",
- "Erik Walthinsen <omega@cse.ogi.edu>",
-};
-
-
-/* DVDReadSrc signals and args */
-enum
-{
- /* FILL ME */
- LAST_SIGNAL
-};
+static void gst_dvd_read_src_do_init (GType dvdreadsrc_type);
enum
{
ARG_0,
- ARG_LOCATION,
+ ARG_DEVICE,
ARG_TITLE,
ARG_CHAPTER,
ARG_ANGLE
};
-static void dvdreadsrc_base_init (gpointer g_class);
-static void dvdreadsrc_class_init (DVDReadSrcClass * klass);
-static void dvdreadsrc_init (DVDReadSrc * dvdreadsrc);
-static void dvdreadsrc_finalize (GObject * object);
-
-static void dvdreadsrc_set_property (GObject * object, guint prop_id,
+static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/mpeg, mpegversion=2, systemstream=(boolean)true"));
+
+static GstFormat title_format;
+static GstFormat angle_format;
+static GstFormat sector_format;
+static GstFormat chapter_format;
+
+static gboolean gst_dvd_read_src_start (GstBaseSrc * basesrc);
+static gboolean gst_dvd_read_src_stop (GstBaseSrc * basesrc);
+static GstFlowReturn gst_dvd_read_src_create (GstPushSrc * pushsrc,
+ GstBuffer ** buf);
+static gboolean gst_dvd_read_src_src_query (GstBaseSrc * basesrc,
+ GstQuery * query);
+static gboolean gst_dvd_read_src_src_event (GstBaseSrc * basesrc,
+ GstEvent * event);
+static gboolean gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title,
+ gint angle);
+static gboolean gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src,
+ gint chapter);
+static gboolean gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, gint angle);
+static void gst_dvd_read_src_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
-static void dvdreadsrc_get_property (GObject * object, guint prop_id,
+static void gst_dvd_read_src_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
+static GstEvent *gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
+ const guint * clut);
+static gboolean gst_dvd_read_src_get_size (GstDvdReadSrc * src, gint64 * size);
+static gboolean gst_dvd_read_src_do_seek (GstBaseSrc * src, GstSegment * s);
+static gint64 gst_dvd_read_src_convert_timecode (dvd_time_t * time);
+static gint gst_dvd_read_src_get_next_cell (GstDvdReadSrc * src,
+ pgc_t * pgc, gint cell);
+static GstClockTime gst_dvd_read_src_get_time_for_sector (GstDvdReadSrc * src,
+ guint sector);
+static gint gst_dvd_read_src_get_sector_from_time (GstDvdReadSrc * src,
+ GstClockTime ts);
+
+GST_BOILERPLATE_FULL (GstDvdReadSrc, gst_dvd_read_src, GstPushSrc,
+ GST_TYPE_PUSH_SRC, gst_dvd_read_src_do_init);
-static GstData *dvdreadsrc_get (GstPad * pad);
-
-/*static GstBuffer * dvdreadsrc_get_region (GstPad *pad,gulong offset,gulong size); */
-
-static GstElementStateReturn dvdreadsrc_change_state (GstElement * element);
-
+static void
+gst_dvd_read_src_base_init (gpointer g_class)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-static GstElementClass *parent_class = NULL;
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&srctemplate));
-/*static guint dvdreadsrc_signals[LAST_SIGNAL] = { 0 }; */
+ gst_element_class_set_details_simple (element_class, "DVD Source",
+ "Source/File/DVD",
+ "Access a DVD title/chapter/angle using libdvdread",
+ "Erik Walthinsen <omega@cse.ogi.edu>");
+}
-GType
-dvdreadsrc_get_type (void)
+static void
+gst_dvd_read_src_finalize (GObject * object)
{
- static GType dvdreadsrc_type = 0;
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
- if (!dvdreadsrc_type) {
- static const GTypeInfo dvdreadsrc_info = {
- sizeof (DVDReadSrcClass),
- dvdreadsrc_base_init,
- NULL,
- (GClassInitFunc) dvdreadsrc_class_init,
- NULL,
- NULL,
- sizeof (DVDReadSrc),
- 0,
- (GInstanceInitFunc) dvdreadsrc_init,
- };
+ g_free (src->location);
+ g_free (src->last_uri);
- dvdreadsrc_type =
- g_type_register_static (GST_TYPE_ELEMENT, "DVDReadSrc",
- &dvdreadsrc_info, 0);
- }
- return dvdreadsrc_type;
+ G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
-dvdreadsrc_base_init (gpointer g_class)
+gst_dvd_read_src_init (GstDvdReadSrc * src, GstDvdReadSrcClass * klass)
{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ src->dvd = NULL;
+ src->vts_file = NULL;
+ src->vmg_file = NULL;
+ src->dvd_title = NULL;
+
+ src->location = g_strdup ("/dev/dvd");
+ src->last_uri = NULL;
+ src->new_seek = TRUE;
+ src->new_cell = TRUE;
+ src->change_cell = FALSE;
+ src->uri_title = 1;
+ src->uri_chapter = 1;
+ src->uri_angle = 1;
+
+ src->title_lang_event_pending = NULL;
+ src->pending_clut_event = NULL;
+
+ gst_pad_use_fixed_caps (GST_BASE_SRC_PAD (src));
+ gst_pad_set_caps (GST_BASE_SRC_PAD (src),
+ gst_static_pad_template_get_caps (&srctemplate));
+}
- gst_element_class_set_details (element_class, &dvdreadsrc_details);
+static gboolean
+gst_dvd_read_src_is_seekable (GstBaseSrc * src)
+{
+ return TRUE;
}
static void
-dvdreadsrc_class_init (DVDReadSrcClass * klass)
+gst_dvd_read_src_class_init (GstDvdReadSrcClass * klass)
{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
-
- g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCATION,
- g_param_spec_string ("location", "location", "location",
- NULL, G_PARAM_READWRITE));
+ GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
+ GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = gst_dvd_read_src_finalize;
+ gobject_class->set_property = gst_dvd_read_src_set_property;
+ gobject_class->get_property = gst_dvd_read_src_get_property;
+
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DEVICE,
+ g_param_spec_string ("device", "Device",
+ "DVD device location", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_TITLE,
g_param_spec_int ("title", "title", "title",
- 0, G_MAXINT, 0, G_PARAM_READWRITE));
+ 1, 999, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CHAPTER,
g_param_spec_int ("chapter", "chapter", "chapter",
- 0, G_MAXINT, 0, G_PARAM_READWRITE));
+ 1, 999, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ANGLE,
g_param_spec_int ("angle", "angle", "angle",
- 0, G_MAXINT, 0, G_PARAM_READWRITE));
+ 1, 999, 1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- gobject_class->set_property = GST_DEBUG_FUNCPTR (dvdreadsrc_set_property);
- gobject_class->get_property = GST_DEBUG_FUNCPTR (dvdreadsrc_get_property);
+ gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_dvd_read_src_start);
+ gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_dvd_read_src_stop);
+ gstbasesrc_class->query = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_query);
+ gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_dvd_read_src_src_event);
+ gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (gst_dvd_read_src_do_seek);
+ gstbasesrc_class->is_seekable =
+ GST_DEBUG_FUNCPTR (gst_dvd_read_src_is_seekable);
- gobject_class->finalize = dvdreadsrc_finalize;
+ gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_dvd_read_src_create);
+}
- gstelement_class->change_state = dvdreadsrc_change_state;
+static gboolean
+gst_dvd_read_src_start (GstBaseSrc * basesrc)
+{
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
- GST_DEBUG_CATEGORY_INIT (gstdvdreadsrc_debug, "dvdreadsrc", 0,
- "DVD reader element based on dvdreadsrc");
+ g_return_val_if_fail (src->location != NULL, FALSE);
+
+ GST_DEBUG_OBJECT (src, "Opening DVD '%s'", src->location);
+
+ if ((src->dvd = DVDOpen (src->location)) == NULL)
+ goto open_failed;
+
+ /* Load the video manager to find out the information about the titles */
+ GST_DEBUG_OBJECT (src, "Loading VMG info");
+
+ if (!(src->vmg_file = ifoOpen (src->dvd, 0)))
+ goto ifo_open_failed;
+
+ src->tt_srpt = src->vmg_file->tt_srpt;
+
+ src->title = src->uri_title - 1;
+ src->chapter = src->uri_chapter - 1;
+ src->angle = src->uri_angle - 1;
+
+ if (!gst_dvd_read_src_goto_title (src, src->title, src->angle))
+ goto title_open_failed;
+
+ if (!gst_dvd_read_src_goto_chapter (src, src->chapter))
+ goto chapter_open_failed;
+
+ src->new_seek = FALSE;
+ src->change_cell = TRUE;
+
+ return TRUE;
+
+ /* ERRORS */
+open_failed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Could not open DVD")),
+ ("DVDOpen(%s) failed: %s", src->location, g_strerror (errno)));
+ return FALSE;
+ }
+ifo_open_failed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Could not open DVD")),
+ ("ifoOpen() failed: %s", g_strerror (errno)));
+ return FALSE;
+ }
+title_open_failed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Could not open DVD title %d"), src->uri_title), (NULL));
+ return FALSE;
+ }
+chapter_open_failed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Failed to go to chapter %d of DVD title %d"),
+ src->uri_chapter, src->uri_title), (NULL));
+ return FALSE;
+ }
+}
+
+static gboolean
+gst_dvd_read_src_stop (GstBaseSrc * basesrc)
+{
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
+
+ if (src->vts_file) {
+ ifoClose (src->vts_file);
+ src->vts_file = NULL;
+ }
+ if (src->vmg_file) {
+ ifoClose (src->vmg_file);
+ src->vmg_file = NULL;
+ }
+ if (src->dvd_title) {
+ DVDCloseFile (src->dvd_title);
+ src->dvd_title = NULL;
+ }
+ if (src->dvd) {
+ DVDClose (src->dvd);
+ src->dvd = NULL;
+ }
+ src->new_cell = TRUE;
+ src->new_seek = TRUE;
+ src->change_cell = FALSE;
+ src->chapter = 0;
+ src->title = 0;
+ src->need_newsegment = TRUE;
+ src->vts_tmapt = NULL;
+ if (src->title_lang_event_pending) {
+ gst_event_unref (src->title_lang_event_pending);
+ src->title_lang_event_pending = NULL;
+ }
+ if (src->pending_clut_event) {
+ gst_event_unref (src->pending_clut_event);
+ src->pending_clut_event = NULL;
+ }
+ if (src->chapter_starts) {
+ g_free (src->chapter_starts);
+ src->chapter_starts = NULL;
+ }
+
+ GST_LOG_OBJECT (src, "closed DVD");
+
+ return TRUE;
}
static void
-dvdreadsrc_init (DVDReadSrc * dvdreadsrc)
+cur_title_get_chapter_pgc (GstDvdReadSrc * src, gint chapter, gint * p_pgn,
+ gint * p_pgc_id, pgc_t ** p_pgc)
{
- dvdreadsrc->priv = g_new (DVDReadSrcPrivate, 1);
- dvdreadsrc->priv->srcpad = gst_pad_new ("src", GST_PAD_SRC);
- gst_pad_set_get_function (dvdreadsrc->priv->srcpad, dvdreadsrc_get);
- gst_element_add_pad (GST_ELEMENT (dvdreadsrc), dvdreadsrc->priv->srcpad);
+ pgc_t *pgc;
+ gint pgn, pgc_id;
- dvdreadsrc->priv->location = g_strdup ("/dev/dvd");
- dvdreadsrc->priv->new_seek = TRUE;
- dvdreadsrc->priv->new_cell = TRUE;
- dvdreadsrc->priv->title = 0;
- dvdreadsrc->priv->chapter = 0;
- dvdreadsrc->priv->angle = 0;
+ g_assert (chapter >= 0 && chapter < src->num_chapters);
+
+ pgc_id = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgcn;
+ pgn = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter].pgn;
+ pgc = src->vts_file->vts_pgcit->pgci_srp[pgc_id - 1].pgc;
+
+ *p_pgn = pgn;
+ *p_pgc_id = pgc_id;
+ *p_pgc = pgc;
}
static void
-dvdreadsrc_finalize (GObject * object)
+cur_title_get_chapter_bounds (GstDvdReadSrc * src, gint chapter,
+ gint * p_first_cell, gint * p_last_cell)
{
- DVDReadSrc *dvdreadsrc = DVDREADSRC (object);
+ pgc_t *pgc;
+ gint pgn, pgc_id, pgn_next_ch;
+
+ g_assert (chapter >= 0 && chapter < src->num_chapters);
+
+ cur_title_get_chapter_pgc (src, chapter, &pgn, &pgc_id, &pgc);
- if (dvdreadsrc->priv) {
- g_free (dvdreadsrc->priv->location);
- g_free (dvdreadsrc->priv);
- dvdreadsrc->priv = NULL;
+ *p_first_cell = pgc->program_map[pgn - 1] - 1;
+
+ if (chapter == (src->num_chapters - 1)) {
+ *p_last_cell = pgc->nr_of_cells;
+ } else {
+ pgn_next_ch = src->vts_ptt_srpt->title[src->ttn - 1].ptt[chapter + 1].pgn;
+ *p_last_cell = pgc->program_map[pgn_next_ch - 1] - 1;
}
- G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gboolean
+gst_dvd_read_src_goto_chapter (GstDvdReadSrc * src, gint chapter)
+{
+ gint i;
+
+ /* make sure the chapter number is valid for this title */
+ if (chapter < 0 || chapter >= src->num_chapters) {
+ GST_WARNING_OBJECT (src, "invalid chapter %d (only %d available)",
+ chapter, src->num_chapters);
+ chapter = CLAMP (chapter, 0, src->num_chapters - 1);
+ }
+
+ /* determine which program chain we want to watch. This is
+ * based on the chapter number */
+ cur_title_get_chapter_pgc (src, chapter, &src->pgn, &src->pgc_id,
+ &src->cur_pgc);
+ cur_title_get_chapter_bounds (src, chapter, &src->start_cell,
+ &src->last_cell);
+
+ GST_LOG_OBJECT (src, "Opened chapter %d - cell %d-%d", chapter + 1,
+ src->start_cell, src->last_cell);
+
+ /* retrieve position */
+ src->cur_pack = 0;
+ for (i = 0; i < chapter; i++) {
+ gint c1, c2;
+
+ cur_title_get_chapter_bounds (src, i, &c1, &c2);
+
+ while (c1 < c2) {
+ src->cur_pack +=
+ src->cur_pgc->cell_playback[c1].last_sector -
+ src->cur_pgc->cell_playback[c1].first_sector;
+ ++c1;
+ }
+ }
+
+ /* prepare reading for new cell */
+ src->new_cell = TRUE;
+ src->next_cell = src->start_cell;
+
+ src->chapter = chapter;
+
+ if (src->pending_clut_event)
+ gst_event_unref (src->pending_clut_event);
+
+ src->pending_clut_event =
+ gst_dvd_read_src_make_clut_change_event (src, src->cur_pgc->palette);
+
+ return TRUE;
}
static void
-dvdreadsrc_set_property (GObject * object, guint prop_id, const GValue * value,
- GParamSpec * pspec)
+gst_dvd_read_src_get_chapter_starts (GstDvdReadSrc * src)
+{
+ GstClockTime uptohere;
+ guint c;
+
+ g_free (src->chapter_starts);
+ src->chapter_starts = g_new (GstClockTime, src->num_chapters);
+
+ uptohere = (GstClockTime) 0;
+ for (c = 0; c < src->num_chapters; ++c) {
+ GstClockTime chapter_duration = 0;
+ gint cell_start, cell_end, cell;
+ gint pgn, pgc_id;
+ pgc_t *pgc;
+
+ cur_title_get_chapter_pgc (src, c, &pgn, &pgc_id, &pgc);
+ cur_title_get_chapter_bounds (src, c, &cell_start, &cell_end);
+
+ cell = cell_start;
+ while (cell < cell_end) {
+ dvd_time_t *cell_duration;
+
+ cell_duration = &pgc->cell_playback[cell].playback_time;
+ chapter_duration += gst_dvd_read_src_convert_timecode (cell_duration);
+ cell = gst_dvd_read_src_get_next_cell (src, pgc, cell);
+ }
+
+ src->chapter_starts[c] = uptohere;
+
+ GST_INFO_OBJECT (src, "[%02u] Chapter %02u starts at %" GST_TIME_FORMAT
+ ", dur = %" GST_TIME_FORMAT ", cells %d-%d", src->title + 1, c + 1,
+ GST_TIME_ARGS (uptohere), GST_TIME_ARGS (chapter_duration),
+ cell_start, cell_end);
+
+ uptohere += chapter_duration;
+ }
+}
+
+static gboolean
+gst_dvd_read_src_goto_title (GstDvdReadSrc * src, gint title, gint angle)
+{
+ GstStructure *s;
+ gchar lang_code[3] = { '\0', '\0', '\0' }, *t;
+ pgc_t *pgc0;
+ gint title_set_nr;
+ gint num_titles;
+ gint pgn0, pgc0_id;
+ gint i;
+
+ /* make sure our title number is valid */
+ num_titles = src->tt_srpt->nr_of_srpts;
+ GST_INFO_OBJECT (src, "There are %d titles on this DVD", num_titles);
+ if (title < 0 || title >= num_titles)
+ goto invalid_title;
+
+ src->num_chapters = src->tt_srpt->title[title].nr_of_ptts;
+ GST_INFO_OBJECT (src, "Title %d has %d chapters", title + 1,
+ src->num_chapters);
+
+ /* make sure the angle number is valid for this title */
+ src->num_angles = src->tt_srpt->title[title].nr_of_angles;
+ GST_LOG_OBJECT (src, "Title %d has %d angles", title + 1, src->num_angles);
+ if (angle < 0 || angle >= src->num_angles) {
+ GST_WARNING_OBJECT (src, "Invalid angle %d (only %d available)",
+ angle, src->num_angles);
+ angle = CLAMP (angle, 0, src->num_angles - 1);
+ }
+
+ /* load the VTS information for the title set our title is in */
+ title_set_nr = src->tt_srpt->title[title].title_set_nr;
+ src->vts_file = ifoOpen (src->dvd, title_set_nr);
+ if (src->vts_file == NULL)
+ goto ifo_open_failed;
+
+ src->ttn = src->tt_srpt->title[title].vts_ttn;
+ src->vts_ptt_srpt = src->vts_file->vts_ptt_srpt;
+
+ /* interactive title? */
+ if (src->num_chapters > 0 &&
+ src->vts_ptt_srpt->title[src->ttn - 1].ptt[0].pgn == 0) {
+ goto commands_only_pgc;
+ }
+
+ /* we've got enough info, time to open the title set data */
+ src->dvd_title = DVDOpenFile (src->dvd, title_set_nr, DVD_READ_TITLE_VOBS);
+ if (src->dvd_title == NULL)
+ goto title_open_failed;
+
+ GST_INFO_OBJECT (src, "Opened title %d, angle %d", title + 1, angle);
+ src->title = title;
+ src->angle = angle;
+
+ /* build event */
+
+ if (src->title_lang_event_pending) {
+ gst_event_unref (src->title_lang_event_pending);
+ src->title_lang_event_pending = NULL;
+ }
+
+ s = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-lang-codes", NULL);
+
+ /* so we can filter out invalid/unused streams (same for all chapters) */
+ cur_title_get_chapter_pgc (src, 0, &pgn0, &pgc0_id, &pgc0);
+
+ /* audio */
+ for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_audio_streams; i++) {
+ const audio_attr_t *a;
+
+ /* audio stream present? */
+ if (pgc0 != NULL && (pgc0->audio_control[i] & 0x8000) == 0)
+ continue;
+
+ a = &src->vts_file->vtsi_mat->vts_audio_attr[i];
+
+ t = g_strdup_printf ("audio-%d-format", i);
+ gst_structure_set (s, t, G_TYPE_INT, (int) a->audio_format, NULL);
+ g_free (t);
+
+ if (a->lang_type) {
+ t = g_strdup_printf ("audio-%d-language", i);
+ lang_code[0] = (a->lang_code >> 8) & 0xff;
+ lang_code[1] = a->lang_code & 0xff;
+ gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
+ g_free (t);
+ } else {
+ lang_code[0] = '\0';
+ }
+
+ GST_INFO_OBJECT (src, "[%02d] Audio %02d: lang='%s', format=%d",
+ src->title + 1, i, lang_code, (gint) a->audio_format);
+ }
+
+ /* subtitle */
+ for (i = 0; i < src->vts_file->vtsi_mat->nr_of_vts_subp_streams; i++) {
+ const subp_attr_t *u;
+
+ /* subpicture stream present? */
+ if (pgc0 != NULL && (pgc0->subp_control[i] & 0x80000000) == 0)
+ continue;
+
+ u = &src->vts_file->vtsi_mat->vts_subp_attr[i];
+
+ if (u->type) {
+ t = g_strdup_printf ("subtitle-%d-language", i);
+ lang_code[0] = (u->lang_code >> 8) & 0xff;
+ lang_code[1] = u->lang_code & 0xff;
+ gst_structure_set (s, t, G_TYPE_STRING, lang_code, NULL);
+ g_free (t);
+ } else {
+ lang_code[0] = '\0';
+ }
+
+ GST_INFO_OBJECT (src, "[%02d] Subtitle %02d: lang='%s', type=%d",
+ src->title + 1, i, lang_code, u->type);
+ }
+
+ src->title_lang_event_pending =
+ gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
+
+ /* dump seek tables */
+ src->vts_tmapt = src->vts_file->vts_tmapt;
+ if (src->vts_tmapt) {
+ gint i, j;
+
+ GST_LOG_OBJECT (src, "nr_of_tmaps = %d", src->vts_tmapt->nr_of_tmaps);
+ for (i = 0; i < src->vts_tmapt->nr_of_tmaps; ++i) {
+ GST_LOG_OBJECT (src, "======= Table %d ===================", i);
+ GST_LOG_OBJECT (src, "Offset relative to VTS_TMAPTI: %d",
+ src->vts_tmapt->tmap_offset[i]);
+ GST_LOG_OBJECT (src, "Time unit (seconds) : %d",
+ src->vts_tmapt->tmap[i].tmu);
+ GST_LOG_OBJECT (src, "Number of entries : %d",
+ src->vts_tmapt->tmap[i].nr_of_entries);
+ for (j = 0; j < src->vts_tmapt->tmap[i].nr_of_entries; j++) {
+ guint64 time;
+
+ time = src->vts_tmapt->tmap[i].tmu * (j + 1) * GST_SECOND;
+ GST_LOG_OBJECT (src, "Time: %" GST_TIME_FORMAT " VOBU "
+ "Sector: 0x%08x %s", GST_TIME_ARGS (time),
+ src->vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff,
+ (src->vts_tmapt->tmap[i].map_ent[j] >> 31) ? "discontinuity" : "");
+ }
+ }
+ } else {
+ GST_WARNING_OBJECT (src, "no vts_tmapt - seeking will suck");
+ }
+
+ gst_dvd_read_src_get_chapter_starts (src);
+
+ return TRUE;
+
+ /* ERRORS */
+invalid_title:
+ {
+ GST_WARNING_OBJECT (src, "Invalid title %d (only %d available)",
+ title, num_titles);
+ return FALSE;
+ }
+ifo_open_failed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Could not open DVD title %d"), title_set_nr),
+ ("ifoOpen(%d) failed: %s", title_set_nr, g_strerror (errno)));
+ return FALSE;
+ }
+title_open_failed:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Could not open DVD title %d"), title_set_nr),
+ ("Can't open title VOBS (VTS_%02d_1.VOB)", title_set_nr));
+ return FALSE;
+ }
+commands_only_pgc:
+ {
+ GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ,
+ (_("Could not open DVD title %d. Interactive titles are not supported "
+ "by this element"), title_set_nr),
+ ("Commands-only PGC, not supported, use rsndvdbin"));
+ return FALSE;
+ }
+}
+
+/* FIXME: double-check this function, compare against original */
+static gint
+gst_dvd_read_src_get_next_cell (GstDvdReadSrc * src, pgc_t * pgc, gint cell)
+{
+ /* Check if we're entering an angle block. */
+ if (pgc->cell_playback[cell].block_type != BLOCK_TYPE_ANGLE_BLOCK)
+ return (cell + 1);
+
+ while (pgc->cell_playback[cell].block_mode != BLOCK_MODE_LAST_CELL)
+ ++cell;
+
+ return cell + 1;
+}
+
+/* Returns true if the pack is a NAV pack */
+static gboolean
+gst_dvd_read_src_is_nav_pack (const guint8 * data, gint lbn, dsi_t * dsi_pack)
+{
+ if (GST_READ_UINT32_BE (data + 0x26) != 0x000001BF)
+ return FALSE;
+
+ /* Check that this is substream 0 (PCI) */
+ if (data[0x2c] != 0)
+ return FALSE;
+
+ if (GST_READ_UINT32_BE (data + 0x400) != 0x000001BF)
+ return FALSE;
+
+ /* Check that this is substream 1 (DSI) */
+ if (data[0x406] != 1)
+ return FALSE;
+
+ /* Check sizes of PCI and DSI packets */
+ if (GST_READ_UINT16_BE (data + 0x2a) != 0x03d4)
+ return FALSE;
+
+ if (GST_READ_UINT16_BE (data + 0x404) != 0x03fa)
+ return FALSE;
+
+ /* Read the DSI packet into the provided struct and check it */
+ navRead_DSI (dsi_pack, (unsigned char *) data + DSI_START_BYTE);
+ if (lbn != dsi_pack->dsi_gi.nv_pck_lbn)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* find time for sector from index, returns NONE if there is no exact match */
+static GstClockTime
+gst_dvd_read_src_get_time_for_sector (GstDvdReadSrc * src, guint sector)
+{
+ gint i, j;
+
+ if (src->vts_tmapt == NULL || src->vts_tmapt->nr_of_tmaps == 0)
+ return GST_CLOCK_TIME_NONE;
+
+ for (i = 0; i < src->vts_tmapt->nr_of_tmaps; ++i) {
+ for (j = 0; j < src->vts_tmapt->tmap[i].nr_of_entries; ++j) {
+ if ((src->vts_tmapt->tmap[i].map_ent[j] & 0x7fffffff) == sector)
+ return src->vts_tmapt->tmap[i].tmu * (j + 1) * GST_SECOND;
+ }
+ }
+
+ if (sector == 0)
+ return (GstClockTime) 0;
+
+ return GST_CLOCK_TIME_NONE;
+}
+
+/* returns the sector in the index at (or before) the given time, or -1 */
+static gint
+gst_dvd_read_src_get_sector_from_time (GstDvdReadSrc * src, GstClockTime ts)
+{
+ gint sector, j;
+
+ if (src->vts_tmapt == NULL || src->vts_tmapt->nr_of_tmaps < src->ttn)
+ return -1;
+
+ sector = 0;
+ for (j = 0; j < src->vts_tmapt->tmap[src->ttn - 1].nr_of_entries; ++j) {
+ GstClockTime entry_time;
+
+ entry_time = src->vts_tmapt->tmap[src->ttn - 1].tmu * (j + 1) * GST_SECOND;
+ if (entry_time <= ts) {
+ sector = src->vts_tmapt->tmap[src->ttn - 1].map_ent[j] & 0x7fffffff;
+ }
+ if (entry_time >= ts) {
+ return sector;
+ }
+ }
+
+ if (ts == 0)
+ return 0;
+
+ return -1;
+}
+
+typedef enum
+{
+ GST_DVD_READ_OK = 0,
+ GST_DVD_READ_ERROR = -1,
+ GST_DVD_READ_EOS = -2,
+ GST_DVD_READ_AGAIN = -3
+} GstDvdReadReturn;
+
+static GstDvdReadReturn
+gst_dvd_read_src_read (GstDvdReadSrc * src, gint angle, gint new_seek,
+ GstBuffer ** p_buf)
+{
+ GstBuffer *buf;
+ GstSegment *seg;
+ guint8 oneblock[DVD_VIDEO_LB_LEN];
+ dsi_t dsi_pack;
+ guint next_vobu, cur_output_size;
+ gint len;
+ gint retries;
+ gint64 next_time;
+
+ seg = &(GST_BASE_SRC (src)->segment);
+
+ /* playback by cell in this pgc, starting at the cell for our chapter */
+ if (new_seek)
+ src->cur_cell = src->start_cell;
+
+again:
+
+ if (src->cur_cell >= src->last_cell) {
+ /* advance to next chapter */
+ if (src->chapter == (src->num_chapters - 1) ||
+ (seg->format == chapter_format && seg->stop != -1 &&
+ src->chapter == (seg->stop - 1))) {
+ GST_DEBUG_OBJECT (src, "end of chapter segment");
+ goto eos;
+ }
+
+ GST_INFO_OBJECT (src, "end of chapter %d, switch to next",
+ src->chapter + 1);
+
+ ++src->chapter;
+ gst_dvd_read_src_goto_chapter (src, src->chapter);
+
+ return GST_DVD_READ_AGAIN;
+ }
+
+ if (src->new_cell || new_seek) {
+ if (!new_seek) {
+ src->cur_cell = src->next_cell;
+ if (src->cur_cell >= src->last_cell) {
+ GST_LOG_OBJECT (src, "last cell in chapter");
+ goto again;
+ }
+ }
+
+ /* take angle into account */
+ if (src->cur_pgc->cell_playback[src->cur_cell].block_type
+ == BLOCK_TYPE_ANGLE_BLOCK)
+ src->cur_cell += angle;
+
+ /* calculate next cell */
+ src->next_cell =
+ gst_dvd_read_src_get_next_cell (src, src->cur_pgc, src->cur_cell);
+
+ /* we loop until we're out of this cell */
+ src->cur_pack = src->cur_pgc->cell_playback[src->cur_cell].first_sector;
+ src->new_cell = FALSE;
+ GST_DEBUG_OBJECT (src, "Starting new cell %d @ pack %d", src->cur_cell,
+ src->cur_pack);
+ }
+
+ if (src->cur_pack >= src->cur_pgc->cell_playback[src->cur_cell].last_sector) {
+ src->new_cell = TRUE;
+ GST_LOG_OBJECT (src, "Beyond last sector for cell %d, going to next cell",
+ src->cur_cell);
+ return GST_DVD_READ_AGAIN;
+ }
+
+ /* read NAV packet */
+ retries = 0;
+nav_retry:
+ retries++;
+
+ len = DVDReadBlocks (src->dvd_title, src->cur_pack, 1, oneblock);
+ if (len != 1)
+ goto read_error;
+
+ if (!gst_dvd_read_src_is_nav_pack (oneblock, src->cur_pack, &dsi_pack)) {
+ GST_LOG_OBJECT (src, "Skipping nav packet @ pack %d", src->cur_pack);
+ src->cur_pack++;
+
+ if (retries < 2000) {
+ goto nav_retry;
+ } else {
+ GST_LOG_OBJECT (src, "No nav packet @ pack %d after 2000 blocks",
+ src->cur_pack);
+ goto read_error;
+ }
+ }
+
+ /* determine where we go next. These values are the ones we
+ * mostly care about */
+ cur_output_size = dsi_pack.dsi_gi.vobu_ea + 1;
+
+ /* If we're not at the end of this cell, we can determine the next
+ * VOBU to display using the VOBU_SRI information section of the
+ * DSI. Using this value correctly follows the current angle,
+ * avoiding the doubled scenes in The Matrix, and makes our life
+ * really happy.
+ *
+ * Otherwise, we set our next address past the end of this cell to
+ * force the code above to go to the next cell in the program. */
+ if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
+ next_vobu = src->cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
+ } else {
+ next_vobu = src->cur_pgc->cell_playback[src->cur_cell].last_sector + 1;
+ }
+
+ g_assert (cur_output_size < 1024);
+
+ /* create the buffer (TODO: use buffer pool?) */
+ buf = gst_buffer_new_and_alloc (cur_output_size * DVD_VIDEO_LB_LEN);
+
+ GST_LOG_OBJECT (src, "Going to read %u sectors @ pack %d", cur_output_size,
+ src->cur_pack);
+
+ /* read in and output cursize packs */
+ len = DVDReadBlocks (src->dvd_title, src->cur_pack, cur_output_size,
+ GST_BUFFER_DATA (buf));
+
+ if (len != cur_output_size)
+ goto block_read_error;
+
+ GST_BUFFER_SIZE (buf) = cur_output_size * DVD_VIDEO_LB_LEN;
+ /* GST_BUFFER_OFFSET (buf) = priv->cur_pack * DVD_VIDEO_LB_LEN; */
+ GST_BUFFER_TIMESTAMP (buf) =
+ gst_dvd_read_src_get_time_for_sector (src, src->cur_pack);
+
+ gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src)));
+
+ *p_buf = buf;
+
+ GST_LOG_OBJECT (src, "Read %u sectors", cur_output_size);
+
+ src->cur_pack = next_vobu;
+
+ next_time = GST_BUFFER_TIMESTAMP (buf);
+ if (GST_CLOCK_TIME_IS_VALID (next_time) && seg->format == GST_FORMAT_TIME &&
+ GST_CLOCK_TIME_IS_VALID (seg->stop) &&
+ next_time > seg->stop + 5 * GST_SECOND) {
+ GST_DEBUG_OBJECT (src, "end of TIME segment");
+ goto eos;
+ }
+
+ return GST_DVD_READ_OK;
+
+ /* ERRORS */
+eos:
+ {
+ GST_INFO_OBJECT (src, "Reached end-of-segment/stream - EOS");
+ return GST_DVD_READ_EOS;
+ }
+read_error:
+ {
+ GST_ERROR_OBJECT (src, "Read failed for block %d", src->cur_pack);
+ return GST_DVD_READ_ERROR;
+ }
+block_read_error:
+ {
+ GST_ERROR_OBJECT (src, "Read failed for %d blocks at %d",
+ cur_output_size, src->cur_pack);
+ gst_buffer_unref (buf);
+ return GST_DVD_READ_ERROR;
+ }
+}
+
+/* we don't cache the result on purpose */
+static gboolean
+gst_dvd_read_descrambler_available (void)
{
- DVDReadSrc *src;
- DVDReadSrcPrivate *priv;
+ GModule *module;
+ gpointer sym;
+ gsize res;
+
+ module = g_module_open ("libdvdcss", 0);
+ if (module != NULL) {
+ res = g_module_symbol (module, "dvdcss_open", &sym);
+ g_module_close (module);
+ } else {
+ res = FALSE;
+ }
+
+ return res;
+}
+
+static GstFlowReturn
+gst_dvd_read_src_create (GstPushSrc * pushsrc, GstBuffer ** p_buf)
+{
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (pushsrc);
+ GstPad *srcpad;
+ gint res;
- /* it's not null if we got it, but it might not be ours */
- g_return_if_fail (GST_IS_DVDREADSRC (object));
+ g_return_val_if_fail (src->dvd != NULL, GST_FLOW_ERROR);
- src = DVDREADSRC (object);
- priv = src->priv;
+ srcpad = GST_BASE_SRC (src)->srcpad;
+
+ if (src->need_newsegment) {
+ gst_pad_push_event (srcpad,
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
+ (gint64) src->cur_pack * DVD_VIDEO_LB_LEN, -1, 0));
+ src->need_newsegment = FALSE;
+ }
+
+ if (src->new_seek) {
+ gst_dvd_read_src_goto_title (src, src->title, src->angle);
+ gst_dvd_read_src_goto_chapter (src, src->chapter);
+
+ src->new_seek = FALSE;
+ src->change_cell = TRUE;
+ }
+
+ if (src->title_lang_event_pending) {
+ gst_pad_push_event (srcpad, src->title_lang_event_pending);
+ src->title_lang_event_pending = NULL;
+ }
+
+ if (src->pending_clut_event) {
+ gst_pad_push_event (srcpad, src->pending_clut_event);
+ src->pending_clut_event = NULL;
+ }
+
+ /* read it in */
+ do {
+ res = gst_dvd_read_src_read (src, src->angle, src->change_cell, p_buf);
+ } while (res == GST_DVD_READ_AGAIN);
+
+ switch (res) {
+ case GST_DVD_READ_ERROR:{
+ /* FIXME: figure out a way to detect if scrambling is the problem */
+ if (!gst_dvd_read_descrambler_available ()) {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ,
+ (_("Could not read DVD. This may be because the DVD is encrypted "
+ "and a DVD decryption library is not installed.")), (NULL));
+ } else {
+ GST_ELEMENT_ERROR (src, RESOURCE, READ, (_("Could not read DVD.")),
+ (NULL));
+ }
+ return GST_FLOW_ERROR;
+ }
+ case GST_DVD_READ_EOS:{
+ return GST_FLOW_UNEXPECTED;
+ }
+ case GST_DVD_READ_OK:{
+ src->change_cell = FALSE;
+ return GST_FLOW_OK;
+ }
+ default:
+ break;
+ }
+
+ g_return_val_if_reached (GST_FLOW_UNEXPECTED);
+}
+
+static void
+gst_dvd_read_src_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
+ gboolean started;
+
+ GST_OBJECT_LOCK (src);
+ started = GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED);
switch (prop_id) {
- case ARG_LOCATION:
- /* the element must be stopped in order to do this */
- /*g_return_if_fail(!GST_FLAG_IS_SET(src,GST_STATE_RUNNING)); */
+ case ARG_DEVICE:{
+ if (started) {
+ g_warning ("%s: property '%s' needs to be set before the device is "
+ "opened", GST_ELEMENT_NAME (src), pspec->name);
+ break;;
+ }
- if (priv->location)
- g_free (priv->location);
+ g_free (src->location);
/* clear the filename if we get a NULL (is that possible?) */
- if (g_value_get_string (value) == NULL)
- priv->location = g_strdup ("/dev/dvd");
- /* otherwise set the new filename */
- else
- priv->location = g_strdup (g_value_get_string (value));
+ if (g_value_get_string (value) == NULL) {
+ src->location = g_strdup ("/dev/dvd");
+ } else {
+ src->location = g_strdup (g_value_get_string (value));
+ }
break;
+ }
case ARG_TITLE:
- priv->title = g_value_get_int (value);
- priv->new_seek = TRUE;
+ src->uri_title = g_value_get_int (value);
+ if (started) {
+ src->title = src->uri_title - 1;
+ src->new_seek = TRUE;
+ }
break;
case ARG_CHAPTER:
- priv->chapter = g_value_get_int (value);
- priv->new_seek = TRUE;
+ src->uri_chapter = g_value_get_int (value);
+ if (started) {
+ src->chapter = src->uri_chapter - 1;
+ src->new_seek = TRUE;
+ }
break;
case ARG_ANGLE:
- priv->angle = g_value_get_int (value);
+ src->uri_angle = g_value_get_int (value);
+ if (started) {
+ src->angle = src->uri_angle - 1;
+ }
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
+ GST_OBJECT_UNLOCK (src);
}
static void
-dvdreadsrc_get_property (GObject * object, guint prop_id, GValue * value,
+gst_dvd_read_src_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
- DVDReadSrc *src;
- DVDReadSrcPrivate *priv;
-
- /* it's not null if we got it, but it might not be ours */
- g_return_if_fail (GST_IS_DVDREADSRC (object));
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (object);
- src = DVDREADSRC (object);
- priv = src->priv;
+ GST_OBJECT_LOCK (src);
switch (prop_id) {
- case ARG_LOCATION:
- g_value_set_string (value, priv->location);
+ case ARG_DEVICE:
+ g_value_set_string (value, src->location);
break;
case ARG_TITLE:
- g_value_set_int (value, priv->title);
+ g_value_set_int (value, src->uri_title);
break;
case ARG_CHAPTER:
- g_value_set_int (value, priv->chapter);
+ g_value_set_int (value, src->uri_chapter);
break;
case ARG_ANGLE:
- g_value_set_int (value, priv->angle);
+ g_value_set_int (value, src->uri_angle);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
-}
-/**
- * Returns true if the pack is a NAV pack. This check is clearly insufficient,
- * and sometimes we incorrectly think that valid other packs are NAV packs. I
- * need to make this stronger.
- */
-static int
-is_nav_pack (unsigned char *buffer)
-{
- return (buffer[41] == 0xbf && buffer[1027] == 0xbf);
+ GST_OBJECT_UNLOCK (src);
}
-static int
-_open (DVDReadSrcPrivate * priv, const gchar * location)
+static gboolean
+gst_dvd_read_src_get_size (GstDvdReadSrc * src, gint64 * size)
{
- g_return_val_if_fail (priv != NULL, -1);
- g_return_val_if_fail (location != NULL, -1);
+ gboolean ret = FALSE;
- /**
- * Open the disc.
- */
- priv->dvd = DVDOpen (location);
- if (!priv->dvd) {
- GST_ERROR ("Couldn't open DVD: %s", location);
- return -1;
- }
+ if (src->dvd_title) {
+ gsize blocks;
+ blocks = DVDFileSize (src->dvd_title);
+ if (blocks >= 0) {
+ *size = (gint64) blocks *DVD_VIDEO_LB_LEN;
- /**
- * Load the video manager to find out the information about the titles on
- * this disc.
- */
- priv->vmg_file = ifoOpen (priv->dvd, 0);
- if (!priv->vmg_file) {
- GST_ERROR ("Can't open VMG info");
- DVDClose (priv->dvd);
- return -1;
+ ret = TRUE;
+ } else {
+ GST_WARNING_OBJECT (src, "DVDFileSize(%p) failed!", src->dvd_title);
+ }
}
- priv->tt_srpt = priv->vmg_file->tt_srpt;
- return 0;
+ return ret;
}
-static int
-_close (DVDReadSrcPrivate * priv)
-{
- ifoClose (priv->vts_file);
- ifoClose (priv->vmg_file);
- DVDCloseFile (priv->dvd_title);
- DVDClose (priv->dvd);
- return 0;
-}
+/*** Querying and seeking ***/
-static int
-_seek (DVDReadSrcPrivate * priv, int title, int chapter, int angle)
+static gboolean
+gst_dvd_read_src_handle_seek_event (GstDvdReadSrc * src, GstEvent * event)
{
- /**
- * Make sure our title number is valid.
- */
- GST_LOG ("There are %d titles on this DVD", priv->tt_srpt->nr_of_srpts);
- if (title < 0 || title >= priv->tt_srpt->nr_of_srpts) {
- GST_ERROR ("Invalid title %d (only %d available)",
- title, priv->tt_srpt->nr_of_srpts);
- ifoClose (priv->vmg_file);
- DVDClose (priv->dvd);
- return -1;
+ GstSeekFlags flags;
+ GstSeekType cur_type, end_type;
+ gint64 new_off, total;
+ GstFormat format;
+ GstPad *srcpad;
+ gboolean query_ok;
+ gdouble rate;
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &new_off,
+ &end_type, NULL);
+
+ if (rate <= 0.0) {
+ GST_DEBUG_OBJECT (src, "cannot do backwards playback yet");
+ return FALSE;
}
+ if (end_type != GST_SEEK_TYPE_NONE) {
+ if ((format != chapter_format && format != GST_FORMAT_TIME) ||
+ end_type != GST_SEEK_TYPE_SET) {
+ GST_DEBUG_OBJECT (src, "end seek type not supported");
+ return FALSE;
+ }
+ }
- /**
- * Make sure the chapter number is valid for this title.
- */
- GST_LOG ("There are %d chapters in this title",
- priv->tt_srpt->title[title].nr_of_ptts);
+ if (cur_type != GST_SEEK_TYPE_SET) {
+ GST_DEBUG_OBJECT (src, "only SEEK_TYPE_SET is supported");
+ return FALSE;
+ }
- if (chapter < 0 || chapter >= priv->tt_srpt->title[title].nr_of_ptts) {
- GST_ERROR ("Invalid chapter %d (only %d available)",
- chapter, priv->tt_srpt->title[title].nr_of_ptts);
- ifoClose (priv->vmg_file);
- DVDClose (priv->dvd);
- return -1;
+ if (format == angle_format) {
+ GST_OBJECT_LOCK (src);
+ if (new_off < 0 || new_off >= src->num_angles) {
+ GST_OBJECT_UNLOCK (src);
+ GST_DEBUG_OBJECT (src, "invalid angle %d, only %d available",
+ src->num_angles, src->num_angles);
+ return FALSE;
+ }
+ src->angle = (gint) new_off;
+ GST_OBJECT_UNLOCK (src);
+ GST_DEBUG_OBJECT (src, "switched to angle %d", (gint) new_off + 1);
+ return TRUE;
}
+ if (format != chapter_format && format != title_format &&
+ format != GST_FORMAT_BYTES && format != GST_FORMAT_TIME) {
+ GST_DEBUG_OBJECT (src, "unsupported seek format %d (%s)", format,
+ gst_format_get_name (format));
+ return FALSE;
+ }
- /**
- * Make sure the angle number is valid for this title.
- */
- GST_LOG ("There are %d angles available in this title",
- priv->tt_srpt->title[title].nr_of_angles);
- if (angle < 0 || angle >= priv->tt_srpt->title[title].nr_of_angles) {
- GST_ERROR ("Invalid angle %d (only %d available)",
- angle, priv->tt_srpt->title[title].nr_of_angles);
- ifoClose (priv->vmg_file);
- DVDClose (priv->dvd);
- return -1;
+ if (format == GST_FORMAT_BYTES) {
+ GST_DEBUG_OBJECT (src, "Requested seek to byte %" G_GUINT64_FORMAT,
+ new_off);
+ } else if (format == GST_FORMAT_TIME) {
+ GST_DEBUG_OBJECT (src, "Requested seek to time %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (new_off));
+ if (gst_dvd_read_src_get_sector_from_time (src, new_off) < 0) {
+ GST_DEBUG_OBJECT (src, "Can't find sector for requested time");
+ return FALSE;
+ }
}
+ srcpad = GST_BASE_SRC_PAD (src);
- /**
- * Load the VTS information for the title set our title is in.
- */
- priv->vts_file =
- ifoOpen (priv->dvd, priv->tt_srpt->title[title].title_set_nr);
- if (!priv->vts_file) {
- GST_ERROR ("Can't open the info file of title %d",
- priv->tt_srpt->title[title].title_set_nr);
- ifoClose (priv->vmg_file);
- DVDClose (priv->dvd);
- return -1;
+ /* check whether the seek looks reasonable (ie within possible range) */
+ if (format == GST_FORMAT_BYTES) {
+ GST_OBJECT_LOCK (src);
+ query_ok = gst_dvd_read_src_get_size (src, &total);
+ GST_OBJECT_UNLOCK (src);
+ } else {
+ query_ok = gst_pad_query_duration (srcpad, &format, &total);
}
+ if (!query_ok) {
+ GST_DEBUG_OBJECT (src, "Failed to query duration in format %s",
+ gst_format_get_name (format));
+ return FALSE;
+ }
- /**
- * Determine which program chain we want to watch. This is based on the
- * chapter number.
- */
- priv->ttn = priv->tt_srpt->title[title].vts_ttn;
- priv->vts_ptt_srpt = priv->vts_file->vts_ptt_srpt;
- priv->pgc_id = priv->vts_ptt_srpt->title[priv->ttn - 1].ptt[chapter].pgcn;
- priv->pgn = priv->vts_ptt_srpt->title[priv->ttn - 1].ptt[chapter].pgn;
- priv->cur_pgc = priv->vts_file->vts_pgcit->pgci_srp[priv->pgc_id - 1].pgc;
- priv->start_cell = priv->cur_pgc->program_map[priv->pgn - 1] - 1;
-
- /**
- * We've got enough info, time to open the title set data.
- */
- priv->dvd_title =
- DVDOpenFile (priv->dvd, priv->tt_srpt->title[title].title_set_nr,
- DVD_READ_TITLE_VOBS);
- if (!priv->dvd_title) {
- GST_ERROR ("Can't open title VOBS (VTS_%02d_1.VOB)",
- priv->tt_srpt->title[title].title_set_nr);
- ifoClose (priv->vts_file);
- ifoClose (priv->vmg_file);
- DVDClose (priv->dvd);
- return -1;
+ GST_DEBUG_OBJECT (src, "Total %s: %12" G_GINT64_FORMAT,
+ gst_format_get_name (format), total);
+ GST_DEBUG_OBJECT (src, "Seek to %s: %12" G_GINT64_FORMAT,
+ gst_format_get_name (format), new_off);
+
+ if (new_off >= total) {
+ GST_DEBUG_OBJECT (src, "Seek position out of range");
+ return FALSE;
}
- return 0;
+ /* set segment to seek format; this allows us to use the do_seek
+ * virtual function and let the base source handle all the tricky
+ * stuff for us. We don't use the segment internally anyway */
+ /* FIXME: can't take the stream lock here - what to do? */
+ GST_OBJECT_LOCK (src);
+ GST_BASE_SRC (src)->segment.format = format;
+ GST_BASE_SRC (src)->segment.start = 0;
+ GST_BASE_SRC (src)->segment.stop = total;
+ GST_BASE_SRC (src)->segment.duration = total;
+ GST_OBJECT_UNLOCK (src);
+
+ return GST_BASE_SRC_CLASS (parent_class)->event (GST_BASE_SRC (src), event);
}
-/*
- * Read function.
- * -1: error, -2: eos, -3: try again, 0: ok.
- */
-
-static int
-_read (DVDReadSrcPrivate * priv, int angle, int new_seek, GstBuffer * buf)
+static gboolean
+gst_dvd_read_src_do_seek (GstBaseSrc * basesrc, GstSegment * s)
{
- unsigned char *data;
-
- data = GST_BUFFER_DATA (buf);
+ GstDvdReadSrc *src;
- /**
- * Playback by cell in this pgc, starting at the cell for our chapter.
- */
- if (new_seek) {
- priv->next_cell = priv->start_cell;
- priv->cur_cell = priv->start_cell;
- }
+ src = GST_DVD_READ_SRC (basesrc);
- if (priv->cur_cell < priv->cur_pgc->nr_of_cells) {
- if (priv->new_cell)
- priv->cur_cell = priv->next_cell;
+ GST_DEBUG_OBJECT (src, "Seeking to %s: %12" G_GINT64_FORMAT,
+ gst_format_get_name (s->format), s->last_stop);
- /* Check if we're entering an angle block. */
- if (priv->cur_pgc->cell_playback[priv->cur_cell].block_type
- == BLOCK_TYPE_ANGLE_BLOCK) {
- int i;
+ if (s->format == sector_format || s->format == GST_FORMAT_BYTES
+ || s->format == GST_FORMAT_TIME) {
+ guint old;
- priv->cur_cell += angle;
- for (i = 0;; ++i) {
- if (priv->cur_pgc->cell_playback[priv->cur_cell + i].block_mode
- == BLOCK_MODE_LAST_CELL) {
- priv->next_cell = priv->cur_cell + i + 1;
- break;
- }
- }
- } else {
- priv->next_cell = priv->cur_cell + 1;
- }
+ old = src->cur_pack;
+ if (s->format == sector_format) {
+ src->cur_pack = s->last_stop;
+ } else if (s->format == GST_FORMAT_TIME) {
+ gint sector;
- /**
- * We loop until we're out of this cell.
- */
- if (priv->new_cell) {
- priv->cur_pack =
- priv->cur_pgc->cell_playback[priv->cur_cell].first_sector;
- priv->new_cell = FALSE;
- }
+ sector = gst_dvd_read_src_get_sector_from_time (src, s->last_stop);
- if (priv->cur_pack <
- priv->cur_pgc->cell_playback[priv->cur_cell].last_sector) {
- dsi_t dsi_pack;
- unsigned int next_vobu, next_ilvu_start, cur_output_size;
- int len;
+ GST_DEBUG_OBJECT (src, "Time %" GST_TIME_FORMAT " => sector %d",
+ GST_TIME_ARGS (s->last_stop), sector);
- /**
- * Read NAV packet.
- */
- len = DVDReadBlocks (priv->dvd_title, priv->cur_pack, 1, data);
- if (len == 0) {
- GST_ERROR ("Read failed for block %d", priv->cur_pack);
- return -1;
- }
- assert (is_nav_pack (data));
-
-
- /**
- * Parse the contained dsi packet.
- */
- navRead_DSI (&dsi_pack, &(data[DSI_START_BYTE]));
- assert (priv->cur_pack == dsi_pack.dsi_gi.nv_pck_lbn);
-
-
- /**
- * Determine where we go next. These values are the ones we mostly
- * care about.
- */
- next_ilvu_start = priv->cur_pack + dsi_pack.sml_agli.data[angle].address;
- cur_output_size = dsi_pack.dsi_gi.vobu_ea;
-
-
- /**
- * If we're not at the end of this cell, we can determine the next
- * VOBU to display using the VOBU_SRI information section of the
- * DSI. Using this value correctly follows the current angle,
- * avoiding the doubled scenes in The Matrix, and makes our life
- * really happy.
- *
- * Otherwise, we set our next address past the end of this cell to
- * force the code above to go to the next cell in the program.
- */
- if (dsi_pack.vobu_sri.next_vobu != SRI_END_OF_CELL) {
- next_vobu = priv->cur_pack + (dsi_pack.vobu_sri.next_vobu & 0x7fffffff);
- } else {
- next_vobu = priv->cur_pack + cur_output_size + 1;
- }
+ /* really shouldn't happen, we've checked this earlier ... */
+ g_return_val_if_fail (sector >= 0, FALSE);
- assert (cur_output_size < 1024);
- priv->cur_pack++;
-
- /**
- * Read in and output cursize packs.
- */
- len =
- DVDReadBlocks (priv->dvd_title, priv->cur_pack, cur_output_size,
- data);
- if (len != cur_output_size) {
- GST_ERROR ("Read failed for %d blocks at %d",
- cur_output_size, priv->cur_pack);
- return -1;
+ src->cur_pack = sector;
+ } else {
+ /* byte format */
+ src->cur_pack = s->last_stop / DVD_VIDEO_LB_LEN;
+ if (((gint64) src->cur_pack * DVD_VIDEO_LB_LEN) != s->last_stop) {
+ GST_LOG_OBJECT (src, "rounded down offset %" G_GINT64_FORMAT " => %"
+ G_GINT64_FORMAT, s->last_stop,
+ (gint64) src->cur_pack * DVD_VIDEO_LB_LEN);
}
+ }
- GST_BUFFER_SIZE (buf) = cur_output_size * DVD_VIDEO_LB_LEN;
- priv->cur_pack = next_vobu;
+ if (!gst_dvd_read_src_goto_sector (src, src->angle)) {
+ GST_DEBUG_OBJECT (src, "seek to sector 0x%08x failed", src->cur_pack);
+ src->cur_pack = old;
+ return FALSE;
+ }
- return 0;
- } else {
- priv->new_cell = TRUE;
+ GST_LOG_OBJECT (src, "seek to sector 0x%08x ok", src->cur_pack);
+ } else if (s->format == chapter_format) {
+ if (!gst_dvd_read_src_goto_chapter (src, (gint) s->last_stop)) {
+ GST_DEBUG_OBJECT (src, "seek to chapter %d failed",
+ (gint) s->last_stop + 1);
+ return FALSE;
}
+ GST_INFO_OBJECT (src, "seek to chapter %d ok", (gint) s->last_stop + 1);
+ src->chapter = s->last_stop;
+ } else if (s->format == title_format) {
+ if (!gst_dvd_read_src_goto_title (src, (gint) s->last_stop, src->angle) ||
+ !gst_dvd_read_src_goto_chapter (src, 0)) {
+ GST_DEBUG_OBJECT (src, "seek to title %d failed", (gint) s->last_stop);
+ return FALSE;
+ }
+ src->title = (gint) s->last_stop;
+ src->chapter = 0;
+ GST_INFO_OBJECT (src, "seek to title %d ok", src->title + 1);
} else {
- return -2;
+ g_return_val_if_reached (FALSE);
}
- /* again */
- return -3;
+ src->need_newsegment = TRUE;
+ return TRUE;
}
-static GstData *
-dvdreadsrc_get (GstPad * pad)
+static gboolean
+gst_dvd_read_src_src_event (GstBaseSrc * basesrc, GstEvent * event)
{
- gint res;
- DVDReadSrc *dvdreadsrc;
- DVDReadSrcPrivate *priv;
- GstBuffer *buf;
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
+ gboolean res;
- g_return_val_if_fail (pad != NULL, NULL);
- g_return_val_if_fail (GST_IS_PAD (pad), NULL);
+ GST_LOG_OBJECT (src, "handling %s event", GST_EVENT_TYPE_NAME (event));
- dvdreadsrc = DVDREADSRC (gst_pad_get_parent (pad));
- priv = dvdreadsrc->priv;
- g_return_val_if_fail (GST_FLAG_IS_SET (dvdreadsrc, DVDREADSRC_OPEN), NULL);
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:
+ res = gst_dvd_read_src_handle_seek_event (src, event);
+ break;
+ default:
+ res = GST_BASE_SRC_CLASS (parent_class)->event (basesrc, event);
+ break;
+ }
+
+ return res;
+}
- /* create the buffer */
- /* FIXME: should eventually use a bufferpool for this */
- buf = gst_buffer_new_and_alloc (1024 * DVD_VIDEO_LB_LEN);
+static GstEvent *
+gst_dvd_read_src_make_clut_change_event (GstDvdReadSrc * src,
+ const guint * clut)
+{
+ GstStructure *structure;
+ gchar name[16];
+ gint i;
- if (priv->new_seek) {
- _seek (priv, priv->title, priv->chapter, priv->angle);
+ structure = gst_structure_new ("application/x-gst-dvd",
+ "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL);
+
+ /* Create a separate field for each value in the table. */
+ for (i = 0; i < 16; i++) {
+ g_snprintf (name, sizeof (name), "clut%02d", i);
+ gst_structure_set (structure, name, G_TYPE_INT, (int) clut[i], NULL);
}
- /* read it in from the file */
- while ((res = _read (priv, priv->angle, priv->new_seek, buf)) == -3);
- switch (res) {
- case -1:
- GST_ELEMENT_ERROR (dvdreadsrc, RESOURCE, READ, (NULL), (NULL));
- gst_buffer_unref (buf);
- return NULL;
- case -2:
- gst_element_set_eos (GST_ELEMENT (dvdreadsrc));
- gst_buffer_unref (buf);
- return GST_DATA (gst_event_new (GST_EVENT_EOS));
- case 0:
+ /* Create the DVD event and put the structure into it. */
+ return gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, structure);
+}
+
+static gint64
+gst_dvd_read_src_convert_timecode (dvd_time_t * time)
+{
+ gint64 ret_time;
+ const gint64 one_hour = 3600 * GST_SECOND;
+ const gint64 one_min = 60 * GST_SECOND;
+
+ g_return_val_if_fail ((time->hour >> 4) < 0xa
+ && (time->hour & 0xf) < 0xa, -1);
+ g_return_val_if_fail ((time->minute >> 4) < 0x7
+ && (time->minute & 0xf) < 0xa, -1);
+ g_return_val_if_fail ((time->second >> 4) < 0x7
+ && (time->second & 0xf) < 0xa, -1);
+
+ ret_time = ((time->hour >> 4) * 10 + (time->hour & 0xf)) * one_hour;
+ ret_time += ((time->minute >> 4) * 10 + (time->minute & 0xf)) * one_min;
+ ret_time += ((time->second >> 4) * 10 + (time->second & 0xf)) * GST_SECOND;
+
+ return ret_time;
+}
+
+static gboolean
+gst_dvd_read_src_do_duration_query (GstDvdReadSrc * src, GstQuery * query)
+{
+ GstFormat format;
+ gint64 val;
+
+ gst_query_parse_duration (query, &format, NULL);
+
+ switch (format) {
+ case GST_FORMAT_TIME:{
+ if (src->cur_pgc == NULL)
+ return FALSE;
+ val = gst_dvd_read_src_convert_timecode (&src->cur_pgc->playback_time);
+ if (val < 0)
+ return FALSE;
break;
- default:
- g_assert_not_reached ();
+ }
+ case GST_FORMAT_BYTES:{
+ if (!gst_dvd_read_src_get_size (src, &val))
+ return FALSE;
+ break;
+ }
+ default:{
+ if (format == sector_format) {
+ val = DVDFileSize (src->dvd_title);
+ } else if (format == title_format) {
+ val = src->tt_srpt->nr_of_srpts;
+ } else if (format == chapter_format) {
+ val = src->num_chapters;
+ } else if (format == angle_format) {
+ val = src->tt_srpt->title[src->title].nr_of_angles;
+ } else {
+ GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
+ format, gst_format_get_name (format));
+ return FALSE;
+ }
+ break;
+ }
}
- priv->new_seek = FALSE;
+ GST_LOG_OBJECT (src, "duration = %" G_GINT64_FORMAT " %s", val,
+ gst_format_get_name (format));
- return GST_DATA (buf);
+ gst_query_set_duration (query, format, val);
+ return TRUE;
}
-/* open the file, necessary to go to RUNNING state */
static gboolean
-dvdreadsrc_open_file (DVDReadSrc * src)
+gst_dvd_read_src_do_position_query (GstDvdReadSrc * src, GstQuery * query)
{
- g_return_val_if_fail (src != NULL, FALSE);
- g_return_val_if_fail (GST_IS_DVDREADSRC (src), FALSE);
- g_return_val_if_fail (!GST_FLAG_IS_SET (src, DVDREADSRC_OPEN), FALSE);
+ GstFormat format;
+ gint64 val;
- if (_open (src->priv, src->priv->location)) {
- GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, (NULL), (NULL));
- return FALSE;
- }
- if (_seek (src->priv, src->priv->title, src->priv->chapter, src->priv->angle)) {
- GST_ELEMENT_ERROR (src, RESOURCE, SEEK, (NULL), (NULL));
- return FALSE;
+ gst_query_parse_position (query, &format, NULL);
+
+ switch (format) {
+ case GST_FORMAT_BYTES:{
+ val = (gint64) src->cur_pack * DVD_VIDEO_LB_LEN;
+ break;
+ }
+ default:{
+ if (format == sector_format) {
+ val = src->cur_pack;
+ } else if (format == title_format) {
+ val = src->title;
+ } else if (format == chapter_format) {
+ val = src->chapter;
+ } else if (format == angle_format) {
+ val = src->angle;
+ } else {
+ GST_DEBUG_OBJECT (src, "Don't know how to handle format %d (%s)",
+ format, gst_format_get_name (format));
+ return FALSE;
+ }
+ break;
+ }
}
- GST_FLAG_SET (src, DVDREADSRC_OPEN);
+ GST_LOG_OBJECT (src, "position = %" G_GINT64_FORMAT " %s", val,
+ gst_format_get_name (format));
+ gst_query_set_position (query, format, val);
return TRUE;
}
-/* close the file */
-static void
-dvdreadsrc_close_file (DVDReadSrc * src)
+static gboolean
+gst_dvd_read_src_do_convert_query (GstDvdReadSrc * src, GstQuery * query)
{
- g_return_if_fail (GST_FLAG_IS_SET (src, DVDREADSRC_OPEN));
+ GstFormat src_format, dest_format;
+ gboolean ret = FALSE;
+ gint64 src_val, dest_val = -1;
+
+ gst_query_parse_convert (query, &src_format, &src_val, &dest_format, NULL);
+
+ if (src_format == dest_format) {
+ dest_val = src_val;
+ ret = TRUE;
+ goto done;
+ }
+
+ /* Formats to consider: TIME, DEFAULT, BYTES, title, chapter, sector.
+ * Note: title and chapter are counted as starting from 0 here, just like
+ * in the context of seek events. Another note: DEFAULT format is undefined */
- _close (src->priv);
+ if (src_format == GST_FORMAT_BYTES) {
+ src_format = sector_format;
+ src_val /= DVD_VIDEO_LB_LEN;
+ }
+
+ if (src_format == sector_format) {
+ /* SECTOR => xyz */
+ if (dest_format == GST_FORMAT_TIME && src_val < G_MAXUINT) {
+ dest_val = gst_dvd_read_src_get_time_for_sector (src, (guint) src_val);
+ ret = (dest_val >= 0);
+ } else if (dest_format == GST_FORMAT_BYTES) {
+ dest_val = src_val * DVD_VIDEO_LB_LEN;
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ }
+ } else if (src_format == title_format) {
+ /* TITLE => xyz */
+ if (dest_format == GST_FORMAT_TIME) {
+ /* not really true, but we use this to trick the base source into
+ * handling seeks in title-format for us (the source won't know that
+ * we changed the title in this case) (changing titles should really
+ * be done with an interface rather than a seek, but for now we're
+ * stuck with this mechanism. Fix in 0.11) */
+ dest_val = (GstClockTime) 0;
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ }
+ } else if (src_format == chapter_format) {
+ /* CHAPTER => xyz */
+ if (dest_format == GST_FORMAT_TIME) {
+ if (src->num_chapters >= 0 && src_val < src->num_chapters) {
+ dest_val = src->chapter_starts[src_val];
+ ret = TRUE;
+ }
+ } else if (dest_format == sector_format) {
+ } else {
+ ret = FALSE;
+ }
+ } else if (src_format == GST_FORMAT_TIME) {
+ /* TIME => xyz */
+ if (dest_format == sector_format || dest_format == GST_FORMAT_BYTES) {
+ dest_val = gst_dvd_read_src_get_sector_from_time (src, src_val);
+ ret = (dest_val >= 0);
+ if (dest_format == GST_FORMAT_BYTES)
+ dest_val *= DVD_VIDEO_LB_LEN;
+ } else if (dest_format == chapter_format) {
+ if (src->chapter_starts != NULL) {
+ gint i;
+
+ for (i = src->num_chapters - 1; i >= 0; --i) {
+ if (src->chapter_starts && src->chapter_starts[i] >= src_val) {
+ dest_val = i;
+ ret = TRUE;
+ break;
+ }
+ }
+ } else {
+ ret = FALSE;
+ }
+ } else {
+ ret = FALSE;
+ }
+ } else {
+ ret = FALSE;
+ }
- GST_FLAG_UNSET (src, DVDREADSRC_OPEN);
+done:
+
+ if (ret) {
+ gst_query_set_convert (query, src_format, src_val, dest_format, dest_val);
+ }
+
+ return ret;
}
-static GstElementStateReturn
-dvdreadsrc_change_state (GstElement * element)
+static gboolean
+gst_dvd_read_src_src_query (GstBaseSrc * basesrc, GstQuery * query)
{
- DVDReadSrc *dvdreadsrc = DVDREADSRC (element);
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (basesrc);
+ gboolean started;
+ gboolean res = TRUE;
- g_return_val_if_fail (GST_IS_DVDREADSRC (element), GST_STATE_FAILURE);
+ GST_LOG_OBJECT (src, "handling %s query",
+ gst_query_type_get_name (GST_QUERY_TYPE (query)));
- GST_DEBUG ("gstdvdreadsrc: state pending %d", GST_STATE_PENDING (element));
+ GST_OBJECT_LOCK (src);
+ started = (GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED));
+ GST_OBJECT_UNLOCK (src);
- /* if going down into NULL state, close the file if it's open */
- switch (GST_STATE_TRANSITION (element)) {
- case GST_STATE_NULL_TO_READY:
- if (!dvdreadsrc_open_file (DVDREADSRC (element)))
- return GST_STATE_FAILURE;
+ if (!started) {
+ GST_DEBUG_OBJECT (src, "query failed: not started");
+ return FALSE;
+ }
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_DURATION:
+ GST_OBJECT_LOCK (src);
+ res = gst_dvd_read_src_do_duration_query (src, query);
+ GST_OBJECT_UNLOCK (src);
break;
- case GST_STATE_PAUSED_TO_READY:
- dvdreadsrc->priv->new_cell = TRUE;
- dvdreadsrc->priv->new_seek = TRUE;
- dvdreadsrc->priv->chapter = 0;
- dvdreadsrc->priv->title = 0;
+ case GST_QUERY_POSITION:
+ GST_OBJECT_LOCK (src);
+ res = gst_dvd_read_src_do_position_query (src, query);
+ GST_OBJECT_UNLOCK (src);
break;
- case GST_STATE_READY_TO_NULL:
- dvdreadsrc_close_file (DVDREADSRC (element));
+ case GST_QUERY_CONVERT:
+ GST_OBJECT_LOCK (src);
+ res = gst_dvd_read_src_do_convert_query (src, query);
+ GST_OBJECT_UNLOCK (src);
break;
default:
+ res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query);
break;
}
- /* if we haven't failed already, give the parent class a chance to ;-) */
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element);
+ return res;
+}
+
+static gboolean
+gst_dvd_read_src_goto_sector (GstDvdReadSrc * src, int angle)
+{
+ gint seek_to = src->cur_pack;
+ gint chapter, sectors, next, cur, i;
+
+ /* retrieve position */
+ src->cur_pack = 0;
+ for (i = 0; i < src->num_chapters; i++) {
+ gint c1, c2;
+
+ cur_title_get_chapter_bounds (src, i, &c1, &c2);
+
+ for (next = cur = c1; cur < c2;) {
+ if (next != cur) {
+ sectors =
+ src->cur_pgc->cell_playback[cur].last_sector -
+ src->cur_pgc->cell_playback[cur].first_sector;
+ if (src->cur_pack + sectors > seek_to) {
+ chapter = i;
+ goto done;
+ }
+ src->cur_pack += sectors;
+ }
+ cur = next;
+ if (src->cur_pgc->cell_playback[cur].block_type == BLOCK_TYPE_ANGLE_BLOCK)
+ cur += angle;
+ next = gst_dvd_read_src_get_next_cell (src, src->cur_pgc, cur);
+ }
+ }
+
+ GST_DEBUG_OBJECT (src, "Seek to sector %u failed", seek_to);
+
+ return FALSE;
+
+done:
+ {
+ /* so chapter $chapter and cell $cur contain our sector
+ * of interest. Let's go there! */
+ GST_INFO_OBJECT (src, "Seek succeeded, going to chapter %u, cell %u",
+ chapter + 1, cur);
+
+ gst_dvd_read_src_goto_chapter (src, chapter);
+ src->cur_cell = cur;
+ src->next_cell = next;
+ src->new_cell = FALSE;
+ src->cur_pack = seek_to;
+
+ return TRUE;
+ }
+}
+
+
+/*** URI interface ***/
+
+static GstURIType
+gst_dvd_read_src_uri_get_type (void)
+{
+ return GST_URI_SRC;
+}
+
+static gchar **
+gst_dvd_read_src_uri_get_protocols (void)
+{
+ static gchar *protocols[] = { (gchar *) "dvd", NULL };
+
+ return protocols;
+}
+
+static const gchar *
+gst_dvd_read_src_uri_get_uri (GstURIHandler * handler)
+{
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
+
+ GST_OBJECT_LOCK (src);
+
+ g_free (src->last_uri);
+ src->last_uri = g_strdup_printf ("dvd://%d,%d,%d", src->uri_title,
+ src->uri_chapter, src->uri_angle);
+
+ GST_OBJECT_UNLOCK (src);
+
+ return src->last_uri;
+}
+
+static gboolean
+gst_dvd_read_src_uri_set_uri (GstURIHandler * handler, const gchar * uri)
+{
+ GstDvdReadSrc *src = GST_DVD_READ_SRC (handler);
+ gboolean ret;
+ gchar *protocol;
+
+ protocol = gst_uri_get_protocol (uri);
+ ret = (protocol != NULL && g_str_equal (protocol, "dvd"));
+ g_free (protocol);
+ protocol = NULL;
+
+ if (!ret)
+ return ret;
+
+ /* parse out the new t/c/a and seek to them */
+ {
+ gchar *location = NULL;
+ gchar **strs;
+ gchar **strcur;
+ gint pos = 0;
+
+ location = gst_uri_get_location (uri);
+
+ if (!location)
+ return ret;
+
+ GST_OBJECT_LOCK (src);
+
+ src->uri_title = 1;
+ src->uri_chapter = 1;
+ src->uri_angle = 1;
+
+ strcur = strs = g_strsplit (location, ",", 0);
+ while (strcur && *strcur) {
+ gint val;
+
+ if (!sscanf (*strcur, "%d", &val))
+ break;
+
+ if (val <= 0) {
+ g_warning ("Invalid value %d in URI '%s'. Must be 1 or greater",
+ val, location);
+ break;
+ }
+
+ switch (pos) {
+ case 0:
+ src->uri_title = val;
+ break;
+ case 1:
+ src->uri_chapter = val;
+ break;
+ case 2:
+ src->uri_angle = val;
+ break;
+ }
+
+ strcur++;
+ pos++;
+ }
+
+ if (pos > 0 && GST_OBJECT_FLAG_IS_SET (src, GST_BASE_SRC_STARTED)) {
+ src->title = src->uri_title - 1;
+ src->chapter = src->uri_chapter - 1;
+ src->angle = src->uri_angle - 1;
+ src->new_seek = TRUE;
+ }
+
+ GST_OBJECT_UNLOCK (src);
+
+ g_strfreev (strs);
+ g_free (location);
+ }
+
+ return ret;
+}
+
+static void
+gst_dvd_read_src_uri_handler_init (gpointer g_iface, gpointer iface_data)
+{
+ GstURIHandlerInterface *iface = (GstURIHandlerInterface *) g_iface;
- return GST_STATE_SUCCESS;
+ iface->get_type = gst_dvd_read_src_uri_get_type;
+ iface->get_protocols = gst_dvd_read_src_uri_get_protocols;
+ iface->get_uri = gst_dvd_read_src_uri_get_uri;
+ iface->set_uri = gst_dvd_read_src_uri_set_uri;
+}
+
+static void
+gst_dvd_read_src_do_init (GType dvdreadsrc_type)
+{
+ static const GInterfaceInfo urihandler_info = {
+ gst_dvd_read_src_uri_handler_init,
+ NULL,
+ NULL
+ };
+
+ g_type_add_interface_static (dvdreadsrc_type, GST_TYPE_URI_HANDLER,
+ &urihandler_info);
+
+ title_format = gst_format_register ("title", "DVD title");
+ angle_format = gst_format_register ("angle", "DVD angle");
+ sector_format = gst_format_register ("sector", "DVD sector");
+ chapter_format = gst_format_register ("chapter", "DVD chapter");
}
static gboolean
plugin_init (GstPlugin * plugin)
{
- if (!gst_element_register (plugin, "dvdreadsrc", GST_RANK_NONE,
- GST_TYPE_DVDREADSRC))
+ GST_DEBUG_CATEGORY_INIT (gstgst_dvd_read_src_debug, "dvdreadsrc", 0,
+ "DVD reader element based on dvdreadsrc");
+
+#ifdef ENABLE_NLS
+ GST_DEBUG ("binding text domain %s to locale dir %s", GETTEXT_PACKAGE,
+ LOCALEDIR);
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+#endif /* ENABLE_NLS */
+
+ if (!gst_element_register (plugin, "dvdreadsrc", GST_RANK_SECONDARY,
+ GST_TYPE_DVD_READ_SRC)) {
return FALSE;
+ }
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
- "dvdreadsrc",
+ "dvdread",
"Access a DVD with dvdread",
- plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN)
+ plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);