summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog16
m---------common0
-rw-r--r--ext/resindvd/resindvdsrc.c492
-rw-r--r--ext/resindvd/resindvdsrc.h22
-rw-r--r--ext/resindvd/rsnbasesrc.c12
5 files changed, 449 insertions, 93 deletions
diff --git a/ChangeLog b/ChangeLog
index 4675746e..96c7b3e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2008-06-20 Jan Schmidt <jan.schmidt@sun.com>
+
+ * ext/resindvd/resindvdsrc.c:
+ * ext/resindvd/resindvdsrc.h:
+ Schedule NAV packets and activate them with an async clock callback
+ at the right moment. This makes delayed menu highlights appear
+ at the correct time and fixes Back To The Future.
+
+ When outputting new segment in do_seek(), calculate our position
+ value properly, so we report the right time when popping in and
+ out of the menus.
+
+ * ext/resindvd/rsnbasesrc.c:
+ When handling a non-flushing seek, accumulate the segment,
+ rather than having every seek start from 0 and messing with sync
+
2008-06-19 Stefan Kost <ensonic@users.sf.net>
* gst/selector/gstoutputselector.c:
diff --git a/common b/common
-Subproject 46ec7dfc1c09ff550ed6b7a4e0d3f2b2ac7d3ee
+Subproject d9cd98b46aebaf143dc43d8563a3bff650be6a7
diff --git a/ext/resindvd/resindvdsrc.c b/ext/resindvd/resindvdsrc.c
index 13fae757..ce733667 100644
--- a/ext/resindvd/resindvdsrc.c
+++ b/ext/resindvd/resindvdsrc.c
@@ -70,6 +70,13 @@ enum
ARG_DEVICE
};
+typedef struct
+{
+ GstBuffer *buffer;
+ GstClockTime ts;
+ GstClockTime running_ts;
+} RsnDvdPendingNav;
+
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
@@ -101,6 +108,8 @@ static gboolean rsn_dvdsrc_unlock_stop (RsnBaseSrc * bsrc);
static gboolean rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
GstSegment * segment);
static gboolean rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment);
+static GstStateChangeReturn
+rsn_dvdsrc_change_state (GstElement * element, GstStateChange transition);
static void rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src,
guint8 phys_stream, gboolean forced_only);
@@ -111,10 +120,42 @@ static void rsn_dvdsrc_prepare_clut_change_event (resinDvdSrc * src,
const guint32 * clut);
static void rsn_dvdsrc_update_highlight (resinDvdSrc * src);
+static void rsn_dvdsrc_enqueue_nav_block (resinDvdSrc * src,
+ GstBuffer * nav_buf, GstClockTime ts);
+static void rsn_dvdsrc_activate_nav_block (resinDvdSrc * src,
+ GstBuffer * nav_buf);
+static void rsn_dvdsrc_clear_nav_blocks (resinDvdSrc * src);
+static void rsn_dvdsrc_check_nav_blocks (resinDvdSrc * src);
+static void rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src,
+ RsnDvdPendingNav * next_nav);
+
static GstFlowReturn rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** buf);
static gboolean rsn_dvdsrc_src_event (RsnBaseSrc * basesrc, GstEvent * event);
static gboolean rsn_dvdsrc_src_query (RsnBaseSrc * basesrc, GstQuery * query);
+static GstClockTime ifotime_to_gsttime (dvd_time_t * ifo_time);
+
+static GstClockTime
+ifotime_to_gsttime (dvd_time_t * ifo_time)
+{
+ GstClockTime ts;
+ guint frames;
+
+ ts = 3600 * GST_SECOND * ifo_time->hour;
+ ts += 60 * GST_SECOND * ifo_time->minute;
+ ts += GST_SECOND * ifo_time->second;
+
+ frames = ((ifo_time->frame_u >> 4) & 0x3) * 10;
+ frames += (ifo_time->frame_u & 0xf);
+
+ if (ifo_time->frame_u & 0x80)
+ ts += GST_SECOND * frames / 30;
+ else
+ ts += GST_SECOND * frames / 25;
+
+ return ts;
+}
+
static void
rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type)
{
@@ -145,10 +186,12 @@ static void
rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
{
GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
RsnBaseSrcClass *gstbasesrc_class;
RsnPushSrcClass *gstpush_src_class;
gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
gstpush_src_class = GST_PUSH_SRC_CLASS (klass);
@@ -156,6 +199,8 @@ rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
gobject_class->set_property = rsn_dvdsrc_set_property;
gobject_class->get_property = rsn_dvdsrc_get_property;
+ gstelement_class->change_state = rsn_dvdsrc_change_state;
+
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (rsn_dvdsrc_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (rsn_dvdsrc_stop);
gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (rsn_dvdsrc_unlock);
@@ -293,6 +338,7 @@ rsn_dvdsrc_start (RsnBaseSrc * bsrc)
src->pgc_duration = GST_CLOCK_TIME_NONE;
src->cur_start_ts = GST_CLOCK_TIME_NONE;
src->cur_end_ts = GST_CLOCK_TIME_NONE;
+ src->cur_vobu_base_ts = GST_CLOCK_TIME_NONE;
src->vts_n = 0;
src->in_menu = FALSE;
@@ -377,6 +423,14 @@ rsn_dvdsrc_stop (RsnBaseSrc * bsrc)
g_mutex_lock (src->dvd_lock);
+ if (src->nav_clock_id) {
+ gst_clock_id_unschedule (src->nav_clock_id);
+ gst_clock_id_unref (src->nav_clock_id);
+ src->nav_clock_id = NULL;
+ }
+ rsn_dvdsrc_clear_nav_blocks (src);
+ src->have_pci = FALSE;
+
/* Clear any allocated output buffer */
gst_buffer_replace (&src->alloc_buf, NULL);
gst_buffer_replace (&src->next_buf, NULL);
@@ -435,6 +489,7 @@ static gboolean
rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
{
GstEvent *still_event;
+ GstEvent *hl_event;
GstStructure *s;
GstEvent *seg_event;
GstSegment *segment = &(GST_BASE_SRC (src)->segment);
@@ -448,18 +503,23 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
"still-state", G_TYPE_BOOLEAN, TRUE, NULL);
still_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s);
- segment->last_stop = src->cur_end_ts;
+ gst_segment_set_last_stop (segment, GST_FORMAT_TIME, src->cur_end_ts);
seg_event = gst_event_new_new_segment_full (TRUE,
segment->rate, segment->applied_rate, segment->format,
segment->start, segment->last_stop, segment->time);
+ /* Grab any pending highlight event to send too */
+ hl_event = src->highlight_event;
+ src->highlight_event = NULL;
+
/* Now, send the events. We need to drop the dvd lock while doing so,
- * and then check after if we got flushed
- */
+ * and then check after if we got flushed */
g_mutex_unlock (src->dvd_lock);
gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event);
gst_pad_push_event (GST_BASE_SRC_PAD (src), seg_event);
+ if (hl_event)
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
g_mutex_lock (src->dvd_lock);
g_mutex_lock (src->branch_lock);
@@ -507,7 +567,7 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
}
static GstFlowReturn
-rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
+rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock)
{
GstFlowReturn ret = GST_FLOW_OK;
dvdnav_status_t dvdnav_ret;
@@ -532,44 +592,65 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
switch (event) {
case DVDNAV_BLOCK_OK:
/* Data block that needs outputting */
- *outbuf = src->alloc_buf;
+ src->next_buf = src->alloc_buf;
+ src->next_is_nav_block = FALSE;
+ src->next_nav_ts = GST_CLOCK_TIME_NONE;
src->alloc_buf = NULL;
break;
- case DVDNAV_NAV_PACKET:{
+ case DVDNAV_NAV_PACKET:
+ {
pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
+ GstClockTime new_start_ptm = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm);
+ GstClockTime new_end_ptm = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm);
+ GstClockTimeDiff new_base_time = ifotime_to_gsttime (&pci->pci_gi.e_eltm);
+ gboolean discont = FALSE;
+
+ if (new_start_ptm != src->cur_end_ts)
+ discont = TRUE;
GST_LOG_OBJECT (src, "NAV packet start TS %" GST_TIME_FORMAT
- " end TS %" GST_TIME_FORMAT " %s",
- GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)),
- GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm)),
- (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) ?
- "discont" : "");
+ " end TS %" GST_TIME_FORMAT " base %" G_GINT64_FORMAT " %s",
+ GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_end_ptm),
+ new_base_time, discont ? "discont" : "");
+
#if 0
g_print ("NAV packet start TS %" GST_TIME_FORMAT
- " end TS %" GST_TIME_FORMAT " %s\n",
- GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)),
- GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm)),
- (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) ?
- "discont" : "");
+ " end TS %" GST_TIME_FORMAT " base %" G_GINT64_FORMAT " %s\n",
+ GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_end_ptm),
+ new_base_time, discont ? "discont" : "");
#endif
- if (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) {
+ if (discont) {
g_print ("NAV packet discont: cur_end_ts %" GST_TIME_FORMAT " != "
- " vobu_s_ptm: %" GST_TIME_FORMAT "\n",
+ " vobu_start_ptm: %" GST_TIME_FORMAT " base %" GST_TIME_FORMAT
+ "\n",
GST_TIME_ARGS (src->cur_end_ts),
- GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)));
+ GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_base_time));
src->need_segment = TRUE;
}
- src->cur_start_ts = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm);
- src->cur_end_ts = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm);
-
- /* highlight might change, let's check */
- rsn_dvdsrc_update_highlight (src);
+ src->cur_start_ts = new_start_ptm;
+ src->cur_end_ts = new_end_ptm;
+ src->cur_vobu_base_ts = new_base_time;
/* NAV packet is also a data block that needs sending */
- *outbuf = src->alloc_buf;
+ src->next_buf = src->alloc_buf;
src->alloc_buf = NULL;
+
+ if (!src->have_pci || pci->hli.hl_gi.hli_ss != 2) {
+ /* Store the nav packet for activation at the right moment
+ * if we don't have a packet yet or the info has changed (hli_ss != 2)
+ */
+ if (pci->hli.hl_gi.hli_s_ptm != 0)
+ new_start_ptm = MPEGTIME_TO_GSTTIME (pci->hli.hl_gi.hli_s_ptm);
+
+ src->next_is_nav_block = TRUE;
+ src->next_nav_ts = new_start_ptm;
+ } else {
+ src->next_is_nav_block = FALSE;
+ src->next_nav_ts = GST_CLOCK_TIME_NONE;
+ }
+
break;
}
case DVDNAV_STOP:
@@ -577,7 +658,8 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
g_print ("STOP found. End of disc\n");
ret = GST_FLOW_UNEXPECTED;
break;
- case DVDNAV_STILL_FRAME:{
+ case DVDNAV_STILL_FRAME:
+ {
dvdnav_still_event_t *info = (dvdnav_still_event_t *) data;
g_print ("STILL frame duration %d\n", info->length);
@@ -664,15 +746,6 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
}
case DVDNAV_HIGHLIGHT:{
rsn_dvdsrc_update_highlight (src);
- if (src->highlight_event && have_dvd_lock) {
- GstEvent *hl_event = src->highlight_event;
-
- src->highlight_event = NULL;
- g_mutex_unlock (src->dvd_lock);
- g_print ("Highlight change - button: %d\n", src->active_button);
- gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
- g_mutex_lock (src->dvd_lock);
- }
break;
}
case DVDNAV_HOP_CHANNEL:
@@ -686,6 +759,16 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
break;
}
+ if (src->highlight_event && have_dvd_lock) {
+ GstEvent *hl_event = src->highlight_event;
+
+ src->highlight_event = NULL;
+ g_mutex_unlock (src->dvd_lock);
+ g_print ("Highlight change - button: %d\n", src->active_button);
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event);
+ g_mutex_lock (src->dvd_lock);
+ }
+
return ret;
read_error:
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL),
@@ -712,7 +795,7 @@ rsn_dvdsrc_prepare_next_block (resinDvdSrc * src, gboolean have_dvd_lock)
return GST_FLOW_OK;
do {
- ret = rsn_dvdsrc_step (src, have_dvd_lock, &src->next_buf);
+ ret = rsn_dvdsrc_step (src, have_dvd_lock);
}
while (ret == GST_FLOW_OK && src->next_buf == NULL);
@@ -743,17 +826,6 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
return ret;
}
- if (src->next_buf != NULL) {
- *outbuf = src->next_buf;
- src->next_buf = NULL;
-
- if (src->discont) {
- g_print ("Discont packet\n");
- GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_DISCONT);
- src->discont = FALSE;
- }
- }
-
streams_event = src->streams_event;
src->streams_event = NULL;
@@ -766,16 +838,19 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
clut_event = src->clut_event;
src->clut_event = NULL;
- highlight_event = src->highlight_event;
- src->highlight_event = NULL;
-
g_mutex_unlock (src->dvd_lock);
- /* Push in-band events now that we've dropped the dvd_lock */
+ /* Push in-band events now that we've dropped the dvd_lock, before
+ * we change segment */
if (streams_event) {
g_print ("Pushing stream event\n");
gst_pad_push_event (GST_BASE_SRC_PAD (src), streams_event);
}
+ if (clut_event) {
+ g_print ("Pushing clut event\n");
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), clut_event);
+ }
+ /* Out of band events */
if (spu_select_event) {
g_print ("Pushing spu_select event\n");
gst_pad_push_event (GST_BASE_SRC_PAD (src), spu_select_event);
@@ -784,16 +859,6 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
g_print ("Pushing audio_select event\n");
gst_pad_push_event (GST_BASE_SRC_PAD (src), audio_select_event);
}
- if (clut_event) {
- g_print ("Pushing clut event\n");
- gst_pad_push_event (GST_BASE_SRC_PAD (src), clut_event);
- }
-
- if (highlight_event) {
- g_print ("Pushing highlight event with TS %" GST_TIME_FORMAT "\n",
- GST_TIME_ARGS (GST_EVENT_TIMESTAMP (highlight_event)));
- gst_pad_push_event (GST_BASE_SRC_PAD (src), highlight_event);
- }
g_mutex_lock (src->dvd_lock);
@@ -807,22 +872,52 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
src->need_segment = FALSE;
}
if (src->cur_end_ts != GST_CLOCK_TIME_NONE)
- segment->last_stop = src->cur_end_ts;
+ gst_segment_set_last_stop (segment, GST_FORMAT_TIME, src->cur_end_ts);
+
+ if (src->next_buf != NULL) {
+ /* Now that we're in the new segment, we can enqueue any nav packet
+ * correctly */
+ if (src->next_is_nav_block)
+ rsn_dvdsrc_enqueue_nav_block (src, src->next_buf, src->next_nav_ts);
+
+ *outbuf = src->next_buf;
+ src->next_buf = NULL;
+
+ if (src->discont) {
+ g_print ("Discont packet\n");
+ GST_BUFFER_FLAG_SET (*outbuf, GST_BUFFER_FLAG_DISCONT);
+ src->discont = FALSE;
+ }
+ }
+
+ highlight_event = src->highlight_event;
+ src->highlight_event = NULL;
+
+ /* Schedule a clock callback for the any pending nav packet */
+ rsn_dvdsrc_check_nav_blocks (src);
+
g_mutex_unlock (src->dvd_lock);
+ if (highlight_event) {
+ g_print ("Pushing highlight event with TS %" GST_TIME_FORMAT "\n",
+ GST_TIME_ARGS (GST_EVENT_TIMESTAMP (highlight_event)));
+ gst_pad_push_event (GST_BASE_SRC_PAD (src), highlight_event);
+ }
+
return ret;
}
static RsnNavResult
rsn_dvdsrc_perform_button_action (resinDvdSrc * src, RsnNavAction action)
{
- pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav);
+ pci_t *pci;
RsnNavResult result = RSN_NAV_RESULT_NONE;
int button = 0;
btni_t *btn_info;
- if (pci == NULL)
+ if (!src->have_pci)
return RSN_NAV_RESULT_NONE;
+ pci = &src->cur_pci;
if (pci->hli.hl_gi.hli_ss == 0)
return RSN_NAV_RESULT_NONE; /* No buttons at the moment */
@@ -877,7 +972,7 @@ rsn_dvdsrc_perform_button_action (resinDvdSrc * src, RsnNavAction action)
}
if (result == RSN_NAV_RESULT_HIGHLIGHT)
- g_cond_signal (src->still_cond);
+ g_cond_broadcast (src->still_cond);
return result;
}
@@ -957,7 +1052,6 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
} else if (strcmp (event_type, "mouse-move") == 0) {
gdouble x, y;
- pci_t *nav;
if (!gst_structure_get_double (s, "pointer_x", &x) ||
!gst_structure_get_double (s, "pointer_y", &y))
@@ -968,14 +1062,13 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
if (!src->running)
goto not_running;
- nav = dvdnav_get_current_nav_pci (src->dvdnav);
- if (nav && dvdnav_mouse_select (src->dvdnav, nav, (int) x, (int) y) ==
+ if (src->have_pci &&
+ dvdnav_mouse_select (src->dvdnav, &src->cur_pci, (int) x, (int) y) ==
DVDNAV_STATUS_OK) {
nav_res = RSN_NAV_RESULT_HIGHLIGHT;
}
} else if (strcmp (event_type, "mouse-button-release") == 0) {
gdouble x, y;
- pci_t *nav;
if (!gst_structure_get_double (s, "pointer_x", &x) ||
!gst_structure_get_double (s, "pointer_y", &y))
@@ -988,9 +1081,8 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
if (!src->running)
goto not_running;
- nav = dvdnav_get_current_nav_pci (src->dvdnav);
- if (nav &&
- dvdnav_mouse_activate (src->dvdnav, nav, (int) x, (int) y) ==
+ if (src->have_pci &&
+ dvdnav_mouse_activate (src->dvdnav, &src->cur_pci, (int) x, (int) y) ==
DVDNAV_STATUS_OK) {
nav_res = RSN_NAV_RESULT_BRANCH;
}
@@ -1012,7 +1104,7 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
g_print ("flush and jump\n");
g_mutex_lock (src->branch_lock);
src->branching = TRUE;
- g_cond_signal (src->still_cond);
+ g_cond_broadcast (src->still_cond);
g_mutex_unlock (src->branch_lock);
hl_event = src->highlight_event;
@@ -1236,26 +1328,28 @@ static void
rsn_dvdsrc_update_highlight (resinDvdSrc * src)
{
int button = 0;
- pci_t *pci;
+ pci_t *pci = &src->cur_pci;
dvdnav_highlight_area_t area;
int mode = 0;
GstEvent *event = NULL;
GstStructure *s;
- if (dvdnav_get_current_highlight (src->dvdnav, &button) != DVDNAV_STATUS_OK) {
- GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
- ("dvdnav_get_current_highlight: %s",
- dvdnav_err_to_string (src->dvdnav)));
- return;
- }
+ if (src->have_pci) {
+ if (dvdnav_get_current_highlight (src->dvdnav, &button) != DVDNAV_STATUS_OK) {
+ GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
+ ("dvdnav_get_current_highlight: %s",
+ dvdnav_err_to_string (src->dvdnav)));
+ return;
+ }
- pci = dvdnav_get_current_nav_pci (src->dvdnav);
- if ((button > pci->hli.hl_gi.btn_ns) || (button < 1)) {
- /* button is out of the range of possible buttons. */
- button = 0;
+ if (pci->hli.hl_gi.hli_ss == 0 || (button > pci->hli.hl_gi.btn_ns) ||
+ (button < 1)) {
+ /* button is out of the range of possible buttons. */
+ button = 0;
+ }
}
- if (pci->hli.hl_gi.hli_ss == 0 || button == 0) {
+ if (button == 0) {
/* No highlight available, or no button selected - clear the SPU */
if (src->active_button != 0) {
src->active_button = 0;
@@ -1303,9 +1397,8 @@ rsn_dvdsrc_update_highlight (resinDvdSrc * src)
src->active_button = button;
- g_print ("Setting highlight. Button %d active %d TS %" GST_TIME_FORMAT
- " palette 0x%x\n", button, mode,
- GST_TIME_ARGS (GST_EVENT_TIMESTAMP (event)), area.palette);
+ g_print ("Setting highlight. Button %d @ %d,%d active %d"
+ " palette 0x%x\n", button, area.sx, area.sy, mode, area.palette);
if (src->highlight_event)
gst_event_unref (src->highlight_event);
@@ -1313,6 +1406,165 @@ rsn_dvdsrc_update_highlight (resinDvdSrc * src)
}
}
+static void
+rsn_dvdsrc_enqueue_nav_block (resinDvdSrc * src, GstBuffer * nav_buf,
+ GstClockTime ts)
+{
+ RsnDvdPendingNav *pend_nav = g_new0 (RsnDvdPendingNav, 1);
+ GstSegment *seg = &(GST_BASE_SRC (src)->segment);
+
+ pend_nav->buffer = gst_buffer_ref (nav_buf);
+ pend_nav->ts = ts;
+ pend_nav->running_ts = gst_segment_to_running_time (seg, GST_FORMAT_TIME, ts);
+
+ if (src->pending_nav_blocks == NULL) {
+ src->pending_nav_blocks = src->pending_nav_blocks_end =
+ g_slist_append (src->pending_nav_blocks_end, pend_nav);
+ } else {
+ src->pending_nav_blocks_end =
+ g_slist_append (src->pending_nav_blocks_end, pend_nav);
+ src->pending_nav_blocks_end = g_slist_next (src->pending_nav_blocks_end);
+ }
+
+#if 0
+ g_print ("Enqueued nav with TS %" GST_TIME_FORMAT " with run ts %"
+ GST_TIME_FORMAT ". %d packs pending\n", GST_TIME_ARGS (ts),
+ GST_TIME_ARGS (pend_nav->running_ts),
+ g_slist_length (src->pending_nav_blocks));
+#endif
+}
+
+static void
+rsn_dvdsrc_activate_nav_block (resinDvdSrc * src, GstBuffer * nav_buf)
+{
+ int32_t forced_button;
+
+ navRead_PCI (&src->cur_pci, GST_BUFFER_DATA (nav_buf) + 0x2d);
+ src->have_pci = TRUE;
+
+ forced_button = src->cur_pci.hli.hl_gi.fosl_btnn & 0x3f;
+
+ if (forced_button != 0)
+ dvdnav_button_select (src->dvdnav, &src->cur_pci, forced_button);
+
+ /* highlight might change, let's check */
+ rsn_dvdsrc_update_highlight (src);
+ if (src->highlight_event)
+ g_cond_broadcast (src->still_cond);
+}
+
+static void
+rsn_dvdsrc_clear_nav_blocks (resinDvdSrc * src)
+{
+ while (src->pending_nav_blocks) {
+ RsnDvdPendingNav *cur = (RsnDvdPendingNav *) src->pending_nav_blocks->data;
+
+ gst_buffer_unref (cur->buffer);
+ g_free (cur);
+
+ src->pending_nav_blocks =
+ g_slist_delete_link (src->pending_nav_blocks, src->pending_nav_blocks);
+ }
+
+ src->pending_nav_blocks_end = NULL;
+}
+
+static gboolean
+rsn_dvdsrc_nav_clock_cb (GstClock * clock, GstClockTime time, GstClockID id,
+ gpointer user_data)
+{
+ resinDvdSrc *src = (resinDvdSrc *) user_data;
+ GstClockTime base_time = gst_element_get_base_time (GST_ELEMENT (src));
+
+ GST_LOG_OBJECT (src, "NAV pack callback for TS %" GST_TIME_FORMAT " at ts %"
+ GST_TIME_FORMAT, GST_TIME_ARGS (time),
+ GST_TIME_ARGS (gst_clock_get_time (clock) - base_time));
+
+ g_mutex_lock (src->dvd_lock);
+
+ /* Destroy the clock id that caused this callback */
+ gst_clock_id_unref (src->nav_clock_id);
+ src->nav_clock_id = NULL;
+
+ while (src->pending_nav_blocks) {
+ RsnDvdPendingNav *cur = (RsnDvdPendingNav *) src->pending_nav_blocks->data;
+
+ if (time < base_time + cur->running_ts)
+ break; /* Next NAV is in the future */
+
+ g_print ("Activating nav pack for with TS %" GST_TIME_FORMAT
+ " at running TS %" GST_TIME_FORMAT "\n",
+ GST_TIME_ARGS (cur->ts), GST_TIME_ARGS (cur->running_ts));
+ rsn_dvdsrc_activate_nav_block (src, cur->buffer);
+
+ gst_buffer_unref (cur->buffer);
+ g_free (cur);
+
+ src->pending_nav_blocks =
+ g_slist_delete_link (src->pending_nav_blocks, src->pending_nav_blocks);
+ }
+
+ if (src->pending_nav_blocks == NULL)
+ src->pending_nav_blocks_end = NULL;
+ else {
+ /* Schedule a next packet, if any */
+ RsnDvdPendingNav *next_nav =
+ (RsnDvdPendingNav *) src->pending_nav_blocks->data;
+ rsn_dvdsrc_schedule_nav_cb (src, next_nav);
+ }
+
+ g_mutex_unlock (src->dvd_lock);
+
+ return TRUE;
+}
+
+static void
+rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src, RsnDvdPendingNav * next_nav)
+{
+ GstClock *clock;
+ GstClockTime base_ts;
+ GstState cur_state;
+
+ gst_element_get_state (GST_ELEMENT (src), &cur_state, NULL, 0);
+ if (cur_state != GST_STATE_PLAYING)
+ return; /* Not in playing state yet */
+
+ GST_OBJECT_LOCK (src);
+ clock = GST_ELEMENT_CLOCK (src);
+ if (clock)
+ gst_object_ref (clock);
+ base_ts = GST_ELEMENT (src)->base_time;
+ GST_OBJECT_UNLOCK (src);
+
+ if (clock == NULL)
+ return;
+
+ GST_LOG_OBJECT (src, "Schedule nav pack for running TS %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (next_nav->running_ts));
+
+ src->nav_clock_id = gst_clock_new_single_shot_id (clock,
+ base_ts + next_nav->running_ts);
+ gst_clock_id_wait_async (src->nav_clock_id, rsn_dvdsrc_nav_clock_cb, src);
+
+ gst_object_unref (clock);
+}
+
+static void
+rsn_dvdsrc_check_nav_blocks (resinDvdSrc * src)
+{
+ RsnDvdPendingNav *next_nav;
+
+ /* Make sure a callback is scheduled for the first nav packet */
+ if (src->nav_clock_id != NULL)
+ return; /* Something already scheduled */
+ if (src->pending_nav_blocks == NULL)
+ return; /* No nav blocks available yet */
+
+ next_nav = (RsnDvdPendingNav *) src->pending_nav_blocks->data;
+
+ rsn_dvdsrc_schedule_nav_cb (src, next_nav);
+}
+
/* Use libdvdread to read and cache info from the IFO file about
* streams in each VTS */
static gboolean
@@ -1335,6 +1587,40 @@ rsn_dvdsrc_src_event (RsnBaseSrc * basesrc, GstEvent * event)
return res;
}
+static GstStateChangeReturn
+rsn_dvdsrc_change_state (GstElement * element, GstStateChange transition)
+{
+ GstStateChangeReturn ret;
+ resinDvdSrc *src = RESINDVDSRC (element);
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+ if (ret == GST_STATE_CHANGE_FAILURE)
+ return ret;
+
+ switch (transition) {
+ case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
+ /* Kick off the NAV packet callback if needed */
+ g_mutex_lock (src->dvd_lock);
+ rsn_dvdsrc_check_nav_blocks (src);
+ g_mutex_unlock (src->dvd_lock);
+ break;
+ case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
+ /* Unschedule any NAV packet callback */
+ g_mutex_lock (src->dvd_lock);
+ if (src->nav_clock_id) {
+ gst_clock_id_unschedule (src->nav_clock_id);
+ gst_clock_id_unref (src->nav_clock_id);
+ src->nav_clock_id = NULL;
+ }
+ g_mutex_unlock (src->dvd_lock);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
static gboolean
rsn_dvdsrc_src_query (RsnBaseSrc * basesrc, GstQuery * query)
{
@@ -1364,6 +1650,7 @@ static gboolean
rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
GstSegment * segment)
{
+ resinDvdSrc *src = RESINDVDSRC (bsrc);
GstSeekType cur_type, stop_type;
gint64 cur, stop;
GstSeekFlags flags;
@@ -1380,7 +1667,9 @@ rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
gst_segment_init (segment, seek_format);
gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur,
stop_type, stop, &update);
- g_print ("Have internal seek event\n");
+
+ if (flags & GST_SEEK_FLAG_FLUSH)
+ src->flushing_seek = TRUE;
return TRUE;
}
@@ -1401,7 +1690,6 @@ rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment)
gboolean ret = FALSE;
if (segment->format == rsndvd_format) {
- g_print ("Handling internal seek event\n");
ret = TRUE;
} else {
/* FIXME: Handle other formats: Time, title, chapter, angle */
@@ -1419,6 +1707,24 @@ rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment)
/* Force a highlight update */
src->active_button = -1;
+ if (src->flushing_seek) {
+ g_mutex_lock (src->dvd_lock);
+ src->flushing_seek = FALSE;
+
+ gst_buffer_replace (&src->next_buf, NULL);
+ src->cur_start_ts = GST_CLOCK_TIME_NONE;
+ src->cur_end_ts = GST_CLOCK_TIME_NONE;
+ src->cur_vobu_base_ts = GST_CLOCK_TIME_NONE;
+ src->have_pci = FALSE;
+ if (src->nav_clock_id) {
+ gst_clock_id_unschedule (src->nav_clock_id);
+ gst_clock_id_unref (src->nav_clock_id);
+ src->nav_clock_id = NULL;
+ }
+ rsn_dvdsrc_clear_nav_blocks (src);
+ g_mutex_unlock (src->dvd_lock);
+ }
+
g_print ("Entering prepare_next_block after seek\n");
if (rsn_dvdsrc_prepare_next_block (src, FALSE) != GST_FLOW_OK)
goto fail;
@@ -1429,7 +1735,11 @@ rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment)
segment->last_stop = segment->start = src->cur_start_ts;
/* time field = position is the 'logical' stream time here: */
- segment->time = src->cur_position;
+ segment->time = 0;
+ if (src->cur_position != GST_CLOCK_TIME_NONE)
+ segment->time += src->cur_position;
+ if (src->cur_vobu_base_ts != GST_CLOCK_TIME_NONE)
+ segment->time += src->cur_vobu_base_ts;
segment->stop = -1;
segment->duration = -1;
diff --git a/ext/resindvd/resindvdsrc.h b/ext/resindvd/resindvdsrc.h
index c0272c81..0b273da7 100644
--- a/ext/resindvd/resindvdsrc.h
+++ b/ext/resindvd/resindvdsrc.h
@@ -29,6 +29,7 @@
#include <dvdnav/dvdnav.h>
#include <dvdread/ifo_read.h>
+#include <dvdread/nav_read.h>
#else
@@ -36,7 +37,7 @@
#include <dvdnav/ifo_read.h>
#include <dvdnav/dvdnav.h>
-#include <dvdnav/nav_print.h>
+#include <dvdnav/nav_read.h>
#endif
@@ -84,16 +85,25 @@ struct _resinDvdSrc
gboolean running;
gboolean discont;
+ gboolean flushing_seek;
gboolean need_segment;
gboolean active_highlight;
GstBuffer *alloc_buf;
GstBuffer *next_buf;
+ /* TRUE if the next_buf is a nav block that needs enqueueing */
+ gboolean next_is_nav_block;
+ /* PTS for activating the pending nav block in next_buf */
+ GstClockTime next_nav_ts;
+ /* Track accumulated segment position, cleared by flushing */
+ GstSegment src_segment;
/* Start timestamp of the previous NAV block */
GstClockTime cur_start_ts;
/* End timestamp of the previous NAV block */
GstClockTime cur_end_ts;
+ /* base ts is cur_start_ts - cell_time for each VOBU */
+ GstClockTime cur_vobu_base_ts;
/* Position info of the previous NAV block */
GstClockTime cur_position;
/* Duration of the current PGC */
@@ -108,6 +118,16 @@ struct _resinDvdSrc
GstEvent *spu_select_event;
GstEvent *audio_select_event;
GstEvent *highlight_event;
+
+ /* GList of NAV packets awaiting activation, and the
+ * running times to activate them. */
+ GSList *pending_nav_blocks;
+ GSList *pending_nav_blocks_end;
+
+ GstClockID nav_clock_id;
+
+ gboolean have_pci;
+ pci_t cur_pci;
};
struct _resinDvdSrcClass
diff --git a/ext/resindvd/rsnbasesrc.c b/ext/resindvd/rsnbasesrc.c
index c4d7f24c..6704ed55 100644
--- a/ext/resindvd/rsnbasesrc.c
+++ b/ext/resindvd/rsnbasesrc.c
@@ -1037,7 +1037,17 @@ rsn_base_src_perform_seek (RsnBaseSrc * src, GstEvent * event, gboolean unlock)
/* if successfull seek, we update our real segment and push
* out the new segment. */
if (res) {
- memcpy (&src->segment, &seeksegment, sizeof (GstSegment));
+ if (flush) {
+ memcpy (&src->segment, &seeksegment, sizeof (GstSegment));
+ } else {
+ gst_segment_set_newsegment_full (&src->segment,
+ FALSE, seeksegment.rate, seeksegment.applied_rate,
+ seeksegment.format, seeksegment.last_stop,
+ seeksegment.stop, seeksegment.time);
+
+ gst_segment_set_last_stop (&src->segment, GST_FORMAT_TIME,
+ seeksegment.last_stop);
+ }
if (src->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT (src),