ext/resindvd/resindvdsrc.*: Schedule NAV packets and activate them with an async clock callback at the right moment. ...

Original commit message from CVS:
* 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
This commit is contained in:
Jan Schmidt 2008-06-20 13:07:56 +00:00
parent d190f3cf84
commit debbed3bff
5 changed files with 451 additions and 95 deletions

View file

@ -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> 2008-06-19 Stefan Kost <ensonic@users.sf.net>
* gst/selector/gstoutputselector.c: * gst/selector/gstoutputselector.c:

2
common

@ -1 +1 @@
Subproject commit 46ec7dfc1c09ff550ed6b7a4e0d3f2b2ac7d3ee8 Subproject commit d9cd98b46aebaf143dc43d8563a3bff650be6a7e

View file

@ -70,6 +70,13 @@ enum
ARG_DEVICE ARG_DEVICE
}; };
typedef struct
{
GstBuffer *buffer;
GstClockTime ts;
GstClockTime running_ts;
} RsnDvdPendingNav;
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC, GST_PAD_SRC,
GST_PAD_ALWAYS, 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, static gboolean rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
GstSegment * segment); GstSegment * segment);
static gboolean rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, 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, static void rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src,
guint8 phys_stream, gboolean forced_only); guint8 phys_stream, gboolean forced_only);
@ -111,10 +120,42 @@ static void rsn_dvdsrc_prepare_clut_change_event (resinDvdSrc * src,
const guint32 * clut); const guint32 * clut);
static void rsn_dvdsrc_update_highlight (resinDvdSrc * src); 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 GstFlowReturn rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** buf);
static gboolean rsn_dvdsrc_src_event (RsnBaseSrc * basesrc, GstEvent * event); static gboolean rsn_dvdsrc_src_event (RsnBaseSrc * basesrc, GstEvent * event);
static gboolean rsn_dvdsrc_src_query (RsnBaseSrc * basesrc, GstQuery * query); 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 static void
rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type) rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type)
{ {
@ -145,10 +186,12 @@ static void
rsn_dvdsrc_class_init (resinDvdSrcClass * klass) rsn_dvdsrc_class_init (resinDvdSrcClass * klass)
{ {
GObjectClass *gobject_class; GObjectClass *gobject_class;
GstElementClass *gstelement_class;
RsnBaseSrcClass *gstbasesrc_class; RsnBaseSrcClass *gstbasesrc_class;
RsnPushSrcClass *gstpush_src_class; RsnPushSrcClass *gstpush_src_class;
gobject_class = (GObjectClass *) klass; gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
gstbasesrc_class = GST_BASE_SRC_CLASS (klass); gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
gstpush_src_class = GST_PUSH_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->set_property = rsn_dvdsrc_set_property;
gobject_class->get_property = rsn_dvdsrc_get_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->start = GST_DEBUG_FUNCPTR (rsn_dvdsrc_start);
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (rsn_dvdsrc_stop); gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (rsn_dvdsrc_stop);
gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (rsn_dvdsrc_unlock); 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->pgc_duration = GST_CLOCK_TIME_NONE;
src->cur_start_ts = GST_CLOCK_TIME_NONE; src->cur_start_ts = GST_CLOCK_TIME_NONE;
src->cur_end_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->vts_n = 0;
src->in_menu = FALSE; src->in_menu = FALSE;
@ -377,6 +423,14 @@ rsn_dvdsrc_stop (RsnBaseSrc * bsrc)
g_mutex_lock (src->dvd_lock); 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 */ /* Clear any allocated output buffer */
gst_buffer_replace (&src->alloc_buf, NULL); gst_buffer_replace (&src->alloc_buf, NULL);
gst_buffer_replace (&src->next_buf, NULL); gst_buffer_replace (&src->next_buf, NULL);
@ -435,6 +489,7 @@ static gboolean
rsn_dvdsrc_do_still (resinDvdSrc * src, int duration) rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
{ {
GstEvent *still_event; GstEvent *still_event;
GstEvent *hl_event;
GstStructure *s; GstStructure *s;
GstEvent *seg_event; GstEvent *seg_event;
GstSegment *segment = &(GST_BASE_SRC (src)->segment); 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-state", G_TYPE_BOOLEAN, TRUE, NULL);
still_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s); 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, seg_event = gst_event_new_new_segment_full (TRUE,
segment->rate, segment->applied_rate, segment->format, segment->rate, segment->applied_rate, segment->format,
segment->start, segment->last_stop, segment->time); 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, /* 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); 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), still_event);
gst_pad_push_event (GST_BASE_SRC_PAD (src), seg_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->dvd_lock);
g_mutex_lock (src->branch_lock); g_mutex_lock (src->branch_lock);
@ -507,7 +567,7 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration)
} }
static GstFlowReturn 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; GstFlowReturn ret = GST_FLOW_OK;
dvdnav_status_t dvdnav_ret; dvdnav_status_t dvdnav_ret;
@ -532,44 +592,65 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
switch (event) { switch (event) {
case DVDNAV_BLOCK_OK: case DVDNAV_BLOCK_OK:
/* Data block that needs outputting */ /* 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; src->alloc_buf = NULL;
break; break;
case DVDNAV_NAV_PACKET:{ case DVDNAV_NAV_PACKET:
{
pci_t *pci = dvdnav_get_current_nav_pci (src->dvdnav); 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 GST_LOG_OBJECT (src, "NAV packet start TS %" GST_TIME_FORMAT
" end TS %" GST_TIME_FORMAT " %s", " end TS %" GST_TIME_FORMAT " base %" G_GINT64_FORMAT " %s",
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)), GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_end_ptm),
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm)), new_base_time, discont ? "discont" : "");
(MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) ?
"discont" : "");
#if 0 #if 0
g_print ("NAV packet start TS %" GST_TIME_FORMAT g_print ("NAV packet start TS %" GST_TIME_FORMAT
" end TS %" GST_TIME_FORMAT " %s\n", " end TS %" GST_TIME_FORMAT " base %" G_GINT64_FORMAT " %s\n",
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm)), GST_TIME_ARGS (new_start_ptm), GST_TIME_ARGS (new_end_ptm),
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm)), new_base_time, discont ? "discont" : "");
(MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm) != src->cur_end_ts) ?
"discont" : "");
#endif #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 " != " 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 (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->need_segment = TRUE;
} }
src->cur_start_ts = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_s_ptm); src->cur_start_ts = new_start_ptm;
src->cur_end_ts = MPEGTIME_TO_GSTTIME (pci->pci_gi.vobu_e_ptm); src->cur_end_ts = new_end_ptm;
src->cur_vobu_base_ts = new_base_time;
/* highlight might change, let's check */
rsn_dvdsrc_update_highlight (src);
/* NAV packet is also a data block that needs sending */ /* NAV packet is also a data block that needs sending */
*outbuf = src->alloc_buf; src->next_buf = src->alloc_buf;
src->alloc_buf = NULL; 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; break;
} }
case DVDNAV_STOP: 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"); g_print ("STOP found. End of disc\n");
ret = GST_FLOW_UNEXPECTED; ret = GST_FLOW_UNEXPECTED;
break; break;
case DVDNAV_STILL_FRAME:{ case DVDNAV_STILL_FRAME:
{
dvdnav_still_event_t *info = (dvdnav_still_event_t *) data; dvdnav_still_event_t *info = (dvdnav_still_event_t *) data;
g_print ("STILL frame duration %d\n", info->length); 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:{ case DVDNAV_HIGHLIGHT:{
rsn_dvdsrc_update_highlight (src); 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; break;
} }
case DVDNAV_HOP_CHANNEL: case DVDNAV_HOP_CHANNEL:
@ -686,6 +759,16 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock, GstBuffer ** outbuf)
break; 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; return ret;
read_error: read_error:
GST_ELEMENT_ERROR (src, RESOURCE, READ, (NULL), 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; return GST_FLOW_OK;
do { 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); while (ret == GST_FLOW_OK && src->next_buf == NULL);
@ -743,17 +826,6 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
return ret; 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; streams_event = src->streams_event;
src->streams_event = NULL; src->streams_event = NULL;
@ -766,16 +838,19 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
clut_event = src->clut_event; clut_event = src->clut_event;
src->clut_event = NULL; src->clut_event = NULL;
highlight_event = src->highlight_event;
src->highlight_event = NULL;
g_mutex_unlock (src->dvd_lock); 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) { if (streams_event) {
g_print ("Pushing stream event\n"); g_print ("Pushing stream event\n");
gst_pad_push_event (GST_BASE_SRC_PAD (src), streams_event); 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) { if (spu_select_event) {
g_print ("Pushing spu_select event\n"); g_print ("Pushing spu_select event\n");
gst_pad_push_event (GST_BASE_SRC_PAD (src), spu_select_event); 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"); g_print ("Pushing audio_select event\n");
gst_pad_push_event (GST_BASE_SRC_PAD (src), audio_select_event); 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); g_mutex_lock (src->dvd_lock);
@ -807,22 +872,52 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf)
src->need_segment = FALSE; src->need_segment = FALSE;
} }
if (src->cur_end_ts != GST_CLOCK_TIME_NONE) 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); 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; return ret;
} }
static RsnNavResult static RsnNavResult
rsn_dvdsrc_perform_button_action (resinDvdSrc * src, RsnNavAction action) 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; RsnNavResult result = RSN_NAV_RESULT_NONE;
int button = 0; int button = 0;
btni_t *btn_info; btni_t *btn_info;
if (pci == NULL) if (!src->have_pci)
return RSN_NAV_RESULT_NONE; return RSN_NAV_RESULT_NONE;
pci = &src->cur_pci;
if (pci->hli.hl_gi.hli_ss == 0) if (pci->hli.hl_gi.hli_ss == 0)
return RSN_NAV_RESULT_NONE; /* No buttons at the moment */ 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) if (result == RSN_NAV_RESULT_HIGHLIGHT)
g_cond_signal (src->still_cond); g_cond_broadcast (src->still_cond);
return result; return result;
} }
@ -957,7 +1052,6 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
} else if (strcmp (event_type, "mouse-move") == 0) { } else if (strcmp (event_type, "mouse-move") == 0) {
gdouble x, y; gdouble x, y;
pci_t *nav;
if (!gst_structure_get_double (s, "pointer_x", &x) || if (!gst_structure_get_double (s, "pointer_x", &x) ||
!gst_structure_get_double (s, "pointer_y", &y)) !gst_structure_get_double (s, "pointer_y", &y))
@ -968,14 +1062,13 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
if (!src->running) if (!src->running)
goto not_running; goto not_running;
nav = dvdnav_get_current_nav_pci (src->dvdnav); if (src->have_pci &&
if (nav && dvdnav_mouse_select (src->dvdnav, nav, (int) x, (int) y) == dvdnav_mouse_select (src->dvdnav, &src->cur_pci, (int) x, (int) y) ==
DVDNAV_STATUS_OK) { DVDNAV_STATUS_OK) {
nav_res = RSN_NAV_RESULT_HIGHLIGHT; nav_res = RSN_NAV_RESULT_HIGHLIGHT;
} }
} else if (strcmp (event_type, "mouse-button-release") == 0) { } else if (strcmp (event_type, "mouse-button-release") == 0) {
gdouble x, y; gdouble x, y;
pci_t *nav;
if (!gst_structure_get_double (s, "pointer_x", &x) || if (!gst_structure_get_double (s, "pointer_x", &x) ||
!gst_structure_get_double (s, "pointer_y", &y)) !gst_structure_get_double (s, "pointer_y", &y))
@ -988,9 +1081,8 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event)
if (!src->running) if (!src->running)
goto not_running; goto not_running;
nav = dvdnav_get_current_nav_pci (src->dvdnav); if (src->have_pci &&
if (nav && dvdnav_mouse_activate (src->dvdnav, &src->cur_pci, (int) x, (int) y) ==
dvdnav_mouse_activate (src->dvdnav, nav, (int) x, (int) y) ==
DVDNAV_STATUS_OK) { DVDNAV_STATUS_OK) {
nav_res = RSN_NAV_RESULT_BRANCH; 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_print ("flush and jump\n");
g_mutex_lock (src->branch_lock); g_mutex_lock (src->branch_lock);
src->branching = TRUE; src->branching = TRUE;
g_cond_signal (src->still_cond); g_cond_broadcast (src->still_cond);
g_mutex_unlock (src->branch_lock); g_mutex_unlock (src->branch_lock);
hl_event = src->highlight_event; hl_event = src->highlight_event;
@ -1236,26 +1328,28 @@ static void
rsn_dvdsrc_update_highlight (resinDvdSrc * src) rsn_dvdsrc_update_highlight (resinDvdSrc * src)
{ {
int button = 0; int button = 0;
pci_t *pci; pci_t *pci = &src->cur_pci;
dvdnav_highlight_area_t area; dvdnav_highlight_area_t area;
int mode = 0; int mode = 0;
GstEvent *event = NULL; GstEvent *event = NULL;
GstStructure *s; GstStructure *s;
if (dvdnav_get_current_highlight (src->dvdnav, &button) != DVDNAV_STATUS_OK) { if (src->have_pci) {
GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL), if (dvdnav_get_current_highlight (src->dvdnav, &button) != DVDNAV_STATUS_OK) {
("dvdnav_get_current_highlight: %s", GST_ELEMENT_ERROR (src, LIBRARY, FAILED, (NULL),
dvdnav_err_to_string (src->dvdnav))); ("dvdnav_get_current_highlight: %s",
return; dvdnav_err_to_string (src->dvdnav)));
return;
}
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;
}
} }
pci = dvdnav_get_current_nav_pci (src->dvdnav); if (button == 0) {
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 == 0) {
/* No highlight available, or no button selected - clear the SPU */ /* No highlight available, or no button selected - clear the SPU */
if (src->active_button != 0) { if (src->active_button != 0) {
src->active_button = 0; src->active_button = 0;
@ -1303,9 +1397,8 @@ rsn_dvdsrc_update_highlight (resinDvdSrc * src)
src->active_button = button; src->active_button = button;
g_print ("Setting highlight. Button %d active %d TS %" GST_TIME_FORMAT g_print ("Setting highlight. Button %d @ %d,%d active %d"
" palette 0x%x\n", button, mode, " palette 0x%x\n", button, area.sx, area.sy, mode, area.palette);
GST_TIME_ARGS (GST_EVENT_TIMESTAMP (event)), area.palette);
if (src->highlight_event) if (src->highlight_event)
gst_event_unref (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 /* Use libdvdread to read and cache info from the IFO file about
* streams in each VTS */ * streams in each VTS */
static gboolean static gboolean
@ -1335,6 +1587,40 @@ rsn_dvdsrc_src_event (RsnBaseSrc * basesrc, GstEvent * event)
return res; 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 static gboolean
rsn_dvdsrc_src_query (RsnBaseSrc * basesrc, GstQuery * query) rsn_dvdsrc_src_query (RsnBaseSrc * basesrc, GstQuery * query)
{ {
@ -1364,6 +1650,7 @@ static gboolean
rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event, rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
GstSegment * segment) GstSegment * segment)
{ {
resinDvdSrc *src = RESINDVDSRC (bsrc);
GstSeekType cur_type, stop_type; GstSeekType cur_type, stop_type;
gint64 cur, stop; gint64 cur, stop;
GstSeekFlags flags; GstSeekFlags flags;
@ -1380,7 +1667,9 @@ rsn_dvdsrc_prepare_seek (RsnBaseSrc * bsrc, GstEvent * event,
gst_segment_init (segment, seek_format); gst_segment_init (segment, seek_format);
gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur, gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur,
stop_type, stop, &update); stop_type, stop, &update);
g_print ("Have internal seek event\n");
if (flags & GST_SEEK_FLAG_FLUSH)
src->flushing_seek = TRUE;
return TRUE; return TRUE;
} }
@ -1401,7 +1690,6 @@ rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment)
gboolean ret = FALSE; gboolean ret = FALSE;
if (segment->format == rsndvd_format) { if (segment->format == rsndvd_format) {
g_print ("Handling internal seek event\n");
ret = TRUE; ret = TRUE;
} else { } else {
/* FIXME: Handle other formats: Time, title, chapter, angle */ /* FIXME: Handle other formats: Time, title, chapter, angle */
@ -1419,6 +1707,24 @@ rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment)
/* Force a highlight update */ /* Force a highlight update */
src->active_button = -1; 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"); g_print ("Entering prepare_next_block after seek\n");
if (rsn_dvdsrc_prepare_next_block (src, FALSE) != GST_FLOW_OK) if (rsn_dvdsrc_prepare_next_block (src, FALSE) != GST_FLOW_OK)
goto fail; goto fail;
@ -1429,7 +1735,11 @@ rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment)
segment->last_stop = segment->start = src->cur_start_ts; segment->last_stop = segment->start = src->cur_start_ts;
/* time field = position is the 'logical' stream time here: */ /* 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->stop = -1;
segment->duration = -1; segment->duration = -1;

View file

@ -29,6 +29,7 @@
#include <dvdnav/dvdnav.h> #include <dvdnav/dvdnav.h>
#include <dvdread/ifo_read.h> #include <dvdread/ifo_read.h>
#include <dvdread/nav_read.h>
#else #else
@ -36,7 +37,7 @@
#include <dvdnav/ifo_read.h> #include <dvdnav/ifo_read.h>
#include <dvdnav/dvdnav.h> #include <dvdnav/dvdnav.h>
#include <dvdnav/nav_print.h> #include <dvdnav/nav_read.h>
#endif #endif
@ -84,16 +85,25 @@ struct _resinDvdSrc
gboolean running; gboolean running;
gboolean discont; gboolean discont;
gboolean flushing_seek;
gboolean need_segment; gboolean need_segment;
gboolean active_highlight; gboolean active_highlight;
GstBuffer *alloc_buf; GstBuffer *alloc_buf;
GstBuffer *next_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 */ /* Start timestamp of the previous NAV block */
GstClockTime cur_start_ts; GstClockTime cur_start_ts;
/* End timestamp of the previous NAV block */ /* End timestamp of the previous NAV block */
GstClockTime cur_end_ts; 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 */ /* Position info of the previous NAV block */
GstClockTime cur_position; GstClockTime cur_position;
/* Duration of the current PGC */ /* Duration of the current PGC */
@ -108,6 +118,16 @@ struct _resinDvdSrc
GstEvent *spu_select_event; GstEvent *spu_select_event;
GstEvent *audio_select_event; GstEvent *audio_select_event;
GstEvent *highlight_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 struct _resinDvdSrcClass

View file

@ -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 /* if successfull seek, we update our real segment and push
* out the new segment. */ * out the new segment. */
if (res) { 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) { if (src->segment.flags & GST_SEEK_FLAG_SEGMENT) {
gst_element_post_message (GST_ELEMENT (src), gst_element_post_message (GST_ELEMENT (src),