From 1218cff3dc70094797476ac531e15b5ab09192ed Mon Sep 17 00:00:00 2001 From: Jan Schmidt Date: Sat, 9 Jun 2012 22:36:06 +1000 Subject: [PATCH] resindvd: Initial partial port to 0.11 --- configure.ac | 2 +- ext/resindvd/Makefile.am | 15 +- ext/resindvd/gstmpegdefs.h | 75 +- ext/resindvd/gstmpegdemux.c | 831 +++++++++----- ext/resindvd/gstmpegdemux.h | 30 +- ext/resindvd/gstpesfilter.c | 198 ++-- ext/resindvd/gstpesfilter.h | 28 +- ext/resindvd/plugin.c | 2 +- ext/resindvd/resindvdbin.c | 159 ++- ext/resindvd/resindvdsrc.c | 137 +-- ext/resindvd/rsnaudiomunge.c | 11 +- ext/resindvd/rsndec.c | 36 +- ext/resindvd/rsninputselector.c | 1784 ++++++++++++++++++++++++++++++ ext/resindvd/rsninputselector.h | 91 ++ ext/resindvd/rsnstreamselector.c | 769 ------------- ext/resindvd/rsnstreamselector.h | 63 -- 16 files changed, 2822 insertions(+), 1409 deletions(-) create mode 100644 ext/resindvd/rsninputselector.c create mode 100644 ext/resindvd/rsninputselector.h delete mode 100644 ext/resindvd/rsnstreamselector.c delete mode 100644 ext/resindvd/rsnstreamselector.h diff --git a/configure.ac b/configure.ac index d58c47bdaa..0f7a00dcd9 100644 --- a/configure.ac +++ b/configure.ac @@ -321,7 +321,7 @@ GST_PLUGINS_NONPORTED=" aiff \ sdi siren speed subenc stereo tta videofilters \ videomeasure videosignal vmnc \ decklink fbdev linsys vcd \ - apexsink cdaudio cog dc1394 dirac directfb resindvd \ + apexsink cdaudio cog dc1394 dirac directfb \ gsettings jasper ladspa \ musepack musicbrainz nas neon ofa openal rsvg sdl sndfile spandsp spc timidity \ directsound directdraw direct3d9 acm wininet \ diff --git a/ext/resindvd/Makefile.am b/ext/resindvd/Makefile.am index 9c3651e728..cf2460ce24 100644 --- a/ext/resindvd/Makefile.am +++ b/ext/resindvd/Makefile.am @@ -5,22 +5,21 @@ plugin_LTLIBRARIES = libgstresindvd.la libgstresindvd_la_SOURCES = \ plugin.c \ resindvdbin.c \ - rsnaudiomunge.c \ - rsndec.c \ - rsnstreamselector.c \ resindvdsrc.c \ + rsndec.c \ gstmpegdesc.c \ gstmpegdemux.c \ gstpesfilter.c \ - rsnparsetter.c \ - rsnwrappedbuffer.c + rsninputselector.c \ + # rsnparsetter.c \ + # rsnwrappedbuffer.c \ + # rsnaudiomunge.c libgstresindvd_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ $(GST_CFLAGS) $(DVDNAV_CFLAGS) libgstresindvd_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ --lgstinterfaces-$(GST_API_VERSION) -lgstvideo-$(GST_API_VERSION) \ --lgstpbutils-$(GST_API_VERSION) \ +-lgstvideo-$(GST_API_VERSION) -lgstpbutils-$(GST_API_VERSION) \ $(GST_BASE_LIBS) $(GST_LIBS) $(DVDNAV_LIBS) libgstresindvd_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstresindvd_la_LIBTOOLFLAGS = --tag=disable-static @@ -28,7 +27,7 @@ libgstresindvd_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = resindvdbin.h \ rsnaudiomunge.h \ rsndec.h \ - rsnstreamselector.h \ + rsninputselector.h \ resindvdsrc.h \ gstmpegdefs.h \ gstmpegdesc.h \ diff --git a/ext/resindvd/gstmpegdefs.h b/ext/resindvd/gstmpegdefs.h index 608769ae0d..a0429e7a2f 100644 --- a/ext/resindvd/gstmpegdefs.h +++ b/ext/resindvd/gstmpegdefs.h @@ -125,30 +125,56 @@ * 0x0F-0x7F ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved * 0x80-0xFF User Private */ -#define ST_RESERVED 0x00 -#define ST_VIDEO_MPEG1 0x01 -#define ST_VIDEO_MPEG2 0x02 -#define ST_AUDIO_MPEG1 0x03 -#define ST_AUDIO_MPEG2 0x04 -#define ST_PRIVATE_SECTIONS 0x05 -#define ST_PRIVATE_DATA 0x06 -#define ST_MHEG 0x07 -#define ST_DSMCC 0x08 -#define ST_H222_1 0x09 +#define ST_RESERVED 0x00 +#define ST_VIDEO_MPEG1 0x01 +#define ST_VIDEO_MPEG2 0x02 +#define ST_AUDIO_MPEG1 0x03 +#define ST_AUDIO_MPEG2 0x04 +#define ST_PRIVATE_SECTIONS 0x05 +#define ST_PRIVATE_DATA 0x06 +#define ST_MHEG 0x07 +#define ST_DSMCC 0x08 +#define ST_H222_1 0x09 /* later extensions */ -#define ST_AUDIO_AAC 0x0f -#define ST_VIDEO_MPEG4 0x10 -#define ST_VIDEO_H264 0x1b +#define ST_AUDIO_AAC_ADTS 0x0f +/* LATM/LOAS AAC syntax */ +#define ST_AUDIO_AAC_LOAS 0x11 +#define ST_VIDEO_MPEG4 0x10 +#define ST_VIDEO_H264 0x1b /* Un-official Dirac extension */ -#define ST_VIDEO_DIRAC 0xd1 +#define ST_VIDEO_DIRAC 0xd1 /* private stream types */ -#define ST_PS_AUDIO_AC3 0x81 -#define ST_PS_AUDIO_DTS 0x8a -#define ST_PS_AUDIO_LPCM 0x8b +#define ST_PS_AUDIO_AC3 0x81 +#define ST_PS_AUDIO_DTS 0x8a +#define ST_PS_AUDIO_LPCM 0x8b #define ST_PS_DVD_SUBPICTURE 0xff +/* Blu-ray related */ +#define ST_BD_AUDIO_LPCM 0x80 +#define ST_BD_AUDIO_AC3 0x81 +#define ST_BD_AUDIO_DTS 0x82 +#define ST_BD_AUDIO_AC3_TRUE_HD 0x83 +#define ST_BD_AUDIO_AC3_PLUS 0x84 +#define ST_BD_AUDIO_DTS_HD 0x85 +#define ST_BD_AUDIO_DTS_HD_MASTER_AUDIO 0x86 +#define ST_BD_AUDIO_EAC3 0x87 +#define ST_BD_PGS_SUBPICTURE 0x90 +#define ST_BD_IGS 0x91 +#define ST_BD_SUBTITLE 0x92 +#define ST_BD_SECONDARY_AC3_PLUS 0xa1 +#define ST_BD_SECONDARY_DTS_HD 0xa2 + +/* defined for VC1 extension in RP227 */ +#define ST_PRIVATE_EA 0xea + +/* HDV AUX stream mapping + * 0xA0 ISO/IEC 61834-11 + * 0xA1 ISO/IEC 61834-11 + */ +#define ST_HDV_AUX_A 0xa0 +#define ST_HDV_AUX_V 0xa1 /* Un-official time-code stream */ #define ST_PS_TIMECODE 0xd2 @@ -168,8 +194,19 @@ #define MPEG_MUX_RATE_MULT 50 +/* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */ +#define READ_TS(data, target, lost_sync_label) \ + if ((*data & 0x01) != 0x01) goto lost_sync_label; \ + target = ((guint64) (*data++ & 0x0E)) << 29; \ + target |= ((guint64) (*data++ )) << 22; \ + if ((*data & 0x01) != 0x01) goto lost_sync_label; \ + target |= ((guint64) (*data++ & 0xFE)) << 14; \ + target |= ((guint64) (*data++ )) << 7; \ + if ((*data & 0x01) != 0x01) goto lost_sync_label; \ + target |= ((guint64) (*data++ & 0xFE)) >> 1; + /* some extra GstFlowReturn values used internally */ -#define GST_FLOW_NEED_MORE_DATA GST_FLOW_CUSTOM_SUCCESS -#define GST_FLOW_LOST_SYNC GST_FLOW_CUSTOM_SUCCESS_1 +#define GST_FLOW_NEED_MORE_DATA GST_FLOW_CUSTOM_SUCCESS +#define GST_FLOW_LOST_SYNC GST_FLOW_CUSTOM_SUCCESS_1 #endif /* __GST_MPEG_DEFS_H__ */ diff --git a/ext/resindvd/gstmpegdemux.c b/ext/resindvd/gstmpegdemux.c index 527cd605af..efac042239 100644 --- a/ext/resindvd/gstmpegdemux.c +++ b/ext/resindvd/gstmpegdemux.c @@ -45,7 +45,7 @@ GST_DEBUG_CATEGORY_STATIC (gstflupsdemux_debug); #define GST_CAT_DEFAULT (gstflupsdemux_debug) -GST_DEBUG_CATEGORY_EXTERN (gstflupesfilter_debug); +GST_DEBUG_CATEGORY_EXTERN (mpegpspesfilter_debug); /* MPEG2Demux signals and args */ enum @@ -104,16 +104,35 @@ static void gst_flups_demux_init (GstFluPSDemux * demux); static void gst_flups_demux_finalize (GstFluPSDemux * demux); static void gst_flups_demux_reset (GstFluPSDemux * demux); -static gboolean gst_flups_demux_sink_event (GstPad * pad, GstEvent * event); -static gboolean gst_flups_demux_src_event (GstPad * pad, GstEvent * event); -static gboolean gst_flups_demux_src_query (GstPad * pad, GstQuery * query); -static GstFlowReturn gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_flups_demux_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static GstFlowReturn gst_flups_demux_chain (GstPad * pad, GstObject * parent, + GstBuffer * buffer); +static gboolean gst_flups_demux_sink_activate (GstPad * sinkpad, + GstObject * parent); +static gboolean gst_flups_demux_sink_activate_mode (GstPad * pad, + GstObject * parent, GstPadMode mode, gboolean active); +// static void gst_flups_demux_loop (GstPad * pad); + +static gboolean gst_flups_demux_src_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_flups_demux_src_query (GstPad * pad, GstObject * parent, + GstQuery * query); static GstStateChangeReturn gst_flups_demux_change_state (GstElement * element, GstStateChange transition); +static inline void gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, + GstClockTime new_time); +static inline void gst_flups_demux_clear_times (GstFluPSDemux * demux); + static GstElementClass *parent_class = NULL; +static void gst_segment_set_position (GstSegment * segment, GstFormat format, + guint64 position); +//static void gst_segment_set_duration (GstSegment * segment, GstFormat format, +// guint64 duration); + /*static guint gst_flups_demux_signals[LAST_SIGNAL] = { 0 };*/ GType @@ -132,6 +151,7 @@ gst_flups_demux_get_type (void) sizeof (GstFluPSDemux), 0, (GInstanceInitFunc) gst_flups_demux_init, + NULL }; flups_demux_type = @@ -159,14 +179,15 @@ gst_flups_demux_base_init (GstFluPSDemuxClass * klass) gst_element_class_add_pad_template (element_class, klass->video_template); gst_element_class_add_pad_template (element_class, klass->audio_template); - gst_element_class_add_pad_template (element_class, klass->private_template); gst_element_class_add_pad_template (element_class, klass->subpicture_template); + gst_element_class_add_pad_template (element_class, klass->private_template); gst_element_class_add_pad_template (element_class, klass->sink_template); - gst_element_class_set_details_simple (element_class, "MPEG Program Demuxer", - "Codec/Demuxer", - "Demultiplexes MPEG Program Streams", "Wim Taymans "); + gst_element_class_set_details_simple (element_class, + "MPEG Program Demuxer", "Codec/Demuxer", + "Demultiplexes MPEG Program Streams", + "Jan Schmidt "); } static void @@ -191,8 +212,15 @@ gst_flups_demux_init (GstFluPSDemux * demux) GstFluPSDemuxClass *klass = GST_FLUPS_DEMUX_GET_CLASS (demux); demux->sinkpad = gst_pad_new_from_template (klass->sink_template, "sink"); - gst_pad_set_event_function (demux->sinkpad, gst_flups_demux_sink_event); - gst_pad_set_chain_function (demux->sinkpad, gst_flups_demux_chain); + gst_pad_set_event_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_flups_demux_sink_event)); + gst_pad_set_chain_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_flups_demux_chain)); + gst_pad_set_activate_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_flups_demux_sink_activate)); + gst_pad_set_activatemode_function (demux->sinkpad, + GST_DEBUG_FUNCPTR (gst_flups_demux_sink_activate_mode)); + gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad); demux->streams = @@ -230,8 +258,8 @@ gst_flups_demux_reset (GstFluPSDemux * demux) } p_ev = &demux->lang_codes; - gst_event_replace (p_ev, NULL); + gst_event_replace (p_ev, NULL); demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE); } @@ -259,7 +287,6 @@ 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; @@ -287,45 +314,62 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) case ST_PRIVATE_DATA: case ST_MHEG: case ST_DSMCC: - case ST_AUDIO_AAC: + break; + case ST_AUDIO_AAC_ADTS: + template = klass->audio_template; + name = g_strdup_printf ("audio_%02x", id); + caps = gst_caps_new_simple ("audio/mpeg", + "mpegversion", G_TYPE_INT, 4, + "stream-format", G_TYPE_STRING, "adts", NULL); + break; + case ST_AUDIO_AAC_LOAS: // LATM/LOAS AAC syntax + template = klass->audio_template; + name = g_strdup_printf ("audio_%02x", id); + caps = gst_caps_new_simple ("audio/mpeg", + "mpegversion", G_TYPE_INT, 4, + "stream-format", G_TYPE_STRING, "loas", NULL); break; case ST_VIDEO_H264: template = klass->video_template; name = g_strdup_printf ("video_%02x", id); - caps = gst_caps_new_simple ("video/x-h264", NULL); + caps = gst_caps_new_empty_simple ("video/x-h264"); threshold = VIDEO_SEGMENT_THRESHOLD; break; case ST_PS_AUDIO_AC3: template = klass->audio_template; name = g_strdup_printf ("audio_%02x", id); - caps = gst_caps_new_simple ("audio/x-private1-ac3", NULL); + caps = gst_caps_new_empty_simple ("audio/x-private1-ac3"); break; case ST_PS_AUDIO_DTS: template = klass->audio_template; name = g_strdup_printf ("audio_%02x", id); - caps = gst_caps_new_simple ("audio/x-private1-dts", NULL); + caps = gst_caps_new_empty_simple ("audio/x-private1-dts"); break; case ST_PS_AUDIO_LPCM: template = klass->audio_template; name = g_strdup_printf ("audio_%02x", id); - caps = gst_caps_new_simple ("audio/x-private1-lpcm", NULL); + caps = gst_caps_new_empty_simple ("audio/x-private1-lpcm"); break; case ST_PS_DVD_SUBPICTURE: template = klass->subpicture_template; name = g_strdup_printf ("subpicture_%02x", id); - caps = gst_caps_new_simple ("subpicture/x-dvd", NULL); + caps = gst_caps_new_empty_simple ("subpicture/x-dvd"); break; case ST_GST_AUDIO_RAWA52: template = klass->audio_template; name = g_strdup_printf ("audio_%02x", id); - caps = gst_caps_new_simple ("audio/ac3", NULL); + caps = gst_caps_new_empty_simple ("audio/ac3"); break; default: break; } - if (name == NULL || template == NULL || caps == NULL) - return NULL; + if (name == NULL || template == NULL || caps == NULL) { + g_free (name); + if (caps) + gst_caps_unref (caps); + return FALSE; + } stream = g_new0 (GstFluPSStream, 1); stream->id = id; @@ -334,12 +378,21 @@ gst_flups_demux_create_stream (GstFluPSDemux * demux, gint id, gint stream_type) stream->type = stream_type; stream->pad = gst_pad_new_from_template (template, name); stream->segment_thresh = threshold; - gst_pad_set_event_function (stream->pad, gst_flups_demux_src_event); - gst_pad_set_query_function (stream->pad, gst_flups_demux_src_query); + gst_pad_set_event_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_flups_demux_src_event)); + gst_pad_set_query_function (stream->pad, + GST_DEBUG_FUNCPTR (gst_flups_demux_src_query)); gst_pad_use_fixed_caps (stream->pad); + + /* needed for set_caps to work */ + if (!gst_pad_set_active (stream->pad, TRUE)) { + GST_WARNING_OBJECT (demux, "Failed to activate pad %" GST_PTR_FORMAT, + stream->pad); + } + gst_pad_set_caps (stream->pad, caps); - gst_caps_unref (caps); GST_DEBUG_OBJECT (demux, "create pad %s, caps %" GST_PTR_FORMAT, name, caps); + gst_caps_unref (caps); g_free (name); return stream; @@ -357,7 +410,6 @@ gst_flups_demux_get_stream (GstFluPSDemux * demux, gint id, gint type) GST_DEBUG_OBJECT (demux, "adding pad for stream id 0x%02x type 0x%02x", id, type); - gst_pad_set_active (stream->pad, TRUE); gst_element_add_pad (GST_ELEMENT (demux), stream->pad); demux->streams[id] = stream; @@ -384,7 +436,7 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, goto no_stream; /* timestamps */ - if (demux->next_pts != G_MAXUINT64) + if (G_UNLIKELY (demux->next_pts != G_MAXUINT64)) timestamp = MPEGTIME_TO_GSTTIME (demux->next_pts); else timestamp = GST_CLOCK_TIME_NONE; @@ -403,19 +455,17 @@ gst_flups_demux_send_data (GstFluPSDemux * demux, GstFluPSStream * stream, } /* OK, sent new segment now prepare the buffer for sending */ - /* caps */ - gst_buffer_set_caps (buf, GST_PAD_CAPS (stream->pad)); GST_BUFFER_TIMESTAMP (buf) = timestamp; /* Set the buffer discont flag, and clear discont state on the stream */ if (stream->discont) { GST_DEBUG_OBJECT (demux, "discont buffer to pad %" GST_PTR_FORMAT " with TS %" GST_TIME_FORMAT, stream->pad, GST_TIME_ARGS (timestamp)); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + stream->discont = FALSE; } - size = GST_BUFFER_SIZE (buf); + size = gst_buffer_get_size (buf); demux->next_pts = G_MAXUINT64; demux->next_dts = G_MAXUINT64; @@ -437,7 +487,7 @@ no_stream: } } -static void +static inline void gst_flups_demux_mark_discont (GstFluPSDemux * demux) { gint id; @@ -453,111 +503,7 @@ gst_flups_demux_mark_discont (GstFluPSDemux * demux) } } -static void -gst_flups_demux_clear_times (GstFluPSDemux * demux) -{ - gint id; - - /* Clear the last ts for all streams */ - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; - - if (stream) { - stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE; - } - } -} - -static void -gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, - GstClockTime new_time) -{ - /* Advance all lagging streams by sending a segment update */ - gint id; - GstEvent *event = NULL; - - if (new_time > demux->src_segment.stop) - return; - - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; - - if (stream) { - if (stream->last_ts == GST_CLOCK_TIME_NONE || - stream->last_ts < demux->src_segment.start) - stream->last_ts = demux->src_segment.start; - if (stream->last_ts + stream->segment_thresh < new_time) { -#if 0 - g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %" - GST_TIME_FORMAT " last_stop %" GST_TIME_FORMAT "\n", - GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time), - GST_TIME_ARGS (demux->src_segment.stop), - GST_TIME_ARGS (demux->src_segment.last_stop)); -#endif - GST_DEBUG_OBJECT (demux, - "Segment update to pad %s time %" GST_TIME_FORMAT, - GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time)); - if (event == NULL) { - event = gst_event_new_new_segment_full (TRUE, - demux->src_segment.rate, demux->src_segment.applied_rate, - GST_FORMAT_TIME, new_time, - demux->src_segment.stop, - demux->src_segment.time + (new_time - demux->src_segment.start)); - } - gst_event_ref (event); - gst_pad_push_event (stream->pad, event); - stream->last_seg_start = stream->last_ts = new_time; - } - } - } - - if (event) - gst_event_unref (event); -} - -static void -gst_flups_demux_send_segment_close (GstFluPSDemux * demux) -{ - gint id; - GstEvent *event = NULL; - GstClockTime stop = demux->src_segment.stop; - - if (demux->src_segment.last_stop != -1 && demux->src_segment.last_stop > stop) - stop = demux->src_segment.last_stop; - - for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { - GstFluPSStream *stream = demux->streams[id]; - - if (stream) { - GstClockTime start = demux->src_segment.start; - - if (stream->last_seg_start != GST_CLOCK_TIME_NONE && - stream->last_seg_start > start) - start = stream->last_seg_start; - -#if 0 - g_print ("Segment close to pad %s start %" GST_TIME_FORMAT - " stop %" GST_TIME_FORMAT "\n", - GST_PAD_NAME (stream->pad), GST_TIME_ARGS (start), - GST_TIME_ARGS (stop)); -#endif - if (start > stop) { - g_print ("Problem on pad %s with start %" GST_TIME_FORMAT " > stop %" - GST_TIME_FORMAT "\n", - gst_object_get_name (GST_OBJECT (stream->pad)), - GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); - } - event = gst_event_new_new_segment_full (TRUE, - demux->src_segment.rate, demux->src_segment.applied_rate, - GST_FORMAT_TIME, start, - stop, demux->src_segment.time + (start - demux->src_segment.start)); - if (event) - gst_pad_push_event (stream->pad, event); - } - } -} - -static gboolean +static inline gboolean gst_flups_demux_send_event (GstFluPSDemux * demux, GstEvent * event) { gint id; @@ -811,13 +757,139 @@ gst_flups_demux_handle_dvd_event (GstFluPSDemux * demux, GstEvent * event) return ret; } +static void +gst_flups_demux_flush (GstFluPSDemux * demux) +{ + GST_DEBUG_OBJECT (demux, "flushing demuxer"); + gst_segment_init (&demux->src_segment, GST_FORMAT_TIME); + gst_adapter_clear (demux->adapter); + gst_adapter_clear (demux->rev_adapter); + gst_pes_filter_drain (&demux->filter); + gst_flups_demux_clear_times (demux); + demux->adapter_offset = G_MAXUINT64; + demux->current_scr = G_MAXUINT64; + demux->bytes_since_scr = 0; + demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE); +} + +static inline void +gst_flups_demux_clear_times (GstFluPSDemux * demux) +{ + gint id; + + /* Clear the last ts for all streams */ + for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { + GstFluPSStream *stream = demux->streams[id]; + + if (stream) { + stream->last_seg_start = stream->last_ts = GST_CLOCK_TIME_NONE; + } + } +} + +static inline void +gst_flups_demux_send_segment_updates (GstFluPSDemux * demux, + GstClockTime new_time) +{ + gint id; + GstEvent *event = NULL; + + /* Advance all lagging streams by sending a segment update */ + if (new_time > demux->src_segment.stop) + return; + + for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { + GstFluPSStream *stream = demux->streams[id]; + + if (stream) { + if (stream->last_ts == GST_CLOCK_TIME_NONE || + stream->last_ts < demux->src_segment.start) + stream->last_ts = demux->src_segment.start; + if (stream->last_ts + stream->segment_thresh < new_time) { +#if 0 + g_print ("Segment update to pad %s time %" GST_TIME_FORMAT " stop now %" + GST_TIME_FORMAT " position %" GST_TIME_FORMAT "\n", + GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time), + GST_TIME_ARGS (demux->src_segment.stop), + GST_TIME_ARGS (demux->src_segment.position)); +#endif + GST_DEBUG_OBJECT (demux, + "Segment update to pad %s time %" GST_TIME_FORMAT, + GST_PAD_NAME (stream->pad), GST_TIME_ARGS (new_time)); + if (event == NULL) { + GstSegment segment; + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.rate = demux->src_segment.rate; + segment.applied_rate = demux->src_segment.applied_rate; + segment.start = new_time; + segment.stop = demux->src_segment.stop; + segment.time = + demux->src_segment.time + (new_time - demux->src_segment.start); + event = gst_event_new_segment (&segment); + } + gst_event_ref (event); + gst_pad_push_event (stream->pad, event); + stream->last_seg_start = stream->last_ts = new_time; + } + } + } + + if (event) + gst_event_unref (event); +} + +static inline void +gst_flups_demux_close_segment (GstFluPSDemux * demux) +{ + gint id; + GstEvent *event = NULL; + GstClockTime stop = demux->src_segment.stop; + + if (demux->src_segment.position != -1 && demux->src_segment.position > stop) + stop = demux->src_segment.position; + + for (id = 0; id < GST_FLUPS_DEMUX_MAX_STREAMS; id++) { + GstFluPSStream *stream = demux->streams[id]; + + if (stream) { + GstClockTime start = demux->src_segment.start; + GstSegment segment; + + if (stream->last_seg_start != GST_CLOCK_TIME_NONE && + stream->last_seg_start > start) + start = stream->last_seg_start; + +#if 0 + g_print ("Segment close to pad %s start %" GST_TIME_FORMAT + " stop %" GST_TIME_FORMAT "\n", + GST_PAD_NAME (stream->pad), GST_TIME_ARGS (start), + GST_TIME_ARGS (stop)); +#endif + if (start > stop) { + GST_WARNING_OBJECT (demux, + "Problem on pad %s with start %" GST_TIME_FORMAT " > stop %" + GST_TIME_FORMAT "\n", + gst_object_get_name (GST_OBJECT (stream->pad)), + GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + } + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.rate = demux->src_segment.rate; + segment.applied_rate = demux->src_segment.applied_rate; + segment.start = start; + segment.stop = stop; + segment.time = + demux->src_segment.time + (start - demux->src_segment.start); + event = gst_event_new_segment (&segment); + gst_pad_push_event (stream->pad, event); + } + } +} + static gboolean -gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) +gst_flups_demux_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean res = TRUE; - GstFluPSDemux *demux; - - demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad)); + GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_FLUSH_START: @@ -825,54 +897,46 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) break; case GST_EVENT_FLUSH_STOP: gst_flups_demux_send_event (demux, event); - gst_segment_init (&demux->sink_segment, GST_FORMAT_UNDEFINED); - gst_segment_init (&demux->src_segment, GST_FORMAT_TIME); - gst_adapter_clear (demux->adapter); - gst_adapter_clear (demux->rev_adapter); - demux->adapter_offset = G_MAXUINT64; - gst_pes_filter_drain (&demux->filter); - demux->current_scr = G_MAXUINT64; - demux->bytes_since_scr = 0; - demux->scr_adjust = GSTTIME_TO_MPEGTIME (SCR_MUNGE); - gst_flups_demux_clear_times (demux); + gst_flups_demux_flush (demux); break; - case GST_EVENT_NEWSEGMENT: + case GST_EVENT_SEGMENT: { - gboolean update; - gdouble rate; - GstFormat format; + const GstSegment *segment; + gint64 start, stop, time; - gint64 accum, dur; - gdouble arate; + gint64 base, dur; GstClockTimeDiff adjust; - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); + /* Close current segment */ + gst_flups_demux_close_segment (demux); - if (format != GST_FORMAT_TIME) + gst_event_parse_segment (event, &segment); + + if (segment->format != GST_FORMAT_TIME) return FALSE; - dur = stop - start; + gst_segment_copy_into (segment, &demux->sink_segment); + + dur = segment->stop - segment->start; + + base = demux->sink_segment.base; + start = demux->sink_segment.start; + stop = demux->sink_segment.stop; + time = demux->sink_segment.time; demux->first_scr = GSTTIME_TO_MPEGTIME (start); demux->current_scr = demux->first_scr + demux->scr_adjust; demux->base_time = time; demux->bytes_since_scr = 0; - gst_segment_set_newsegment_full (&demux->sink_segment, update, rate, - arate, format, start, stop, time); - GST_DEBUG_OBJECT (demux, - "demux: got segment update %d start %" G_GINT64_FORMAT " stop %" - G_GINT64_FORMAT " time %" G_GINT64_FORMAT, update, start, stop, time); + "demux: received new segment start %" G_GINT64_FORMAT " stop %" + G_GINT64_FORMAT " time %" G_GINT64_FORMAT, start, stop, time); - accum = demux->sink_segment.accum; - start = demux->sink_segment.start; - stop = demux->sink_segment.stop; - adjust = accum - start + SCR_MUNGE; - start = accum + SCR_MUNGE; + adjust = base - start + SCR_MUNGE; + start = base + SCR_MUNGE; if (adjust >= 0) demux->scr_adjust = GSTTIME_TO_MPEGTIME (adjust); @@ -881,31 +945,29 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) if (stop != -1) { stop = start + dur; - if (demux->src_segment.last_stop != -1 - && demux->src_segment.last_stop > stop) - stop = demux->src_segment.last_stop; + if (demux->src_segment.position != -1 + && demux->src_segment.position > stop) + stop = demux->src_segment.position; } GST_DEBUG_OBJECT (demux, - "sending new segment: update %d rate %g format %d, start: %" + "sending new segment: rate %g format %d, start: %" G_GINT64_FORMAT ", stop: %" G_GINT64_FORMAT ", time: %" G_GINT64_FORMAT " scr_adjust: %" G_GINT64_FORMAT "(%" GST_TIME_FORMAT - ")", update, rate, format, start, stop, time, demux->scr_adjust, + ")", segment->rate, segment->format, start, stop, time, + demux->scr_adjust, GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (demux->scr_adjust))); - gst_segment_set_newsegment_full (&demux->src_segment, update, - rate, arate, format, start, stop, time); + demux->src_segment.rate = segment->rate; + demux->src_segment.applied_rate = segment->applied_rate; + demux->src_segment.format = segment->format; + demux->src_segment.start = segment->start; + demux->src_segment.stop = segment->stop; + demux->src_segment.time = segment->time; gst_event_unref (event); - if (update) { - /* Segment closing, send it as per-pad updates to manage the accum - * properly */ - gst_flups_demux_send_segment_close (demux); - } else { - event = gst_event_new_new_segment_full (update, - rate, arate, GST_FORMAT_TIME, start, stop, time); - gst_flups_demux_send_event (demux, event); - } + event = gst_event_new_segment (&demux->src_segment); + gst_flups_demux_send_event (demux, event); break; } @@ -930,23 +992,22 @@ gst_flups_demux_sink_event (GstPad * pad, GstEvent * event) } break; } + case GST_EVENT_CAPS: + gst_event_unref (event); + break; default: gst_flups_demux_send_event (demux, event); break; } - gst_object_unref (demux); - return res; } static gboolean -gst_flups_demux_src_event (GstPad * pad, GstEvent * event) +gst_flups_demux_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean res = FALSE; - GstFluPSDemux *demux; - - demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad)); + GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: @@ -1011,13 +1072,10 @@ gst_flups_demux_src_event (GstPad * pad, GstEvent * event) break; } - gst_object_unref (demux); - return res; not_supported: { - gst_object_unref (demux); gst_event_unref (event); return FALSE; @@ -1025,12 +1083,10 @@ not_supported: } static gboolean -gst_flups_demux_src_query (GstPad * pad, GstQuery * query) +gst_flups_demux_src_query (GstPad * pad, GstObject * parent, GstQuery * query) { gboolean res = FALSE; - GstFluPSDemux *demux; - - demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad)); + GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent); GST_LOG_OBJECT (demux, "Have query of type %d on pad %" GST_PTR_FORMAT, GST_QUERY_TYPE (query), pad); @@ -1038,36 +1094,33 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query) switch (GST_QUERY_TYPE (query)) { case GST_QUERY_POSITION: { - GstPad *peer; + GstClockTime pos; GstFormat format; - gint64 position; + + /* See if upstream can immediately answer */ + res = gst_pad_peer_query (demux->sinkpad, query); + if (res) + break; gst_query_parse_position (query, &format, NULL); - if ((peer = gst_pad_get_peer (demux->sinkpad)) != NULL) { - res = gst_pad_query (peer, query); - gst_object_unref (peer); - if (res) - break; - } - if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (demux, "position not supported for format %d", format); goto not_supported; } - position = demux->base_time; + pos = demux->base_time; if (demux->current_scr != G_MAXUINT64 && demux->first_scr != G_MAXUINT64) { - position += + pos += MPEGTIME_TO_GSTTIME (demux->current_scr - demux->scr_adjust - demux->first_scr); } GST_LOG_OBJECT (demux, "Position at GStreamer Time:%" GST_TIME_FORMAT, - GST_TIME_ARGS (position)); + GST_TIME_ARGS (pos)); - gst_query_set_position (query, format, position); + gst_query_set_position (query, format, pos); res = TRUE; break; } @@ -1075,76 +1128,79 @@ gst_flups_demux_src_query (GstPad * pad, GstQuery * query) { GstFormat format; gint64 duration; - GstPad *peer; + GstQuery *byte_query; gst_query_parse_duration (query, &format, NULL); - if ((peer = gst_pad_get_peer (demux->sinkpad)) == NULL) { - GST_DEBUG_OBJECT (demux, "duration not possible, no peer"); - goto not_supported; + if (G_LIKELY (format == GST_FORMAT_TIME && + GST_CLOCK_TIME_IS_VALID (demux->src_segment.duration))) { + gst_query_set_duration (query, GST_FORMAT_TIME, + demux->src_segment.duration); + res = TRUE; + break; } /* For any format other than bytes, see if upstream knows first */ if (format == GST_FORMAT_BYTES) { GST_DEBUG_OBJECT (demux, "duration not supported for format %d", format); - gst_object_unref (peer); goto not_supported; } - if (gst_pad_query (peer, query)) { - gst_object_unref (peer); + if (gst_pad_peer_query (demux->sinkpad, query)) { res = TRUE; break; } - /* Upstream didn't know, so we can only answer TIME queries from + /* Upstream didn't know, so we can only answer TIME queries from * here on */ if (format != GST_FORMAT_TIME) { GST_DEBUG_OBJECT (demux, "duration not supported for format %d", format); - gst_object_unref (peer); goto not_supported; } if (demux->mux_rate == -1) { GST_DEBUG_OBJECT (demux, "duration not possible, no mux_rate"); - gst_object_unref (peer); goto not_supported; } - gst_query_set_duration (query, GST_FORMAT_BYTES, -1); + byte_query = gst_query_new_duration (GST_FORMAT_BYTES); - if (!gst_pad_query (peer, query)) { + if (!gst_pad_peer_query (demux->sinkpad, byte_query)) { GST_LOG_OBJECT (demux, "query on peer pad failed"); - gst_object_unref (peer); + gst_query_unref (byte_query); goto not_supported; } - gst_object_unref (peer); - gst_query_parse_duration (query, &format, &duration); + gst_query_parse_duration (byte_query, &format, &duration); + gst_query_unref (byte_query); - duration = BYTES_TO_GSTTIME (duration); + GST_LOG_OBJECT (demux, + "query on peer pad reported bytes %" G_GUINT64_FORMAT, duration); + + duration = BYTES_TO_GSTTIME ((guint64) duration); + + GST_LOG_OBJECT (demux, "converted to time %" GST_TIME_FORMAT, + GST_TIME_ARGS (duration)); gst_query_set_duration (query, GST_FORMAT_TIME, duration); res = TRUE; break; } + case GST_QUERY_SEEKING:{ + /* Just ask upstream */ + res = gst_pad_peer_query (demux->sinkpad, query); + break; + } default: - res = gst_pad_query_default (pad, query); + res = gst_pad_query_default (pad, parent, query); break; } - gst_object_unref (demux); - return res; - not_supported: - { - gst_object_unref (demux); - - return FALSE; - } + return FALSE; } static void @@ -1170,6 +1226,33 @@ gst_flups_demux_reset_psm (GstFluPSDemux * demux) #undef FILL_TYPE } +/* ISO/IEC 13818-1: + * pack_header() { + * pack_start_code 32 bslbf -+ + * '01' 2 bslbf | + * system_clock_reference_base [32..30] 3 bslbf | + * marker_bit 1 bslbf | + * system_clock_reference_base [29..15] 15 bslbf | + * marker_bit 1 bslbf | + * system_clock_reference_base [14..0] 15 bslbf | + * marker_bit 1 bslbf | 112 bits + * system_clock_reference_extension 9 ubslbf | + * marker_bit 1 bslbf | + * program_mux_rate 22 ubslbf | + * marker_bit 1 bslbf | + * marker_bit 1 bslbf | + * reserved 5 bslbf | + * pack_stuffing_length 3 ubslbf -+ + * + * for (i = 0; i < pack_stuffing_length; i++) { + * stuffing_byte '1111 1111' 8 bslbf + * } + * + * 112 bits = 14 bytes, as max value for pack_stuffing_length is 7, then + * in total it's needed 14 + 7 = 21 bytes. + */ +#define PACK_START_SIZE 21 + static GstFlowReturn gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) { @@ -1177,22 +1260,27 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) guint length; guint32 scr1, scr2; guint64 scr, scr_adjusted, new_rate; + guint64 scr_rate_n; + guint64 scr_rate_d; GstClockTime new_time; + guint avail = gst_adapter_available (demux->adapter); GST_DEBUG ("parsing pack start"); - /* fixed length to begin with, start code and two scr values */ - length = 8 + 4; - - if (!(data = gst_adapter_peek (demux->adapter, length))) + if (G_UNLIKELY (avail < PACK_START_SIZE)) goto need_more_data; + data = gst_adapter_map (demux->adapter, PACK_START_SIZE); + /* skip start code */ data += 4; scr1 = GST_READ_UINT32_BE (data); scr2 = GST_READ_UINT32_BE (data + 4); + /* fixed length to begin with, start code and two scr values */ + length = 8 + 4; + /* start parsing the stream */ if ((*data & 0xc0) == 0x40) { guint32 scr_ext; @@ -1204,13 +1292,11 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* mpeg2 has more data */ length += 2; - if (gst_adapter_available (demux->adapter) < length) - goto need_more_data; /* :2=01 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 */ /* check markers */ - if ((scr1 & 0xc4000400) != 0x44000400) + if (G_UNLIKELY ((scr1 & 0xc4000400) != 0x44000400)) goto lost_sync; scr = ((guint64) scr1 & 0x38000000) << 3; @@ -1219,14 +1305,15 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) scr |= ((guint64) scr2 & 0xf8000000) >> 27; /* marker:1==1 ! scr_ext:9 ! marker:1==1 */ - if ((scr2 & 0x04010000) != 0x04010000) + if (G_UNLIKELY ((scr2 & 0x04010000) != 0x04010000)) goto lost_sync; scr_ext = (scr2 & 0x03fe0000) >> 17; /* We keep the offset of this scr */ - demux->last_scr_offset = demux->adapter_offset + 12; + demux->cur_scr_offset = demux->adapter_offset + 12; - GST_DEBUG_OBJECT (demux, "SCR: 0x%08x SCRE: 0x%08x", (guint) scr, scr_ext); + GST_DEBUG_OBJECT (demux, "SCR: 0x%08" G_GINT64_MODIFIER "x SCRE: 0x%08x", + scr, scr_ext); if (scr_ext) { scr = (scr * 300 + scr_ext % 300) / 300; @@ -1236,7 +1323,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) data += 6; /* PMR:22 ! :2==11 ! reserved:5 ! stuffing_len:3 */ next32 = GST_READ_UINT32_BE (data); - if ((next32 & 0x00000300) != 0x00000300) + if (G_UNLIKELY ((next32 & 0x00000300) != 0x00000300)) goto lost_sync; new_rate = (next32 & 0xfffffc00) >> 10; @@ -1245,6 +1332,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) GST_DEBUG_OBJECT (demux, "stuffing bytes: %d", stuffing_bytes); data += 4; + length += stuffing_bytes; while (stuffing_bytes--) { if (*data++ != 0xff) goto lost_sync; @@ -1254,10 +1342,10 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) demux->is_mpeg2_pack = FALSE; /* check markers */ - if ((scr1 & 0xf1000100) != 0x21000100) + if (G_UNLIKELY ((scr1 & 0xf1000100) != 0x21000100)) goto lost_sync; - if ((scr2 & 0x01800001) != 0x01800001) + if (G_UNLIKELY ((scr2 & 0x01800001) != 0x01800001)) goto lost_sync; /* :4=0010 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 ! marker:1==1 */ @@ -1267,7 +1355,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) scr |= ((guint64) scr2 & 0xfe000000) >> 25; /* We keep the offset of this scr */ - demux->last_scr_offset = demux->adapter_offset + 8; + demux->cur_scr_offset = demux->adapter_offset + 8; /* marker:1==1 ! mux_rate:22 ! marker:1==1 */ new_rate = (scr2 & 0x007ffffe) >> 1; @@ -1279,10 +1367,16 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* scr adjusted is the new scr found + the colected adjustment */ scr_adjusted = scr + demux->scr_adjust; + GST_LOG_OBJECT (demux, + "SCR: %" G_GINT64_FORMAT " (%" G_GINT64_FORMAT "), mux_rate %" + G_GINT64_FORMAT ", GStreamer Time:%" GST_TIME_FORMAT, + scr, scr_adjusted, new_rate, + GST_TIME_ARGS (MPEGTIME_TO_GSTTIME ((guint64) scr))); + /* keep the first src in order to calculate delta time */ - if (demux->first_scr == G_MAXUINT64) { + if (G_UNLIKELY (demux->first_scr == G_MAXUINT64)) { demux->first_scr = scr; - demux->first_scr_offset = demux->last_scr_offset; + demux->first_scr_offset = demux->cur_scr_offset; if (demux->sink_segment.format == GST_FORMAT_TIME) { demux->base_time = demux->sink_segment.time; @@ -1290,12 +1384,15 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) demux->base_time = MPEGTIME_TO_GSTTIME (demux->first_scr); } /* at begin consider the new_rate as the scr rate, bytes/clock ticks */ - demux->scr_rate_n = new_rate; - demux->scr_rate_d = CLOCK_FREQ; - } else if (demux->first_scr_offset != demux->last_scr_offset) { + scr_rate_n = new_rate; + scr_rate_d = CLOCK_FREQ; + } else if (G_LIKELY (demux->first_scr_offset != demux->cur_scr_offset)) { /* estimate byte rate related to the SCR */ - demux->scr_rate_n = demux->last_scr_offset - demux->first_scr_offset; - demux->scr_rate_d = scr - demux->first_scr; + scr_rate_n = demux->cur_scr_offset - demux->first_scr_offset; + scr_rate_d = scr_adjusted - demux->first_scr; + } else { + scr_rate_n = demux->scr_rate_n; + scr_rate_d = demux->scr_rate_d; } GST_DEBUG_OBJECT (demux, @@ -1309,15 +1406,14 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) " at %" G_GUINT64_FORMAT ", scr rate: %" G_GUINT64_FORMAT "/%" G_GUINT64_FORMAT "(%f)", ((demux->sink_segment.rate >= 0.0) ? "forward" : "backward"), - scr, demux->last_scr_offset, + scr, demux->cur_scr_offset, demux->first_scr, demux->first_scr_offset, - demux->scr_rate_n, demux->scr_rate_d, - (float) demux->scr_rate_n / demux->scr_rate_d); + scr_rate_n, scr_rate_d, (float) scr_rate_n / scr_rate_d); /* adjustment of the SCR */ - if (demux->current_scr != G_MAXUINT64) { + if (G_LIKELY (demux->current_scr != G_MAXUINT64)) { gint64 diff; - guint64 old_scr, old_mux_rate, bss, adjust; + guint64 old_scr, old_mux_rate, bss, adjust = 0; /* keep SCR of the previous packet */ old_scr = demux->current_scr; @@ -1326,12 +1422,13 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* Bytes since SCR is the amount we placed in the adapter since then * (demux->bytes_since_scr) minus the amount remaining in the adapter, * clamped to >= 0 */ - bss = MAX (0, (gint) (demux->bytes_since_scr - - gst_adapter_available (demux->adapter))); + bss = MAX (0, (gint) (demux->bytes_since_scr - avail)); /* estimate the new SCR using the previous one according the notes on point 2.5.2.2 of the ISO/IEC 13818-1 document */ - adjust = (bss * CLOCK_FREQ) / old_mux_rate; + if (old_mux_rate != 0) + adjust = (bss * CLOCK_FREQ) / old_mux_rate; + if (demux->sink_segment.rate >= 0.0) demux->next_scr = old_scr + adjust; else @@ -1344,7 +1441,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* calculate the absolute deference between the last scr and the new one */ - if (old_scr > scr_adjusted) + if (G_UNLIKELY (old_scr > scr_adjusted)) diff = old_scr - scr_adjusted; else diff = scr_adjusted - old_scr; @@ -1369,18 +1466,21 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) /* update the current_scr and rate members */ demux->mux_rate = new_rate; demux->current_scr = scr_adjusted; + demux->scr_rate_n = scr_rate_n; + demux->scr_rate_d = scr_rate_d; new_time = MPEGTIME_TO_GSTTIME (scr_adjusted); if (new_time != GST_CLOCK_TIME_NONE) { // g_print ("SCR now %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (new_time)); - gst_segment_set_last_stop (&demux->src_segment, GST_FORMAT_TIME, new_time); + gst_segment_set_position (&demux->src_segment, GST_FORMAT_TIME, new_time); gst_flups_demux_send_segment_updates (demux, new_time); } /* Reset the bytes_since_scr value to count the data remaining in the * adapter */ - demux->bytes_since_scr = gst_adapter_available (demux->adapter); + demux->bytes_since_scr = avail; + gst_adapter_unmap (demux->adapter); gst_adapter_flush (demux->adapter, length); ADAPTER_OFFSET_FLUSH (length); return GST_FLOW_OK; @@ -1388,6 +1488,7 @@ gst_flups_demux_parse_pack_start (GstFluPSDemux * demux) lost_sync: { GST_DEBUG_OBJECT (demux, "lost sync"); + gst_adapter_unmap (demux->adapter); return GST_FLOW_LOST_SYNC; } need_more_data: @@ -1397,6 +1498,32 @@ need_more_data: } } +/* ISO/IEC 13818-1: + * system_header () { + * system_header_start_code 32 bslbf -+ + * header_length 16 uimsbf | + * marker_bit 1 bslbf | + * rate_bound 22 uimsbf | + * marker_bit 1 bslbf | + * audio_bound 6 uimsbf | + * fixed_flag 1 bslbf | + * CSPS_flag 1 bslbf | 96 bits + * system_audio_lock_flag 1 bslbf | + * system_video_lock_flag 1 bslbf | + * marker_bit 1 bslbf | + * video_bound 5 uimsbf | + * packet_rate_restriction_flag 1 bslbf | + * reserved_bits 7 bslbf -+ + * while (nextbits () = = '1') { + * stream_id 8 uimsbf -+ + * '11' 2 bslbf | 24 bits + * P-STD_buffer_bound_scale 1 bslbf | + * P-STD_buffer_size_bound 13 uimsbf -+ + * } + * } + * 96 bits = 12 bytes, 24 bits = 3 bytes. + */ + static GstFlowReturn gst_flups_demux_parse_sys_head (GstFluPSDemux * demux) { @@ -1404,10 +1531,12 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux) const guint8 *data; gboolean csps; - /* start code + length */ - if (!(data = gst_adapter_peek (demux->adapter, 6))) + if (gst_adapter_available (demux->adapter) < 6) goto need_more_data; + /* start code + length */ + data = gst_adapter_map (demux->adapter, 6); + /* skip start code */ data += 4; @@ -1416,9 +1545,12 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux) length += 6; - if (!(data = gst_adapter_peek (demux->adapter, length))) + gst_adapter_unmap (demux->adapter); + if (gst_adapter_available (demux->adapter) < length) goto need_more_data; + data = gst_adapter_map (demux->adapter, length); + /* skip start code and length */ data += 6; @@ -1533,6 +1665,7 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux) } } + gst_adapter_unmap (demux->adapter); gst_adapter_flush (demux->adapter, length); ADAPTER_OFFSET_FLUSH (length); return GST_FLOW_OK; @@ -1541,22 +1674,26 @@ gst_flups_demux_parse_sys_head (GstFluPSDemux * demux) marker_expected: { GST_DEBUG_OBJECT (demux, "expecting marker"); + gst_adapter_unmap (demux->adapter); return GST_FLOW_LOST_SYNC; } no_placeholder_bits: { GST_DEBUG_OBJECT (demux, "expecting placeholder bit values" " '11' after stream id"); + gst_adapter_unmap (demux->adapter); return GST_FLOW_LOST_SYNC; } sys_len_error: { GST_DEBUG_OBJECT (demux, "error in system header length"); + gst_adapter_unmap (demux->adapter); return GST_FLOW_LOST_SYNC; } need_more_data: { GST_DEBUG_OBJECT (demux, "need more data"); + gst_adapter_unmap (demux->adapter); return GST_FLOW_NEED_MORE_DATA; } } @@ -1569,10 +1706,12 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux) const guint8 *data, *es_map_base; gboolean applicable; - /* start code + length */ - if (!(data = gst_adapter_peek (demux->adapter, 6))) + if (gst_adapter_available (demux->adapter) < 6) goto need_more_data; + /* start code + length */ + data = gst_adapter_map (demux->adapter, 6); + /* skip start code */ data += 4; @@ -1584,9 +1723,13 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux) length += 6; - if (!(data = gst_adapter_peek (demux->adapter, length))) + gst_adapter_unmap (demux->adapter); + + if (gst_adapter_available (demux->adapter) < length) goto need_more_data; + data = gst_adapter_map (demux->adapter, length); + /* skip start code and length */ data += 6; @@ -1636,10 +1779,19 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux) GST_DEBUG_OBJECT (demux, "Stream type %02X with id %02X and %u bytes info", stream_type, stream_id, stream_info_length); - demux->psm[stream_id] = stream_type; + if (G_LIKELY (stream_id != 0xbd)) + demux->psm[stream_id] = stream_type; + else { + /* Ignore stream type for private_stream_1 and discover it looking at + * the stream data. + * Fixes demuxing some clips with lpcm that was wrongly declared as + * mpeg audio */ + GST_DEBUG_OBJECT (demux, "stream type for private_stream_1 ignored"); + } es_map_base += stream_info_length; } + gst_adapter_unmap (demux->adapter); gst_adapter_flush (demux->adapter, length); ADAPTER_OFFSET_FLUSH (length); return GST_FLOW_OK; @@ -1647,6 +1799,7 @@ gst_flups_demux_parse_psm (GstFluPSDemux * demux) psm_len_error: { GST_DEBUG_OBJECT (demux, "error in PSM length"); + gst_adapter_unmap (demux->adapter); return GST_FLOW_LOST_SYNC; } need_more_data: @@ -1670,12 +1823,12 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, gint stream_type; guint32 start_code; guint8 id; - guint8 *data; - guint datalen; + GstMapInfo map; + gsize datalen; guint offset = 0; - data = GST_BUFFER_DATA (buffer); - datalen = GST_BUFFER_SIZE (buffer); + gst_buffer_map (buffer, &map, GST_MAP_READ); + datalen = map.size; start_code = filter->start_code; id = filter->id; @@ -1688,10 +1841,10 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, if (start_code == ID_PRIVATE_STREAM_1 && datalen >= 2) { guint8 nframes; - /* VDR writes A52 streams without any header bytes + /* VDR writes A52 streams without any header bytes * (see ftp://ftp.mplayerhq.hu/MPlayer/samples/MPEG-VOB/vdr-AC3) */ if (datalen >= 4) { - guint hdr = GST_READ_UINT32_BE (data); + guint hdr = GST_READ_UINT32_BE (map.data); if (G_UNLIKELY ((hdr & 0xffff0000) == AC3_SYNC_WORD)) { id = 0x80; @@ -1702,7 +1855,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, if (G_LIKELY (stream_type == -1)) { /* new id is in the first byte */ - id = data[offset++]; + id = map.data[offset++]; datalen--; /* and remap */ @@ -1713,7 +1866,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, * streams and our backwards compat convention is to strip it off */ if (stream_type != ST_PS_DVD_SUBPICTURE) { /* Number of audio frames in this packet */ - nframes = data[offset++]; + nframes = map.data[offset++]; datalen--; GST_DEBUG_OBJECT (demux, "private type 0x%02x, %d frames", id, nframes); @@ -1744,7 +1897,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, demux->current_stream = gst_flups_demux_get_stream (demux, id, stream_type); } - if (demux->current_stream == NULL) { + if (G_UNLIKELY (demux->current_stream == NULL)) { GST_DEBUG_OBJECT (demux, "Dropping buffer for unknown stream id 0x%02x", id); goto done; @@ -1768,7 +1921,8 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, } if (demux->current_stream->notlinked == FALSE) { - out_buf = gst_buffer_create_sub (buffer, offset, datalen); + out_buf = + gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, datalen); ret = gst_flups_demux_send_data (demux, demux->current_stream, out_buf); if (ret == GST_FLOW_NOT_LINKED) { @@ -1778,6 +1932,7 @@ gst_flups_demux_data_cb (GstPESFilter * filter, gboolean first, } done: + gst_buffer_unmap (buffer, &map); gst_buffer_unref (buffer); return ret; @@ -1801,11 +1956,11 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save) gboolean found; avail = gst_adapter_available (demux->adapter); - if (avail < 4) + if (G_UNLIKELY (avail < 4)) goto need_data; /* Common case, read 4 bytes an check it */ - data = gst_adapter_peek (demux->adapter, 4); + data = gst_adapter_map (demux->adapter, 4); /* read currect code */ code = GST_READ_UINT32_BE (data); @@ -1814,22 +1969,25 @@ gst_flups_demux_resync (GstFluPSDemux * demux, gboolean save) if (G_LIKELY ((code & 0xffffff00) == 0x100L)) { GST_LOG_OBJECT (demux, "Found resync code %08x after 0 bytes", code); demux->last_sync_code = code; + gst_adapter_unmap (demux->adapter); return TRUE; } - /* Otherwise, we are starting at byte 4 and we need to search + /* Otherwise, we are starting at byte 4 and we need to search the sync code in all available data in the adapter */ offset = 4; if (offset >= avail) goto need_data; /* Not enough data to find sync */ - data = gst_adapter_peek (demux->adapter, avail); + data = gst_adapter_map (demux->adapter, avail); do { code = (code << 8) | data[offset++]; found = (code & 0xffffff00) == 0x100L; } while (offset < avail && !found); + gst_adapter_unmap (demux->adapter); + if (!save || demux->sink_segment.rate >= 0.0) { GST_LOG_OBJECT (demux, "flushing %d bytes", offset - 4); /* forward playback, we can discard and flush the skipped bytes */ @@ -1875,10 +2033,78 @@ gst_flups_demux_is_pes_sync (guint32 sync) ((sync & 0xe0) == 0xc0) || ((sync & 0xf0) == 0xe0); } -static GstFlowReturn -gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer) +/* If we can pull that's prefered */ +static gboolean +gst_flups_demux_sink_activate (GstPad * sinkpad, GstObject * parent) { - GstFluPSDemux *demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad)); + gboolean res = FALSE; + GstQuery *query = gst_query_new_scheduling (); + + if (gst_pad_peer_query (sinkpad, query)) { + if (gst_query_has_scheduling_mode (query, GST_PAD_MODE_PULL)) { + res = gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE); + } else { + res = gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); + } + } else { + res = gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); + } + + gst_query_unref (query); + + return res; +} + +/* This function gets called when we activate ourselves in push mode. */ +static gboolean +gst_flups_demux_sink_activate_push (GstPad * sinkpad, GstObject * parent, + gboolean active) +{ + GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent); + + demux->random_access = FALSE; + + return TRUE; +} + +#if 0 +/* this function gets called when we activate ourselves in pull mode. + * We can perform random access to the resource and we start a task + * to start reading */ +static gboolean +gst_flups_demux_sink_activate_pull (GstPad * sinkpad, GstObject * parent, + gboolean active) +{ + GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent); + + if (active) { + GST_DEBUG ("pull mode activated"); + demux->random_access = TRUE; + return gst_pad_start_task (sinkpad, (GstTaskFunction) gst_flups_demux_loop, + sinkpad, NULL); + } else { + demux->random_access = FALSE; + return gst_pad_stop_task (sinkpad); + } +} +#endif + +static gboolean +gst_flups_demux_sink_activate_mode (GstPad * pad, GstObject * parent, + GstPadMode mode, gboolean active) +{ + if (mode == GST_PAD_MODE_PUSH) { + return gst_flups_demux_sink_activate_push (pad, parent, active); + } else if (mode == GST_PAD_MODE_PULL) { +// return gst_flups_demux_sink_activate_pull (pad, parent, active); + } + return FALSE; +} + +static GstFlowReturn +gst_flups_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) +{ + GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent); GstFlowReturn ret = GST_FLOW_OK; guint32 avail; gboolean save, discont; @@ -1906,7 +2132,7 @@ gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer) demux->adapter_offset = GST_BUFFER_OFFSET (buffer); gst_adapter_push (demux->adapter, buffer); - demux->bytes_since_scr += GST_BUFFER_SIZE (buffer); + demux->bytes_since_scr += gst_buffer_get_size (buffer); avail = gst_adapter_available (demux->rev_adapter); if (avail > 0) { @@ -2006,8 +2232,6 @@ gst_flups_demux_chain (GstPad * pad, GstBuffer * buffer) } } done: - gst_object_unref (demux); - return ret; } @@ -2050,6 +2274,7 @@ gst_flups_demux_change_state (GstElement * element, GstStateChange transition) gst_flups_demux_reset_psm (demux); gst_segment_init (&demux->sink_segment, GST_FORMAT_UNDEFINED); gst_segment_init (&demux->src_segment, GST_FORMAT_TIME); + gst_flups_demux_flush (demux); break; default: break; @@ -2075,10 +2300,20 @@ gst_flups_demux_change_state (GstElement * element, GstStateChange transition) return result; } +static void +gst_segment_set_position (GstSegment * segment, GstFormat format, + guint64 position) +{ + if (segment->format == GST_FORMAT_UNDEFINED) { + segment->format = format; + } + segment->position = position; +} + gboolean gst_flups_demux_plugin_init (GstPlugin * plugin) { - GST_DEBUG_CATEGORY_INIT (gstflupesfilter_debug, "rsnpesfilter", 0, + GST_DEBUG_CATEGORY_INIT (mpegpspesfilter_debug, "rsnpesfilter", 0, "MPEG program stream PES filter debug"); GST_DEBUG_CATEGORY_INIT (gstflupsdemux_debug, "rsndvddemux", 0, diff --git a/ext/resindvd/gstmpegdemux.h b/ext/resindvd/gstmpegdemux.h index f62915008d..1696e25fb7 100644 --- a/ext/resindvd/gstmpegdemux.h +++ b/ext/resindvd/gstmpegdemux.h @@ -1,4 +1,10 @@ -/* +/* + * This library is licensed under 2 different licenses and you + * can choose to use it under the terms of either one of them. The + * two licenses are the MPL 1.1 and the LGPL. + * + * MPL: + * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at @@ -9,6 +15,23 @@ * License for the specific language governing rights and limitations * under the License. * + * LGPL: + * + * 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. + * * The Original Code is Fluendo MPEG Demuxer plugin. * * The Initial Developer of the Original Code is Fluendo, S.L. @@ -72,6 +95,9 @@ struct _GstFluPSDemux { GstElement parent; GstPad * sinkpad; + gboolean random_access; /* If we operate in pull mode */ + gboolean flushing; + GstAdapter * adapter; GstAdapter * rev_adapter; @@ -90,7 +116,7 @@ struct _GstFluPSDemux { guint64 scr_rate_n; guint64 scr_rate_d; guint64 first_scr_offset; - guint64 last_scr_offset; + guint64 cur_scr_offset; gint16 psm[GST_FLUPS_DEMUX_MAX_PSM]; diff --git a/ext/resindvd/gstpesfilter.c b/ext/resindvd/gstpesfilter.c index f2ccc7849d..d58c726026 100644 --- a/ext/resindvd/gstpesfilter.c +++ b/ext/resindvd/gstpesfilter.c @@ -1,23 +1,45 @@ -/* - * The contents of this file are subject to the Mozilla Public License - * Version 1.1 (the "License"); you may not use this file except in - * compliance with the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/. - * - * Software distributed under the License is distributed on an "AS IS" - * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - * License for the specific language governing rights and limitations - * under the License. - * - * The Original Code is Fluendo MPEG Demuxer plugin. - * - * The Initial Developer of the Original Code is Fluendo, S.L. - * Portions created by Fluendo, S.L. are Copyright (C) 2005 - * Fluendo, S.L. All Rights Reserved. - * - * Contributor(s): Wim Taymans - * Jan Schmidt - */ + /* + * This library is licensed under 2 different licenses and you + * can choose to use it under the terms of either one of them. The + * two licenses are the MPL 1.1 and the LGPL. + * + * MPL: + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * + * LGPL: + * + * 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. + * + * The Original Code is Fluendo MPEG Demuxer plugin. + * + * The Initial Developer of the Original Code is Fluendo, S.L. + * Portions created by Fluendo, S.L. are Copyright (C) 2005 + * Fluendo, S.L. All Rights Reserved. + * + * Contributor(s): Wim Taymans + */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -26,8 +48,8 @@ #include "gstmpegdefs.h" #include "gstpesfilter.h" -GST_DEBUG_CATEGORY (gstflupesfilter_debug); -#define GST_CAT_DEFAULT (gstflupesfilter_debug) +GST_DEBUG_CATEGORY (mpegpspesfilter_debug); +#define GST_CAT_DEFAULT (mpegpspesfilter_debug) static GstFlowReturn gst_pes_filter_data_push (GstPESFilter * filter, gboolean first, GstBuffer * buffer); @@ -75,17 +97,6 @@ gst_pes_filter_set_callbacks (GstPESFilter * filter, filter->user_data = user_data; } -/* sync:4 == 00xx ! pts:3 ! 1 ! pts:15 ! 1 | pts:15 ! 1 */ -#define READ_TS(data, target, lost_sync_label) \ - if ((*data & 0x01) != 0x01) goto lost_sync_label; \ - target = ((guint64) (*data++ & 0x0E)) << 29; \ - target |= ((guint64) (*data++ )) << 22; \ - if ((*data & 0x01) != 0x01) goto lost_sync_label; \ - target |= ((guint64) (*data++ & 0xFE)) << 14; \ - target |= ((guint64) (*data++ )) << 7; \ - if ((*data & 0x01) != 0x01) goto lost_sync_label; \ - target |= ((guint64) (*data++ & 0xFE)) >> 1; - static gboolean gst_pes_filter_is_sync (guint32 sync) { @@ -101,15 +112,21 @@ gst_pes_filter_parse (GstPESFilter * filter) GstFlowReturn ret; guint32 start_code; + gboolean STD_buffer_bound_scale G_GNUC_UNUSED; guint16 STD_buffer_size_bound; const guint8 *data; gint avail, datalen; gboolean have_size = FALSE; - /* read start code and length */ - if (!(data = gst_adapter_peek (filter->adapter, 6))) + avail = gst_adapter_available (filter->adapter); + + if (avail < 6) goto need_more_data; + data = gst_adapter_map (filter->adapter, 6); + + /* read start code and length */ + /* get start code */ start_code = GST_READ_UINT32_BE (data); if (!gst_pes_filter_is_sync (start_code)) @@ -124,9 +141,6 @@ gst_pes_filter_parse (GstPESFilter * filter) /* start parsing length */ filter->length = GST_READ_UINT16_BE (data); - /* see how much is available */ - avail = gst_adapter_available (filter->adapter); - GST_DEBUG ("id 0x%02x length %d, avail %d start code 0x%02x", filter->id, filter->length, avail, filter->start_code); @@ -139,6 +153,7 @@ gst_pes_filter_parse (GstPESFilter * filter) * to set the allow_unbounded flag if they want */ if (filter->length == 0 && ((filter->start_code & 0xFFFFFFF0) == PACKET_VIDEO_START_CODE || + filter->start_code == ID_EXTENDED_STREAM_ID || filter->allow_unbounded)) { GST_DEBUG ("id 0x%02x, unbounded length", filter->id); filter->unbounded_packet = TRUE; @@ -155,10 +170,14 @@ gst_pes_filter_parse (GstPESFilter * filter) avail = MIN (avail, filter->length + 6); } + if (avail < 6) + goto need_more_data; + + gst_adapter_unmap (filter->adapter); + /* read more data, either the whole packet if there is a length * or whatever we have available if this in an unbounded packet. */ - if (!(data = gst_adapter_peek (filter->adapter, avail))) - goto need_more_data; + data = gst_adapter_map (filter->adapter, avail); /* This will make us flag LOST_SYNC if we run out of data from here onward */ have_size = TRUE; @@ -177,7 +196,8 @@ gst_pes_filter_parse (GstPESFilter * filter) case ID_PROGRAM_STREAM_DIRECTORY: case ID_DSMCC_STREAM: case ID_ITU_TREC_H222_TYPE_E_STREAM: - goto skip; + /* Push directly out */ + goto push_out; case ID_PADDING_STREAM: GST_DEBUG ("skipping padding stream"); goto skip; @@ -185,9 +205,8 @@ gst_pes_filter_parse (GstPESFilter * filter) break; } - if (datalen < 1) + if (datalen == 0) goto need_more_data; - filter->pts = filter->dts = -1; /* stuffing bits, first two bits are '10' for mpeg2 pes so this code is @@ -212,7 +231,7 @@ gst_pes_filter_parse (GstPESFilter * filter) if (datalen < 3) goto need_more_data; - /* STD_buffer_bound_scale = *data & 0x20; */ + STD_buffer_bound_scale = *data & 0x20; STD_buffer_size_bound = ((guint16) (*data++ & 0x1F)) << 8; STD_buffer_size_bound |= *data++; @@ -264,7 +283,7 @@ gst_pes_filter_parse (GstPESFilter * filter) /* check PES scrambling control */ if ((flags & 0x30) != 0) - goto encrypted; + GST_DEBUG ("PES scrambling control: %x", (flags >> 4) & 0x3); /* 2: PTS_DTS_flags * 1: ESCR_flag @@ -376,9 +395,53 @@ gst_pes_filter_parse (GstPESFilter * filter) } /* PES_extension_flag */ if ((flags & 0x01)) { - GST_DEBUG ("%x PES_extension", filter->id); + flags = *data++; + header_data_length -= 1; + datalen -= 1; + GST_DEBUG ("%x PES_extension, flags 0x%02x", filter->id, flags); + /* PES_private_data_flag */ + if ((flags & 0x80)) { + GST_DEBUG ("%x PES_private_data_flag", filter->id); + data += 16; + header_data_length -= 16; + datalen -= 16; + } + /* pack_header_field_flag */ + if ((flags & 0x40)) { + guint8 pack_field_length = *data; + GST_DEBUG ("%x pack_header_field_flag, pack_field_length %d", + filter->id, pack_field_length); + data += pack_field_length + 1; + header_data_length -= pack_field_length + 1; + datalen -= pack_field_length + 1; + } + /* program_packet_sequence_counter_flag */ + if ((flags & 0x20)) { + GST_DEBUG ("%x program_packet_sequence_counter_flag", filter->id); + data += 2; + header_data_length -= 2; + datalen -= 2; + } + /* P-STD_buffer_flag */ + if ((flags & 0x10)) { + GST_DEBUG ("%x P-STD_buffer_flag", filter->id); + data += 2; + header_data_length -= 2; + datalen -= 2; + } + /* PES_extension_flag_2 */ + if ((flags & 0x01)) { + guint8 PES_extension_field_length = *data++; + GST_DEBUG ("%x PES_extension_flag_2, len %d", + filter->id, PES_extension_field_length & 0x7f); + if (PES_extension_field_length == 0x81) { + GST_DEBUG ("%x substream id 0x%02x", filter->id, *data); + } + data += PES_extension_field_length & 0x7f; + header_data_length -= (PES_extension_field_length & 0x7f) + 1; + datalen -= (PES_extension_field_length & 0x7f) + 1; + } } - /* calculate the amount of real data in this PES packet */ data += header_data_length; datalen -= header_data_length; @@ -392,6 +455,7 @@ gst_pes_filter_parse (GstPESFilter * filter) goto lost_sync; } +push_out: { GstBuffer *out; guint16 consumed; @@ -408,11 +472,8 @@ gst_pes_filter_parse (GstPESFilter * filter) } if (datalen > 0) { - out = gst_buffer_new (); - GST_BUFFER_DATA (out) = g_memdup (data, datalen); - GST_BUFFER_SIZE (out) = datalen; - GST_BUFFER_MALLOCDATA (out) = GST_BUFFER_DATA (out); - + out = gst_buffer_new_allocate (NULL, datalen, NULL); + gst_buffer_fill (out, 0, data, datalen); ret = gst_pes_filter_data_push (filter, TRUE, out); filter->first = FALSE; } else { @@ -425,6 +486,7 @@ gst_pes_filter_parse (GstPESFilter * filter) filter->state = STATE_DATA_PUSH; } + gst_adapter_unmap (filter->adapter); gst_adapter_flush (filter->adapter, avail); ADAPTER_OFFSET_FLUSH (avail); @@ -434,36 +496,27 @@ need_more_data: { if (filter->unbounded_packet == FALSE) { if (have_size == TRUE) { - GST_DEBUG ("bounded need more data %d, lost sync", + GST_DEBUG ("bounded need more data %" G_GSIZE_FORMAT " , lost sync", gst_adapter_available (filter->adapter)); ret = GST_FLOW_LOST_SYNC; } else { - GST_DEBUG ("bounded need more data %d, breaking for more", - gst_adapter_available (filter->adapter)); + GST_DEBUG ("bounded need more data %" G_GSIZE_FORMAT + ", breaking for more", gst_adapter_available (filter->adapter)); ret = GST_FLOW_NEED_MORE_DATA; } } else { - GST_DEBUG ("unbounded need more data %d", + GST_DEBUG ("unbounded need more data %" G_GSIZE_FORMAT, gst_adapter_available (filter->adapter)); ret = GST_FLOW_NEED_MORE_DATA; } - + gst_adapter_unmap (filter->adapter); return ret; } skip: { - GST_DEBUG ("skipping 0x%02x", filter->id); - gst_adapter_flush (filter->adapter, avail); - ADAPTER_OFFSET_FLUSH (avail); + gst_adapter_unmap (filter->adapter); - filter->length -= avail - 6; - if (filter->length > 0 || filter->unbounded_packet) - filter->state = STATE_DATA_SKIP; - return GST_FLOW_OK; - } -encrypted: - { - GST_DEBUG ("skipping encrypted 0x%02x", filter->id); + GST_DEBUG ("skipping 0x%02x", filter->id); gst_adapter_flush (filter->adapter, avail); ADAPTER_OFFSET_FLUSH (avail); @@ -474,6 +527,7 @@ encrypted: } lost_sync: { + gst_adapter_unmap (filter->adapter); GST_DEBUG ("lost sync"); gst_adapter_flush (filter->adapter, 4); ADAPTER_OFFSET_FLUSH (4); @@ -562,14 +616,8 @@ gst_pes_filter_process (GstPESFilter * filter) ret = GST_FLOW_OK; } else { GstBuffer *out; - guint8 *data; - data = gst_adapter_take (filter->adapter, avail); - - out = gst_buffer_new (); - GST_BUFFER_DATA (out) = data; - GST_BUFFER_SIZE (out) = avail; - GST_BUFFER_MALLOCDATA (out) = data; + out = gst_adapter_take_buffer (filter->adapter, avail); ret = gst_pes_filter_data_push (filter, filter->first, out); filter->first = FALSE; diff --git a/ext/resindvd/gstpesfilter.h b/ext/resindvd/gstpesfilter.h index 27c618c2d4..ccc8461da6 100644 --- a/ext/resindvd/gstpesfilter.h +++ b/ext/resindvd/gstpesfilter.h @@ -1,4 +1,10 @@ -/* +/* + * This library is licensed under 2 different licenses and you + * can choose to use it under the terms of either one of them. The + * two licenses are the MPL 1.1 and the LGPL. + * + * MPL: + * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at @@ -9,6 +15,23 @@ * License for the specific language governing rights and limitations * under the License. * + * LGPL: + * + * 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. + * * The Original Code is Fluendo MPEG Demuxer plugin. * * The Initial Developer of the Original Code is Fluendo, S.L. @@ -16,7 +39,6 @@ * Fluendo, S.L. All Rights Reserved. * * Contributor(s): Wim Taymans - * Jan Schmidt */ #ifndef __GST_PES_FILTER_H__ @@ -62,8 +84,6 @@ struct _GstPESFilter { gboolean unbounded_packet; guint16 length; - guint8 type; - gint64 pts; gint64 dts; }; diff --git a/ext/resindvd/plugin.c b/ext/resindvd/plugin.c index ea939eb623..3b2ac6b423 100644 --- a/ext/resindvd/plugin.c +++ b/ext/resindvd/plugin.c @@ -35,7 +35,7 @@ plugin_init (GstPlugin * plugin) { gboolean result = TRUE; - GST_DEBUG_CATEGORY_INIT (resindvd_debug, "resindvd elements", + GST_DEBUG_CATEGORY_INIT (resindvd_debug, "resindvd", 0, "DVD playback elements from resindvd"); #ifdef ENABLE_NLS diff --git a/ext/resindvd/resindvdbin.c b/ext/resindvd/resindvdbin.c index 78a74e2bbe..119c2b6af6 100644 --- a/ext/resindvd/resindvdbin.c +++ b/ext/resindvd/resindvdbin.c @@ -26,16 +26,20 @@ #include #include #include +#include +#include #include "resindvdbin.h" #include "resindvdsrc.h" -#include "rsnstreamselector.h" -#include "rsnaudiomunge.h" +#include "rsninputselector.h" +// #include "rsnaudiomunge.h" #include "rsndec.h" -#include "rsnparsetter.h" +// #include "rsnparsetter.h" #include "gstmpegdemux.h" +#define RSN_TYPE_INPUT_SELECTOR GST_TYPE_INPUT_SELECTOR + GST_DEBUG_CATEGORY_EXTERN (resindvd_debug); #define GST_CAT_DEFAULT resindvd_debug @@ -62,14 +66,14 @@ static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("video", GST_PAD_SRC, GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("video/x-raw-yuv") + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)) ); static GstStaticPadTemplate audio_src_template = - GST_STATIC_PAD_TEMPLATE ("audio", +GST_STATIC_PAD_TEMPLATE ("audio", GST_PAD_SRC, GST_PAD_SOMETIMES, - GST_STATIC_CAPS ("audio/x-raw-int; audio/x-raw-float") + GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL)) ); static GstStaticPadTemplate subpicture_src_template = @@ -79,12 +83,12 @@ GST_STATIC_PAD_TEMPLATE ("subpicture", GST_STATIC_CAPS ("subpicture/x-dvd") ); -static void rsn_dvdbin_do_init (GType rsn_dvdbin_type); static void rsn_dvdbin_finalize (GObject * object); static void rsn_dvdbin_uri_handler_init (gpointer g_iface, gpointer iface_data); -GST_BOILERPLATE_FULL (RsnDvdBin, rsn_dvdbin, GstBin, - GST_TYPE_BIN, rsn_dvdbin_do_init); +#define rsn_dvdbin_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (RsnDvdBin, rsn_dvdbin, GST_TYPE_BIN, + G_IMPLEMENT_INTERFACE (GST_TYPE_URI_HANDLER, rsn_dvdbin_uri_handler_init)); static void demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin); @@ -96,31 +100,14 @@ static void rsn_dvdbin_get_property (GObject * object, guint prop_id, static GstStateChangeReturn rsn_dvdbin_change_state (GstElement * element, GstStateChange transition); -static void -rsn_dvdbin_base_init (gpointer gclass) -{ - - GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); - - gst_element_class_add_pad_template (element_class, - 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_simple (element_class, "rsndvdbin", - "Generic/Bin/Player", - "DVD playback element", "Jan Schmidt "); - - element_class->change_state = GST_DEBUG_FUNCPTR (rsn_dvdbin_change_state); -} - static void rsn_dvdbin_class_init (RsnDvdBinClass * klass) { GObjectClass *gobject_class; + GstElementClass *element_class; gobject_class = (GObjectClass *) klass; + element_class = (GstElementClass *) klass; gobject_class->finalize = rsn_dvdbin_finalize; gobject_class->set_property = rsn_dvdbin_set_property; @@ -129,23 +116,23 @@ rsn_dvdbin_class_init (RsnDvdBinClass * klass) g_object_class_install_property (gobject_class, ARG_DEVICE, g_param_spec_string ("device", "Device", "DVD device location", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (element_class, + 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)); + + element_class->change_state = GST_DEBUG_FUNCPTR (rsn_dvdbin_change_state); + + gst_element_class_set_details_simple (element_class, "rsndvdbin", + "Generic/Bin/Player", + "DVD playback element", "Jan Schmidt "); } static void -rsn_dvdbin_do_init (GType rsn_dvdbin_type) -{ - static const GInterfaceInfo urihandler_info = { - rsn_dvdbin_uri_handler_init, - NULL, - NULL - }; - - g_type_add_interface_static (rsn_dvdbin_type, GST_TYPE_URI_HANDLER, - &urihandler_info); -} - -static void -rsn_dvdbin_init (RsnDvdBin * dvdbin, RsnDvdBinClass * gclass) +rsn_dvdbin_init (RsnDvdBin * dvdbin) { dvdbin->dvd_lock = g_mutex_new (); dvdbin->preroll_lock = g_mutex_new (); @@ -166,20 +153,20 @@ rsn_dvdbin_finalize (GObject * object) /* URI interface */ static GstURIType -rsn_dvdbin_uri_get_type (void) +rsn_dvdbin_uri_get_type (GType type) { return GST_URI_SRC; } static const gchar *const * -rsn_dvdbin_uri_get_protocols (void) +rsn_dvdbin_uri_get_protocols (GType type) { static const gchar *protocols[] = { "dvd", NULL }; return protocols; } -static const gchar * +static gchar * rsn_dvdbin_uri_get_uri (GstURIHandler * handler) { RsnDvdBin *dvdbin = RESINDVDBIN (handler); @@ -192,11 +179,12 @@ rsn_dvdbin_uri_get_uri (GstURIHandler * handler) dvdbin->last_uri = g_strdup ("dvd://"); DVDBIN_UNLOCK (dvdbin); - return dvdbin->last_uri; + return g_strdup (dvdbin->last_uri); } static gboolean -rsn_dvdbin_uri_set_uri (GstURIHandler * handler, const gchar * uri) +rsn_dvdbin_uri_set_uri (GstURIHandler * handler, const gchar * uri, + GError ** error) { RsnDvdBin *dvdbin = RESINDVDBIN (handler); gboolean ret; @@ -335,10 +323,12 @@ typedef struct { RsnDvdBin *dvdbin; GstPad *pad; + gulong pad_block_id; } RsnDvdBinPadBlockCtx; -static void dvdbin_pad_blocked_cb (GstPad * pad, gboolean blocked, - RsnDvdBinPadBlockCtx * ctx); +static GstPadProbeReturn dvdbin_pad_blocked_cb (GstPad * pad, + GstPadProbeInfo * info, RsnDvdBinPadBlockCtx * ctx); + static void _pad_block_destroy_notify (RsnDvdBinPadBlockCtx * ctx) { @@ -366,6 +356,7 @@ create_elements (RsnDvdBin * dvdbin) "device", dvdbin->device, NULL); } + /* FIXME: Import and use local copy of mpeg PS demuxer */ if (!try_create_piece (dvdbin, DVD_ELEM_DEMUX, NULL, GST_TYPE_FLUPS_DEMUX, "dvddemux", "DVD demuxer")) return FALSE; @@ -394,7 +385,8 @@ create_elements (RsnDvdBin * dvdbin) "viddec", "video decoder")) return FALSE; - if (!try_create_piece (dvdbin, DVD_ELEM_PARSET, NULL, RSN_TYPE_RSNPARSETTER, + /* FIXME: Replace identity */ + if (!try_create_piece (dvdbin, DVD_ELEM_PARSET, "identity", 0, //RSN_TYPE_RSNPARSETTER, "rsnparsetter", "Aspect ratio adjustment")) return FALSE; @@ -420,14 +412,16 @@ create_elements (RsnDvdBin * dvdbin) bctx = g_slice_new (RsnDvdBinPadBlockCtx); bctx->dvdbin = gst_object_ref (dvdbin); bctx->pad = gst_object_ref (dvdbin->video_pad); - gst_pad_set_blocked_async_full (src, TRUE, - (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify) + bctx->pad_block_id = + gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, + (GstPadProbeCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify) _pad_block_destroy_notify); gst_object_unref (src); src = NULL; + /* FIXME: Core input selector OK? */ if (!try_create_piece (dvdbin, DVD_ELEM_SPU_SELECT, NULL, - RSN_TYPE_STREAM_SELECTOR, "subpselect", "Subpicture stream selector")) + RSN_TYPE_INPUT_SELECTOR, "subpselect", "Subpicture stream selector")) return FALSE; /* Add a single standalone queue to hold a single buffer of SPU data */ @@ -461,18 +455,20 @@ create_elements (RsnDvdBin * dvdbin) bctx = g_slice_new (RsnDvdBinPadBlockCtx); bctx->dvdbin = gst_object_ref (dvdbin); bctx->pad = gst_object_ref (dvdbin->subpicture_pad); - gst_pad_set_blocked_async_full (src, TRUE, - (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify) + bctx->pad_block_id = + gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, + (GstPadProbeCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify) _pad_block_destroy_notify); gst_object_unref (src); src = NULL; - if (!try_create_piece (dvdbin, DVD_ELEM_AUD_SELECT, NULL, - RSN_TYPE_STREAM_SELECTOR, "audioselect", "Audio stream selector")) + if (!try_create_piece (dvdbin, DVD_ELEM_AUD_SELECT, "input-selector", + RSN_TYPE_INPUT_SELECTOR, "audioselect", "Audio stream selector")) return FALSE; - if (!try_create_piece (dvdbin, DVD_ELEM_AUD_MUNGE, NULL, - RSN_TYPE_AUDIOMUNGE, "audioearlymunge", "Audio output filter")) + if (!try_create_piece (dvdbin, DVD_ELEM_AUD_MUNGE, "identity", + 0 /* RSN_TYPE_AUDIOMUNGE */ , "audioearlymunge", + "Audio output filter")) return FALSE; if (!try_create_piece (dvdbin, DVD_ELEM_AUDDEC, NULL, @@ -513,8 +509,9 @@ create_elements (RsnDvdBin * dvdbin) bctx = g_slice_new (RsnDvdBinPadBlockCtx); bctx->dvdbin = gst_object_ref (dvdbin); bctx->pad = gst_object_ref (dvdbin->audio_pad); - gst_pad_set_blocked_async_full (src, TRUE, - (GstPadBlockCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify) + bctx->pad_block_id = + gst_pad_add_probe (src, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM, + (GstPadProbeCallback) dvdbin_pad_blocked_cb, bctx, (GDestroyNotify) _pad_block_destroy_notify); gst_object_unref (src); src = NULL; @@ -634,7 +631,7 @@ connect_thru_mq (RsnDvdBin * dvdbin, GstPad * pad) return FALSE; sinkname = gst_pad_get_name (mq_sink); - tmp = sinkname + 4; + tmp = sinkname + 5; srcname = g_strdup_printf ("src_%s", tmp); mq_src = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_MQUEUE], @@ -653,9 +650,9 @@ can_sink_caps (GstElement * e, GstCaps * caps) GstPad *sink = gst_element_get_static_pad (e, "sink"); if (sink) { - GstCaps *sink_caps = gst_pad_get_caps (sink); + GstCaps *sink_caps = gst_pad_query_caps (sink, caps); if (sink_caps) { - res = gst_caps_can_intersect (sink_caps, caps); + res = !gst_caps_is_empty (sink_caps); gst_caps_unref (sink_caps); } gst_object_unref (sink); @@ -675,7 +672,7 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin) GST_DEBUG_OBJECT (dvdbin, "New pad: %" GST_PTR_FORMAT, pad); - caps = gst_pad_get_caps (pad); + caps = gst_pad_query_caps (pad, NULL); if (caps == NULL) { GST_WARNING_OBJECT (dvdbin, "NULL caps from pad %" GST_PTR_FORMAT, pad); return; @@ -694,9 +691,12 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin) g_return_if_fail (s != NULL); if (can_sink_caps (dvdbin->pieces[DVD_ELEM_VIDDEC], caps)) { + GST_LOG_OBJECT (dvdbin, "Found video pad w/ caps %" GST_PTR_FORMAT, caps); dest_pad = gst_element_get_static_pad (dvdbin->pieces[DVD_ELEM_VIDDEC], "sink"); } else if (g_str_equal (gst_structure_get_name (s), "subpicture/x-dvd")) { + GST_LOG_OBJECT (dvdbin, "Found subpicture pad w/ caps %" GST_PTR_FORMAT, + caps); dest_pad = gst_element_get_request_pad (dvdbin->pieces[DVD_ELEM_SPU_SELECT], "sink_%u"); @@ -720,7 +720,7 @@ demux_pad_added (GstElement * element, GstPad * pad, RsnDvdBin * dvdbin) ("No MPEG video decoder found")); } else { GST_ELEMENT_WARNING (dvdbin, STREAM, CODEC_NOT_FOUND, (NULL), - ("No MPEG audio decoder found")); + ("No audio decoder found")); } } @@ -763,6 +763,7 @@ demux_no_more_pads (GstElement * element, RsnDvdBin * dvdbin) gboolean no_more_pads = FALSE; guint n_audio_pads = 0; + GST_DEBUG_OBJECT (dvdbin, "Received no more pads from demuxer"); DVDBIN_PREROLL_LOCK (dvdbin); g_object_get (dvdbin->pieces[DVD_ELEM_AUD_SELECT], "n-pads", &n_audio_pads, @@ -781,21 +782,15 @@ demux_no_more_pads (GstElement * element, RsnDvdBin * dvdbin) } } -static void -dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked, - RsnDvdBinPadBlockCtx * ctx) +static GstPadProbeReturn +dvdbin_pad_blocked_cb (GstPad * opad, + GstPadProbeInfo * info, RsnDvdBinPadBlockCtx * ctx) { RsnDvdBin *dvdbin; GstPad *pad; gboolean added_last_pad = FALSE; gboolean added = FALSE; - /* If not blocked ctx is NULL! */ - if (!blocked) { - GST_DEBUG_OBJECT (opad, "Pad unblocked"); - return; - } - dvdbin = ctx->dvdbin; pad = ctx->pad; @@ -812,8 +807,8 @@ dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked, } DVDBIN_PREROLL_UNLOCK (dvdbin); - gst_pad_set_blocked_async (opad, FALSE, - (GstPadBlockCallback) dvdbin_pad_blocked_cb, NULL); + if (ctx->pad_block_id) + gst_pad_remove_probe (opad, ctx->pad_block_id); } else if (pad == dvdbin->audio_pad) { GST_DEBUG_OBJECT (opad, "Pad block -> audio pad"); DVDBIN_PREROLL_LOCK (dvdbin); @@ -826,8 +821,8 @@ dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked, } DVDBIN_PREROLL_UNLOCK (dvdbin); - gst_pad_set_blocked_async (opad, FALSE, - (GstPadBlockCallback) dvdbin_pad_blocked_cb, NULL); + if (ctx->pad_block_id) + gst_pad_remove_probe (opad, ctx->pad_block_id); } else if (pad == dvdbin->video_pad) { GST_DEBUG_OBJECT (opad, "Pad block -> video pad"); @@ -842,14 +837,16 @@ dvdbin_pad_blocked_cb (GstPad * opad, gboolean blocked, } DVDBIN_PREROLL_UNLOCK (dvdbin); - gst_pad_set_blocked_async (opad, FALSE, - (GstPadBlockCallback) dvdbin_pad_blocked_cb, NULL); + if (ctx->pad_block_id) + gst_pad_remove_probe (opad, ctx->pad_block_id); } if (added_last_pad) { GST_DEBUG_OBJECT (dvdbin, "Firing no more pads from pad-blocked cb"); gst_element_no_more_pads (GST_ELEMENT (dvdbin)); } + + return GST_PAD_PROBE_OK; } static void diff --git a/ext/resindvd/resindvdsrc.c b/ext/resindvd/resindvdsrc.c index fff79b22f4..f5807069cf 100644 --- a/ext/resindvd/resindvdsrc.c +++ b/ext/resindvd/resindvdsrc.c @@ -24,11 +24,12 @@ #include #include +#include #include #include #include -#include #include +#include #include "resindvdsrc.h" @@ -89,6 +90,7 @@ typedef struct static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + // GST_STATIC_CAPS ("video/mpeg,mpegversion=2,systemstream=true") GST_STATIC_CAPS ("application/x-resin-dvd") ); @@ -100,8 +102,9 @@ static GstFormat chapter_format; static void rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type); -GST_BOILERPLATE_FULL (resinDvdSrc, rsn_dvdsrc, GstBaseSrc, - GST_TYPE_BASE_SRC, rsn_dvdsrc_register_extra); +#define rsn_dvdsrc_parent_class parent_class +G_DEFINE_TYPE_EXTENDED (resinDvdSrc, rsn_dvdsrc, GST_TYPE_BASE_SRC, + 0, rsn_dvdsrc_register_extra (g_define_type_id)); static gboolean read_vts_info (resinDvdSrc * src); @@ -118,11 +121,11 @@ static gboolean rsn_dvdsrc_unlock (GstBaseSrc * bsrc); static gboolean rsn_dvdsrc_unlock_stop (GstBaseSrc * bsrc); static gboolean rsn_dvdsrc_is_seekable (GstBaseSrc * bsrc); -static gboolean rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc, GstEvent * event, - GstSegment * segment); +static gboolean rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc, + GstEvent * event, GstSegment * segment); static gboolean rsn_dvdsrc_do_seek (GstBaseSrc * bsrc, GstSegment * segment); -static GstStateChangeReturn -rsn_dvdsrc_change_state (GstElement * element, GstStateChange transition); +static GstStateChangeReturn rsn_dvdsrc_change_state (GstElement * element, + GstStateChange transition); static void rsn_dvdsrc_prepare_spu_stream_event (resinDvdSrc * src, guint8 logical_stream, guint8 phys_stream, gboolean forced_only); @@ -142,7 +145,6 @@ static void rsn_dvdsrc_check_nav_blocks (resinDvdSrc * src); static void rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src, RsnDvdPendingNav * next_nav); -static gboolean rsn_dvdsrc_check_get_range (GstBaseSrc * src); static GstFlowReturn rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset, guint length, GstBuffer ** buf); static gboolean rsn_dvdsrc_src_event (GstBaseSrc * basesrc, GstEvent * event); @@ -188,18 +190,6 @@ rsn_dvdsrc_register_extra (GType rsn_dvdsrc_type) chapter_format = gst_format_register ("chapter", "DVD chapter format"); } -static void -rsn_dvdsrc_base_init (gpointer gclass) -{ - - 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_set_details_simple (element_class, "Resin DVD Src", - "Source/DVD", "DVD source element", "Jan Schmidt "); -} - static void rsn_dvdsrc_class_init (resinDvdSrcClass * klass) { @@ -228,8 +218,6 @@ rsn_dvdsrc_class_init (resinDvdSrcClass * klass) GST_DEBUG_FUNCPTR (rsn_dvdsrc_prepare_seek); gstbasesrc_class->do_seek = GST_DEBUG_FUNCPTR (rsn_dvdsrc_do_seek); - gstbasesrc_class->check_get_range = - GST_DEBUG_FUNCPTR (rsn_dvdsrc_check_get_range); gstbasesrc_class->create = GST_DEBUG_FUNCPTR (rsn_dvdsrc_create); g_object_class_install_property (gobject_class, ARG_DEVICE, @@ -240,10 +228,15 @@ rsn_dvdsrc_class_init (resinDvdSrcClass * klass) g_param_spec_boolean ("fast-start", "Fast start", "Skip straight to the DVD menu on start", DEFAULT_FASTSTART, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_set_details_simple (gstelement_class, "Resin DVD Src", + "Source/DVD", "DVD source element", "Jan Schmidt "); } static void -rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc, resinDvdSrcClass * gclass) +rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc) { const gchar *envvar; @@ -637,11 +630,9 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration) * event, then sleep */ still_event = gst_video_event_new_still_frame (TRUE); - gst_segment_set_last_stop (segment, GST_FORMAT_TIME, src->cur_end_ts); + segment->position = 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_segment (segment); /* Grab any pending highlight event to send too */ hl_event = src->highlight_event; @@ -757,9 +748,8 @@ rsn_dvdsrc_do_still (resinDvdSrc * src, int duration) still_event = gst_video_event_new_still_frame (FALSE); /* If the segment was too short in a timed still, it may need extending */ - if (segment->last_stop < segment->start + GST_SECOND * duration) - gst_segment_set_last_stop (segment, GST_FORMAT_TIME, - segment->start + (GST_SECOND * duration)); + if (segment->position < segment->start + GST_SECOND * duration) + segment->position = segment->start + (GST_SECOND * duration); g_mutex_unlock (src->dvd_lock); gst_pad_push_event (GST_BASE_SRC_PAD (src), still_event); @@ -875,9 +865,7 @@ update_title_info (resinDvdSrc * src, gboolean force) } } if (title_str) { - GstTagList *tags = gst_tag_list_new (); - gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE, - title_str, NULL); + GstTagList *tags = gst_tag_list_new (GST_TAG_TITLE, title_str, NULL); g_free (title_str); return tags; } @@ -910,19 +898,18 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) { GstFlowReturn ret = GST_FLOW_OK; dvdnav_status_t dvdnav_ret; - guint8 *data; gint event, len; + GstMapInfo mmap; /* Allocate an output buffer if there isn't a pending one */ - if (src->alloc_buf == NULL) { - src->alloc_buf = gst_buffer_new_and_alloc (DVD_VIDEO_LB_LEN); - gst_buffer_set_caps (src->alloc_buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (src))); - } + if (src->alloc_buf == NULL) + src->alloc_buf = gst_buffer_new_allocate (NULL, DVD_VIDEO_LB_LEN, NULL); + + gst_buffer_map (src->alloc_buf, &mmap, GST_MAP_WRITE); - data = GST_BUFFER_DATA (src->alloc_buf); len = DVD_VIDEO_LB_LEN; - dvdnav_ret = dvdnav_get_next_block (src->dvdnav, data, &event, &len); + dvdnav_ret = dvdnav_get_next_block (src->dvdnav, mmap.data, &event, &len); if (dvdnav_ret != DVDNAV_STATUS_OK) goto read_error; g_mutex_lock (src->branch_lock); @@ -933,10 +920,12 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) switch (event) { case DVDNAV_BLOCK_OK: /* Data block that needs outputting */ + gst_buffer_unmap (src->alloc_buf, &mmap); src->next_buf = src->alloc_buf; + src->alloc_buf = NULL; + 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: @@ -988,6 +977,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) src->cur_vobu_base_ts = new_base_time; /* NAV packet is also a data block that needs sending */ + gst_buffer_unmap (src->alloc_buf, &mmap); src->next_buf = src->alloc_buf; src->alloc_buf = NULL; @@ -1011,11 +1001,11 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) case DVDNAV_STOP: /* End of the disc. EOS */ dvdnav_reset (src->dvdnav); - ret = GST_FLOW_UNEXPECTED; + ret = GST_FLOW_EOS; break; case DVDNAV_STILL_FRAME: { - dvdnav_still_event_t *info = (dvdnav_still_event_t *) data; + dvdnav_still_event_t *info = (dvdnav_still_event_t *) mmap.data; if (!have_dvd_lock) { /* At a still frame but can't block, handle it later */ @@ -1042,7 +1032,8 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) goto internal_error; break; case DVDNAV_CELL_CHANGE:{ - dvdnav_cell_change_event_t *event = (dvdnav_cell_change_event_t *) data; + dvdnav_cell_change_event_t *event = + (dvdnav_cell_change_event_t *) mmap.data; GstMessage *message; src->pgc_duration = MPEGTIME_TO_GSTTIME (event->pgc_length); @@ -1067,10 +1058,11 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) break; } case DVDNAV_SPU_CLUT_CHANGE: - rsn_dvdsrc_prepare_clut_change_event (src, (const guint32 *) data); + rsn_dvdsrc_prepare_clut_change_event (src, (const guint32 *) mmap.data); break; case DVDNAV_VTS_CHANGE:{ - dvdnav_vts_change_event_t *event = (dvdnav_vts_change_event_t *) data; + dvdnav_vts_change_event_t *event = + (dvdnav_vts_change_event_t *) mmap.data; if (dvdnav_is_domain_vmgm (src->dvdnav)) { src->vts_n = 0; @@ -1089,7 +1081,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) } case DVDNAV_AUDIO_STREAM_CHANGE:{ dvdnav_audio_stream_change_event_t *event = - (dvdnav_audio_stream_change_event_t *) data; + (dvdnav_audio_stream_change_event_t *) mmap.data; rsn_dvdsrc_prepare_audio_stream_event (src, event->logical, event->physical); @@ -1100,7 +1092,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) } case DVDNAV_SPU_STREAM_CHANGE:{ dvdnav_spu_stream_change_event_t *event = - (dvdnav_spu_stream_change_event_t *) data; + (dvdnav_spu_stream_change_event_t *) mmap.data; gint phys_track = event->physical_wide & 0x1f; gboolean forced_only = (event->physical_wide & 0x80) ? TRUE : FALSE; @@ -1117,7 +1109,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) } case DVDNAV_HIGHLIGHT:{ GST_DEBUG_OBJECT (src, "highlight change event, button %d", - ((dvdnav_highlight_event_t *) data)->buttonN); + ((dvdnav_highlight_event_t *) mmap.data)->buttonN); rsn_dvdsrc_update_highlight (src); break; } @@ -1131,6 +1123,9 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) GST_WARNING_OBJECT (src, "Unknown dvdnav event %d", event); break; } + if (src->alloc_buf) { + gst_buffer_unmap (src->alloc_buf, &mmap); + } if (src->highlight_event && have_dvd_lock && src->in_playing) { GstEvent *hl_event = src->highlight_event; @@ -1148,6 +1143,7 @@ rsn_dvdsrc_step (resinDvdSrc * src, gboolean have_dvd_lock) /* ERRORS */ read_error: { + gst_buffer_unmap (src->alloc_buf, &mmap); if (!rsn_descrambler_available ()) { GST_ELEMENT_ERROR (src, RESOURCE, READ, (_("Could not read DVD. This may be because the DVD is encrypted " @@ -1163,6 +1159,7 @@ read_error: } internal_error: { + gst_buffer_unmap (src->alloc_buf, &mmap); GST_ELEMENT_ERROR (src, RESOURCE, READ, (_("Could not read DVD.")), ("Internal error processing DVD commands. Error: %s", dvdnav_err_to_string (src->dvdnav))); @@ -1171,6 +1168,7 @@ internal_error: branching: { g_mutex_unlock (src->branch_lock); + gst_buffer_unmap (src->alloc_buf, &mmap); return GST_FLOW_FLUSHING; } } @@ -1288,14 +1286,6 @@ rsn_dvdsrc_prepare_next_block (resinDvdSrc * src, gboolean have_dvd_lock) return ret; } -static gboolean -rsn_dvdsrc_check_get_range (GstBaseSrc * src) -{ - /* ResinDVD never operates in pull mode. There might be - * a reason to in the future though? */ - return FALSE; -} - static GstFlowReturn rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset, guint length, GstBuffer ** outbuf) @@ -1396,11 +1386,11 @@ rsn_dvdsrc_create (GstBaseSrc * bsrc, guint64 offset, } if (src->cur_end_ts != GST_CLOCK_TIME_NONE) - gst_segment_set_last_stop (segment, GST_FORMAT_TIME, src->cur_end_ts); + segment->position = src->cur_end_ts; if (tags) { - gst_element_found_tags_for_pad (GST_ELEMENT_CAST (src), - GST_BASE_SRC_PAD (src), tags); + GstEvent *tag_event = gst_event_new_tag (tags); + gst_pad_push_event (GST_BASE_SRC_PAD (src), tag_event); tags = NULL; } g_mutex_lock (src->dvd_lock); @@ -2254,7 +2244,15 @@ 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); + { + GstMapInfo mmap; + gst_buffer_map (nav_buf, &mmap, GST_MAP_READ); + + navRead_PCI (&src->cur_pci, mmap.data + 0x2d); + + gst_buffer_unmap (nav_buf, &mmap); + } + src->have_pci = TRUE; forced_button = src->cur_pci.hli.hl_gi.fosl_btnn & 0x3f; @@ -2374,7 +2372,8 @@ rsn_dvdsrc_schedule_nav_cb (resinDvdSrc * src, RsnDvdPendingNav * next_nav) GST_TIME_ARGS (next_nav->running_ts)); g_mutex_unlock (src->dvd_lock); - gst_clock_id_wait_async (src->nav_clock_id, rsn_dvdsrc_nav_clock_cb, src); + gst_clock_id_wait_async (src->nav_clock_id, rsn_dvdsrc_nav_clock_cb, src, + NULL); gst_object_unref (clock); g_mutex_lock (src->dvd_lock); } @@ -2615,6 +2614,16 @@ rsn_dvdsrc_src_query (GstBaseSrc * basesrc, GstQuery * query) res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query); break; } + case GST_QUERY_SCHEDULING: + { + /* Make sure we operate in pull mode */ + gst_query_set_scheduling (query, GST_SCHEDULING_FLAG_SEQUENTIAL, 1, -1, + 0); + gst_query_add_scheduling_mode (query, GST_PAD_MODE_PUSH); + + res = TRUE; + break; + } default: res = GST_BASE_SRC_CLASS (parent_class)->query (basesrc, query); break; @@ -2653,7 +2662,7 @@ rsn_dvdsrc_prepare_seek (GstBaseSrc * bsrc, GstEvent * event, /* Seeks in our internal formats are passed directly through to the do_seek * method. */ gst_segment_init (segment, seek_format); - gst_segment_set_seek (segment, rate, seek_format, flags, cur_type, cur, + gst_segment_do_seek (segment, rate, seek_format, flags, cur_type, cur, stop_type, stop, &update); return TRUE; @@ -2898,7 +2907,7 @@ rsn_dvdsrc_do_seek (GstBaseSrc * bsrc, GstSegment * segment) segment->format = GST_FORMAT_TIME; /* The first TS output: */ - segment->last_stop = segment->start = src->cur_start_ts; + segment->position = segment->start = src->cur_start_ts; /* time field = position is the 'logical' stream time here: */ segment->time = 0; diff --git a/ext/resindvd/rsnaudiomunge.c b/ext/resindvd/rsnaudiomunge.c index 2b78dfea96..da0f73122b 100644 --- a/ext/resindvd/rsnaudiomunge.c +++ b/ext/resindvd/rsnaudiomunge.c @@ -103,8 +103,6 @@ static void rsn_audiomunge_init (RsnAudioMunge * munge) { munge->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); - gst_pad_set_setcaps_function (munge->sinkpad, - GST_DEBUG_FUNCPTR (rsn_audiomunge_set_caps)); gst_pad_set_getcaps_function (munge->sinkpad, GST_DEBUG_FUNCPTR (gst_pad_proxy_getcaps)); gst_pad_set_chain_function (munge->sinkpad, @@ -156,7 +154,6 @@ rsn_audiomunge_set_caps (GstPad * pad, GstCaps * caps) otherpad = (pad == munge->srcpad) ? munge->sinkpad : munge->srcpad; - ret = gst_pad_set_caps (otherpad, caps); gst_object_unref (munge); return ret; } @@ -238,6 +235,14 @@ rsn_audiomunge_sink_event (GstPad * pad, GstEvent * event) RsnAudioMunge *munge = RSN_AUDIOMUNGE (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + ret = gst_pad_set_caps (munge->src_pad, caps); + gst_event_unref (caps); + } case GST_EVENT_FLUSH_STOP: rsn_audiomunge_reset (munge); ret = gst_pad_push_event (munge->srcpad, event); diff --git a/ext/resindvd/rsndec.c b/ext/resindvd/rsndec.c index 788857a3be..2c999d5e03 100644 --- a/ext/resindvd/rsndec.c +++ b/ext/resindvd/rsndec.c @@ -23,6 +23,8 @@ #endif #include +#include +#include #include "rsndec.h" @@ -53,9 +55,9 @@ rsn_dec_class_init (RsnDecClass * klass) } static gboolean -rsn_dec_sink_event (GstPad * pad, GstEvent * event) +rsn_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { - RsnDec *self = RSN_DEC (gst_pad_get_parent (pad)); + RsnDec *self = RSN_DEC (parent); gboolean ret = TRUE; const GstStructure *s = gst_event_get_structure (event); const gchar *name = (s ? gst_structure_get_name (s) : NULL); @@ -63,9 +65,7 @@ rsn_dec_sink_event (GstPad * pad, GstEvent * event) if (name && g_str_equal (name, "application/x-gst-dvd")) ret = gst_pad_push_event (GST_PAD_CAST (self->srcpad), event); else - ret = self->sink_event_func (pad, event); - - gst_object_unref (self); + ret = self->sink_event_func (pad, parent, event); return ret; } @@ -246,10 +246,13 @@ _get_decoder_factories (gpointer arg) RsnDecFactoryFilterCtx ctx = { NULL, }; GstCaps *raw; gboolean raw_audio; + GstRegistry *registry = gst_registry_get (); ctx.desired_caps = gst_pad_template_get_caps (templ); - raw = gst_caps_from_string ("audio/x-raw-float"); + raw = + gst_caps_from_string + ("audio/x-raw,format=(string){ F32LE, F32BE, F64LE, F64BE }"); raw_audio = gst_caps_can_intersect (raw, ctx.desired_caps); if (raw_audio) { GstCaps *sub = gst_caps_subtract (ctx.desired_caps, raw); @@ -263,7 +266,7 @@ _get_decoder_factories (gpointer arg) ctx.decoder_caps = gst_caps_new_empty (); GST_DEBUG ("Finding factories for caps: %" GST_PTR_FORMAT, ctx.desired_caps); - factories = gst_default_registry_feature_filter ( + factories = gst_registry_feature_filter (registry, (GstPluginFeatureFilter) rsndec_factory_filter, FALSE, &ctx); /* If these are audio caps, we add audioconvert, which is not a decoder, @@ -274,7 +277,7 @@ _get_decoder_factories (gpointer arg) GstPluginFeature *feature; GST_DEBUG ("These are audio caps, adding audioconvert"); feature = - gst_default_registry_find_feature ("audioconvert", + gst_registry_find_feature (registry, "audioconvert", GST_TYPE_ELEMENT_FACTORY); if (feature) { factories = g_list_append (factories, feature); @@ -372,23 +375,14 @@ static GstStaticPadTemplate audio_sink_template = GST_STATIC_CAPS ("audio/mpeg,mpegversion=(int)1;" "audio/x-private1-lpcm;" "audio/x-private1-ac3;" "audio/ac3;" "audio/x-ac3;" - "audio/x-private1-dts; audio/x-raw-float") + "audio/x-private1-dts; audio/x-raw,format=(string)" + GST_AUDIO_FORMATS_ALL) ); static GstStaticPadTemplate audio_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-raw-float, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, MAX ], " - "endianness = (int) BYTE_ORDER, " - "width = (int) { 32, 64 }; " - "audio/x-raw-int, " - "rate = (int) [ 1, MAX ], " - "channels = (int) [ 1, MAX ], " - "endianness = (int) { 1234, 4321 }," - "width = (int) [ 1, 32 ], " - "depth = (int) [ 1, 32 ], " "signed = (boolean) { false, true }") + GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_FORMATS_ALL)) ); G_DEFINE_TYPE (RsnAudioDec, rsn_audiodec, RSN_TYPE_DEC); @@ -438,7 +432,7 @@ GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/x-raw-yuv") + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE (GST_VIDEO_FORMATS_ALL)) ); G_DEFINE_TYPE (RsnVideoDec, rsn_videodec, RSN_TYPE_DEC); diff --git a/ext/resindvd/rsninputselector.c b/ext/resindvd/rsninputselector.c new file mode 100644 index 0000000000..88bcc05f17 --- /dev/null +++ b/ext/resindvd/rsninputselector.c @@ -0,0 +1,1784 @@ +/* GStreamer input selector + * Copyright (C) 2003 Julien Moutte + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2005 Jan Schmidt + * Copyright (C) 2007 Wim Taymans + * Copyright (C) 2007 Andy Wingo + * Copyright (C) 2008 Nokia Corporation. (contact ) + * Copyright (C) 2011 Sebastian Dröge + * + * 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. + */ + +/** + * SECTION:element-input-selector + * @see_also: #GstOutputSelector + * + * Direct one out of N input streams to the output pad. + * + * The input pads are from a GstPad subclass and have additional + * properties, which users may find useful, namely: + * + * + * + * "running-time": Running time of stream on pad (#gint64) + * + * + * "tags": The currently active tags on the pad (#GstTagList, boxed type) + * + * + * "active": If the pad is currently active (#gboolean) + * + * + * "always-ok" : Make an inactive pads return #GST_FLOW_OK instead of + * #GST_FLOW_NOT_LINKED + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "rsninputselector.h" + +#define DEBUG_CACHED_BUFFERS 0 + +GST_DEBUG_CATEGORY_STATIC (input_selector_debug); +#define GST_CAT_DEFAULT input_selector_debug + +#define GST_TYPE_INPUT_SELECTOR_SYNC_MODE (gst_input_selector_sync_mode_get_type()) +static GType +gst_input_selector_sync_mode_get_type (void) +{ + static GType type = 0; + static const GEnumValue data[] = { + {GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT, + "Sync using the current active segment", + "active-segment"}, + {GST_INPUT_SELECTOR_SYNC_MODE_CLOCK, "Sync using the clock", "clock"}, + {0, NULL, NULL}, + }; + + if (!type) { + type = g_enum_register_static ("RsnInputSelectorSyncMode", data); + } + return type; +} + +#define GST_INPUT_SELECTOR_GET_LOCK(sel) (&((RsnInputSelector*)(sel))->lock) +#define GST_INPUT_SELECTOR_GET_COND(sel) (&((RsnInputSelector*)(sel))->cond) +#define GST_INPUT_SELECTOR_LOCK(sel) (g_mutex_lock (GST_INPUT_SELECTOR_GET_LOCK(sel))) +#define GST_INPUT_SELECTOR_UNLOCK(sel) (g_mutex_unlock (GST_INPUT_SELECTOR_GET_LOCK(sel))) +#define GST_INPUT_SELECTOR_WAIT(sel) (g_cond_wait (GST_INPUT_SELECTOR_GET_COND(sel), \ + GST_INPUT_SELECTOR_GET_LOCK(sel))) +#define GST_INPUT_SELECTOR_BROADCAST(sel) (g_cond_broadcast (GST_INPUT_SELECTOR_GET_COND(sel))) + +static GstStaticPadTemplate gst_input_selector_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_input_selector_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +enum +{ + PROP_0, + PROP_N_PADS, + PROP_ACTIVE_PAD, + PROP_SYNC_STREAMS, + PROP_SYNC_MODE, + PROP_CACHE_BUFFERS +}; + +#define DEFAULT_SYNC_STREAMS TRUE +#define DEFAULT_SYNC_MODE GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT +#define DEFAULT_CACHE_BUFFERS FALSE +#define DEFAULT_PAD_ALWAYS_OK TRUE + +enum +{ + PROP_PAD_0, + PROP_PAD_RUNNING_TIME, + PROP_PAD_TAGS, + PROP_PAD_ACTIVE, + PROP_PAD_ALWAYS_OK +}; + +enum +{ + /* methods */ + SIGNAL_BLOCK, + SIGNAL_SWITCH, + LAST_SIGNAL +}; +static guint gst_input_selector_signals[LAST_SIGNAL] = { 0 }; + +static void gst_input_selector_active_pad_changed (RsnInputSelector * sel, + GParamSpec * pspec, gpointer user_data); +static inline gboolean gst_input_selector_is_active_sinkpad (RsnInputSelector * + sel, GstPad * pad); +static GstPad *gst_input_selector_activate_sinkpad (RsnInputSelector * sel, + GstPad * pad); +static gboolean gst_input_selector_set_active_pad (RsnInputSelector * self, + GstPad * pad); +static GstPad *gst_input_selector_get_linked_pad (RsnInputSelector * sel, + GstPad * pad, gboolean strict); +static gboolean forward_sticky_events (GstPad * sinkpad, GstEvent ** event, + gpointer user_data); + +#define GST_TYPE_SELECTOR_PAD \ + (gst_selector_pad_get_type()) +#define GST_SELECTOR_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_SELECTOR_PAD, RsnSelectorPad)) +#define GST_SELECTOR_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_SELECTOR_PAD, RsnSelectorPadClass)) +#define GST_IS_SELECTOR_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_SELECTOR_PAD)) +#define GST_IS_SELECTOR_PAD_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_SELECTOR_PAD)) +#define GST_SELECTOR_PAD_CAST(obj) \ + ((RsnSelectorPad *)(obj)) + +typedef struct _RsnSelectorPad RsnSelectorPad; +typedef struct _RsnSelectorPadClass RsnSelectorPadClass; +typedef struct _RsnSelectorPadCachedBuffer RsnSelectorPadCachedBuffer; + +struct _RsnSelectorPad +{ + GstPad parent; + + gboolean active; /* when buffer have passed the pad */ + gboolean pushed; /* when buffer was pushed downstream since activation */ + gboolean eos; /* when EOS has been received */ + gboolean eos_sent; /* when EOS was sent downstream */ + gboolean discont; /* after switching we create a discont */ + gboolean flushing; /* set after flush-start and before flush-stop */ + gboolean always_ok; + GstTagList *tags; /* last tags received on the pad */ + + GstClockTime position; /* the current position in the segment */ + GstSegment segment; /* the current segment on the pad */ + guint32 segment_seqnum; /* sequence number of the current segment */ + + gboolean events_pending; /* TRUE if sticky events need to be updated */ + + gboolean sending_cached_buffers; + GQueue *cached_buffers; +}; + +struct _RsnSelectorPadCachedBuffer +{ + GstBuffer *buffer; + GstSegment segment; +}; + +struct _RsnSelectorPadClass +{ + GstPadClass parent; +}; + +GType gst_selector_pad_get_type (void); +static void gst_selector_pad_finalize (GObject * object); +static void gst_selector_pad_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); +static void gst_selector_pad_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); + +static gint64 gst_selector_pad_get_running_time (RsnSelectorPad * pad); +static void gst_selector_pad_reset (RsnSelectorPad * pad); +static gboolean gst_selector_pad_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_selector_pad_query (GstPad * pad, GstObject * parent, + GstQuery * query); +static GstIterator *gst_selector_pad_iterate_linked_pads (GstPad * pad, + GstObject * parent); +static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstObject * parent, + GstBuffer * buf); +static void gst_selector_pad_cache_buffer (RsnSelectorPad * selpad, + GstBuffer * buffer); +static void gst_selector_pad_free_cached_buffers (RsnSelectorPad * selpad); + +G_DEFINE_TYPE (RsnSelectorPad, gst_selector_pad, GST_TYPE_PAD); + +static void +gst_selector_pad_class_init (RsnSelectorPadClass * klass) +{ + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = gst_selector_pad_finalize; + + gobject_class->get_property = gst_selector_pad_get_property; + gobject_class->set_property = gst_selector_pad_set_property; + + g_object_class_install_property (gobject_class, PROP_PAD_RUNNING_TIME, + g_param_spec_int64 ("running-time", "Running time", + "Running time of stream on pad", 0, G_MAXINT64, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_TAGS, + g_param_spec_boxed ("tags", "Tags", + "The currently active tags on the pad", GST_TYPE_TAG_LIST, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE, + g_param_spec_boolean ("active", "Active", + "If the pad is currently active", FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /* FIXME: better property name? */ + g_object_class_install_property (gobject_class, PROP_PAD_ALWAYS_OK, + g_param_spec_boolean ("always-ok", "Always OK", + "Make an inactive pad return OK instead of NOT_LINKED", + DEFAULT_PAD_ALWAYS_OK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_selector_pad_init (RsnSelectorPad * pad) +{ + pad->always_ok = DEFAULT_PAD_ALWAYS_OK; + gst_selector_pad_reset (pad); +} + +static void +gst_selector_pad_finalize (GObject * object) +{ + RsnSelectorPad *pad; + + pad = GST_SELECTOR_PAD_CAST (object); + + if (pad->tags) + gst_tag_list_unref (pad->tags); + gst_selector_pad_free_cached_buffers (pad); + + G_OBJECT_CLASS (gst_selector_pad_parent_class)->finalize (object); +} + +static void +gst_selector_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + RsnSelectorPad *spad = GST_SELECTOR_PAD_CAST (object); + + switch (prop_id) { + case PROP_PAD_ALWAYS_OK: + GST_OBJECT_LOCK (object); + spad->always_ok = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_selector_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + RsnSelectorPad *spad = GST_SELECTOR_PAD_CAST (object); + + switch (prop_id) { + case PROP_PAD_RUNNING_TIME: + g_value_set_int64 (value, gst_selector_pad_get_running_time (spad)); + break; + case PROP_PAD_TAGS: + GST_OBJECT_LOCK (object); + g_value_set_boxed (value, spad->tags); + GST_OBJECT_UNLOCK (object); + break; + case PROP_PAD_ACTIVE: + { + RsnInputSelector *sel; + + sel = GST_INPUT_SELECTOR (gst_pad_get_parent (spad)); + g_value_set_boolean (value, gst_input_selector_is_active_sinkpad (sel, + GST_PAD_CAST (spad))); + gst_object_unref (sel); + break; + } + case PROP_PAD_ALWAYS_OK: + GST_OBJECT_LOCK (object); + g_value_set_boolean (value, spad->always_ok); + GST_OBJECT_UNLOCK (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gint64 +gst_selector_pad_get_running_time (RsnSelectorPad * pad) +{ + gint64 ret = 0; + + GST_OBJECT_LOCK (pad); + if (pad->active) { + guint64 position = pad->position; + GstFormat format = pad->segment.format; + + ret = gst_segment_to_running_time (&pad->segment, format, position); + } + GST_OBJECT_UNLOCK (pad); + + GST_DEBUG_OBJECT (pad, "running time: %" GST_TIME_FORMAT + " segment: %" GST_SEGMENT_FORMAT, GST_TIME_ARGS (ret), &pad->segment); + + return ret; +} + +/* must be called with the SELECTOR_LOCK */ +static void +gst_selector_pad_reset (RsnSelectorPad * pad) +{ + GST_OBJECT_LOCK (pad); + pad->active = FALSE; + pad->pushed = FALSE; + pad->eos = FALSE; + pad->eos_sent = FALSE; + pad->events_pending = FALSE; + pad->discont = FALSE; + pad->flushing = FALSE; + pad->position = GST_CLOCK_TIME_NONE; + gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED); + pad->sending_cached_buffers = FALSE; + gst_selector_pad_free_cached_buffers (pad); + GST_OBJECT_UNLOCK (pad); +} + +static RsnSelectorPadCachedBuffer * +gst_selector_pad_new_cached_buffer (RsnSelectorPad * selpad, GstBuffer * buffer) +{ + RsnSelectorPadCachedBuffer *cached_buffer = + g_slice_new (RsnSelectorPadCachedBuffer); + cached_buffer->buffer = buffer; + cached_buffer->segment = selpad->segment; + return cached_buffer; +} + +static void +gst_selector_pad_free_cached_buffer (RsnSelectorPadCachedBuffer * cached_buffer) +{ + gst_buffer_unref (cached_buffer->buffer); + g_slice_free (RsnSelectorPadCachedBuffer, cached_buffer); +} + +/* must be called with the SELECTOR_LOCK */ +static void +gst_selector_pad_cache_buffer (RsnSelectorPad * selpad, GstBuffer * buffer) +{ + if (selpad->segment.format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (selpad, "Buffer %p with segment not in time format, " + "not caching", buffer); + return; + } + + GST_DEBUG_OBJECT (selpad, "Caching buffer %p", buffer); + if (!selpad->cached_buffers) + selpad->cached_buffers = g_queue_new (); + g_queue_push_tail (selpad->cached_buffers, + gst_selector_pad_new_cached_buffer (selpad, buffer)); +} + +/* must be called with the SELECTOR_LOCK */ +static void +gst_selector_pad_free_cached_buffers (RsnSelectorPad * selpad) +{ + RsnSelectorPadCachedBuffer *cached_buffer; + + if (!selpad->cached_buffers) + return; + + GST_DEBUG_OBJECT (selpad, "Freeing cached buffers"); + while ((cached_buffer = g_queue_pop_head (selpad->cached_buffers))) + gst_selector_pad_free_cached_buffer (cached_buffer); + g_queue_free (selpad->cached_buffers); + selpad->cached_buffers = NULL; +} + +/* strictly get the linked pad from the sinkpad. If the pad is active we return + * the srcpad else we return NULL */ +static GstIterator * +gst_selector_pad_iterate_linked_pads (GstPad * pad, GstObject * parent) +{ + RsnInputSelector *sel; + GstPad *otherpad; + GstIterator *it = NULL; + GValue val = { 0, }; + + sel = GST_INPUT_SELECTOR (parent); + + otherpad = gst_input_selector_get_linked_pad (sel, pad, TRUE); + if (otherpad) { + g_value_init (&val, GST_TYPE_PAD); + g_value_set_object (&val, otherpad); + it = gst_iterator_new_single (GST_TYPE_PAD, &val); + g_value_unset (&val); + gst_object_unref (otherpad); + } + + return it; +} + +static gboolean +gst_selector_pad_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + gboolean res = TRUE; + gboolean forward; + gboolean new_tags = FALSE; + RsnInputSelector *sel; + RsnSelectorPad *selpad; + GstPad *prev_active_sinkpad; + GstPad *active_sinkpad; + + sel = GST_INPUT_SELECTOR (parent); + selpad = GST_SELECTOR_PAD_CAST (pad); + GST_DEBUG_OBJECT (selpad, "received event %" GST_PTR_FORMAT, event); + + GST_INPUT_SELECTOR_LOCK (sel); + prev_active_sinkpad = sel->active_sinkpad; + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + GST_INPUT_SELECTOR_UNLOCK (sel); + + if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) { + g_object_notify (G_OBJECT (sel), "active-pad"); + } + + GST_INPUT_SELECTOR_LOCK (sel); + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + + /* only forward if we are dealing with the active sinkpad */ + forward = (pad == active_sinkpad); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + /* Unblock the pad if it's waiting */ + selpad->flushing = TRUE; + GST_INPUT_SELECTOR_BROADCAST (sel); + break; + case GST_EVENT_FLUSH_STOP: + gst_selector_pad_reset (selpad); + break; + case GST_EVENT_SEGMENT: + { + gst_event_copy_segment (event, &selpad->segment); + selpad->segment_seqnum = gst_event_get_seqnum (event); + + /* Update the position */ + if (selpad->position == GST_CLOCK_TIME_NONE + || selpad->segment.position > selpad->position) { + selpad->position = selpad->segment.position; + } else if (selpad->position != GST_CLOCK_TIME_NONE + && selpad->position > selpad->segment.position) { + selpad->segment.position = selpad->position; + + if (forward) { + gst_event_unref (event); + event = gst_event_new_segment (&selpad->segment); + gst_event_set_seqnum (event, selpad->segment_seqnum); + } + } + GST_DEBUG_OBJECT (pad, "configured SEGMENT %" GST_SEGMENT_FORMAT, + &selpad->segment); + break; + } + case GST_EVENT_TAG: + { + GstTagList *tags, *oldtags, *newtags; + + gst_event_parse_tag (event, &tags); + + oldtags = selpad->tags; + + newtags = gst_tag_list_merge (oldtags, tags, GST_TAG_MERGE_REPLACE); + selpad->tags = newtags; + if (oldtags) + gst_tag_list_unref (oldtags); + GST_DEBUG_OBJECT (pad, "received tags %" GST_PTR_FORMAT, newtags); + + new_tags = TRUE; + break; + } + case GST_EVENT_EOS: + selpad->eos = TRUE; + + if (forward) { + selpad->eos_sent = TRUE; + } else { + RsnSelectorPad *active_selpad; + + /* If the active sinkpad is in EOS state but EOS + * was not sent downstream this means that the pad + * got EOS before it was set as active pad and that + * the previously active pad got EOS after it was + * active + */ + active_selpad = GST_SELECTOR_PAD (active_sinkpad); + forward = (active_selpad->eos && !active_selpad->eos_sent); + active_selpad->eos_sent = TRUE; + } + GST_DEBUG_OBJECT (pad, "received EOS"); + break; + case GST_EVENT_CUSTOM_DOWNSTREAM: + { + 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 (strcmp (type, "select-pad") == 0) { + gst_input_selector_set_active_pad (sel, pad); + forward = FALSE; + } + } + } + break; + + default: + break; + } + GST_INPUT_SELECTOR_UNLOCK (sel); + if (new_tags) + g_object_notify (G_OBJECT (selpad), "tags"); + if (forward) { + GST_DEBUG_OBJECT (pad, "forwarding event"); + res = gst_pad_push_event (sel->srcpad, event); + } else { + /* If we aren't forwarding the event because the pad is not the + * active_sinkpad, then set the flag on the pad + * that says a segment needs sending if/when that pad is activated. + * For all other cases, we send the event immediately, which makes + * sparse streams and other segment updates work correctly downstream. + */ + if (GST_EVENT_IS_STICKY (event)) + selpad->events_pending = TRUE; + gst_event_unref (event); + } + + return res; +} + +static gboolean +gst_selector_pad_query (GstPad * pad, GstObject * parent, GstQuery * query) +{ + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_ALLOCATION:{ + GstPad *active_sinkpad; + RsnInputSelector *sel = GST_INPUT_SELECTOR (parent); + + /* Only do the allocation query for the active sinkpad, + * after switching a reconfigure event is sent and upstream + * should reconfigure and do a new allocation query + */ + if (GST_PAD_DIRECTION (pad) == GST_PAD_SINK) { + GST_INPUT_SELECTOR_LOCK (sel); + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + GST_INPUT_SELECTOR_UNLOCK (sel); + + if (pad != active_sinkpad) { + res = FALSE; + goto done; + } + } + } + /* fall through */ + default: + res = gst_pad_query_default (pad, parent, query); + break; + } + +done: + return res; +} + +/* must be called with the SELECTOR_LOCK, will block while the pad is blocked + * or return TRUE when flushing */ +static gboolean +gst_input_selector_wait (RsnInputSelector * self, RsnSelectorPad * pad) +{ + while (self->blocked && !self->flushing && !pad->flushing) { + /* we can be unlocked here when we are shutting down (flushing) or when we + * get unblocked */ + GST_INPUT_SELECTOR_WAIT (self); + } + return self->flushing; +} + +/* must be called without the SELECTOR_LOCK, will wait until the running time + * of the active pad is after this pad or return TRUE when flushing */ +static gboolean +gst_input_selector_wait_running_time (RsnInputSelector * sel, + RsnSelectorPad * selpad, GstBuffer * buf) +{ + GstSegment *seg; + + GST_DEBUG_OBJECT (selpad, "entering wait for buffer %p", buf); + + /* If we have no valid timestamp we can't sync this buffer */ + if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { + GST_DEBUG_OBJECT (selpad, "leaving wait for buffer with " + "invalid timestamp"); + return FALSE; + } + + seg = &selpad->segment; + + /* Wait until + * a) this is the active pad + * b) the pad or the selector is flushing + * c) the selector is not blocked + * d) the buffer running time is before the current running time + * (either active-seg or clock, depending on sync-mode) + */ + + GST_INPUT_SELECTOR_LOCK (sel); + while (TRUE) { + GstPad *active_sinkpad; + RsnSelectorPad *active_selpad; + GstClock *clock; + gint64 cur_running_time; + GstClockTime running_time; + + active_sinkpad = + gst_input_selector_activate_sinkpad (sel, GST_PAD_CAST (selpad)); + active_selpad = GST_SELECTOR_PAD_CAST (active_sinkpad); + + if (seg->format != GST_FORMAT_TIME) { + GST_DEBUG_OBJECT (selpad, + "Not waiting because we don't have a TIME segment"); + GST_INPUT_SELECTOR_UNLOCK (sel); + return FALSE; + } + + running_time = GST_BUFFER_TIMESTAMP (buf); + /* If possible try to get the running time at the end of the buffer */ + if (GST_BUFFER_DURATION_IS_VALID (buf)) + running_time += GST_BUFFER_DURATION (buf); + /* Only use the segment to convert to running time if the segment is + * in TIME format, otherwise do our best to try to sync */ + if (GST_CLOCK_TIME_IS_VALID (seg->stop)) { + if (running_time > seg->stop) { + running_time = seg->stop; + } + } + running_time = + gst_segment_to_running_time (seg, GST_FORMAT_TIME, running_time); + /* If this is outside the segment don't sync */ + if (running_time == -1) { + GST_DEBUG_OBJECT (selpad, + "Not waiting because buffer is outside segment"); + GST_INPUT_SELECTOR_UNLOCK (sel); + return FALSE; + } + + cur_running_time = GST_CLOCK_TIME_NONE; + if (sel->sync_mode == GST_INPUT_SELECTOR_SYNC_MODE_CLOCK) { + clock = gst_element_get_clock (GST_ELEMENT_CAST (sel)); + if (clock) { + GstClockTime base_time; + + cur_running_time = gst_clock_get_time (clock); + base_time = gst_element_get_base_time (GST_ELEMENT_CAST (sel)); + if (base_time <= cur_running_time) + cur_running_time -= base_time; + else + cur_running_time = 0; + } + } else { + GstSegment *active_seg; + + active_seg = &active_selpad->segment; + + /* If the active segment is configured but not to time format + * we can't do any syncing at all */ + if (active_seg->format != GST_FORMAT_TIME + && active_seg->format != GST_FORMAT_UNDEFINED) { + GST_DEBUG_OBJECT (selpad, + "Not waiting because active segment isn't in TIME format"); + GST_INPUT_SELECTOR_UNLOCK (sel); + return FALSE; + } + + /* Get active pad's running time, if no configured segment yet keep at -1 */ + if (active_seg->format == GST_FORMAT_TIME) + cur_running_time = gst_segment_to_running_time (active_seg, + GST_FORMAT_TIME, active_seg->position); + } + + if (selpad != active_selpad && !sel->flushing && !selpad->flushing && + (sel->blocked || cur_running_time == -1 + || running_time >= cur_running_time)) { + if (!sel->blocked) { + GST_DEBUG_OBJECT (selpad, + "Waiting for active streams to advance. %" GST_TIME_FORMAT " >= %" + GST_TIME_FORMAT, GST_TIME_ARGS (running_time), + GST_TIME_ARGS (cur_running_time)); + } else + GST_DEBUG_OBJECT (selpad, "Waiting for selector to unblock"); + + GST_INPUT_SELECTOR_WAIT (sel); + } else { + GST_INPUT_SELECTOR_UNLOCK (sel); + break; + } + } + + /* Return TRUE if the selector or the pad is flushing */ + return (sel->flushing || selpad->flushing); +} + +static gboolean +forward_sticky_events (GstPad * sinkpad, GstEvent ** event, gpointer user_data) +{ + RsnInputSelector *sel = GST_INPUT_SELECTOR (user_data); + + if (GST_EVENT_TYPE (*event) == GST_EVENT_SEGMENT) { + GstSegment *seg = &GST_SELECTOR_PAD (sinkpad)->segment; + GstEvent *e; + + e = gst_event_new_segment (seg); + gst_event_set_seqnum (e, GST_SELECTOR_PAD_CAST (sinkpad)->segment_seqnum); + + gst_pad_push_event (sel->srcpad, e); + } else if (GST_EVENT_TYPE (*event) != GST_EVENT_STREAM_START) { + GST_WARNING ("Pushing event %" GST_PTR_FORMAT, *event); + gst_pad_push_event (sel->srcpad, gst_event_ref (*event)); + } + + return TRUE; +} + +#if DEBUG_CACHED_BUFFERS +static void +gst_input_selector_debug_cached_buffers (RsnInputSelector * sel) +{ + GList *walk; + + for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) { + RsnSelectorPad *selpad; + GString *timestamps; + gchar *str; + int i; + + selpad = GST_SELECTOR_PAD_CAST (walk->data); + if (!selpad->cached_buffers) { + GST_DEBUG_OBJECT (selpad, "Cached buffers timestamps: "); + continue; + } + + timestamps = g_string_new ("Cached buffers timestamps:"); + for (i = 0; i < selpad->cached_buffers->length; ++i) { + RsnSelectorPadCachedBuffer *cached_buffer; + + cached_buffer = g_queue_peek_nth (selpad->cached_buffers, i); + g_string_append_printf (timestamps, " %" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (cached_buffer->buffer))); + } + str = g_string_free (timestamps, FALSE); + GST_DEBUG_OBJECT (selpad, str); + g_free (str); + } +} +#endif + +/* must be called with the SELECTOR_LOCK */ +static void +gst_input_selector_cleanup_old_cached_buffers (RsnInputSelector * sel, + GstPad * pad) +{ + GstClock *clock; + gint64 cur_running_time; + GList *walk; + + cur_running_time = GST_CLOCK_TIME_NONE; + if (sel->sync_mode == GST_INPUT_SELECTOR_SYNC_MODE_CLOCK) { + clock = gst_element_get_clock (GST_ELEMENT_CAST (sel)); + if (clock) { + GstClockTime base_time; + + cur_running_time = gst_clock_get_time (clock); + base_time = gst_element_get_base_time (GST_ELEMENT_CAST (sel)); + if (base_time <= cur_running_time) + cur_running_time -= base_time; + else + cur_running_time = 0; + } + } else { + GstPad *active_sinkpad; + RsnSelectorPad *active_selpad; + GstSegment *active_seg; + + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + active_selpad = GST_SELECTOR_PAD_CAST (active_sinkpad); + active_seg = &active_selpad->segment; + + /* Get active pad's running time, if no configured segment yet keep at -1 */ + if (active_seg->format == GST_FORMAT_TIME) + cur_running_time = gst_segment_to_running_time (active_seg, + GST_FORMAT_TIME, active_seg->position); + } + + if (!GST_CLOCK_TIME_IS_VALID (cur_running_time)) + return; + + GST_DEBUG_OBJECT (sel, "Cleaning up old cached buffers"); + for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) { + RsnSelectorPad *selpad; + GstSegment *seg; + RsnSelectorPadCachedBuffer *cached_buffer; + GSList *maybe_remove; + guint queue_position; + + selpad = GST_SELECTOR_PAD_CAST (walk->data); + if (!selpad->cached_buffers) + continue; + + seg = &selpad->segment; + + maybe_remove = NULL; + queue_position = 0; + while ((cached_buffer = g_queue_peek_nth (selpad->cached_buffers, + queue_position))) { + GstBuffer *buffer = cached_buffer->buffer; + GstClockTime running_time; + GSList *l; + + /* If we have no valid timestamp we can't sync this buffer */ + if (!GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) { + maybe_remove = g_slist_append (maybe_remove, cached_buffer); + queue_position = g_slist_length (maybe_remove); + continue; + } + + /* the buffer is still valid if its duration is valid and the + * timestamp + duration is >= time, or if its duration is invalid + * and the timestamp is >= time */ + running_time = GST_BUFFER_TIMESTAMP (buffer); + /* If possible try to get the running time at the end of the buffer */ + if (GST_BUFFER_DURATION_IS_VALID (buffer)) + running_time += GST_BUFFER_DURATION (buffer); + /* Only use the segment to convert to running time if the segment is + * in TIME format, otherwise do our best to try to sync */ + if (GST_CLOCK_TIME_IS_VALID (seg->stop)) { + if (running_time > seg->stop) { + running_time = seg->stop; + } + } + running_time = + gst_segment_to_running_time (seg, GST_FORMAT_TIME, running_time); + + GST_DEBUG_OBJECT (selpad, + "checking if buffer %p running time=%" GST_TIME_FORMAT + " >= stream time=%" GST_TIME_FORMAT, buffer, + GST_TIME_ARGS (running_time), GST_TIME_ARGS (cur_running_time)); + if (running_time >= cur_running_time) { + break; + } + + GST_DEBUG_OBJECT (selpad, "Removing old cached buffer %p", buffer); + g_queue_pop_nth (selpad->cached_buffers, queue_position); + gst_selector_pad_free_cached_buffer (cached_buffer); + + for (l = maybe_remove; l != NULL; l = g_slist_next (l)) { + /* A buffer after some invalid buffers was removed, it means the invalid buffers + * are old, lets also remove them */ + cached_buffer = l->data; + g_queue_remove (selpad->cached_buffers, cached_buffer); + gst_selector_pad_free_cached_buffer (cached_buffer); + } + + g_slist_free (maybe_remove); + maybe_remove = NULL; + queue_position = 0; + } + + g_slist_free (maybe_remove); + maybe_remove = NULL; + + if (g_queue_is_empty (selpad->cached_buffers)) { + g_queue_free (selpad->cached_buffers); + selpad->cached_buffers = NULL; + } + } + +#if DEBUG_CACHED_BUFFERS + gst_input_selector_debug_cached_buffers (sel); +#endif +} + +static GstFlowReturn +gst_selector_pad_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + RsnInputSelector *sel; + GstFlowReturn res; + GstPad *active_sinkpad; + GstPad *prev_active_sinkpad; + RsnSelectorPad *selpad; + GstClockTime start_time; + + sel = GST_INPUT_SELECTOR (parent); + selpad = GST_SELECTOR_PAD_CAST (pad); + + GST_DEBUG_OBJECT (selpad, + "entering chain for buf %p with timestamp %" GST_TIME_FORMAT, buf, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + GST_INPUT_SELECTOR_LOCK (sel); + /* wait or check for flushing */ + if (gst_input_selector_wait (sel, selpad)) { + GST_INPUT_SELECTOR_UNLOCK (sel); + goto flushing; + } + + GST_LOG_OBJECT (pad, "getting active pad"); + + prev_active_sinkpad = sel->active_sinkpad; + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + + /* In sync mode wait until the active pad has advanced + * after the running time of the current buffer */ + if (sel->sync_streams) { + /* call chain for each cached buffer if we are not the active pad + * or if we are the active pad but didn't push anything yet. */ + if (active_sinkpad != pad || !selpad->pushed) { + /* no need to check for sel->cache_buffers as selpad->cached_buffers + * will only be valid if cache_buffers is TRUE */ + if (selpad->cached_buffers && !selpad->sending_cached_buffers) { + RsnSelectorPadCachedBuffer *cached_buffer; + GstSegment saved_segment; + + saved_segment = selpad->segment; + + selpad->sending_cached_buffers = TRUE; + while (!sel->flushing && !selpad->flushing && + (cached_buffer = g_queue_pop_head (selpad->cached_buffers))) { + GST_DEBUG_OBJECT (pad, "Cached buffers found, " + "invoking chain for cached buffer %p", cached_buffer->buffer); + + selpad->segment = cached_buffer->segment; + selpad->events_pending = TRUE; + GST_INPUT_SELECTOR_UNLOCK (sel); + gst_selector_pad_chain (pad, parent, cached_buffer->buffer); + GST_INPUT_SELECTOR_LOCK (sel); + + /* we may have cleaned up the queue in the meantime because of + * old buffers */ + if (!selpad->cached_buffers) { + break; + } + } + selpad->sending_cached_buffers = FALSE; + + /* all cached buffers sent, restore segment for current buffer */ + selpad->segment = saved_segment; + selpad->events_pending = TRUE; + + /* Might have changed while calling chain for cached buffers */ + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + } + } + + if (active_sinkpad != pad) { + GST_INPUT_SELECTOR_UNLOCK (sel); + if (gst_input_selector_wait_running_time (sel, selpad, buf)) + goto flushing; + GST_INPUT_SELECTOR_LOCK (sel); + } + + /* Might have changed while waiting */ + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + } + + /* update the segment on the srcpad */ + start_time = GST_BUFFER_TIMESTAMP (buf); + if (GST_CLOCK_TIME_IS_VALID (start_time)) { + GST_LOG_OBJECT (pad, "received start time %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time)); + if (GST_BUFFER_DURATION_IS_VALID (buf)) + GST_LOG_OBJECT (pad, "received end time %" GST_TIME_FORMAT, + GST_TIME_ARGS (start_time + GST_BUFFER_DURATION (buf))); + + GST_OBJECT_LOCK (pad); + selpad->position = start_time; + selpad->segment.position = start_time; + GST_OBJECT_UNLOCK (pad); + } + + /* Ignore buffers from pads except the selected one */ + if (pad != active_sinkpad) + goto ignore; + + /* Tell all non-active pads that we advanced the running time */ + if (sel->sync_streams) + GST_INPUT_SELECTOR_BROADCAST (sel); + + GST_INPUT_SELECTOR_UNLOCK (sel); + + if (prev_active_sinkpad != active_sinkpad && pad == active_sinkpad) { + g_object_notify (G_OBJECT (sel), "active-pad"); + } + + /* if we have a pending events, push them now */ + if (G_UNLIKELY (prev_active_sinkpad != active_sinkpad + || selpad->events_pending)) { + gst_pad_sticky_events_foreach (GST_PAD_CAST (selpad), forward_sticky_events, + sel); + selpad->events_pending = FALSE; + } + + if (selpad->discont) { + buf = gst_buffer_make_writable (buf); + + GST_DEBUG_OBJECT (pad, "Marking discont buffer %p", buf); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); + selpad->discont = FALSE; + } + + /* forward */ + GST_LOG_OBJECT (pad, "Forwarding buffer %p with timestamp %" GST_TIME_FORMAT, + buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + + res = gst_pad_push (sel->srcpad, gst_buffer_ref (buf)); + GST_LOG_OBJECT (pad, "Buffer %p forwarded result=%d", buf, res); + + GST_INPUT_SELECTOR_LOCK (sel); + + if (sel->sync_streams && sel->cache_buffers) { + /* Might have changed while pushing */ + active_sinkpad = gst_input_selector_activate_sinkpad (sel, pad); + /* only set pad to pushed if we are still the active pad */ + if (active_sinkpad == pad) + selpad->pushed = TRUE; + + /* cache buffer as we may need it again if we change pads */ + gst_selector_pad_cache_buffer (selpad, buf); + gst_input_selector_cleanup_old_cached_buffers (sel, pad); + } else { + selpad->pushed = TRUE; + gst_buffer_unref (buf); + } + GST_INPUT_SELECTOR_UNLOCK (sel); + +done: + return res; + + /* dropped buffers */ +ignore: + { + gboolean active_pad_pushed = GST_SELECTOR_PAD_CAST (active_sinkpad)->pushed; + + GST_DEBUG_OBJECT (pad, "Pad not active, discard buffer %p", buf); + /* when we drop a buffer, we're creating a discont on this pad */ + selpad->discont = TRUE; + GST_INPUT_SELECTOR_UNLOCK (sel); + gst_buffer_unref (buf); + + /* figure out what to return upstream */ + GST_OBJECT_LOCK (selpad); + if (selpad->always_ok || !active_pad_pushed) + res = GST_FLOW_OK; + else + res = GST_FLOW_NOT_LINKED; + GST_OBJECT_UNLOCK (selpad); + + goto done; + } +flushing: + { + GST_DEBUG_OBJECT (pad, "We are flushing, discard buffer %p", buf); + gst_buffer_unref (buf); + res = GST_FLOW_FLUSHING; + goto done; + } +} + +static void gst_input_selector_dispose (GObject * object); +static void gst_input_selector_finalize (GObject * object); + +static void gst_input_selector_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_input_selector_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstPad *gst_input_selector_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * unused, const GstCaps * caps); +static void gst_input_selector_release_pad (GstElement * element, GstPad * pad); + +static GstStateChangeReturn gst_input_selector_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_input_selector_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_input_selector_query (GstPad * pad, GstObject * parent, + GstQuery * query); +static gint64 gst_input_selector_block (RsnInputSelector * self); + +#define _do_init \ + GST_DEBUG_CATEGORY_INIT (input_selector_debug, \ + "rsninputselector", 0, "An input stream selector element"); +#define gst_input_selector_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (RsnInputSelector, gst_input_selector, GST_TYPE_ELEMENT, + _do_init); + +static void +gst_input_selector_class_init (RsnInputSelectorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->dispose = gst_input_selector_dispose; + gobject_class->finalize = gst_input_selector_finalize; + + gobject_class->set_property = gst_input_selector_set_property; + gobject_class->get_property = gst_input_selector_get_property; + + g_object_class_install_property (gobject_class, PROP_N_PADS, + g_param_spec_uint ("n-pads", "Number of Pads", + "The number of sink pads", 0, G_MAXUINT, 0, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD, + g_param_spec_object ("active-pad", "Active pad", + "The currently active sink pad", GST_TYPE_PAD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * RsnInputSelector:sync-streams + * + * If set to %TRUE all inactive streams will be synced to the + * running time of the active stream or to the current clock. + * + * To make sure no buffers are dropped by input-selector + * that might be needed when switching the active pad, + * sync-mode should be set to "clock" and cache-buffers to TRUE. + */ + g_object_class_install_property (gobject_class, PROP_SYNC_STREAMS, + g_param_spec_boolean ("sync-streams", "Sync Streams", + "Synchronize inactive streams to the running time of the active " + "stream or to the current clock", + DEFAULT_SYNC_STREAMS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + /** + * RsnInputSelector:sync-mode + * + * Select how input-selector will sync buffers when in sync-streams mode. + * + * Note that when using the "active-segment" mode, the "active-segment" may + * be ahead of current clock time when switching the active pad, as the current + * active pad may have pushed more buffers than what was displayed/consumed, + * which may cause delays and some missing buffers. + */ + g_object_class_install_property (gobject_class, PROP_SYNC_MODE, + g_param_spec_enum ("sync-mode", "Sync mode", + "Behavior in sync-streams mode", GST_TYPE_INPUT_SELECTOR_SYNC_MODE, + DEFAULT_SYNC_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + /** + * RsnInputSelector:cache-buffers + * + * If set to %TRUE and RsnInputSelector:sync-streams is also set to %TRUE, + * the active pad will cache the buffers still considered valid (after current + * running time, see sync-mode) to avoid missing frames if/when the pad is + * reactivated. + * + * The active pad may push more buffers than what is currently displayed/consumed + * and when changing pads those buffers will be discarded and the only way to + * reactivate that pad without loosing the already consumed buffers is to enable cache. + */ + g_object_class_install_property (gobject_class, PROP_CACHE_BUFFERS, + g_param_spec_boolean ("cache-buffers", "Cache Buffers", + "Cache buffers for active-pad", + DEFAULT_CACHE_BUFFERS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY)); + + /** + * RsnInputSelector::block: + * @inputselector: the #RsnInputSelector + * + * Block all sink pads in preparation for a switch. Returns the stop time of + * the current switch segment, as a running time, or 0 if there is no current + * active pad or the current active pad never received data. + */ + gst_input_selector_signals[SIGNAL_BLOCK] = + g_signal_new ("block", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (RsnInputSelectorClass, block), NULL, NULL, + g_cclosure_marshal_generic, G_TYPE_INT64, 0); + + gst_element_class_set_static_metadata (gstelement_class, "Input selector", + "Generic", "N-to-1 input stream selector", + "Julien Moutte , " + "Jan Schmidt , " + "Wim Taymans "); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_input_selector_sink_factory)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_input_selector_src_factory)); + + gstelement_class->request_new_pad = gst_input_selector_request_new_pad; + gstelement_class->release_pad = gst_input_selector_release_pad; + gstelement_class->change_state = gst_input_selector_change_state; + + klass->block = GST_DEBUG_FUNCPTR (gst_input_selector_block); +} + +static void +gst_input_selector_init (RsnInputSelector * sel) +{ + sel->srcpad = gst_pad_new ("src", GST_PAD_SRC); + gst_pad_set_iterate_internal_links_function (sel->srcpad, + GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads)); + gst_pad_set_query_function (sel->srcpad, + GST_DEBUG_FUNCPTR (gst_input_selector_query)); + gst_pad_set_event_function (sel->srcpad, + GST_DEBUG_FUNCPTR (gst_input_selector_event)); + GST_OBJECT_FLAG_SET (sel->srcpad, GST_PAD_FLAG_PROXY_CAPS); + gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad); + /* sinkpad management */ + sel->active_sinkpad = NULL; + sel->padcount = 0; + sel->sync_streams = DEFAULT_SYNC_STREAMS; + + g_mutex_init (&sel->lock); + g_cond_init (&sel->cond); + sel->blocked = FALSE; + + /* lets give a change for downstream to do something on + * active-pad change before we start pushing new buffers */ + g_signal_connect_data (sel, "notify::active-pad", + (GCallback) gst_input_selector_active_pad_changed, NULL, + NULL, G_CONNECT_AFTER); +} + +static void +gst_input_selector_dispose (GObject * object) +{ + RsnInputSelector *sel = GST_INPUT_SELECTOR (object); + + if (sel->active_sinkpad) { + gst_object_unref (sel->active_sinkpad); + sel->active_sinkpad = NULL; + } + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_input_selector_finalize (GObject * object) +{ + RsnInputSelector *sel = GST_INPUT_SELECTOR (object); + + g_mutex_clear (&sel->lock); + g_cond_clear (&sel->cond); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* this function must be called with the SELECTOR_LOCK. It returns TRUE when the + * active pad changed. */ +static gboolean +gst_input_selector_set_active_pad (RsnInputSelector * self, GstPad * pad) +{ + RsnSelectorPad *old, *new; + GstPad **active_pad_p; + + if (pad == self->active_sinkpad) + return FALSE; + + old = GST_SELECTOR_PAD_CAST (self->active_sinkpad); + new = GST_SELECTOR_PAD_CAST (pad); + + GST_DEBUG_OBJECT (self, "setting active pad to %s:%s", + GST_DEBUG_PAD_NAME (new)); + + if (old) + old->pushed = FALSE; + if (new) + new->pushed = FALSE; + + /* Send a new SEGMENT event on the new pad next */ + if (old != new && new) + new->events_pending = TRUE; + + active_pad_p = &self->active_sinkpad; + gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad)); + + if (old && old != new) + gst_pad_push_event (GST_PAD_CAST (old), gst_event_new_reconfigure ()); + if (new) + gst_pad_push_event (GST_PAD_CAST (new), gst_event_new_reconfigure ()); + + GST_DEBUG_OBJECT (self, "New active pad is %" GST_PTR_FORMAT, + self->active_sinkpad); + + return TRUE; +} + +static void +gst_input_selector_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + RsnInputSelector *sel = GST_INPUT_SELECTOR (object); + + switch (prop_id) { + case PROP_ACTIVE_PAD: + { + GstPad *pad; + + pad = g_value_get_object (value); + + GST_INPUT_SELECTOR_LOCK (sel); + +#if DEBUG_CACHED_BUFFERS + gst_input_selector_debug_cached_buffers (sel); +#endif + + gst_input_selector_set_active_pad (sel, pad); + +#if DEBUG_CACHED_BUFFERS + gst_input_selector_debug_cached_buffers (sel); +#endif + + GST_INPUT_SELECTOR_UNLOCK (sel); + break; + } + case PROP_SYNC_STREAMS: + GST_INPUT_SELECTOR_LOCK (sel); + sel->sync_streams = g_value_get_boolean (value); + GST_INPUT_SELECTOR_UNLOCK (sel); + break; + case PROP_SYNC_MODE: + GST_INPUT_SELECTOR_LOCK (sel); + sel->sync_mode = g_value_get_enum (value); + GST_INPUT_SELECTOR_UNLOCK (sel); + break; + case PROP_CACHE_BUFFERS: + GST_INPUT_SELECTOR_LOCK (object); + sel->cache_buffers = g_value_get_boolean (value); + GST_INPUT_SELECTOR_UNLOCK (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_input_selector_active_pad_changed (RsnInputSelector * sel, + GParamSpec * pspec, gpointer user_data) +{ + /* Wake up all non-active pads in sync mode, they might be + * the active pad now */ + if (sel->sync_streams) + GST_INPUT_SELECTOR_BROADCAST (sel); +} + +static void +gst_input_selector_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + RsnInputSelector *sel = GST_INPUT_SELECTOR (object); + + switch (prop_id) { + case PROP_N_PADS: + GST_INPUT_SELECTOR_LOCK (object); + g_value_set_uint (value, sel->n_pads); + GST_INPUT_SELECTOR_UNLOCK (object); + break; + case PROP_ACTIVE_PAD: + GST_INPUT_SELECTOR_LOCK (object); + g_value_set_object (value, sel->active_sinkpad); + GST_INPUT_SELECTOR_UNLOCK (object); + break; + case PROP_SYNC_STREAMS: + GST_INPUT_SELECTOR_LOCK (object); + g_value_set_boolean (value, sel->sync_streams); + GST_INPUT_SELECTOR_UNLOCK (object); + break; + case PROP_SYNC_MODE: + GST_INPUT_SELECTOR_LOCK (object); + g_value_set_enum (value, sel->sync_mode); + GST_INPUT_SELECTOR_UNLOCK (object); + break; + case PROP_CACHE_BUFFERS: + GST_INPUT_SELECTOR_LOCK (object); + g_value_set_boolean (value, sel->cache_buffers); + GST_INPUT_SELECTOR_UNLOCK (object); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstPad * +gst_input_selector_get_linked_pad (RsnInputSelector * sel, GstPad * pad, + gboolean strict) +{ + GstPad *otherpad = NULL; + + GST_INPUT_SELECTOR_LOCK (sel); + if (pad == sel->srcpad) + otherpad = sel->active_sinkpad; + else if (pad == sel->active_sinkpad || !strict) + otherpad = sel->srcpad; + if (otherpad) + gst_object_ref (otherpad); + GST_INPUT_SELECTOR_UNLOCK (sel); + + return otherpad; +} + +static gboolean +gst_input_selector_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + RsnInputSelector *sel; + gboolean result = FALSE; + GstIterator *iter; + gboolean done = FALSE; + GValue item = { 0, }; + GstPad *eventpad; + GList *pushed_pads = NULL; + + sel = GST_INPUT_SELECTOR (parent); + /* Send upstream events to all sinkpads */ + iter = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (sel)); + + /* This is now essentially a copy of gst_pad_event_default_dispatch + * with a different iterator */ + while (!done) { + switch (gst_iterator_next (iter, &item)) { + case GST_ITERATOR_OK: + eventpad = g_value_get_object (&item); + + /* if already pushed, skip */ + if (g_list_find (pushed_pads, eventpad)) { + g_value_reset (&item); + break; + } + + gst_event_ref (event); + result |= gst_pad_push_event (eventpad, event); + + g_value_reset (&item); + break; + case GST_ITERATOR_RESYNC: + /* We don't reset the result here because we don't push the event + * again on pads that got the event already and because we need + * to consider the result of the previous pushes */ + gst_iterator_resync (iter); + break; + case GST_ITERATOR_ERROR: + GST_ERROR_OBJECT (pad, "Could not iterate over sinkpads"); + done = TRUE; + break; + case GST_ITERATOR_DONE: + done = TRUE; + break; + } + } + g_value_unset (&item); + gst_iterator_free (iter); + + g_list_free (pushed_pads); + + gst_event_unref (event); + + return result; +} + +/* query on the srcpad. We override this function because by default it will + * only forward the query to one random sinkpad */ +static gboolean +gst_input_selector_query (GstPad * pad, GstObject * parent, GstQuery * query) +{ + gboolean res = FALSE; + RsnInputSelector *sel; + + sel = GST_INPUT_SELECTOR (parent); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_LATENCY: + { + GList *walk; + GstClockTime resmin, resmax; + gboolean reslive; + + resmin = 0; + resmax = -1; + reslive = FALSE; + + /* perform the query on all sinkpads and combine the results. We take the + * max of min and the min of max for the result latency. */ + GST_INPUT_SELECTOR_LOCK (sel); + for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; + walk = g_list_next (walk)) { + GstPad *sinkpad = GST_PAD_CAST (walk->data); + + if (gst_pad_peer_query (sinkpad, query)) { + GstClockTime min, max; + gboolean live; + + /* one query succeeded, we succeed too */ + res = TRUE; + + gst_query_parse_latency (query, &live, &min, &max); + + GST_DEBUG_OBJECT (sinkpad, + "peer latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT + ", live %d", GST_TIME_ARGS (min), GST_TIME_ARGS (max), live); + + if (live) { + if (min > resmin) + resmin = min; + if (resmax == -1) + resmax = max; + else if (max < resmax) + resmax = max; + if (reslive == FALSE) + reslive = live; + } + } + } + GST_INPUT_SELECTOR_UNLOCK (sel); + if (res) { + gst_query_set_latency (query, reslive, resmin, resmax); + + GST_DEBUG_OBJECT (sel, + "total latency min %" GST_TIME_FORMAT ", max %" GST_TIME_FORMAT + ", live %d", GST_TIME_ARGS (resmin), GST_TIME_ARGS (resmax), + reslive); + } + + break; + } + default: + res = gst_pad_query_default (pad, parent, query); + break; + } + + return res; +} + +/* check if the pad is the active sinkpad */ +static inline gboolean +gst_input_selector_is_active_sinkpad (RsnInputSelector * sel, GstPad * pad) +{ + gboolean res; + + GST_INPUT_SELECTOR_LOCK (sel); + res = (pad == sel->active_sinkpad); + GST_INPUT_SELECTOR_UNLOCK (sel); + + return res; +} + +/* Get or create the active sinkpad, must be called with SELECTOR_LOCK */ +static GstPad * +gst_input_selector_activate_sinkpad (RsnInputSelector * sel, GstPad * pad) +{ + GstPad *active_sinkpad; + RsnSelectorPad *selpad; + + selpad = GST_SELECTOR_PAD_CAST (pad); + + selpad->active = TRUE; + active_sinkpad = sel->active_sinkpad; + if (sel->active_sinkpad == NULL) { + GValue item = G_VALUE_INIT; + GstIterator *iter = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (sel)); + GstIteratorResult ires; + + while ((ires = gst_iterator_next (iter, &item)) == GST_ITERATOR_RESYNC) + gst_iterator_resync (iter); + if (ires == GST_ITERATOR_OK) { + /* If no pad is currently selected, we return the first usable pad to + * guarantee consistency */ + + active_sinkpad = sel->active_sinkpad = g_value_dup_object (&item); + g_value_reset (&item); + GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", + GST_DEBUG_PAD_NAME (active_sinkpad)); + } else + GST_WARNING_OBJECT (sel, "Couldn't find a default sink pad"); + gst_iterator_free (iter); + } + + return active_sinkpad; +} + +static GstPad * +gst_input_selector_request_new_pad (GstElement * element, + GstPadTemplate * templ, const gchar * unused, const GstCaps * caps) +{ + RsnInputSelector *sel; + gchar *name = NULL; + GstPad *sinkpad = NULL; + + g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL); + + sel = GST_INPUT_SELECTOR (element); + + GST_INPUT_SELECTOR_LOCK (sel); + + GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount); + name = g_strdup_printf ("sink_%u", sel->padcount++); + sinkpad = g_object_new (GST_TYPE_SELECTOR_PAD, + "name", name, "direction", templ->direction, "template", templ, NULL); + g_free (name); + + sel->n_pads++; + + gst_pad_set_event_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_selector_pad_event)); + gst_pad_set_query_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_selector_pad_query)); + gst_pad_set_chain_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_selector_pad_chain)); + gst_pad_set_iterate_internal_links_function (sinkpad, + GST_DEBUG_FUNCPTR (gst_selector_pad_iterate_linked_pads)); + + GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_CAPS); + GST_OBJECT_FLAG_SET (sinkpad, GST_PAD_FLAG_PROXY_ALLOCATION); + gst_pad_set_active (sinkpad, TRUE); + gst_element_add_pad (GST_ELEMENT (sel), sinkpad); + GST_INPUT_SELECTOR_UNLOCK (sel); + + return sinkpad; +} + +static void +gst_input_selector_release_pad (GstElement * element, GstPad * pad) +{ + RsnInputSelector *sel; + + sel = GST_INPUT_SELECTOR (element); + GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + GST_INPUT_SELECTOR_LOCK (sel); + /* if the pad was the active pad, makes us select a new one */ + if (sel->active_sinkpad == pad) { + GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + gst_object_unref (sel->active_sinkpad); + sel->active_sinkpad = NULL; + } + sel->n_pads--; + + gst_pad_set_active (pad, FALSE); + gst_element_remove_pad (GST_ELEMENT (sel), pad); + GST_INPUT_SELECTOR_UNLOCK (sel); +} + +static void +gst_input_selector_reset (RsnInputSelector * sel) +{ + GList *walk; + + GST_INPUT_SELECTOR_LOCK (sel); + /* clear active pad */ + if (sel->active_sinkpad) { + gst_object_unref (sel->active_sinkpad); + sel->active_sinkpad = NULL; + } + /* reset each of our sinkpads state */ + for (walk = GST_ELEMENT_CAST (sel)->sinkpads; walk; walk = g_list_next (walk)) { + RsnSelectorPad *selpad = GST_SELECTOR_PAD_CAST (walk->data); + + gst_selector_pad_reset (selpad); + + if (selpad->tags) { + gst_tag_list_unref (selpad->tags); + selpad->tags = NULL; + } + } + GST_INPUT_SELECTOR_UNLOCK (sel); +} + +static GstStateChangeReturn +gst_input_selector_change_state (GstElement * element, + GstStateChange transition) +{ + RsnInputSelector *self = GST_INPUT_SELECTOR (element); + GstStateChangeReturn result; + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + GST_INPUT_SELECTOR_LOCK (self); + self->blocked = FALSE; + self->flushing = FALSE; + GST_INPUT_SELECTOR_UNLOCK (self); + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + /* first unlock before we call the parent state change function, which + * tries to acquire the stream lock when going to ready. */ + GST_INPUT_SELECTOR_LOCK (self); + self->blocked = FALSE; + self->flushing = TRUE; + GST_INPUT_SELECTOR_BROADCAST (self); + GST_INPUT_SELECTOR_UNLOCK (self); + break; + default: + break; + } + + result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_input_selector_reset (self); + break; + default: + break; + } + + return result; +} + +static gint64 +gst_input_selector_block (RsnInputSelector * self) +{ + gint64 ret = 0; + RsnSelectorPad *spad; + + GST_INPUT_SELECTOR_LOCK (self); + + if (self->blocked) + GST_WARNING_OBJECT (self, "switch already blocked"); + + self->blocked = TRUE; + spad = GST_SELECTOR_PAD_CAST (self->active_sinkpad); + + if (spad) + ret = gst_selector_pad_get_running_time (spad); + else + GST_DEBUG_OBJECT (self, "no active pad while blocking"); + + GST_INPUT_SELECTOR_UNLOCK (self); + + return ret; +} diff --git a/ext/resindvd/rsninputselector.h b/ext/resindvd/rsninputselector.h new file mode 100644 index 0000000000..d7d2a8d325 --- /dev/null +++ b/ext/resindvd/rsninputselector.h @@ -0,0 +1,91 @@ +/* GStreamer + * Copyright (C) 2003 Julien Moutte + * Copyright (C) 2005 Ronald S. Bultje + * Copyright (C) 2008 Nokia Corporation. (contact ) + * + * 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_INPUT_SELECTOR_H__ +#define __RSN_INPUT_SELECTOR_H__ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_INPUT_SELECTOR \ + (gst_input_selector_get_type()) +#define GST_INPUT_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_INPUT_SELECTOR, RsnInputSelector)) +#define GST_INPUT_SELECTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_INPUT_SELECTOR, RsnInputSelectorClass)) +#define GST_IS_INPUT_SELECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_INPUT_SELECTOR)) +#define GST_IS_INPUT_SELECTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_INPUT_SELECTOR)) + +typedef struct _RsnInputSelector RsnInputSelector; +typedef struct _RsnInputSelectorClass RsnInputSelectorClass; + +#define GST_INPUT_SELECTOR_GET_LOCK(sel) (&((RsnInputSelector*)(sel))->lock) +#define GST_INPUT_SELECTOR_GET_COND(sel) (&((RsnInputSelector*)(sel))->cond) +#define GST_INPUT_SELECTOR_LOCK(sel) (g_mutex_lock (GST_INPUT_SELECTOR_GET_LOCK(sel))) +#define GST_INPUT_SELECTOR_UNLOCK(sel) (g_mutex_unlock (GST_INPUT_SELECTOR_GET_LOCK(sel))) +#define GST_INPUT_SELECTOR_WAIT(sel) (g_cond_wait (GST_INPUT_SELECTOR_GET_COND(sel), \ + GST_INPUT_SELECTOR_GET_LOCK(sel))) +#define GST_INPUT_SELECTOR_BROADCAST(sel) (g_cond_broadcast (GST_INPUT_SELECTOR_GET_COND(sel))) + +/** + * RsnInputSelectorSyncMode: + * @GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT: Sync using the current active segment. + * @GST_INPUT_SELECTOR_SYNC_MODE_CLOCK: Sync using the clock. + * + * The different ways that input-selector can behave when in sync-streams mode. + */ +typedef enum { + GST_INPUT_SELECTOR_SYNC_MODE_ACTIVE_SEGMENT, + GST_INPUT_SELECTOR_SYNC_MODE_CLOCK +} RsnInputSelectorSyncMode; + +struct _RsnInputSelector { + GstElement element; + + GstPad *srcpad; + + GstPad *active_sinkpad; + guint n_pads; + guint padcount; + gboolean sync_streams; + RsnInputSelectorSyncMode sync_mode; + gboolean cache_buffers; + + GMutex lock; + GCond cond; + gboolean blocked; + gboolean flushing; +}; + +struct _RsnInputSelectorClass { + GstElementClass parent_class; + + gint64 (*block) (RsnInputSelector *self); +}; + +G_GNUC_INTERNAL GType gst_input_selector_get_type (void); + +G_END_DECLS + +#endif /* __GST_INPUT_SELECTOR_H__ */ diff --git a/ext/resindvd/rsnstreamselector.c b/ext/resindvd/rsnstreamselector.c deleted file mode 100644 index d61067705a..0000000000 --- a/ext/resindvd/rsnstreamselector.c +++ /dev/null @@ -1,769 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Julien Moutte - * Copyright (C) 2005 Ronald S. Bultje - * Copyright (C) 2005 Jan Schmidt - * Copyright (C) 2007 Wim Taymans - * - * 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 - -#include "rsnstreamselector.h" - -GST_DEBUG_CATEGORY_STATIC (stream_selector_debug); -#define GST_CAT_DEFAULT stream_selector_debug - -static GstStaticPadTemplate rsn_stream_selector_sink_factory = -GST_STATIC_PAD_TEMPLATE ("sink_%u", - GST_PAD_SINK, - GST_PAD_REQUEST, - GST_STATIC_CAPS_ANY); - -static GstStaticPadTemplate rsn_stream_selector_src_factory = -GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS_ANY); - -enum -{ - PROP_0, - PROP_N_PADS, - PROP_ACTIVE_PAD, - PROP_LAST -}; - -static gboolean rsn_stream_selector_is_active_sinkpad (RsnStreamSelector * sel, - GstPad * pad); -static GstPad *rsn_stream_selector_get_active (RsnStreamSelector * sel, - GstPad * pad); -static void rsn_stream_selector_set_active (RsnStreamSelector * sel, - GstPad * pad); -static GstPad *rsn_stream_selector_get_linked_pad (GstPad * pad, - gboolean strict); - -#define RSN_TYPE_SELECTOR_PAD \ - (gst_selector_pad_get_type()) -#define GST_SELECTOR_PAD(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSN_TYPE_SELECTOR_PAD, RsnSelectorPad)) -#define GST_SELECTOR_PAD_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), RSN_TYPE_SELECTOR_PAD, RsnSelectorPadClass)) -#define RSN_IS_SELECTOR_PAD(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RSN_TYPE_SELECTOR_PAD)) -#define RSN_IS_SELECTOR_PAD_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), RSN_TYPE_SELECTOR_PAD)) -#define GST_SELECTOR_PAD_CAST(obj) \ - ((RsnSelectorPad *)(obj)) - -typedef struct _RsnSelectorPad RsnSelectorPad; -typedef struct _RsnSelectorPadClass RsnSelectorPadClass; - -struct _RsnSelectorPad -{ - GstPad parent; - - gboolean active; - gboolean eos; - GstSegment segment; - GstTagList *tags; -}; - -struct _RsnSelectorPadClass -{ - GstPadClass parent; -}; - -static void gst_selector_pad_class_init (RsnSelectorPadClass * klass); -static void gst_selector_pad_init (RsnSelectorPad * pad); -static void gst_selector_pad_finalize (GObject * object); - -static void gst_selector_pad_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static GstPadClass *selector_pad_parent_class = NULL; - -static void gst_selector_pad_reset (RsnSelectorPad * pad); -static gboolean gst_selector_pad_event (GstPad * pad, GstEvent * event); -static GstCaps *gst_selector_pad_getcaps (GstPad * pad); -static GstFlowReturn gst_selector_pad_chain (GstPad * pad, GstBuffer * buf); -static GstFlowReturn gst_selector_pad_bufferalloc (GstPad * pad, - guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); - -enum -{ - PROP_PAD_0, - PROP_PAD_TAGS, - PROP_PAD_ACTIVE, - PROP_PAD_LAST -}; - -static GType -gst_selector_pad_get_type (void) -{ - static GType selector_pad_type = 0; - - if (!selector_pad_type) { - static const GTypeInfo selector_pad_info = { - sizeof (RsnSelectorPadClass), - NULL, - NULL, - (GClassInitFunc) gst_selector_pad_class_init, - NULL, - NULL, - sizeof (RsnSelectorPad), - 0, - (GInstanceInitFunc) gst_selector_pad_init, - }; - - selector_pad_type = - g_type_register_static (GST_TYPE_PAD, "RsnSelectorPad", - &selector_pad_info, 0); - } - return selector_pad_type; -} - -static void -gst_selector_pad_class_init (RsnSelectorPadClass * klass) -{ - GObjectClass *gobject_class; - - gobject_class = (GObjectClass *) klass; - - selector_pad_parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_selector_pad_finalize; - gobject_class->get_property = - GST_DEBUG_FUNCPTR (gst_selector_pad_get_property); - - g_object_class_install_property (gobject_class, PROP_PAD_TAGS, - g_param_spec_boxed ("tags", "Tags", - "The currently active tags on the pad", GST_TYPE_TAG_LIST, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property (gobject_class, PROP_PAD_ACTIVE, - g_param_spec_boolean ("active", "Active", - "If the pad is currently active", FALSE, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); -} - -static void -gst_selector_pad_init (RsnSelectorPad * pad) -{ - gst_selector_pad_reset (pad); -} - -static void -gst_selector_pad_finalize (GObject * object) -{ - RsnSelectorPad *pad; - - pad = GST_SELECTOR_PAD_CAST (object); - - if (pad->tags) - gst_tag_list_unref (pad->tags); - - G_OBJECT_CLASS (selector_pad_parent_class)->finalize (object); -} - -static void -gst_selector_pad_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - RsnSelectorPad *pad; - - pad = GST_SELECTOR_PAD (object); - - switch (prop_id) { - case PROP_PAD_TAGS: - GST_OBJECT_LOCK (object); - g_value_set_boxed (value, pad->tags); - GST_OBJECT_UNLOCK (object); - break; - case PROP_PAD_ACTIVE: - { - RsnStreamSelector *sel; - - sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); - g_value_set_boolean (value, rsn_stream_selector_is_active_sinkpad (sel, - GST_PAD_CAST (pad))); - gst_object_unref (sel); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -gst_selector_pad_reset (RsnSelectorPad * pad) -{ - pad->active = FALSE; - pad->eos = FALSE; - gst_segment_init (&pad->segment, GST_FORMAT_UNDEFINED); -} - -static gboolean -gst_selector_pad_event (GstPad * pad, GstEvent * event) -{ - gboolean res = TRUE; - gboolean forward = TRUE; - RsnStreamSelector *sel; - RsnSelectorPad *selpad; - GstPad *active_sinkpad; - - sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); - selpad = GST_SELECTOR_PAD_CAST (pad); - - /* only forward if we are dealing with the active sinkpad */ - active_sinkpad = rsn_stream_selector_get_active (sel, pad); - forward = (active_sinkpad == pad); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_STOP: - gst_selector_pad_reset (selpad); - break; - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - GST_DEBUG_OBJECT (selpad, - "configured NEWSEGMENT update %d, rate %lf, applied rate %lf, " - "format %d, " - "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" - G_GINT64_FORMAT, update, rate, arate, format, start, stop, time); - - gst_segment_set_newsegment_full (&selpad->segment, update, - rate, arate, format, start, stop, time); - break; - } - case GST_EVENT_TAG: - { - GstTagList *tags; - - GST_OBJECT_LOCK (selpad); - if (selpad->tags) - gst_tag_list_unref (selpad->tags); - gst_event_parse_tag (event, &tags); - if (tags) - tags = gst_tag_list_copy (tags); - selpad->tags = tags; - GST_DEBUG_OBJECT (sel, "received tags %" GST_PTR_FORMAT, selpad->tags); - GST_OBJECT_UNLOCK (selpad); - break; - } - case GST_EVENT_CUSTOM_DOWNSTREAM: - { - 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 (strcmp (type, "select-pad") == 0) { - rsn_stream_selector_set_active (sel, pad); - forward = FALSE; - } - } - } - case GST_EVENT_EOS: - selpad->eos = TRUE; - break; - default: - break; - } - if (forward) - res = gst_pad_push_event (sel->srcpad, event); - else - gst_event_unref (event); - - gst_object_unref (sel); - - return res; -} - -static GstCaps * -gst_selector_pad_getcaps (GstPad * pad) -{ - RsnStreamSelector *sel; - GstCaps *caps; - - sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); - - GST_DEBUG_OBJECT (sel, "Getting caps of srcpad peer"); - caps = gst_pad_peer_get_caps (sel->srcpad); - if (caps == NULL) - caps = gst_caps_new_any (); - - gst_object_unref (sel); - - return caps; -} - -static GstFlowReturn -gst_selector_pad_bufferalloc (GstPad * pad, guint64 offset, - guint size, GstCaps * caps, GstBuffer ** buf) -{ - RsnStreamSelector *sel; - GstFlowReturn result; - GstPad *active_sinkpad; - - sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); - - active_sinkpad = rsn_stream_selector_get_active (sel, pad); - - /* Fallback allocation for buffers from pads except the selected one */ - if (pad != active_sinkpad) { - GST_DEBUG_OBJECT (sel, - "Pad %s:%s is not selected. Performing fallback allocation", - GST_DEBUG_PAD_NAME (pad)); - - *buf = NULL; - result = GST_FLOW_OK; - } else { - result = gst_pad_alloc_buffer (sel->srcpad, offset, size, caps, buf); - - /* FIXME: HACK. If buffer alloc returns not-linked, perform a fallback - * allocation. This should NOT be necessary, because playbin should - * properly block the source pad from running until it's finished hooking - * everything up, but playbin needs refactoring first. */ - if (result == GST_FLOW_NOT_LINKED) { - GST_DEBUG_OBJECT (sel, - "No peer pad yet - performing fallback allocation for pad %s:%s", - GST_DEBUG_PAD_NAME (pad)); - - *buf = NULL; - result = GST_FLOW_OK; - } - } - - gst_object_unref (sel); - - return result; -} - -static GstFlowReturn -gst_selector_pad_chain (GstPad * pad, GstBuffer * buf) -{ - RsnStreamSelector *sel; - GstFlowReturn res; - GstPad *active_sinkpad; - RsnSelectorPad *selpad; - GstClockTime timestamp; - GstSegment *seg; - gboolean discont; - - sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); - selpad = GST_SELECTOR_PAD_CAST (pad); - seg = &selpad->segment; - - active_sinkpad = rsn_stream_selector_get_active (sel, pad); - - timestamp = GST_BUFFER_TIMESTAMP (buf); - if (GST_CLOCK_TIME_IS_VALID (timestamp)) { - GST_DEBUG_OBJECT (sel, "received timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); - gst_segment_set_last_stop (seg, seg->format, timestamp); - } - - /* Ignore buffers from pads except the selected one */ - if (pad != active_sinkpad) - goto ignore; - - /* If we just switched pads, mark a discont buffer */ - GST_OBJECT_LOCK (sel); - discont = sel->mark_discont; - sel->mark_discont = FALSE; - GST_OBJECT_UNLOCK (sel); - - if (discont) { - GST_DEBUG_OBJECT (sel, "Marking buffer discont due to pad switch"); - buf = gst_buffer_make_metadata_writable (buf); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - } - - /* forward */ - GST_LOG_OBJECT (sel, "Forwarding buffer %p from pad %s:%s", buf, - GST_DEBUG_PAD_NAME (pad)); - res = gst_pad_push (sel->srcpad, buf); -done: - gst_object_unref (sel); - return res; - /* dropped buffers */ -ignore: - { - GST_DEBUG_OBJECT (sel, "Ignoring buffer %p from pad %s:%s", - buf, GST_DEBUG_PAD_NAME (pad)); - gst_buffer_unref (buf); - res = GST_FLOW_NOT_LINKED; - goto done; - } -} - -static void rsn_stream_selector_dispose (GObject * object); - -static void rsn_stream_selector_init (RsnStreamSelector * sel); -static void rsn_stream_selector_base_init (RsnStreamSelectorClass * klass); -static void rsn_stream_selector_class_init (RsnStreamSelectorClass * klass); - -static void rsn_stream_selector_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void rsn_stream_selector_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static GstPad *rsn_stream_selector_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * unused); -static void rsn_stream_selector_release_pad (GstElement * element, - GstPad * pad); -static GstIterator *rsn_stream_selector_iterate_linked_pads (GstPad * pad); -static GstCaps *rsn_stream_selector_getcaps (GstPad * pad); - -static GstElementClass *parent_class = NULL; - -GType -rsn_stream_selector_get_type (void) -{ - static GType stream_selector_type = 0; - - if (!stream_selector_type) { - static const GTypeInfo stream_selector_info = { - sizeof (RsnStreamSelectorClass), - (GBaseInitFunc) rsn_stream_selector_base_init, - NULL, - (GClassInitFunc) rsn_stream_selector_class_init, - NULL, - NULL, - sizeof (RsnStreamSelector), - 0, - (GInstanceInitFunc) rsn_stream_selector_init, - }; - stream_selector_type = - g_type_register_static (GST_TYPE_ELEMENT, - "RsnStreamSelector", &stream_selector_info, 0); - GST_DEBUG_CATEGORY_INIT (stream_selector_debug, - "streamselector", 0, "A stream-selector element"); - } - - return stream_selector_type; -} - -static void -rsn_stream_selector_base_init (RsnStreamSelectorClass * klass) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (klass); - - gst_element_class_set_details_simple (element_class, "StreamSelector", - "Generic", "N-to-1 input stream_selectoring", - "Julien Moutte , " - "Ronald S. Bultje , " - "Jan Schmidt , " - "Wim Taymans "); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rsn_stream_selector_sink_factory)); - gst_element_class_add_pad_template (element_class, - gst_static_pad_template_get (&rsn_stream_selector_src_factory)); -} - -static void -rsn_stream_selector_class_init (RsnStreamSelectorClass * klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->dispose = rsn_stream_selector_dispose; - - gobject_class->set_property = - GST_DEBUG_FUNCPTR (rsn_stream_selector_set_property); - gobject_class->get_property = - GST_DEBUG_FUNCPTR (rsn_stream_selector_get_property); - - g_object_class_install_property (gobject_class, PROP_N_PADS, - g_param_spec_uint ("n-pads", "Number of Pads", - "The number of sink pads", 0, G_MAXUINT, 0, - G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_ACTIVE_PAD, - g_param_spec_object ("active-pad", "Active Pad", - "The currently active sink pad", GST_TYPE_PAD, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - gstelement_class->request_new_pad = rsn_stream_selector_request_new_pad; - gstelement_class->release_pad = rsn_stream_selector_release_pad; -} - -static void -rsn_stream_selector_init (RsnStreamSelector * sel) -{ - sel->srcpad = gst_pad_new ("src", GST_PAD_SRC); - gst_pad_set_iterate_internal_links_function (sel->srcpad, - GST_DEBUG_FUNCPTR (rsn_stream_selector_iterate_linked_pads)); - gst_pad_set_getcaps_function (sel->srcpad, - GST_DEBUG_FUNCPTR (rsn_stream_selector_getcaps)); - gst_element_add_pad (GST_ELEMENT (sel), sel->srcpad); - /* sinkpad management */ - sel->padcount = 0; - sel->active_sinkpad = NULL; - gst_segment_init (&sel->segment, GST_FORMAT_UNDEFINED); -} - -static void -rsn_stream_selector_dispose (GObject * object) -{ - RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object); - - if (sel->active_sinkpad) { - gst_object_unref (sel->active_sinkpad); - sel->active_sinkpad = NULL; - } - - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -rsn_stream_selector_set_property (GObject * object, guint prop_id, - const GValue * value, GParamSpec * pspec) -{ - RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object); - - switch (prop_id) { - case PROP_ACTIVE_PAD: - { - GstPad *pad = NULL; - - pad = g_value_get_object (value); - rsn_stream_selector_set_active (sel, pad); - - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -rsn_stream_selector_get_property (GObject * object, guint prop_id, - GValue * value, GParamSpec * pspec) -{ - RsnStreamSelector *sel = RSN_STREAM_SELECTOR (object); - - switch (prop_id) { - case PROP_N_PADS: - GST_OBJECT_LOCK (object); - g_value_set_uint (value, sel->n_pads); - GST_OBJECT_UNLOCK (object); - break; - case PROP_ACTIVE_PAD:{ - GST_OBJECT_LOCK (object); - g_value_set_object (value, sel->active_sinkpad); - GST_OBJECT_UNLOCK (object); - break; - } - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static GstPad * -rsn_stream_selector_get_linked_pad (GstPad * pad, gboolean strict) -{ - RsnStreamSelector *sel; - GstPad *otherpad = NULL; - - sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); - - GST_OBJECT_LOCK (sel); - if (pad == sel->srcpad) - otherpad = sel->active_sinkpad; - else if (pad == sel->active_sinkpad || !strict) - otherpad = sel->srcpad; - if (otherpad) - gst_object_ref (otherpad); - GST_OBJECT_UNLOCK (sel); - gst_object_unref (sel); - return otherpad; -} - -static GstCaps * -rsn_stream_selector_getcaps (GstPad * pad) -{ - GstPad *otherpad; - GstObject *parent; - GstCaps *caps; - - otherpad = rsn_stream_selector_get_linked_pad (pad, FALSE); - parent = gst_object_get_parent (GST_OBJECT (pad)); - if (!otherpad) { - GST_DEBUG_OBJECT (parent, - "Pad %s:%s not linked, returning ANY", GST_DEBUG_PAD_NAME (pad)); - caps = gst_caps_new_any (); - } else { - GST_DEBUG_OBJECT (parent, - "Pad %s:%s is linked (to %s:%s), returning peer caps", - GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (otherpad)); - /* if the peer has caps, use those. If the pad is not linked, this function - * returns NULL and we return ANY */ - if (!(caps = gst_pad_peer_get_caps (otherpad))) - caps = gst_caps_new_any (); - gst_object_unref (otherpad); - } - - gst_object_unref (parent); - return caps; -} - -/* check if the pad is the active sinkpad */ -static gboolean -rsn_stream_selector_is_active_sinkpad (RsnStreamSelector * sel, GstPad * pad) -{ - gboolean res; - - GST_OBJECT_LOCK (sel); - res = (pad == sel->active_sinkpad); - GST_OBJECT_UNLOCK (sel); - - return res; -} - -/* Get or create the active sinkpad */ -static GstPad * -rsn_stream_selector_get_active (RsnStreamSelector * sel, GstPad * pad) -{ - GstPad *active_sinkpad; - RsnSelectorPad *selpad; - - selpad = GST_SELECTOR_PAD_CAST (pad); - - GST_OBJECT_LOCK (sel); - selpad->active = TRUE; - active_sinkpad = sel->active_sinkpad; - if (active_sinkpad == NULL) { - /* first pad we get an alloc on becomes the activated pad by default */ - active_sinkpad = sel->active_sinkpad = gst_object_ref (pad); - GST_DEBUG_OBJECT (sel, "Activating pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - } - GST_OBJECT_UNLOCK (sel); - - return active_sinkpad; -} - -static void -rsn_stream_selector_set_active (RsnStreamSelector * sel, GstPad * pad) -{ - GstPad **active_pad_p; - - GST_OBJECT_LOCK (GST_OBJECT_CAST (sel)); - if (pad != sel->active_sinkpad) { - RsnSelectorPad *selpad; - - selpad = GST_SELECTOR_PAD_CAST (pad); - /* we can only activate pads that have data received */ - if (selpad && !selpad->active) { - GST_DEBUG_OBJECT (sel, "No data received on pad %" GST_PTR_FORMAT, pad); - } else { - active_pad_p = &sel->active_sinkpad; - gst_object_replace ((GstObject **) active_pad_p, GST_OBJECT_CAST (pad)); - GST_DEBUG_OBJECT (sel, "New active pad is %" GST_PTR_FORMAT, - sel->active_sinkpad); - } - /* Mark the next buffer as discontinuous */ - sel->mark_discont = TRUE; - } - GST_OBJECT_UNLOCK (GST_OBJECT_CAST (sel)); -} - -static GstIterator * -rsn_stream_selector_iterate_linked_pads (GstPad * pad) -{ - RsnStreamSelector *sel = RSN_STREAM_SELECTOR (gst_pad_get_parent (pad)); - GstPad *otherpad = rsn_stream_selector_get_linked_pad (pad, TRUE); - GstIterator *it = gst_iterator_new_single (GST_TYPE_PAD, otherpad, - (GstCopyFunction) gst_object_ref, (GFreeFunc) gst_object_unref); - - if (otherpad) - gst_object_unref (otherpad); - gst_object_unref (sel); - - return it; -} - -static GstPad * -rsn_stream_selector_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * unused) -{ - RsnStreamSelector *sel; - gchar *name = NULL; - GstPad *sinkpad = NULL; - - sel = RSN_STREAM_SELECTOR (element); - g_return_val_if_fail (templ->direction == GST_PAD_SINK, NULL); - GST_LOG_OBJECT (sel, "Creating new pad %d", sel->padcount); - GST_OBJECT_LOCK (sel); - name = g_strdup_printf ("sink_%u", sel->padcount++); - sinkpad = g_object_new (RSN_TYPE_SELECTOR_PAD, - "name", name, "direction", templ->direction, "template", templ, NULL); - g_free (name); - sel->n_pads++; - GST_OBJECT_UNLOCK (sel); - - gst_pad_set_event_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_event)); - gst_pad_set_getcaps_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_getcaps)); - gst_pad_set_chain_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_chain)); - gst_pad_set_iterate_internal_links_function (sinkpad, - GST_DEBUG_FUNCPTR (rsn_stream_selector_iterate_linked_pads)); - gst_pad_set_bufferalloc_function (sinkpad, - GST_DEBUG_FUNCPTR (gst_selector_pad_bufferalloc)); - - gst_pad_set_active (sinkpad, TRUE); - gst_element_add_pad (GST_ELEMENT (sel), sinkpad); - return sinkpad; -} - -static void -rsn_stream_selector_release_pad (GstElement * element, GstPad * pad) -{ - RsnStreamSelector *sel; - - sel = RSN_STREAM_SELECTOR (element); - GST_LOG_OBJECT (sel, "Releasing pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - - GST_OBJECT_LOCK (sel); - /* if the pad was the active pad, makes us select a new one */ - if (sel->active_sinkpad == pad) { - GST_DEBUG_OBJECT (sel, "Deactivating pad %s:%s", GST_DEBUG_PAD_NAME (pad)); - sel->active_sinkpad = NULL; - } - sel->n_pads--; - GST_OBJECT_UNLOCK (sel); - - gst_pad_set_active (pad, FALSE); - gst_element_remove_pad (GST_ELEMENT (sel), pad); -} diff --git a/ext/resindvd/rsnstreamselector.h b/ext/resindvd/rsnstreamselector.h deleted file mode 100644 index a8ff1af559..0000000000 --- a/ext/resindvd/rsnstreamselector.h +++ /dev/null @@ -1,63 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Julien Moutte - * Copyright (C) 2005 Ronald S. Bultje - * - * 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_STREAM_SELECTOR_H__ -#define __RSN_STREAM_SELECTOR_H__ - -#include - -G_BEGIN_DECLS - -#define RSN_TYPE_STREAM_SELECTOR \ - (rsn_stream_selector_get_type()) -#define RSN_STREAM_SELECTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), RSN_TYPE_STREAM_SELECTOR, RsnStreamSelector)) -#define RSN_STREAM_SELECTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), RSN_TYPE_STREAM_SELECTOR, RsnStreamSelectorClass)) -#define RSN_IS_STREAM_SELECTOR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RSN_TYPE_STREAM_SELECTOR)) -#define RSN_IS_STREAM_SELECTOR_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), RSN_TYPE_STREAM_SELECTOR)) - -typedef struct _RsnStreamSelector RsnStreamSelector; -typedef struct _RsnStreamSelectorClass RsnStreamSelectorClass; - -struct _RsnStreamSelector { - GstElement element; - - GstPad *srcpad; - - GstPad *active_sinkpad; - guint n_pads; - guint padcount; - - GstSegment segment; - gboolean mark_discont; -}; - -struct _RsnStreamSelectorClass { - GstElementClass parent_class; -}; - -GType rsn_stream_selector_get_type (void); - -G_END_DECLS - -#endif /* __RSN_STREAM_SELECTOR_H__ */