diff options
-rw-r--r-- | ChangeLog | 35 | ||||
-rw-r--r-- | ext/resindvd/Makefile.am | 11 | ||||
-rw-r--r-- | ext/resindvd/gstmpegdemux.c | 28 | ||||
-rwxr-xr-x | ext/resindvd/resin-play | 5 | ||||
-rw-r--r-- | ext/resindvd/resindvdbin.c | 122 | ||||
-rw-r--r-- | ext/resindvd/resindvdbin.h | 13 | ||||
-rw-r--r-- | ext/resindvd/resindvdsrc.c | 193 | ||||
-rw-r--r-- | ext/resindvd/resindvdsrc.h | 7 | ||||
-rw-r--r-- | ext/resindvd/rsnaudiomunge.c | 22 | ||||
-rw-r--r-- | ext/resindvd/rsnbasesrc.c | 3 | ||||
-rw-r--r-- | ext/resindvd/rsnparsetter.c | 405 | ||||
-rw-r--r-- | ext/resindvd/rsnparsetter.h | 54 | ||||
-rw-r--r-- | ext/resindvd/rsnwrappedbuffer.c | 152 | ||||
-rw-r--r-- | ext/resindvd/rsnwrappedbuffer.h | 69 |
14 files changed, 973 insertions, 146 deletions
@@ -1,3 +1,38 @@ +2008-07-11 Jan Schmidt <thaytan@noraisin.net> + + * docs/plugins/.cvsignore: + Ignore *-undeclared.txt + + * ext/resindvd/rsnaudiomunge.c: + Turn g_print's into debug statements. + + * ext/resindvd/resin-play: + * ext/resindvd/Makefile.am: + * ext/resindvd/resindvdbin.c: + * ext/resindvd/resindvdbin.h: + * ext/resindvd/rsnparsetter.c: + * ext/resindvd/rsnparsetter.h: + * ext/resindvd/rsnwrappedbuffer.c: + * ext/resindvd/rsnwrappedbuffer.h: + Add a bloated implementation of a really simple idea: Replace the + pixel-aspect-ratio in the output video with a prescribed one when + necessary. There must be an easier way. + + Split the dvdspu out of the resindvdbin and put out the subpicture + stream on the subpicture pad. + + * ext/resindvd/gstmpegdemux.c: + Send video-aspect events down the pipe from the demuxer. + + * ext/resindvd/resindvdsrc.c: + * ext/resindvd/resindvdsrc.h: + Handle timed-stills somewhat using g_cond_timed_wait, with a FIXME + to make it use clock-waiting later. + + * ext/resindvd/rsnbasesrc.c: + Don't overwrite the last_stop in the basesrc segment after a seamless + seek. + 2008-07-10 Zaheer Abbas Merali <zaheerabbas at merali dot org> * gst/mpegtsparse/mpegtspacketizer.c: diff --git a/ext/resindvd/Makefile.am b/ext/resindvd/Makefile.am index 9f4d3c8f..ace8fc33 100644 --- a/ext/resindvd/Makefile.am +++ b/ext/resindvd/Makefile.am @@ -12,10 +12,12 @@ libresindvd_la_SOURCES = \ resindvdsrc.c \ gstmpegdesc.c \ gstmpegdemux.c \ - gstpesfilter.c + gstpesfilter.c \ + rsnparsetter.c \ + rsnwrappedbuffer.c -libresindvd_la_CFLAGS = $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(DVDNAV_CFLAGS) -libresindvd_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) $(DVDNAV_LIBS) +libresindvd_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(DVDNAV_CFLAGS) +libresindvd_la_LIBADD = $(GST_PLUGINS_BASE_CFLAGS) -lgstvideo-$(GST_MAJORMINOR) $(GST_BASE_LIBS) $(GST_LIBS) $(DVDNAV_LIBS) libresindvd_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) noinst_HEADERS = resindvdbin.h \ @@ -27,4 +29,5 @@ noinst_HEADERS = resindvdbin.h \ gstmpegdefs.h \ gstmpegdesc.h \ gstmpegdemux.h \ - gstpesfilter.h + gstpesfilter.h \ + rsnparsetter.h diff --git a/ext/resindvd/gstmpegdemux.c b/ext/resindvd/gstmpegdemux.c index 5e1da719..358acfa0 100644 --- a/ext/resindvd/gstmpegdemux.c +++ b/ext/resindvd/gstmpegdemux.c @@ -35,7 +35,7 @@ /* The SCR_MUNGE value is used to offset the scr_adjust value, to avoid * ever generating a negative timestamp */ -#define SCR_MUNGE (2 * GST_SECOND) +#define SCR_MUNGE (10 * GST_SECOND) /* We clamp scr delta with 0 so negative bytes won't be possible */ #define GSTTIME_TO_BYTES(time) \ @@ -266,6 +266,7 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) case ST_GST_VIDEO_MPEG1_OR_2: { gint mpeg_version = 1; + if (stream_type == ST_VIDEO_MPEG2 || (stream_type == ST_GST_VIDEO_MPEG1_OR_2 && demux->is_mpeg2_pack)) { mpeg_version = 2; @@ -584,19 +585,35 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) if (strcmp (type, "dvd-lang-codes") == 0) { GstEvent **p_ev; + /* Store the language codes event on the element, then iterate over the * streams it specifies and retrieve them. The stream creation code then * creates the pad appropriately and sends tag events as needed */ p_ev = &demux->lang_codes; gst_event_replace (p_ev, event); - GST_ERROR_OBJECT (demux, "*********************\nLang codes event %s", - gst_structure_to_string (structure)); - GST_DEBUG_OBJECT (demux, "Handling language codes event"); - /* Create a video pad to ensure have it before emit no more pads */ + /* Create a video pad to ensure it exists before emit no more pads */ temp = gst_flups_demux_get_stream (demux, 0xe0, ST_VIDEO_MPEG2); + /* Send a video format event downstream */ + { + gboolean is_widescreen, is_pal; + + if (gst_structure_get_boolean (structure, + "video-widescreen", &is_widescreen) && + gst_structure_get_boolean (structure, "video-pal-format", &is_pal)) { + GstEvent *v_format; + GstStructure *v_struct; + + v_struct = gst_structure_new ("application/x-gst-dvd", + "event", G_TYPE_STRING, "dvd-video-format", + "video-widescreen", G_TYPE_BOOLEAN, is_widescreen, + "video-pal-format", G_TYPE_BOOLEAN, is_pal, NULL); + v_format = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, v_struct); + gst_pad_push_event (temp->pad, v_format); + } + } /* Read out the languages for audio streams and request each one that * is present */ @@ -646,6 +663,7 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) /* And subtitle streams */ for (i = 0; i < MAX_DVD_SUBPICTURE_STREAMS; i++) { gint stream_format; + g_snprintf (cur_stream_name, 32, "subpicture-%d-format", i); if (!gst_structure_get_int (structure, cur_stream_name, &stream_format)) diff --git a/ext/resindvd/resin-play b/ext/resindvd/resin-play index c2368116..f4211e19 100755 --- a/ext/resindvd/resin-play +++ b/ext/resindvd/resin-play @@ -7,5 +7,6 @@ else fi gst-launch rsndvdbin name=dvd "$DEVICE_OPT" \ - dvd. ! ffmpegcolorspace ! videoscale ! autovideosink \ - dvd. ! audioconvert ! autoaudiosink + dvd. ! dvdspu name=spu ! ffmpegcolorspace ! videoscale ! ximagesink force-aspect-ratio=true \ + dvd. ! spu. \ + dvd. ! audioconvert ! autoaudiosink $@ diff --git a/ext/resindvd/resindvdbin.c b/ext/resindvd/resindvdbin.c index 81d511a0..14583bab 100644 --- a/ext/resindvd/resindvdbin.c +++ b/ext/resindvd/resindvdbin.c @@ -29,6 +29,7 @@ #include "resindvdsrc.h" #include "rsnstreamselector.h" #include "rsnaudiomunge.h" +#include "rsnparsetter.h" #include "gstmpegdemux.h" @@ -58,13 +59,23 @@ static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("video", GST_PAD_SRC, GST_PAD_SOMETIMES, - GST_STATIC_CAPS_ANY); + GST_STATIC_CAPS + ("video/mpeg, mpegversion=(int) { 1, 2 }, systemstream=false") + ); static GstStaticPadTemplate audio_src_template = -GST_STATIC_PAD_TEMPLATE ("audio", + GST_STATIC_PAD_TEMPLATE ("audio", GST_PAD_SRC, GST_PAD_SOMETIMES, - GST_STATIC_CAPS_ANY); + GST_STATIC_CAPS ("audio/x-raw-int;audio/x-raw-float") + ); + +static GstStaticPadTemplate subpicture_src_template = +GST_STATIC_PAD_TEMPLATE ("subpicture", + GST_PAD_SRC, + GST_PAD_SOMETIMES, + GST_STATIC_CAPS ("video/x-dvd-subpicture") + ); static void rsn_dvdbin_do_init (GType rsn_dvdbin_type); static void rsn_dvdbin_finalize (GObject * object); @@ -103,6 +114,8 @@ rsn_dvdbin_base_init (gpointer gclass) gst_static_pad_template_get (&video_src_template)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&audio_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&subpicture_src_template)); gst_element_class_set_details (element_class, &element_details); element_class->change_state = GST_DEBUG_FUNCPTR (rsn_dvdbin_change_state); @@ -365,53 +378,56 @@ create_elements (RsnDvdBin * dvdbin) g_signal_connect (G_OBJECT (dvdbin->pieces[DVD_ELEM_VIDDEC]), "new-decoded-pad", G_CALLBACK (viddec_pad_added), dvdbin); +#if 1 + if (!try_create_piece (dvdbin, DVD_ELEM_PARSET, NULL, RSN_TYPE_RSNPARSETTER, + "rsnparsetter", "Aspect ratio adjustment")) + return FALSE; +#else + if (!try_create_piece (dvdbin, DVD_ELEM_PARSET, "identity", 0, + "rsnparsetter", "Aspect ratio adjustment")) + return FALSE; +#endif + if (!try_create_piece (dvdbin, DVD_ELEM_VIDQ, "queue", 0, "vid_q", "video decoder buffer")) return FALSE; + src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_PARSET], "src"); + sink = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDQ], "sink"); + if (src == NULL || sink == NULL) + goto failed_vidq_connect; + if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink))) + goto failed_vidq_connect; + g_object_set (dvdbin->pieces[DVD_ELEM_VIDQ], "max-size-time", G_GUINT64_CONSTANT (0), "max-size-bytes", 0, "max-size-buffers", 3, NULL); + src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDQ], "src"); + if (src == NULL) + goto failed_vidq_ghost; + ghost = gst_ghost_pad_new ("video", src); + if (ghost == NULL) + goto failed_vidq_ghost; + if (!gst_element_add_pad (GST_ELEMENT (dvdbin), ghost)) + goto failed_vidq_ghost; + gst_object_unref (src); + ghost = src = sink = NULL; + if (!try_create_piece (dvdbin, DVD_ELEM_SPU_SELECT, NULL, RSN_TYPE_STREAM_SELECTOR, "subpselect", "Subpicture stream selector")) return FALSE; - if (!try_create_piece (dvdbin, DVD_ELEM_SPU, "dvdspu", 0, "spu", - "Subpicture overlay")) - return FALSE; - - sink = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU], "video"); - src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDQ], "src"); - if (src == NULL || sink == NULL) - goto failed_spu_connect; - if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink))) - goto failed_spu_connect; - gst_object_unref (sink); - gst_object_unref (src); - src = sink = NULL; - - sink = - gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU], "subpicture"); src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU_SELECT], "src"); - if (src == NULL || sink == NULL) - goto failed_spu_connect; - if (GST_PAD_LINK_FAILED (gst_pad_link (src, sink))) - goto failed_spu_connect; - gst_object_unref (sink); - gst_object_unref (src); - src = sink = NULL; - - src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_SPU], "src"); if (src == NULL) goto failed_spu_ghost; - ghost = gst_ghost_pad_new ("video", src); + ghost = gst_ghost_pad_new ("subpicture", src); if (ghost == NULL) goto failed_spu_ghost; if (!gst_element_add_pad (GST_ELEMENT (dvdbin), ghost)) goto failed_spu_ghost; gst_object_unref (src); - src = sink = NULL; + ghost = src = sink = NULL; if (!try_create_piece (dvdbin, DVD_ELEM_AUD_SELECT, NULL, RSN_TYPE_STREAM_SELECTOR, "audioselect", "Audio stream selector")) @@ -468,43 +484,41 @@ create_elements (RsnDvdBin * dvdbin) if (!gst_element_add_pad (GST_ELEMENT (dvdbin), ghost)) goto failed_aud_ghost; gst_object_unref (src); - src = sink = NULL; + ghost = src = sink = NULL; return TRUE; failed_connect: GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL), ("Could not connect DVD source and demuxer elements")); - return FALSE; -failed_spu_connect: + goto error_out; +failed_vidq_connect: GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL), - ("Could not connect DVD video buffer and spu elements")); - if (src != NULL) - gst_object_unref (src); - if (sink != NULL) - gst_object_unref (sink); - return FALSE; + ("Could not connect DVD aspect ratio adjuster and video buffer elements")); + goto error_out; +failed_vidq_ghost: + GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL), + ("Could not ghost SPU output pad")); + goto error_out; failed_spu_ghost: GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL), ("Could not ghost SPU output pad")); - if (src != NULL) - gst_object_unref (src); - if (ghost != NULL) - gst_object_unref (ghost); - return FALSE; + goto error_out; failed_aud_connect: GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL), ("Could not connect DVD audio decoder")); - if (src != NULL) - gst_object_unref (src); - if (sink != NULL) - gst_object_unref (sink); - return FALSE; + goto error_out; failed_aud_ghost: GST_ELEMENT_ERROR (dvdbin, CORE, FAILED, (NULL), ("Could not ghost audio output pad")); + goto error_out; +error_out: if (ghost != NULL) gst_object_unref (ghost); + if (src != NULL) + gst_object_unref (src); + if (sink != NULL) + gst_object_unref (sink); return FALSE; } @@ -578,7 +592,7 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin) GstCaps *caps; GstStructure *s; - GST_ERROR_OBJECT (dvdbin, "New pad: %" GST_PTR_FORMAT, pad); + GST_DEBUG_OBJECT (dvdbin, "New pad: %" GST_PTR_FORMAT, pad); caps = gst_pad_get_caps (pad); if (caps == NULL) { @@ -622,7 +636,7 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin) mq_pad = connect_thru_mq (dvdbin, pad); if (mq_pad == NULL) goto failed; - GST_ERROR_OBJECT (dvdbin, "Linking new pad %" GST_PTR_FORMAT + GST_DEBUG_OBJECT (dvdbin, "Linking new pad %" GST_PTR_FORMAT " through multiqueue to %" GST_PTR_FORMAT, pad, dest_pad); } @@ -648,9 +662,9 @@ viddec_pad_added (GstElement * element, GstPad * pad, gboolean last, { GstPad *q_pad; - GST_ERROR_OBJECT (dvdbin, "New video pad: %" GST_PTR_FORMAT, pad); + GST_DEBUG_OBJECT (dvdbin, "New video pad: %" GST_PTR_FORMAT, pad); - q_pad = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDQ], "sink"); + q_pad = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_PARSET], "sink"); gst_pad_link (pad, q_pad); gst_object_unref (q_pad); @@ -663,7 +677,7 @@ auddec_pad_added (GstElement * element, GstPad * pad, gboolean last, { GstPad *out_pad; - GST_ERROR_OBJECT (dvdbin, "New audio pad: %" GST_PTR_FORMAT, pad); + GST_DEBUG_OBJECT (dvdbin, "New audio pad: %" GST_PTR_FORMAT, pad); out_pad = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_AUD_MUNGE], "sink"); diff --git a/ext/resindvd/resindvdbin.h b/ext/resindvd/resindvdbin.h index 87f4261e..7844e94a 100644 --- a/ext/resindvd/resindvdbin.h +++ b/ext/resindvd/resindvdbin.h @@ -44,12 +44,13 @@ typedef struct _RsnDvdBinClass RsnDvdBinClass; #define DVD_ELEM_MQUEUE 2 #define DVD_ELEM_SPU 3 #define DVD_ELEM_VIDDEC 4 -#define DVD_ELEM_AUDDEC 5 -#define DVD_ELEM_VIDQ 6 -#define DVD_ELEM_SPU_SELECT 7 -#define DVD_ELEM_AUD_SELECT 8 -#define DVD_ELEM_AUD_MUNGE 9 -#define DVD_ELEM_LAST 10 +#define DVD_ELEM_PARSET 5 +#define DVD_ELEM_AUDDEC 6 +#define DVD_ELEM_VIDQ 7 +#define DVD_ELEM_SPU_SELECT 8 +#define DVD_ELEM_AUD_SELECT 9 +#define DVD_ELEM_AUD_MUNGE 10 +#define DVD_ELEM_LAST 11 struct _RsnDvdBin { diff --git a/ext/resindvd/resindvdsrc.c b/ext/resindvd/resindvdsrc.c index ce733667..3f06855e 100644 --- a/ext/resindvd/resindvdsrc.c +++ b/ext/resindvd/resindvdsrc.c @@ -344,6 +344,12 @@ rsn_dvdsrc_start (RsnBaseSrc * bsrc) src->in_menu = FALSE; src->active_button = -1; + + src->cur_spu_phys_stream = -1; + src->cur_spu_forced_only = FALSE; + memset (src->cur_clut, 0, sizeof (guint32) * 16); + src->cur_audio_phys_stream = -1; + g_mutex_unlock (src->dvd_lock); return TRUE; @@ -494,36 +500,46 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration) GstEvent *seg_event; GstSegment *segment = &(GST_BASE_SRC (src)->segment); - g_print ("**** STILL FRAME. Duration %d ****\n", duration); + if (src->in_still_state == FALSE) { + g_print ("**** STILL FRAME. Duration %d ****\n", duration); + /* Send a close-segment event, and a dvd-still start + * event, then sleep */ + s = gst_structure_new ("application/x-gst-dvd", + "event", G_TYPE_STRING, "dvd-still", + "still-state", G_TYPE_BOOLEAN, TRUE, NULL); + still_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s); - /* Send a close-segment event, and a dvd-still start - * event, then sleep */ - s = gst_structure_new ("application/x-gst-dvd", - "event", G_TYPE_STRING, "dvd-still", - "still-state", G_TYPE_BOOLEAN, TRUE, NULL); - still_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s); + gst_segment_set_last_stop (segment, GST_FORMAT_TIME, 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); - 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; - /* 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 */ + 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) { + g_print ("Sending highlight event before still\n"); + gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event); + } + g_mutex_lock (src->dvd_lock); - /* Now, send the events. We need to drop the dvd lock while doing so, - * 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); + + src->in_still_state = TRUE; + } else { + GST_DEBUG_OBJECT (src, "Re-entering still wait"); + g_mutex_lock (src->branch_lock); + } - g_mutex_lock (src->branch_lock); if (src->branching) { + GST_INFO_OBJECT (src, "Branching - aborting still"); g_mutex_unlock (src->branch_lock); return TRUE; } @@ -535,29 +551,63 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration) * don't skip it */ src->need_segment = TRUE; + g_mutex_unlock (src->dvd_lock); + GST_LOG_OBJECT (src, "Entering cond_wait still"); g_cond_wait (src->still_cond, src->branch_lock); + GST_LOG_OBJECT (src, "cond_wait still over, branching = %d", + src->branching); + if (src->branching) { g_mutex_unlock (src->branch_lock); g_mutex_lock (src->dvd_lock); return TRUE; } + src->in_still_state = FALSE; + g_mutex_unlock (src->branch_lock); g_mutex_lock (src->dvd_lock); } else { + GTimeVal end_time; + gboolean was_signalled; + + g_get_current_time (&end_time); + g_time_val_add (&end_time, duration * G_USEC_PER_SEC); + /* FIXME: Implement timed stills by sleeping on the clock, possibly * in multiple steps if we get paused/unpaused */ - if (dvdnav_still_skip (src->dvdnav) != DVDNAV_STATUS_OK) + g_mutex_unlock (src->dvd_lock); + GST_LOG_OBJECT (src, "cond_timed_wait still"); + was_signalled = + g_cond_timed_wait (src->still_cond, src->branch_lock, &end_time); + was_signalled |= src->branching; + + g_mutex_unlock (src->branch_lock); + g_mutex_lock (src->dvd_lock); + + if (was_signalled) { + /* Signalled - must be flushing */ + GST_LOG_OBJECT (src, + "cond_timed_wait still over. Signalled, branching = %d", + src->branching); + return TRUE; + } + + /* Else timed out, end the still */ + GST_DEBUG_OBJECT (src, + "Timed still of %d secs over, calling dvdnav_still_skip", duration); + + if (dvdnav_still_skip (src->dvdnav) != DVDNAV_STATUS_OK) { return FALSE; + } - /* Later: We'll only do this if the still isn't interrupted: */ + /* Tell downstream the still is over. + * Later: We'll only do this if the still isn't interrupted: */ s = gst_structure_new ("application/x-gst-dvd", "event", G_TYPE_STRING, "dvd-still", - "still-state", G_TYPE_BOOLEAN, TRUE, NULL); + "still-state", G_TYPE_BOOLEAN, FALSE, NULL); still_event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s); - g_mutex_unlock (src->branch_lock); - g_mutex_unlock (src->dvd_lock); gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event); g_mutex_lock (src->dvd_lock); @@ -596,6 +646,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) src->next_is_nav_block = FALSE; src->next_nav_ts = GST_CLOCK_TIME_NONE; src->alloc_buf = NULL; + src->in_still_state = FALSE; break; case DVDNAV_NAV_PACKET: { @@ -605,6 +656,8 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) GstClockTimeDiff new_base_time = ifotime_to_gsttime (&pci->pci_gi.e_eltm); gboolean discont = FALSE; + src->in_still_state = FALSE; + if (new_start_ptm != src->cur_end_ts) discont = TRUE; @@ -655,15 +708,13 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) } case DVDNAV_STOP: /* End of the disc. EOS */ - g_print ("STOP found. End of disc\n"); + dvdnav_reset (src->dvdnav); ret = GST_FLOW_UNEXPECTED; break; case DVDNAV_STILL_FRAME: { dvdnav_still_event_t *info = (dvdnav_still_event_t *) data; - g_print ("STILL frame duration %d\n", info->length); - if (!have_dvd_lock) { /* At a still frame but can't block, handle it later */ return GST_FLOW_WOULD_BLOCK; @@ -764,7 +815,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) src->highlight_event = NULL; g_mutex_unlock (src->dvd_lock); - g_print ("Highlight change - button: %d\n", src->active_button); + g_print ("Sending highlight event - button %d\n", src->active_button); gst_pad_push_event (GST_BASE_SRC_PAD (src), hl_event); g_mutex_lock (src->dvd_lock); } @@ -860,8 +911,6 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf) gst_pad_push_event (GST_BASE_SRC_PAD (src), audio_select_event); } - g_mutex_lock (src->dvd_lock); - if (src->need_segment) { /* Seamless segment update */ GstEvent *seek; @@ -871,6 +920,9 @@ rsn_dvdsrc_create (RsnPushSrc * psrc, GstBuffer ** outbuf) gst_element_send_event (GST_ELEMENT (src), seek); src->need_segment = FALSE; } + + g_mutex_lock (src->dvd_lock); + if (src->cur_end_ts != GST_CLOCK_TIME_NONE) gst_segment_set_last_stop (segment, GST_FORMAT_TIME, src->cur_end_ts); @@ -1037,15 +1089,8 @@ rsn_dvdsrc_handle_navigation_event (resinDvdSrc * src, GstEvent * event) channel_hop = TRUE; } } else if (g_str_equal (key, "period")) { - gint title = 0; - gint part = 0; - - if (dvdnav_current_title_info (src->dvdnav, &title, &part) && title > 0) { - if (dvdnav_part_play (src->dvdnav, title, part + 1) == - DVDNAV_STATUS_ERR) - dvdnav_next_pg_search (src->dvdnav); - channel_hop = TRUE; - } + dvdnav_next_pg_search (src->dvdnav); + channel_hop = TRUE; } else { g_print ("Unknown keypress: %s\n", key); } @@ -1152,6 +1197,10 @@ rsn_dvdsrc_prepare_audio_stream_event (resinDvdSrc * src, guint8 phys_stream) GstStructure *s; GstEvent *e; + if (phys_stream == src->cur_audio_phys_stream) + return; + src->cur_audio_phys_stream = phys_stream; + s = gst_structure_new ("application/x-gst-dvd", "event", G_TYPE_STRING, "dvd-set-audio-track", "physical-id", G_TYPE_INT, (gint) phys_stream, NULL); @@ -1170,6 +1219,14 @@ rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src, guint8 phys_stream, GstStructure *s; GstEvent *e; + if (phys_stream == src->cur_spu_phys_stream && + forced_only == src->cur_spu_forced_only) { + g_print ("Not preparing SPU change\n"); + return; + } + src->cur_spu_phys_stream = phys_stream; + src->cur_spu_forced_only = forced_only; + s = gst_structure_new ("application/x-gst-dvd", "event", G_TYPE_STRING, "dvd-set-subpicture-track", "physical-id", G_TYPE_INT, (gint) phys_stream, @@ -1186,6 +1243,7 @@ static gboolean rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) { vtsi_mat_t *vts_attr; + video_attr_t *v_attr; audio_attr_t *a_attrs; subp_attr_t *s_attrs; gint n_audio, n_subp; @@ -1194,6 +1252,7 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) gint i; gchar lang_code[3] = { '\0', '\0', '\0' }; gchar *t; + gboolean is_widescreen; if (src->vts_attrs == NULL || src->vts_n >= src->vts_attrs->len) { if (src->vts_attrs) @@ -1207,6 +1266,7 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) if (src->vts_n == 0) { /* VMGM info */ vts_attr = NULL; + v_attr = &src->vmgm_attr.vmgm_video_attr; a_attrs = &src->vmgm_attr.vmgm_audio_attr; n_audio = MIN (1, src->vmgm_attr.nr_of_vmgm_audio_streams); s_attrs = &src->vmgm_attr.vmgm_subp_attr; @@ -1214,6 +1274,7 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) } else if (src->in_menu) { /* VTSM attrs */ vts_attr = &g_array_index (src->vts_attrs, vtsi_mat_t, src->vts_n); + v_attr = &vts_attr->vtsm_video_attr; a_attrs = &vts_attr->vtsm_audio_attr; n_audio = vts_attr->nr_of_vtsm_audio_streams; s_attrs = &vts_attr->vtsm_subp_attr; @@ -1221,6 +1282,7 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) } else { /* VTS domain */ vts_attr = &g_array_index (src->vts_attrs, vtsi_mat_t, src->vts_n); + v_attr = &vts_attr->vts_video_attr; a_attrs = vts_attr->vts_audio_attr; n_audio = vts_attr->nr_of_vts_audio_streams; s_attrs = vts_attr->vts_subp_attr; @@ -1232,6 +1294,13 @@ rsn_dvdsrc_prepare_streamsinfo_event (resinDvdSrc * src) "event", G_TYPE_STRING, "dvd-lang-codes", NULL); e = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, s); + /* video */ + is_widescreen = (v_attr->display_aspect_ratio != 0); + gst_structure_set (s, "video-pal-format", G_TYPE_BOOLEAN, + (v_attr->video_format != 0), NULL); + gst_structure_set (s, "video-widescreen", G_TYPE_BOOLEAN, is_widescreen, + NULL); + /* audio */ if (n_audio == 0) { /* Always create at least one audio stream */ @@ -1301,6 +1370,10 @@ rsn_dvdsrc_prepare_clut_change_event (resinDvdSrc * src, const guint32 * clut) gchar name[16]; int i; + if (memcmp (src->cur_clut, clut, sizeof (guint32) * 16) == 0) + return; + memcpy (src->cur_clut, clut, sizeof (guint32) * 16); + structure = gst_structure_new ("application/x-gst-dvd", "event", G_TYPE_STRING, "dvd-spu-clut-change", NULL); @@ -1492,9 +1565,9 @@ rsn_dvdsrc_nav_clock_cb (GstClock * clock, GstClockTime time, GstClockID id, 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)); + GST_DEBUG_OBJECT (src, "Activating nav pack for with TS %" GST_TIME_FORMAT + " at running TS %" GST_TIME_FORMAT, GST_TIME_ARGS (cur->ts), + GST_TIME_ARGS (cur->running_ts)); rsn_dvdsrc_activate_nav_block (src, cur->buffer); gst_buffer_unref (cur->buffer); @@ -1523,29 +1596,31 @@ 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) + GST_OBJECT_LOCK (src); + if (GST_STATE (src) != GST_STATE_PLAYING) { + GST_OBJECT_UNLOCK (src); 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) + if (clock == NULL) { + GST_OBJECT_UNLOCK (src); return; + } + gst_object_ref (clock); + + src->nav_clock_id = gst_clock_new_single_shot_id (clock, + base_ts + next_nav->running_ts); + + GST_OBJECT_UNLOCK (src); 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); } @@ -1725,10 +1800,10 @@ rsn_dvdsrc_do_seek (RsnBaseSrc * bsrc, GstSegment * segment) g_mutex_unlock (src->dvd_lock); } - g_print ("Entering prepare_next_block after seek\n"); + GST_LOG_OBJECT (src, "Entering prepare_next_block after seek"); if (rsn_dvdsrc_prepare_next_block (src, FALSE) != GST_FLOW_OK) goto fail; - g_print ("prepare_next_block after seek done\n"); + GST_LOG_OBJECT (src, "prepare_next_block after seek done"); segment->format = GST_FORMAT_TIME; /* The first TS output: */ diff --git a/ext/resindvd/resindvdsrc.h b/ext/resindvd/resindvdsrc.h index 0b273da7..c0fa8e1d 100644 --- a/ext/resindvd/resindvdsrc.h +++ b/ext/resindvd/resindvdsrc.h @@ -88,6 +88,7 @@ struct _resinDvdSrc gboolean flushing_seek; gboolean need_segment; gboolean active_highlight; + gboolean in_still_state; GstBuffer *alloc_buf; GstBuffer *next_buf; @@ -128,6 +129,12 @@ struct _resinDvdSrc gboolean have_pci; pci_t cur_pci; + + /* Current state tracking */ + gint8 cur_audio_phys_stream; + gint8 cur_spu_phys_stream; + gboolean cur_spu_forced_only; + guint32 cur_clut[16]; }; struct _resinDvdSrcClass diff --git a/ext/resindvd/rsnaudiomunge.c b/ext/resindvd/rsnaudiomunge.c index f0429ee9..457be163 100644 --- a/ext/resindvd/rsnaudiomunge.c +++ b/ext/resindvd/rsnaudiomunge.c @@ -273,8 +273,6 @@ rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_STOP: rsn_audiomunge_reset (munge); - - g_print ("*********** AUDIO MUNGE: FLUSH\n"); ret = gst_pad_push_event (munge->srcpad, event); break; case GST_EVENT_NEWSEGMENT: @@ -299,11 +297,6 @@ rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event) rate, arate, format, start, stop, time); if (munge->have_audio) { - g_print ("*********** AUDIO MUNGE NEWSEG: start %" GST_TIME_FORMAT - " stop %" GST_TIME_FORMAT " accum now %" GST_TIME_FORMAT - "\n", GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (segment->accum)); - ret = gst_pad_push_event (munge->srcpad, event); break; } @@ -319,21 +312,23 @@ rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event) * in the closing segment. */ if (segment->accum >= AUDIO_FILL_THRESHOLD || munge->in_still) { - g_print ("*********** Send audio mebbe: accum = %" GST_TIME_FORMAT + g_print ("*********** Sending audio fill: accum = %" GST_TIME_FORMAT " still-state=%d\n", GST_TIME_ARGS (segment->accum), munge->in_still); + /* Just generate a 100ms silence buffer for now. FIXME: Fill the gap */ if (rsn_audiomunge_make_audio (munge, segment->start, GST_SECOND / 10) == GST_FLOW_OK) munge->have_audio = TRUE; } else { - g_print ("*********** below thresh: accum = %" GST_TIME_FORMAT - "\n", GST_TIME_ARGS (segment->accum)); + GST_LOG_OBJECT (munge, "Not sending audio fill buffer: " + "segment accum below thresh: accum = %" GST_TIME_FORMAT, + GST_TIME_ARGS (segment->accum)); } - g_print ("*********** AUDIO MUNGE NEWSEG: start %" GST_TIME_FORMAT - " stop %" GST_TIME_FORMAT " accum now %" GST_TIME_FORMAT - "\n", GST_TIME_ARGS (start), GST_TIME_ARGS (stop), + GST_DEBUG_OBJECT (munge, "Sending newsegment: start %" GST_TIME_FORMAT + " stop %" GST_TIME_FORMAT " accum now %" GST_TIME_FORMAT, + GST_TIME_ARGS (start), GST_TIME_ARGS (stop), GST_TIME_ARGS (segment->accum)); ret = gst_pad_push_event (munge->srcpad, event); @@ -342,6 +337,7 @@ rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event) case GST_EVENT_CUSTOM_DOWNSTREAM: { const GstStructure *s = gst_event_get_structure (event); + if (s && gst_structure_has_name (s, "application/x-gst-dvd")) rsn_audiomunge_handle_dvd_event (munge, event); diff --git a/ext/resindvd/rsnbasesrc.c b/ext/resindvd/rsnbasesrc.c index 220b350c..abbdf5e4 100644 --- a/ext/resindvd/rsnbasesrc.c +++ b/ext/resindvd/rsnbasesrc.c @@ -1046,9 +1046,6 @@ rsn_base_src_perform_seek (RsnBaseSrc * src, GstEvent * event, gboolean unlock) 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) { diff --git a/ext/resindvd/rsnparsetter.c b/ext/resindvd/rsnparsetter.c new file mode 100644 index 00000000..65de2ac3 --- /dev/null +++ b/ext/resindvd/rsnparsetter.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net> + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <string.h> + +#include "rsnparsetter.h" +#include "rsnwrappedbuffer.h" + +GST_DEBUG_CATEGORY_STATIC (rsn_parsetter_debug); +#define GST_CAT_DEFAULT rsn_parsetter_debug + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-rgb; video/x-raw-yuv") + ); + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw-rgb; video/x-raw-yuv") + ); + +static void rsn_parsetter_register_extra (GType rsn_parsetter_type); + +GST_BOILERPLATE_FULL (RsnParSetter, rsn_parsetter, GstElement, + GST_TYPE_ELEMENT, rsn_parsetter_register_extra); + +static void rsn_parsetter_finalize (GObject * object); +static GstFlowReturn rsn_parsetter_chain (GstPad * pad, GstBuffer * buf); +static gboolean rsn_parsetter_sink_event (GstPad * pad, GstEvent * event); +static gboolean rsn_parsetter_sink_setcaps (GstPad * pad, GstCaps * caps); +static GstFlowReturn rsn_parsetter_sink_bufferalloc (GstPad * pad, + guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); + +static GstCaps *rsn_parsetter_src_getcaps (GstPad * pad); +static GstCaps *rsn_parsetter_convert_caps (RsnParSetter * parset, + GstCaps * caps, gboolean widescreen); +static gboolean rsn_parsetter_check_caps (RsnParSetter * parset, + GstCaps * caps); + +static void +rsn_parsetter_register_extra (GType rsn_parsetter_type) +{ + GST_DEBUG_CATEGORY_INIT (rsn_parsetter_debug, "rsnparsetter", 0, + "Resin DVD aspect ratio adjuster"); +} + +static void +rsn_parsetter_base_init (gpointer gclass) +{ + static GstElementDetails element_details = { + "Resin Aspect Ratio Setter", + "Filter/Video", + "Overrides caps on video buffers to force a particular display ratio", + "Jan Schmidt <thaytan@noraisin.net>" + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&sink_factory)); + gst_element_class_set_details (element_class, &element_details); +} + +static void +rsn_parsetter_class_init (RsnParSetterClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = rsn_parsetter_finalize; +} + +static void +rsn_parsetter_init (RsnParSetter * parset, RsnParSetterClass * gclass) +{ + parset->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); + gst_pad_set_getcaps_function (parset->sinkpad, + GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); + gst_pad_set_chain_function (parset->sinkpad, + GST_DEBUG_FUNCPTR (rsn_parsetter_chain)); + gst_pad_set_event_function (parset->sinkpad, + GST_DEBUG_FUNCPTR (rsn_parsetter_sink_event)); + gst_pad_set_setcaps_function (parset->sinkpad, + GST_DEBUG_FUNCPTR (rsn_parsetter_sink_setcaps)); + gst_pad_set_bufferalloc_function (parset->sinkpad, + GST_DEBUG_FUNCPTR (rsn_parsetter_sink_bufferalloc)); + gst_element_add_pad (GST_ELEMENT (parset), parset->sinkpad); + + parset->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); + gst_pad_set_getcaps_function (parset->srcpad, + GST_DEBUG_FUNCPTR (rsn_parsetter_src_getcaps)); + gst_element_add_pad (GST_ELEMENT (parset), parset->srcpad); + + parset->caps_lock = g_mutex_new (); +} + +static void +rsn_parsetter_finalize (GObject * object) +{ + RsnParSetter *parset = RSN_PARSETTER (object); + + gst_caps_replace (&parset->outcaps, NULL); + gst_caps_replace (&parset->in_caps_last, NULL); + gst_caps_replace (&parset->in_caps_converted, NULL); + + g_mutex_free (parset->caps_lock); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GstFlowReturn +rsn_parsetter_chain (GstPad * pad, GstBuffer * buf) +{ + RsnParSetter *parset = RSN_PARSETTER (GST_OBJECT_PARENT (pad)); + + /* If this is a buffer we wrapped up earlier, unwrap it now */ + if (RSN_IS_WRAPPEDBUFFER (buf)) { + RsnWrappedBuffer *wrap_buf = RSN_WRAPPEDBUFFER (buf); + + if (wrap_buf->owner == GST_ELEMENT (parset)) { + buf = rsn_wrappedbuffer_unwrap_and_unref (wrap_buf); + GST_DEBUG_OBJECT (parset, "Unwrapping %p yields buffer %p with caps %" + GST_PTR_FORMAT, wrap_buf, buf, GST_BUFFER_CAPS (buf)); + } + } + + if (parset->outcaps != GST_BUFFER_CAPS (buf)) { + if (parset->override_outcaps == FALSE && + gst_caps_is_equal (parset->outcaps, GST_BUFFER_CAPS (buf))) { + /* Just update our output caps var */ + gst_caps_replace (&parset->outcaps, GST_BUFFER_CAPS (buf)); + goto out; + } + + /* Replace the caps on the output buffer */ + buf = gst_buffer_make_metadata_writable (buf); + gst_buffer_set_caps (buf, parset->outcaps); + + GST_DEBUG_OBJECT (parset, + "Replacing caps on buffer %p with caps %" GST_PTR_FORMAT, + buf, parset->outcaps); + } + +out: + return gst_pad_push (parset->srcpad, buf); +} + +static gboolean +rsn_parsetter_sink_event (GstPad * pad, GstEvent * event) +{ + RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad)); + const GstStructure *structure = gst_event_get_structure (event); + + if (structure != NULL && + gst_structure_has_name (structure, "application/x-gst-dvd")) { + const char *type = gst_structure_get_string (structure, "event"); + if (type == NULL) + goto out; + + if (strcmp (type, "dvd-video-format") == 0) { + gboolean is_widescreen; + + gst_structure_get_boolean (structure, "video-widescreen", &is_widescreen); + + GST_DEBUG_OBJECT (parset, "Video is %s", + parset->is_widescreen ? "16:9" : "4:3"); + + g_mutex_lock (parset->caps_lock); + if (parset->is_widescreen != is_widescreen) { + /* Force caps check */ + gst_caps_replace (&parset->in_caps_last, NULL); + gst_caps_replace (&parset->in_caps_converted, NULL); + } + parset->is_widescreen = is_widescreen; + + /* FIXME: Added for testing: */ + // parset->is_widescreen = FALSE; + + g_mutex_unlock (parset->caps_lock); + } + } + +out: + gst_object_unref (GST_OBJECT (parset)); + return gst_pad_event_default (pad, event); +} + +static GstCaps * +rsn_parsetter_src_getcaps (GstPad * pad) +{ + RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad)); + GstCaps *ret; + const GstCaps *templ_caps = gst_pad_get_pad_template_caps (pad); + + ret = gst_pad_peer_get_caps (parset->sinkpad); + if (ret == NULL) + ret = gst_caps_copy (templ_caps); + else { + GstCaps *temp; + temp = gst_caps_intersect (templ_caps, ret); + gst_caps_unref (ret); + ret = rsn_parsetter_convert_caps (parset, temp, parset->is_widescreen); + gst_caps_unref (temp); + } + + gst_object_unref (parset); + return ret; +} + +static gboolean +rsn_parsetter_check_caps (RsnParSetter * parset, GstCaps * caps) +{ + GstStructure *s; + gint width, height; + gint par_n, par_d; + guint dar_n, dar_d; + gboolean ret = FALSE; + + g_mutex_lock (parset->caps_lock); + + if (caps == parset->in_caps_last || + gst_caps_is_equal (caps, parset->in_caps_last)) { + ret = parset->in_caps_was_ok; + goto out; + } + + /* Calculate the DAR from the incoming caps, and return TRUE if it matches + * the required DAR, FALSE if not */ + s = gst_caps_get_structure (caps, 0); + if (s == NULL) + goto out; + + if (!gst_structure_get_int (s, "width", &width) || + !gst_structure_get_int (s, "height", &height)) + goto out; + + if (!gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) + par_n = par_d = 1; + + if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, width, height, + par_n, par_d, 1, 1)) + goto out; + + GST_DEBUG_OBJECT (parset, + "Incoming video caps now: w %d h %d PAR %d/%d = DAR %d/%d", + width, height, par_n, par_d, dar_n, dar_d); + + if (parset->is_widescreen) { + if (dar_n == 16 && dar_d == 9) + ret = TRUE; + } else { + if (dar_n == 4 && dar_d == 3) + ret = TRUE; + } + + gst_caps_replace (&parset->in_caps_last, caps); + gst_caps_replace (&parset->in_caps_converted, NULL); + parset->in_caps_was_ok = ret; + +out: + g_mutex_unlock (parset->caps_lock); + return ret; +} + +static GstCaps * +rsn_parsetter_convert_caps (RsnParSetter * parset, GstCaps * caps, + gboolean widescreen) +{ + /* Duplicate the given caps, with a PAR that provides the desired DAR */ + GstCaps *outcaps; + GstStructure *s; + gint width, height; + gint par_n, par_d; + guint dar_n, dar_d; + GValue par = { 0, }; + + g_mutex_lock (parset->caps_lock); + if (caps == parset->in_caps_last && parset->in_caps_converted) { + outcaps = gst_caps_ref (parset->in_caps_converted); + goto out; + } + + outcaps = gst_caps_copy (caps); + + /* Calculate the DAR from the incoming caps, and return TRUE if it matches + * the required DAR, FALSE if not */ + s = gst_caps_get_structure (outcaps, 0); + if (s == NULL) + goto out; + + if (!gst_structure_get_int (s, "width", &width) || + !gst_structure_get_int (s, "height", &height)) + goto out; + + if (widescreen) { + dar_n = 16; + dar_d = 9; + } else { + dar_n = 4; + dar_d = 3; + } + + par_n = dar_n * height; + par_d = dar_d * width; + + g_value_init (&par, GST_TYPE_FRACTION); + gst_value_set_fraction (&par, par_n, par_d); + gst_structure_set_value (s, "pixel-aspect-ratio", &par); + g_value_unset (&par); + + gst_caps_replace (&parset->in_caps_converted, outcaps); +out: + g_mutex_unlock (parset->caps_lock); + return outcaps; +} + +static gboolean +rsn_parsetter_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + /* Check the new incoming caps against our current DAR, and mark + * whether the buffers will need adjusting */ + RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad)); + + if (rsn_parsetter_check_caps (parset, caps)) { + parset->override_outcaps = FALSE; + gst_caps_replace (&parset->outcaps, caps); + } else { + GstCaps *override_caps = rsn_parsetter_convert_caps (parset, caps, + parset->is_widescreen); + if (parset->outcaps) + gst_caps_unref (parset->outcaps); + parset->outcaps = override_caps; + + parset->override_outcaps = TRUE; + } + + GST_DEBUG_OBJECT (parset, "caps changed: need_override now = %d", + parset->override_outcaps); + + gst_object_unref (parset); + return TRUE; +} + +static GstFlowReturn +rsn_parsetter_sink_bufferalloc (GstPad * pad, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + RsnParSetter *parset = RSN_PARSETTER (gst_pad_get_parent (pad)); + GstFlowReturn ret; + + GST_LOG_OBJECT (parset, "Entering bufferalloc"); + + if (rsn_parsetter_check_caps (parset, caps)) { + ret = gst_pad_alloc_buffer (parset->srcpad, offset, size, caps, buf); + GST_LOG_OBJECT (parset, "Not wrapping buf %p", *buf); + } else { + /* Allocate and wrap a downstream buffer */ + GstBuffer *orig_buf; + GstBuffer *outbuf; + GstCaps *override_caps = rsn_parsetter_convert_caps (parset, caps, + parset->is_widescreen); + + ret = gst_pad_alloc_buffer (parset->srcpad, offset, size, + override_caps, &orig_buf); + gst_caps_unref (override_caps); + + if (ret != GST_FLOW_OK) + return ret; + + outbuf = (GstBuffer *) rsn_wrapped_buffer_new (orig_buf); + if (!outbuf) { + /* FIXME: Throw error */ + return GST_FLOW_ERROR; + } + + rsn_wrapped_buffer_set_owner (RSN_WRAPPEDBUFFER (outbuf), + GST_ELEMENT (parset)); + + gst_buffer_set_caps (outbuf, caps); + + GST_LOG_OBJECT (parset, + "Wrapped ds buf %p with caps %" GST_PTR_FORMAT + " into new buf %p with caps %" GST_PTR_FORMAT, + orig_buf, GST_BUFFER_CAPS (orig_buf), outbuf, GST_BUFFER_CAPS (outbuf)); + + *buf = outbuf; + } + + gst_object_unref (GST_OBJECT (parset)); + + return ret; +} diff --git a/ext/resindvd/rsnparsetter.h b/ext/resindvd/rsnparsetter.h new file mode 100644 index 00000000..5e89bb1c --- /dev/null +++ b/ext/resindvd/rsnparsetter.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net> + */ + +#ifndef __RSN_PARSETTER_H__ +#define __RSN_PARSETTER_H__ + +#include <gst/gst.h> + +#include "rsnwrappedbuffer.h" + +G_BEGIN_DECLS + +#define RSN_TYPE_RSNPARSETTER \ + (rsn_parsetter_get_type()) +#define RSN_PARSETTER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),RSN_TYPE_RSNPARSETTER,RsnParSetter)) +#define RSN_PARSETTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),RSN_TYPE_RSNPARSETTER,RsnParSetterClass)) +#define RSN_IS_PARSETTER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),RSN_TYPE_RSNPARSETTER)) +#define RSN_IS_PARSETTER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),RSN_TYPE_RSNPARSETTER)) + +typedef struct _RsnParSetter RsnParSetter; +typedef struct _RsnParSetterClass RsnParSetterClass; + +struct _RsnParSetter +{ + GstElement element; + + GstPad *sinkpad, *srcpad; + + gboolean override_outcaps; + GstCaps *outcaps; + + gboolean is_widescreen; + + GMutex *caps_lock; + GstCaps *in_caps_last; + gboolean in_caps_was_ok; + GstCaps *in_caps_converted; +}; + +struct _RsnParSetterClass +{ + GstElementClass parent_class; +}; + +GType rsn_parsetter_get_type (void); + +G_END_DECLS + +#endif /* __RSN_PARSETTER_H__ */ diff --git a/ext/resindvd/rsnwrappedbuffer.c b/ext/resindvd/rsnwrappedbuffer.c new file mode 100644 index 00000000..e394e429 --- /dev/null +++ b/ext/resindvd/rsnwrappedbuffer.c @@ -0,0 +1,152 @@ +/* GStreamer + * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <string.h> + +#include <gst/gst.h> + +#include "rsnwrappedbuffer.h" + +GST_BOILERPLATE (RsnWrappedBuffer, rsn_wrappedbuffer, + GstBuffer, GST_TYPE_BUFFER); + +static gboolean +rsn_wrapped_buffer_default_release (GstElement * owner, RsnWrappedBuffer * buf); + +static void rsn_wrapped_buffer_finalize (RsnWrappedBuffer * wrap_buf); + +static void +rsn_wrappedbuffer_base_init (gpointer gclass) +{ +} + +static void +rsn_wrappedbuffer_class_init (RsnWrappedBufferClass * klass) +{ + GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (klass); + + mini_object_class->finalize = (GstMiniObjectFinalizeFunction) + rsn_wrapped_buffer_finalize; +} + +static void +rsn_wrappedbuffer_init (RsnWrappedBuffer * buf, RsnWrappedBufferClass * gclass) +{ + buf->release = rsn_wrapped_buffer_default_release; +} + +static void +rsn_wrapped_buffer_finalize (RsnWrappedBuffer * wrap_buf) +{ + if (wrap_buf->release) { + /* Release might increment the refcount to recycle and return TRUE, + * in which case, exit without chaining up */ + if (wrap_buf->release (wrap_buf->owner, wrap_buf)) + return; + } + + GST_MINI_OBJECT_CLASS (parent_class)->finalize (GST_MINI_OBJECT (wrap_buf)); +} + +RsnWrappedBuffer * +rsn_wrapped_buffer_new (GstBuffer * buf_to_wrap) +{ + RsnWrappedBuffer *buf; + g_return_val_if_fail (buf_to_wrap, NULL); + + buf = (RsnWrappedBuffer *) gst_mini_object_new (RSN_TYPE_WRAPPEDBUFFER); + if (buf == NULL) + return NULL; + + buf->wrapped_buffer = buf_to_wrap; + + GST_BUFFER_DATA (buf) = GST_BUFFER_DATA (buf_to_wrap); + GST_BUFFER_SIZE (buf) = GST_BUFFER_SIZE (buf_to_wrap); + gst_buffer_copy_metadata (GST_BUFFER (buf), buf_to_wrap, GST_BUFFER_COPY_ALL); + + /* If the wrapped buffer isn't writable, make sure this one isn't either */ + if (!gst_buffer_is_writable (buf_to_wrap)) + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY); + + return buf; +} + +void +rsn_wrapped_buffer_set_owner (RsnWrappedBuffer * wrapped_buf, + GstElement * owner) +{ + g_return_if_fail (wrapped_buf != NULL); + + if (wrapped_buf->owner) + gst_object_unref (wrapped_buf->owner); + + if (owner) + wrapped_buf->owner = gst_object_ref (owner); + else + wrapped_buf->owner = NULL; +} + +void +rsn_wrapped_buffer_set_releasefunc (RsnWrappedBuffer * wrapped_buf, + RsnWrappedBufferReleaseFunc release_func) +{ + g_return_if_fail (wrapped_buf != NULL); + + wrapped_buf->release = release_func; +} + +static gboolean +rsn_wrapped_buffer_default_release (GstElement * owner, RsnWrappedBuffer * buf) +{ + g_return_val_if_fail (buf != NULL, FALSE); + g_return_val_if_fail (buf->wrapped_buffer != NULL, FALSE); + + gst_buffer_unref (buf->wrapped_buffer); + if (buf->owner) + gst_object_unref (buf->owner); + + return FALSE; +} + +GstBuffer * +rsn_wrappedbuffer_unwrap_and_unref (RsnWrappedBuffer * wrap_buf) +{ + GstBuffer *buf; + gboolean is_readonly; + + g_return_val_if_fail (wrap_buf != NULL, NULL); + g_return_val_if_fail (wrap_buf->wrapped_buffer != NULL, NULL); + + buf = gst_buffer_ref (wrap_buf->wrapped_buffer); + + /* Copy changed metadata back to the wrapped buffer from the wrapper, + * except the the read-only flag and the caps. */ + is_readonly = GST_BUFFER_FLAG_IS_SET (wrap_buf, GST_BUFFER_FLAG_READONLY); + gst_buffer_copy_metadata (buf, GST_BUFFER (wrap_buf), + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS); + if (!is_readonly) + GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_READONLY); + + gst_buffer_unref (GST_BUFFER (wrap_buf)); + + return buf; +} diff --git a/ext/resindvd/rsnwrappedbuffer.h b/ext/resindvd/rsnwrappedbuffer.h new file mode 100644 index 00000000..7c6a0e73 --- /dev/null +++ b/ext/resindvd/rsnwrappedbuffer.h @@ -0,0 +1,69 @@ +/* GStreamer + * Copyright (C) 2008 Jan Schmidt <thaytan@noraisin.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __RSN_WRAPPERBUFFER_H__ +#define __RSN_WRAPPERBUFFER_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define RSN_TYPE_WRAPPEDBUFFER (rsn_wrappedbuffer_get_type()) +#define RSN_WRAPPEDBUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), RSN_TYPE_WRAPPEDBUFFER, \ + RsnWrappedBuffer)) +#define RSN_WRAPPEDBUFFER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), RSN_TYPE_WRAPPEDBUFFER, \ + RsnWrappedBufferClass)) +#define RSN_IS_WRAPPEDBUFFER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), RSN_TYPE_WRAPPEDBUFFER)) +#define RSN_IS_WRAPPEDBUFFER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), RSN_TYPE_WRAPPEDBUFFER)) + +typedef struct _RsnWrappedBuffer RsnWrappedBuffer; +typedef struct _RsnWrappedBufferClass RsnWrappedBufferClass; + +typedef gboolean (*RsnWrappedBufferReleaseFunc)(GstElement *owner, + RsnWrappedBuffer *buf); + +struct _RsnWrappedBuffer { + GstBuffer buffer; + GstBuffer *wrapped_buffer; + + GstElement *owner; + RsnWrappedBufferReleaseFunc release; +}; + +struct _RsnWrappedBufferClass +{ + GstBufferClass parent_class; +}; + +RsnWrappedBuffer *rsn_wrapped_buffer_new (GstBuffer *buf_to_wrap); +GstBuffer *rsn_wrappedbuffer_unwrap_and_unref (RsnWrappedBuffer *wrap_buf); +void rsn_wrapped_buffer_set_owner (RsnWrappedBuffer *wrapped_buf, + GstElement *owner); +void rsn_wrapped_buffer_set_releasefunc (RsnWrappedBuffer *wrapped_buf, + RsnWrappedBufferReleaseFunc release_func); + +GType rsn_wrappedbuffer_get_type (void); + +G_END_DECLS + +#endif |