/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>,
+ * <2006> Edward Hervey <bilboed@bilboed.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
gboolean handled[MAX_STREAMS];
guint64 last_ts[MAX_STREAMS];
gint videopads, audiopads;
+
+ /* segment stuff */
+ /* TODO : replace with GstSegment */
+ gdouble segment_rate;
+ GstSeekFlags segment_flags;
+ /* GST_FORMAT_TIME */
+ gint64 segment_start;
+ gint64 segment_stop;
+ GstEvent *seek_event;
};
typedef struct _GstFFMpegDemuxClassParams
GstPadTemplate *audiosrctempl;
};
-#define GST_TYPE_FFMPEGDEC \
- (gst_ffmpegdec_get_type())
-#define GST_FFMPEGDEC(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FFMPEGDEC,GstFFMpegDemux))
-#define GST_FFMPEGDEC_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FFMPEGDEC,GstFFMpegDemuxClass))
-#define GST_IS_FFMPEGDEC(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FFMPEGDEC))
-#define GST_IS_FFMPEGDEC_CLASS(obj) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FFMPEGDEC))
-
-enum
-{
- /* FILL ME */
- LAST_SIGNAL
-};
-
-enum
-{
- ARG_0,
- /* FILL ME */
-};
-
static GHashTable *global_plugins;
/* A number of functon prototypes are given so we can refer to them later. */
static void gst_ffmpegdemux_base_init (GstFFMpegDemuxClass * klass);
static void gst_ffmpegdemux_init (GstFFMpegDemux * demux);
-static void gst_ffmpegdemux_loop (GstElement * element);
+static void gst_ffmpegdemux_loop (GstPad * pad);
+static gboolean
+gst_ffmpegdemux_sink_activate (GstPad * sinkpad);
+static gboolean
+gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active);
+static gboolean
+gst_ffmpegdemux_src_convert (GstPad * pad,
+ GstFormat src_fmt,
+ gint64 src_value, GstFormat * dest_fmt, gint64 * dest_value);
static GstStateChangeReturn
gst_ffmpegdemux_change_state (GstElement * element, GstStateChange transition);
static GstElementClass *parent_class = NULL;
-/*static guint gst_ffmpegdemux_signals[LAST_SIGNAL] = { 0 }; */
-
static const gchar *
gst_ffmpegdemux_averror (gint av_errno)
{
details.longname = g_strdup_printf ("FFMPEG %s demuxer",
params->in_plugin->long_name);
details.klass = "Codec/Demuxer";
- details.description = g_strdup_printf ("FFMPEG %s decoder",
+ details.description = g_strdup_printf ("FFMPEG %s demuxer",
params->in_plugin->long_name);
details.author = "Wim Taymans <wim.taymans@chello.be>, "
- "Ronald Bultje <rbultje@ronald.bitfreak.net>";
+ "Ronald Bultje <rbultje@ronald.bitfreak.net>, "
+ "Edward Hervey <bilboed@bilboed.com>";
gst_element_class_set_details (element_class, &details);
g_free (details.longname);
g_free (details.description);
gint n;
demux->sinkpad = gst_pad_new_from_template (oclass->sinktempl, "sink");
+ gst_pad_set_activate_function (demux->sinkpad, gst_ffmpegdemux_sink_activate);
+ gst_pad_set_activatepull_function (demux->sinkpad,
+ gst_ffmpegdemux_sink_activate_pull);
gst_element_add_pad (GST_ELEMENT (demux), demux->sinkpad);
- gst_element_set_loop_function (GST_ELEMENT (demux), gst_ffmpegdemux_loop);
demux->opened = FALSE;
demux->context = NULL;
}
demux->videopads = 0;
demux->audiopads = 0;
+
+ demux->segment_rate = 1.0;
+ demux->segment_flags = 0;
+ demux->segment_start = -1;
+ demux->segment_stop = -1;
+ demux->seek_event = NULL;
}
static void
}
}
+ gst_object_unref (demux);
return stream;
}
-static const GstEventMask *
-gst_ffmpegdemux_src_event_mask (GstPad * pad)
+static gboolean
+gst_ffmpegdemux_handle_seek (GstFFMpegDemux * demux, gboolean update)
{
- static const GstEventMask masks[] = {
- {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT},
- {0,}
- };
+ gboolean flush, keyframe;
+ guint stream;
- return masks;
+ GST_DEBUG_OBJECT (demux, "update:%d", update);
+
+ flush = demux->segment_flags & GST_SEEK_FLAG_FLUSH;
+ keyframe = demux->segment_flags & GST_SEEK_FLAG_KEY_UNIT;
+
+ if (flush) {
+ for (stream = 0; stream < demux->context->nb_streams; stream++) {
+ gst_pad_push_event (demux->srcpads[stream],
+ gst_event_new_flush_start());
+ }
+ gst_pad_push_event (demux->sinkpad, gst_event_new_flush_start ());
+ } else
+ gst_pad_pause_task (demux->sinkpad);
+
+ GST_PAD_STREAM_LOCK (demux->sinkpad);
+
+ GST_DEBUG_OBJECT (demux, "Creating new segment (%"GST_TIME_FORMAT" / %"GST_TIME_FORMAT,
+ GST_TIME_ARGS (demux->segment_start),
+ GST_TIME_ARGS (demux->segment_stop));
+
+ demux->seek_event = gst_event_new_new_segment (!update, demux->segment_rate, GST_FORMAT_TIME,
+ demux->segment_start, demux->segment_stop,
+ demux->segment_start);
+
+ if (flush) {
+ for (stream = 0; stream < demux->context->nb_streams; stream++)
+ gst_pad_push_event (demux->srcpads[stream],
+ gst_event_new_flush_stop ());
+ gst_pad_push_event (demux->sinkpad, gst_event_new_flush_stop ());
+ }
+
+ if (demux->segment_flags & GST_SEEK_FLAG_SEGMENT)
+ gst_element_post_message (GST_ELEMENT (demux),
+ gst_message_new_segment_start (GST_OBJECT (demux),
+ GST_FORMAT_TIME,
+ demux->segment_start));
+
+ gst_pad_start_task (demux->sinkpad, (GstTaskFunction) gst_ffmpegdemux_loop,
+ demux->sinkpad);
+
+ GST_PAD_STREAM_UNLOCK (demux->sinkpad);
+
+ return TRUE;
}
static gboolean
return FALSE;
switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_SEEK:
- offset = GST_EVENT_SEEK_OFFSET (event);
- switch (GST_EVENT_SEEK_FORMAT (event)) {
- case GST_FORMAT_DEFAULT:
- if (stream->codec->codec_type != CODEC_TYPE_VIDEO) {
- res = FALSE;
- break;
- } else {
- GstFormat fmt = GST_FORMAT_TIME;
-
- if (!(res = gst_pad_convert (pad, GST_FORMAT_DEFAULT, offset,
- &fmt, &offset)))
- break;
- }
- /* fall-through */
- case GST_FORMAT_TIME:
- if (av_seek_frame (demux->context, stream->index,
- offset / (GST_SECOND / AV_TIME_BASE) , 0))
- res = FALSE;
+ case GST_EVENT_SEEK:
+ {
+ GstFormat format;
+ GstSeekFlags flags;
+ gdouble rate;
+ gint64 start, stop;
+ gint64 tstart, tstop;
+ gint64 duration;
+ GstFormat tformat = GST_FORMAT_TIME;
+ GstSeekType start_type, stop_type;
+ gboolean update_start = TRUE;
+ gboolean update_stop = TRUE;
+
+ gst_event_parse_seek (event, &rate, &format, &flags, &start_type, &start,
+ &stop_type, &stop);
+
+ GST_DEBUG_OBJECT (demux,
+ "seek format %d, flags:%d, start:%lld, stop:%lld", format,
+ flags, start, stop);
+
+ if (format != GST_FORMAT_TIME) {
+ res &= gst_ffmpegdemux_src_convert (pad, format, start, &tformat, &tstart);
+ res &= gst_ffmpegdemux_src_convert (pad, format, stop, &tformat, &tstop);
+ } else {
+ tstart = start;
+ tstop = stop;
+ }
+
+ duration = gst_ffmpeg_time_ff_to_gst (stream->duration, stream->time_base);
+
+ switch (start_type) {
+ case GST_SEEK_TYPE_CUR:
+ tstart = demux->segment_start + tstart;
break;
- default:
- res = FALSE;
+ case GST_SEEK_TYPE_END:
+ tstart = duration + tstart;
+ break;
+ case GST_SEEK_TYPE_NONE:
+ tstart = demux->segment_start;
+ update_start = FALSE;
+ break;
+ case GST_SEEK_TYPE_SET:
break;
}
- break;
- default:
- res = FALSE;
- break;
- }
+ tstart = CLAMP (tstart, 0, duration);
- return res;
-}
+ switch (stop_type) {
+ case GST_SEEK_TYPE_CUR:
+ tstop = demux->segment_stop + tstop;
+ break;
+ case GST_SEEK_TYPE_END:
+ tstop = duration + tstop;
+ break;
+ case GST_SEEK_TYPE_NONE:
+ tstop = demux->segment_stop;
+ update_stop = FALSE;
+ break;
+ case GST_SEEK_TYPE_SET:
+ break;
+ }
+ tstop = CLAMP (tstop, 0, duration);
-static const GstFormat *
-gst_ffmpegdemux_src_format_list (GstPad * pad)
-{
- AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
- static const GstFormat src_v_formats[] = {
- GST_FORMAT_TIME,
- GST_FORMAT_DEFAULT,
- 0
- }, src_a_formats[] = {
- GST_FORMAT_TIME, 0};
+ /* now store the values */
+ demux->segment_rate = rate;
+ demux->segment_flags = flags;
+ demux->segment_start = tstart;
+ demux->segment_stop = tstop;
- return (stream->codec->codec_type == CODEC_TYPE_VIDEO) ?
- src_v_formats : src_a_formats;
+ gst_ffmpegdemux_handle_seek (demux, update_start || update_stop);
+ break;
+ }
+ break;
+ default:
+ res = FALSE;
+ break;
+ }
+
+
+ gst_event_unref (event);
+ return res;
}
static const GstQueryType *
gst_ffmpegdemux_src_query_list (GstPad * pad)
{
static const GstQueryType src_types[] = {
- GST_QUERY_TOTAL,
+ GST_QUERY_DURATION,
GST_QUERY_POSITION,
0
};
}
static gboolean
-gst_ffmpegdemux_src_query (GstPad * pad,
- GstQueryType type, GstFormat * fmt, gint64 * value)
+gst_ffmpegdemux_src_query (GstPad * pad, GstQuery *query)
{
- GstFFMpegDemux *demux = (GstFFMpegDemux *) gst_pad_get_parent (pad);
+ GstFFMpegDemux *demux = (GstFFMpegDemux *) GST_PAD_PARENT (pad);
AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
gboolean res = FALSE;
- switch (type) {
- case GST_QUERY_TOTAL:
- switch (*fmt) {
- case GST_FORMAT_TIME:
- if (stream) {
- *value = stream->duration * (GST_SECOND / AV_TIME_BASE);
- res = TRUE;
- }
- break;
- case GST_FORMAT_DEFAULT:
- if (stream->codec_info_nb_frames &&
- stream->codec->codec_type == CODEC_TYPE_VIDEO) {
- *value = stream->codec_info_nb_frames;
- res = TRUE;
- }
- break;
- case GST_FORMAT_BYTES:
- if (demux->videopads + demux->audiopads == 1 &&
- GST_PAD_PEER (demux->sinkpad) != NULL) {
- res = gst_pad_query (GST_PAD_PEER (demux->sinkpad),
- type, fmt, value);
- }
- break;
- default:
- break;
+ if (stream == NULL)
+ return FALSE;
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_POSITION:
+ {
+ GstFormat format;
+ gint64 timeposition;
+ gst_query_parse_position (query, &format, NULL);
+ timeposition = demux->last_ts[stream->index];
+ if (!(GST_CLOCK_TIME_IS_VALID (timeposition)))
+ break;
+
+ switch (format) {
+ case GST_FORMAT_TIME:
+ gst_query_set_position (query, GST_FORMAT_TIME, timeposition);
+ res = TRUE;
+ break;
+ case GST_FORMAT_DEFAULT:
+ gst_query_set_position (query, GST_FORMAT_DEFAULT,
+ timeposition * stream->r_frame_rate.num /
+ (GST_SECOND * stream->r_frame_rate.den));
+ res = TRUE;
+ break;
+ case GST_FORMAT_BYTES:
+ if (demux->videopads + demux->audiopads == 1 &&
+ GST_PAD_PEER (demux->sinkpad) != NULL)
+ res = gst_pad_query_default (pad, query);
+ break;
+ default:
+ break;
}
- break;
- case GST_QUERY_POSITION:
- switch (*fmt) {
- case GST_FORMAT_TIME:
- if (stream &&
- GST_CLOCK_TIME_IS_VALID (demux->last_ts[stream->index])) {
- *value = demux->last_ts[stream->index];
- res = TRUE;
- }
- break;
- case GST_FORMAT_DEFAULT:
- if (stream && stream->codec->codec_type == CODEC_TYPE_VIDEO &&
- GST_CLOCK_TIME_IS_VALID (demux->last_ts[stream->index])) {
- res = gst_pad_convert (pad, GST_FORMAT_TIME,
- demux->last_ts[stream->index], fmt, value);
- }
- break;
- case GST_FORMAT_BYTES:
- if (demux->videopads + demux->audiopads == 1 &&
- GST_PAD_PEER (demux->sinkpad) != NULL) {
- res = gst_pad_query (GST_PAD_PEER (demux->sinkpad),
- type, fmt, value);
- }
- default:
- break;
+ }
+ break;
+ case GST_QUERY_DURATION:
+ {
+ GstFormat format;
+ gint64 timeduration;
+ gst_query_parse_duration (query, &format, NULL);
+ timeduration = gst_ffmpeg_time_ff_to_gst (stream->duration, stream->time_base);
+ if (!(GST_CLOCK_TIME_IS_VALID (timeduration)))
+ break;
+
+ switch (format) {
+ case GST_FORMAT_TIME:
+ gst_query_set_duration (query, GST_FORMAT_TIME, timeduration);
+ res = TRUE;
+ break;
+ case GST_FORMAT_DEFAULT:
+ gst_query_set_duration (query, GST_FORMAT_DEFAULT,
+ timeduration * stream->r_frame_rate.num /
+ (GST_SECOND * stream->r_frame_rate.den));
+ res = TRUE;
+ break;
+ case GST_FORMAT_BYTES:
+ if (demux->videopads + demux->audiopads == 1 &&
+ GST_PAD_PEER (demux->sinkpad) != NULL)
+ res = gst_pad_query_default (pad, query);
+ break;
+ default:
+ break;
}
- break;
- default:
- break;
+ }
+ break;
+ default:
+ /* FIXME : ADD GST_QUERY_CONVERT */
+ res = gst_pad_query_default (pad, query);
+ break;
}
return res;
pad = gst_pad_new_from_template (templ, padname);
g_free (padname);
- gst_pad_use_explicit_caps (pad);
- gst_pad_set_formats_function (pad, gst_ffmpegdemux_src_format_list);
- gst_pad_set_event_mask_function (pad, gst_ffmpegdemux_src_event_mask);
- gst_pad_set_event_function (pad, gst_ffmpegdemux_src_event);
+ gst_pad_use_fixed_caps (pad);
+
gst_pad_set_query_type_function (pad, gst_ffmpegdemux_src_query_list);
gst_pad_set_query_function (pad, gst_ffmpegdemux_src_query);
- gst_pad_set_convert_function (pad, gst_ffmpegdemux_src_convert);
+ gst_pad_set_event_function (pad, gst_ffmpegdemux_src_event);
/* store pad internally */
demux->srcpads[stream->index] = pad;
/* get caps that belongs to this stream */
caps =
gst_ffmpeg_codecid_to_caps (stream->codec->codec_id, stream->codec, TRUE);
- gst_pad_set_explicit_caps (pad, caps);
+ gst_pad_set_caps (pad, caps);
gst_element_add_pad (GST_ELEMENT (demux), pad);
gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
(stream->codec->codec_type == CODEC_TYPE_VIDEO) ?
GST_TAG_VIDEO_CODEC : GST_TAG_AUDIO_CODEC, codec, NULL);
- gst_element_found_tags_for_pad (GST_ELEMENT (demux), pad, 0, list);
+ gst_element_found_tags_for_pad (GST_ELEMENT (demux), pad, list);
}
return TRUE;
/* open via our input protocol hack */
location = g_strdup_printf ("gstreamer://%p", demux->sinkpad);
+ GST_DEBUG_OBJECT (demux, "about to call av_open_input_file %s",
+ location);
res = av_open_input_file (&demux->context, location,
oclass->in_plugin, 0, NULL);
+ GST_DEBUG_OBJECT (demux, "av_open_input returned %d", res);
if (res >= 0)
res = av_find_stream_info (demux->context);
g_free (location);
}
}
+/* Task */
static void
-gst_ffmpegdemux_loop (GstElement * element)
+gst_ffmpegdemux_loop (GstPad * pad)
{
- GstFFMpegDemux *demux = (GstFFMpegDemux *) (element);
+ GstFFMpegDemux *demux = (GstFFMpegDemux*) (GST_PAD_PARENT (pad));
+ GstFlowReturn ret = GST_FLOW_OK;
gint res;
AVPacket pkt;
- GstPad *pad;
+ GstPad *srcpad;
/* open file if we didn't so already */
if (!demux->opened) {
- if (!gst_ffmpegdemux_open (demux))
- return;
- gst_element_no_more_pads (element);
- return;
+ if (!gst_ffmpegdemux_open (demux)) {
+ ret = GST_FLOW_ERROR;
+ goto pause;
+ }
+ gst_element_no_more_pads (GST_ELEMENT (demux));
+
+ /* Calculate min start and maximum end */
+ for (res = 0; res < demux->context->nb_streams; res++) {
+ gint64 start_time, duration, end;
+ AVStream *stream;
+
+ stream = demux->context->streams[res];
+
+ start_time = gst_ffmpeg_time_ff_to_gst (stream->start_time, stream->time_base);
+ duration = gst_ffmpeg_time_ff_to_gst (stream->duration, stream->time_base);
+ if (start_time == GST_CLOCK_TIME_NONE)
+ start_time = 0;
+ if (duration == GST_CLOCK_TIME_NONE)
+ end = GST_CLOCK_TIME_NONE;
+ else
+ end = start_time + duration;
+
+ if ((start_time < demux->segment_start) || (demux->segment_start == -1))
+ demux->segment_start = start_time;
+
+ if ((end > demux->segment_stop) || (demux->segment_stop == -1))
+ demux->segment_stop = end;
+ }
+ /* Send newsegment on all src pads */
+ for (res = 0; res < demux->context->nb_streams; res++) {
+ AVStream *stream;
+
+ GST_DEBUG_OBJECT (demux, "sending newsegment start:%"GST_TIME_FORMAT" duration:%"GST_TIME_FORMAT,
+ GST_TIME_ARGS (demux->segment_start),
+ GST_TIME_ARGS (demux->segment_stop));
+
+ gst_pad_push_event(demux->srcpads[res],
+ gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_TIME,
+ demux->segment_start, demux->segment_stop,
+ demux->segment_start));
+ }
+ }
+
+ if (demux->seek_event) {
+ /* pending seek */
+ GST_DEBUG_OBJECT (demux, "About to call av_seek_frame (context, -1, %lld, 0)",
+ gst_ffmpeg_time_gst_to_ff (demux->segment_start, demux->context->streams[0]->time_base));
+ if ((av_seek_frame (demux->context, -1,
+ gst_ffmpeg_time_gst_to_ff (demux->segment_start, demux->context->streams[0]->time_base), 0)) < 0) {
+ GST_WARNING_OBJECT (demux, "Call to av_seek_frame failed");
+ ret = GST_FLOW_ERROR;
+ goto pause;
+ }
}
/* read a package */
res = av_read_frame (demux->context, &pkt);
if (res < 0) {
-#if 0
- /* This doesn't work - FIXME */
- if (url_feof (&demux->context->pb)) {
- gst_ffmpegdemux_close (demux);
- gst_pad_event_default (demux->sinkpad, gst_event_new (GST_EVENT_EOS));
- } else {
- GST_ELEMENT_ERROR (demux, LIBRARY, FAILED, (NULL),
- (gst_ffmpegdemux_averror (res)));
- }
-#endif
-
- /* so, we do it the hacky way. */
- GstData *data = NULL;
-
- do {
- data = gst_pad_pull (demux->sinkpad);
-
- if (!GST_IS_EVENT (data) ||
- GST_EVENT_TYPE (GST_EVENT (data)) != GST_EVENT_EOS) {
- gst_data_unref (data);
- data = NULL;
- }
- } while (!data);
-
- gst_pad_event_default (demux->sinkpad, GST_EVENT (data));
- //gst_ffmpegdemux_close (demux);
+ /* something went wrong... */
+ GST_WARNING_OBJECT (demux, "av_read_frame returned %d", res);
+ ret = GST_FLOW_ERROR;
+ goto pause;
+ }
- return;
+ if (demux->seek_event) {
+ GST_DEBUG_OBJECT (demux, "About to send pending newsegment event (%"GST_TIME_FORMAT"/%"GST_TIME_FORMAT")",
+ GST_TIME_ARGS (demux->segment_start),
+ GST_TIME_ARGS (demux->segment_stop));
+ for (res = 0; res < demux->context->nb_streams; res++) {
+ gst_event_ref (demux->seek_event);
+ gst_pad_push_event (demux->srcpads[res], demux->seek_event);
+ }
+ gst_event_unref (demux->seek_event);
+ demux->seek_event = NULL;
}
+ GST_DEBUG_OBJECT (demux, "pkt pts:%lld / dts:%lld / size:%d / stream_index:%d / flags:%d / duration:%d / pos:%lld",
+ pkt.pts, pkt.dts, pkt.size, pkt.stream_index, pkt.flags, pkt.duration, pkt.pos);
+
/* for stream-generation-while-playing */
if (!demux->handled[pkt.stream_index]) {
gst_ffmpegdemux_add (demux, demux->context->streams[pkt.stream_index]);
}
/* shortcut to pad belonging to this stream */
- pad = demux->srcpads[pkt.stream_index];
+ srcpad = demux->srcpads[pkt.stream_index];
- /* and handle the data by pushing it forward... */
- if (pad && GST_PAD_IS_USABLE (pad)) {
- AVStream *stream = gst_ffmpegdemux_stream_from_pad (pad);
+ if (srcpad) {
+ AVStream *stream = gst_ffmpegdemux_stream_from_pad (srcpad);
GstBuffer *outbuf;
- outbuf = gst_buffer_new_and_alloc (pkt.size);
- memcpy (GST_BUFFER_DATA (outbuf), pkt.data, pkt.size);
- GST_BUFFER_SIZE (outbuf) = pkt.size;
+ ret = gst_pad_alloc_buffer_and_set_caps (srcpad,
+ GST_CLOCK_TIME_NONE,
+ pkt.size,
+ GST_PAD_CAPS (srcpad),
+ &outbuf);
+ if (ret != GST_FLOW_OK) {
+ pkt.destruct (&pkt);
+ goto pause;
+ }
+ memcpy (GST_BUFFER_DATA (outbuf), pkt.data, pkt.size);
GST_BUFFER_TIMESTAMP (outbuf) =
- gst_ffmpeg_time_ff_to_gst (pkt.pts,
+ gst_ffmpeg_time_ff_to_gst (pkt.pts,
demux->context->streams[pkt.stream_index]->time_base);
if (GST_BUFFER_TIMESTAMP_IS_VALID (outbuf))
demux->last_ts[stream->index] = GST_BUFFER_TIMESTAMP (outbuf);
+ if (!(pkt.flags & PKT_FLAG_KEY))
+ GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
- if (pkt.flags & PKT_FLAG_KEY)
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_KEY_UNIT);
- else
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_DELTA_UNIT);
+
+
+ GST_DEBUG_OBJECT (demux, "Sending out buffer time:%"GST_TIME_FORMAT" size:%d offset:%lld",
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
+ GST_BUFFER_SIZE (outbuf),
+ GST_BUFFER_OFFSET (outbuf));
- gst_pad_push (pad, GST_DATA (outbuf));
+ ret = gst_pad_push (srcpad, outbuf);
+
+ } else {
+ GST_WARNING_OBJECT (demux, "No pad from stream %d",
+ pkt.stream_index);
}
pkt.destruct (&pkt);
+
+ return;
+ pause:
+ GST_LOG_OBJECT (demux, "pausing task");
+ gst_pad_pause_task (demux->sinkpad);
+ if (GST_FLOW_IS_FATAL (ret)) {
+ int i;
+ GST_ELEMENT_ERROR (demux, STREAM, FAILED,
+ ("Internal data stream error."),
+ ("streaming stopped, reason %s", gst_flow_get_name (ret)));
+ for (i = 0; i < MAX_STREAMS; i++)
+ if (demux->srcpads[i])
+ gst_pad_push_event (demux->srcpads[i], gst_event_new_eos());
+ }
+}
+
+
+static gboolean
+gst_ffmpegdemux_sink_activate (GstPad * sinkpad)
+{
+ if (gst_pad_check_pull_range (sinkpad))
+ return gst_pad_activate_pull (sinkpad, TRUE);
+
+ return FALSE;
+}
+
+static gboolean
+gst_ffmpegdemux_sink_activate_pull (GstPad * sinkpad, gboolean active)
+{
+ if (active) {
+ /* if we have a scheduler we can start the task */
+ gst_pad_start_task (sinkpad, (GstTaskFunction) gst_ffmpegdemux_loop, sinkpad);
+ } else {
+ gst_pad_stop_task (sinkpad);
+ }
+
+ return TRUE;
}
static GstStateChangeReturn
gst_ffmpegdemux_change_state (GstElement * element, GstStateChange transition)
{
GstFFMpegDemux *demux = (GstFFMpegDemux *) (element);
+ GstStateChangeReturn ret;
+
+ ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
break;
}
- if (GST_ELEMENT_CLASS (parent_class)->change_state)
- return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- return GST_STATE_CHANGE_SUCCESS;
+ return ret;
}
gboolean
/* if it's already registered, drop it */
if (g_type_from_name (type_name)) {
- gst_caps_free (videosrccaps);
- gst_caps_free (audiosrccaps);
+ gst_caps_unref (videosrccaps);
+ gst_caps_unref (audiosrccaps);
g_free (type_name);
goto next;
}
if (!gst_element_register (plugin, type_name, rank, type) ||
(register_typefind_func == TRUE &&
!gst_type_find_register (plugin, typefind_name, rank,
- gst_ffmpegdemux_type_find, extensions, sinkcaps, params))) {
+ gst_ffmpegdemux_type_find, extensions, sinkcaps, params, NULL))) {
g_warning ("Register of type ffdemux_%s failed", name);
g_free (type_name);
g_free (typefind_name);
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * <2006> Edward Hervey <bilboed@bilboed.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
#endif
#include <gst/gst.h>
-#include <gst/bytestream/bytestream.h>
#include "gstffmpeg.h"
{
GstPad *pad;
- GstByteStream *bs;
+ guint64 offset;
gboolean eos;
- gboolean set_streamheader;
+ gint set_streamheader;
};
static int
/* we don't support R/W together */
if (flags != URL_RDONLY && flags != URL_WRONLY) {
- g_warning ("Only read-only or write-only are supported");
+ GST_WARNING ("Only read-only or write-only are supported");
return -EINVAL;
}
if (sscanf (&filename[12], "%p", &pad) != 1) {
- g_warning ("could not decode pad from %s", filename);
+ GST_WARNING ("could not decode pad from %s", filename);
return -EIO;
}
switch (flags) {
case URL_RDONLY:
g_return_val_if_fail (GST_PAD_IS_SINK (pad), -EINVAL);
- info->bs = gst_bytestream_new (pad);
break;
case URL_WRONLY:
g_return_val_if_fail (GST_PAD_IS_SRC (pad), -EINVAL);
- info->bs = NULL;
break;
}
info->eos = FALSE;
info->pad = pad;
+ info->offset = 0;
h->priv_data = (void *) info;
h->is_streamed = FALSE;
static int
gst_ffmpegdata_peek (URLContext * h, unsigned char *buf, int size)
{
- GstByteStream *bs;
- guint32 total, request;
- guint8 *data;
GstProtocolInfo *info;
- gboolean have_event, will_get_eos;
-
- info = (GstProtocolInfo *) h->priv_data;
-
+ GstBuffer *inbuf = NULL;
+ int total;
+
g_return_val_if_fail (h->flags == URL_RDONLY, AVERROR_IO);
+ info = (GstProtocolInfo *) h->priv_data;
- bs = info->bs;
-
- if (info->eos)
- return 0;
-
- do {
- have_event = FALSE, will_get_eos = FALSE;
-
- /* prevent EOS */
- if (gst_bytestream_tell (bs) + size >= gst_bytestream_length (bs)) {
- request = (int) (gst_bytestream_length (bs) - gst_bytestream_tell (bs));
- will_get_eos = TRUE;
- } else {
- request = size;
- }
-
- if (request > 0) {
- total = gst_bytestream_peek_bytes (bs, &data, request);
- } else {
- total = 0;
- }
-
- if (total < request) {
- GstEvent *event;
- guint32 remaining;
-
- gst_bytestream_get_status (bs, &remaining, &event);
-
- if (!event) {
- g_warning ("gstffmpegprotocol: no bytestream event");
- return total;
- }
- GST_LOG ("Reading (req %d, got %d) gave event of type %d",
- request, total, GST_EVENT_TYPE (event));
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_DISCONTINUOUS:
- gst_event_unref (event);
- break;
- case GST_EVENT_EOS:
- g_warning ("Unexpected/unwanted eos in data function");
- info->eos = TRUE;
- have_event = TRUE;
- gst_event_unref (event);
- break;
- case GST_EVENT_FLUSH:
- gst_event_unref (event);
- break;
- case GST_EVENT_INTERRUPT:
- have_event = TRUE;
- gst_event_unref (event);
- break;
- default:
- gst_pad_event_default (info->pad, event);
- break;
- }
- } else {
- GST_DEBUG ("got data (%d bytes)", request);
- if (will_get_eos)
- info->eos = TRUE;
- }
- } while ((!info->eos && total != request) && !have_event);
-
- memcpy (buf, data, total);
+ if (gst_pad_pull_range(info->pad, info->offset, (guint) size, &inbuf) != GST_FLOW_OK) {
+ total = 0;
+ } else {
+ total = (gint) GST_BUFFER_SIZE (inbuf);
+ memcpy (buf, GST_BUFFER_DATA (inbuf), total);
+ gst_buffer_unref (inbuf);
+ }
return total;
}
gst_ffmpegdata_read (URLContext * h, unsigned char *buf, int size)
{
gint res;
- GstByteStream *bs;
GstProtocolInfo *info;
- GST_DEBUG ("Reading %d bytes of data", size);
-
info = (GstProtocolInfo *) h->priv_data;
- bs = info->bs;
- res = gst_ffmpegdata_peek (h, buf, size);
- if (res > 0) {
- gst_bytestream_flush_fast (bs, res);
- }
+
+ GST_DEBUG ("Reading %d bytes of data at position %lld", size, info->offset);
+
+ res = gst_ffmpegdata_peek(h, buf, size);
+ info->offset += res;
GST_DEBUG ("Returning %d bytes", res);
g_return_val_if_fail (h->flags != URL_RDONLY, -EIO);
/* create buffer and push data further */
- outbuf = gst_buffer_new_and_alloc (size);
- GST_BUFFER_SIZE (outbuf) = size;
+ if (gst_pad_alloc_buffer_and_set_caps(info->pad,
+ info->offset,
+ size, GST_PAD_CAPS (info->pad),
+ &outbuf) != GST_FLOW_OK)
+ return 0;
+
memcpy (GST_BUFFER_DATA (outbuf), buf, size);
- if (info->set_streamheader) {
- GstCaps *caps = gst_pad_get_caps (info->pad);
- GstStructure *structure = gst_caps_get_structure (caps, 0);
- GValue list = { 0 }, value = { 0 };
-
- GST_DEBUG ("Using buffer (size %i) as streamheader", size);
-
- g_value_init (&list, GST_TYPE_FIXED_LIST);
-
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_IN_CAPS);
-
- g_value_init (&value, GST_TYPE_BUFFER);
- g_value_set_boxed (&value, outbuf);
- gst_value_list_append_value (&list, &value);
- g_value_unset (&value);
-
- gst_structure_set_value (structure, "streamheader", &list);
- g_value_unset (&list);
-
- gst_pad_try_set_caps (info->pad, caps);
-
- /* only set the first buffer */
- info->set_streamheader = FALSE;
- }
-
- gst_pad_push (info->pad, GST_DATA (outbuf));
+ if (gst_pad_push(info->pad, outbuf) != GST_FLOW_OK)
+ return 0;
+ info->offset += size;
return size;
}
info = (GstProtocolInfo *) h->priv_data;
- if (h->flags == URL_RDONLY) {
- /* get data (typefind hack) */
- if (gst_bytestream_tell (info->bs) != gst_bytestream_length (info->bs)) {
- gchar buf;
- gst_ffmpegdata_peek (h, &buf, 1);
- }
-
- /* hack in ffmpeg to get filesize... */
- if (whence == SEEK_END && pos == -1)
- return gst_bytestream_length (info->bs) - 1;
- else if (whence == SEEK_END && pos == 0)
- return gst_bytestream_length (info->bs);
- /* another hack to get the current position... */
- else if (whence == SEEK_CUR && pos == 0)
- return gst_bytestream_tell (info->bs);
- }
-
- switch (whence) {
- case SEEK_SET:
- seek_type = GST_SEEK_METHOD_SET;
- break;
- case SEEK_CUR:
- seek_type = GST_SEEK_METHOD_CUR;
- break;
- case SEEK_END:
- seek_type = GST_SEEK_METHOD_END;
- break;
- default:
- g_assert (0);
- break;
- }
-
switch (h->flags) {
- case URL_RDONLY: {
- GstEvent *event;
- guint8 *data;
- guint32 remaining;
-
- /* handle discont */
- gst_bytestream_seek (info->bs, pos, seek_type);
-
- /* prevent eos */
- if (gst_bytestream_tell (info->bs) ==
- gst_bytestream_length (info->bs)) {
- info->eos = TRUE;
- break;
- }
- info->eos = FALSE;
- while (gst_bytestream_peek_bytes (info->bs, &data, 1) == 0) {
- gst_bytestream_get_status (info->bs, &remaining, &event);
-
- if (!event) {
- g_warning ("no data, no event - panic!");
- return -1;
- }
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- g_warning ("unexpected/unwanted EOS event after seek");
- info->eos = TRUE;
- gst_event_unref (event);
- return -1;
- case GST_EVENT_DISCONTINUOUS:
- gst_event_unref (event); /* we expect this */
- break;
- case GST_EVENT_FLUSH:
- break;
- case GST_EVENT_INTERRUPT:
- gst_event_unref (event);
- return -1;
- default:
- gst_pad_event_default (info->pad, event);
- break;
- }
+ case URL_RDONLY:
+ {
+ /* sinkpad */
+ switch (whence) {
+ case SEEK_SET:
+ info->offset = (guint64) pos;
+ break;
+ case SEEK_CUR:
+ info->offset += pos;
+ break;
+ case SEEK_END:
+ GST_WARNING ("Can't handle SEEK_END yet");
+ default:
+ break;
}
- newpos = gst_bytestream_tell (info->bs);
- break;
+ /* FIXME : implement case for push-based behaviour */
+ newpos = info->offset;
}
-
- case URL_WRONLY:
- gst_pad_push (info->pad,
- GST_DATA (gst_event_new_seek (seek_type | GST_FORMAT_BYTES, pos)));
- /* this is screwy because there might be queues or scheduler-queued
- * buffers... Argh! */
- if (whence == SEEK_SET) {
- newpos = pos;
- } else {
- g_warning ("Writer reposition: implement me\n");
- newpos = 0;
- }
- break;
-
- default:
- g_assert (0);
- break;
+ break;
+ case URL_WRONLY:
+ {
+ /* srcpad */
+ /* FIXME : implement */
+ newpos = info->offset;
+ }
+ break;
+ default:
+ g_assert(0);
+ break;
}
+ GST_DEBUG ("Now at offset %lld", info->offset);
return newpos;
}
GST_LOG ("Closing file");
switch (h->flags) {
- case URL_WRONLY:{
- /* send EOS - that closes down the stream */
- GstEvent *event = gst_event_new (GST_EVENT_EOS);
-
- gst_pad_push (info->pad, GST_DATA (event));
- }
- break;
-
- case URL_RDONLY:
- /* unref bytestream */
- gst_bytestream_destroy (info->bs);
- break;
+ case URL_WRONLY:{
+ /* send EOS - that closes down the stream */
+ gst_pad_push_event (info->pad, gst_event_new_eos());
}
-
+ break;
+ default:
+ break;
+ }
+
/* clean up data */
g_free (info);