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 \
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
noinst_HEADERS = resindvdbin.h \
rsnaudiomunge.h \
rsndec.h \
- rsnstreamselector.h \
+ rsninputselector.h \
resindvdsrc.h \
gstmpegdefs.h \
gstmpegdesc.h \
* 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
#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__ */
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
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
sizeof (GstFluPSDemux),
0,
(GInstanceInitFunc) gst_flups_demux_init,
+ NULL
};
flups_demux_type =
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 <wim@fluendo.com>");
+ gst_element_class_set_details_simple (element_class,
+ "MPEG Program Demuxer", "Codec/Demuxer",
+ "Demultiplexes MPEG Program Streams",
+ "Jan Schmidt <thaytan@noraisin.net>");
}
static void
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 =
}
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);
}
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;
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;
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;
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;
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;
}
/* 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;
}
}
-static void
+static inline void
gst_flups_demux_mark_discont (GstFluPSDemux * demux)
{
gint id;
}
}
-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;
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:
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);
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;
}
}
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:
break;
}
- gst_object_unref (demux);
-
return res;
not_supported:
{
- gst_object_unref (demux);
gst_event_unref (event);
return FALSE;
}
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);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:
{
- GstPad *peer;
+ GstClockTime pos;
GstFormat format;
- gint64 position;
- gst_query_parse_position (query, &format, NULL);
+ /* See if upstream can immediately answer */
+ res = gst_pad_peer_query (demux->sinkpad, query);
+ if (res)
+ break;
- if ((peer = gst_pad_get_peer (demux->sinkpad)) != NULL) {
- res = gst_pad_query (peer, query);
- gst_object_unref (peer);
- if (res)
- break;
- }
+ gst_query_parse_position (query, &format, NULL);
if (format != GST_FORMAT_TIME) {
GST_DEBUG_OBJECT (demux, "position not supported for format %d",
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;
}
{
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);
+
+ GST_LOG_OBJECT (demux,
+ "query on peer pad reported bytes %" G_GUINT64_FORMAT, duration);
- duration = BYTES_TO_GSTTIME (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
#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)
{
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;
/* 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;
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;
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;
GST_DEBUG_OBJECT (demux, "stuffing bytes: %d", stuffing_bytes);
data += 4;
+ length += stuffing_bytes;
while (stuffing_bytes--) {
if (*data++ != 0xff)
goto lost_sync;
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 */
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;
/* 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;
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,
" 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;
/* 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
/* 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;
/* 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;
lost_sync:
{
GST_DEBUG_OBJECT (demux, "lost sync");
+ gst_adapter_unmap (demux->adapter);
return GST_FLOW_LOST_SYNC;
}
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)
{
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;
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;
}
}
+ gst_adapter_unmap (demux->adapter);
gst_adapter_flush (demux->adapter, length);
ADAPTER_OFFSET_FLUSH (length);
return GST_FLOW_OK;
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;
}
}
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;
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;
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;
psm_len_error:
{
GST_DEBUG_OBJECT (demux, "error in PSM length");
+ gst_adapter_unmap (demux->adapter);
return GST_FLOW_LOST_SYNC;
}
need_more_data:
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;
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;
if (G_LIKELY (stream_type == -1)) {
/* new id is in the first byte */
- id = data[offset++];
+ id = map.data[offset++];
datalen--;
/* and remap */
* 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);
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;
}
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) {
}
done:
+ gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
return ret;
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);
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 */
((sync & 0xe0) == 0xc0) || ((sync & 0xf0) == 0xe0);
}
+/* If we can pull that's prefered */
+static gboolean
+gst_flups_demux_sink_activate (GstPad * sinkpad, GstObject * parent)
+{
+ 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, GstBuffer * buffer)
+gst_flups_demux_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
- GstFluPSDemux *demux = GST_FLUPS_DEMUX (gst_pad_get_parent (pad));
+ GstFluPSDemux *demux = GST_FLUPS_DEMUX (parent);
GstFlowReturn ret = GST_FLOW_OK;
guint32 avail;
gboolean save, discont;
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) {
}
}
done:
- gst_object_unref (demux);
-
return ret;
}
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;
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,
-/*
+/*
+ * 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
* 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.
GstElement parent;
GstPad * sinkpad;
+ gboolean random_access; /* If we operate in pull mode */
+ gboolean flushing;
+
GstAdapter * adapter;
GstAdapter * rev_adapter;
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];
-/*
- * 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 <wim@fluendo.com>
- * Jan Schmidt <thaytan@noraisin.net>
- */
+ /*
+ * 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 <wim@fluendo.com>
+ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#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);
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)
{
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))
/* 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);
* 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;
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;
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;
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
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++;
/* 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
}
/* 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;
goto lost_sync;
}
+push_out:
{
GstBuffer *out;
guint16 consumed;
}
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 {
filter->state = STATE_DATA_PUSH;
}
+ gst_adapter_unmap (filter->adapter);
gst_adapter_flush (filter->adapter, avail);
ADAPTER_OFFSET_FLUSH (avail);
{
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);
}
lost_sync:
{
+ gst_adapter_unmap (filter->adapter);
GST_DEBUG ("lost sync");
gst_adapter_flush (filter->adapter, 4);
ADAPTER_OFFSET_FLUSH (4);
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;
-/*
+/*
+ * 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
* 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.
* Fluendo, S.L. All Rights Reserved.
*
* Contributor(s): Wim Taymans <wim@fluendo.com>
- * Jan Schmidt <thaytan@noraisin.net>
*/
#ifndef __GST_PES_FILTER_H__
gboolean unbounded_packet;
guint16 length;
- guint8 type;
-
gint64 pts;
gint64 dts;
};
{
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
#include <gst/gst.h>
#include <gst/glib-compat-private.h>
#include <gst/pbutils/missing-plugins.h>
+#include <gst/video/video.h>
+#include <gst/audio/audio.h>
#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
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 =
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);
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 <thaytan@noraisin.net>");
-
- 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;
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));
-}
-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);
+ 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 <thaytan@noraisin.net>");
}
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 ();
/* 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);
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;
{
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)
{
"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;
"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;
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 */
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,
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;
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],
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);
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;
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");
("No MPEG video decoder found"));
} else {
GST_ELEMENT_WARNING (dvdbin, STREAM, CODEC_NOT_FOUND, (NULL),
- ("No MPEG audio decoder found"));
+ ("No audio decoder found"));
}
}
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,
}
}
-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;
}
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);
}
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");
}
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
#include <stdio.h>
#include <string.h>
+#include <gmodule.h>
#include <gst/gst.h>
#include <gst/glib-compat-private.h>
#include <gst/gst-i18n-plugin.h>
-#include <gst/interfaces/navigation.h>
#include <gst/video/video.h>
+#include <gst/video/navigation.h>
#include "resindvdsrc.h"
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")
);
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);
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);
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);
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 <thaytan@noraisin.net>");
-}
-
static void
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,
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 <thaytan@noraisin.net>");
}
static void
-rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc, resinDvdSrcClass * gclass)
+rsn_dvdsrc_init (resinDvdSrc * rsndvdsrc)
{
const gchar *envvar;
* 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;
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);
}
}
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;
}
{
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);
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:
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;
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 */
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);
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;
}
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);
}
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;
}
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;
}
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;
/* 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 "
}
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)));
branching:
{
g_mutex_unlock (src->branch_lock);
+ gst_buffer_unmap (src->alloc_buf, &mmap);
return GST_FLOW_FLUSHING;
}
}
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)
}
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);
{
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;
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);
}
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;
/* 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;
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;
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,
otherpad = (pad == munge->srcpad) ? munge->sinkpad : munge->srcpad;
- ret = gst_pad_set_caps (otherpad, caps);
gst_object_unref (munge);
return ret;
}
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);
#endif
#include <string.h>
+#include <gst/video/video.h>
+#include <gst/audio/audio.h>
#include "rsndec.h"
}
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);
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;
}
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);
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,
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);
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);
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);
--- /dev/null
+/* GStreamer input selector
+ * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
+ * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
+ * Copyright (C) 2007 Andy Wingo <wingo@pobox.com>
+ * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
+ * Copyright (C) 2011 Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *
+ * 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:
+ *
+ * <itemizedlist>
+ * <listitem>
+ * "running-time": Running time of stream on pad (#gint64)
+ * </listitem>
+ * <listitem>
+ * "tags": The currently active tags on the pad (#GstTagList, boxed type)
+ * </listitem>
+ * <listitem>
+ * "active": If the pad is currently active (#gboolean)
+ * </listitem>
+ * <listitem>
+ * "always-ok" : Make an inactive pads return #GST_FLOW_OK instead of
+ * #GST_FLOW_NOT_LINKED
+ * </listitem>
+ * </itemizedlist>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+
+#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: <none>");
+ 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 <julien@moutte.net>, "
+ "Jan Schmidt <thaytan@mad.scientist.com>, "
+ "Wim Taymans <wim.taymans@gmail.com>");
+ 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;
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
+ * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
+ * Copyright (C) 2008 Nokia Corporation. (contact <stefan.kost@nokia.com>)
+ *
+ * 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 <gst/gst.h>
+
+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__ */
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
- * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
- * Copyright (C) 2005 Jan Schmidt <thaytan@mad.scientist.com>
- * Copyright (C) 2007 Wim Taymans <wim.taymans@gmail.com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-
-#include "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 <julien@moutte.net>, "
- "Ronald S. Bultje <rbultje@ronald.bitfreak.net>, "
- "Jan Schmidt <thaytan@mad.scientist.com>, "
- "Wim Taymans <wim.taymans@gmail.com>");
- 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);
-}
+++ /dev/null
-/* GStreamer
- * Copyright (C) 2003 Julien Moutte <julien@moutte.net>
- * Copyright (C) 2005 Ronald S. Bultje <rbultje@ronald.bitfreak.net>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __RSN_STREAM_SELECTOR_H__
-#define __RSN_STREAM_SELECTOR_H__
-
-#include <gst/gst.h>
-
-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__ */