diff options
author | Wim Taymans <wim.taymans@collabora.co.uk> | 2013-03-22 12:25:16 +0100 |
---|---|---|
committer | Wim Taymans <wim.taymans@collabora.co.uk> | 2013-03-22 12:25:16 +0100 |
commit | 803f5640138c4a117d93fb2118a26837288f5d4a (patch) | |
tree | 13bbab6d6ceda52c9cc63f7cbb31f03aa72f96bd /gst/midi | |
parent | 87041064f8095a5bd4b92069d1839c2e21ad9642 (diff) | |
download | gstreamer-plugins-bad-803f5640138c4a117d93fb2118a26837288f5d4a.tar.gz |
midiparse: implement seeking
Seek back to 0 for backward seeking and let things run and clip for forwards
seeking.
Diffstat (limited to 'gst/midi')
-rw-r--r-- | gst/midi/midiparse.c | 172 | ||||
-rw-r--r-- | gst/midi/midiparse.h | 3 |
2 files changed, 169 insertions, 6 deletions
diff --git a/gst/midi/midiparse.c b/gst/midi/midiparse.c index 65e8fb71c..b77268a2d 100644 --- a/gst/midi/midiparse.c +++ b/gst/midi/midiparse.c @@ -103,6 +103,8 @@ static void gst_midi_parse_set_property (GObject * object, guint prop_id, static void gst_midi_parse_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); +static void reset_track (GstMidiTrack * track, GstMidiParse * midiparse); + static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, @@ -224,13 +226,161 @@ gst_midi_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query) } static gboolean +gst_midi_parse_do_seek (GstMidiParse * midiparse, GstSegment * segment) +{ + GST_DEBUG_OBJECT (midiparse, "seek to %" GST_TIME_FORMAT, + GST_TIME_ARGS (segment->position)); + + /* if seeking backwards, start from 0 else we just let things run and + * have it clip downstream */ + if (segment->position < midiparse->segment.position) { + GST_DEBUG_OBJECT (midiparse, "seeking back to 0"); + segment->position = 0; + g_list_foreach (midiparse->tracks, (GFunc) reset_track, midiparse); + midiparse->pulse = 0; + } + return TRUE; +} + +static gboolean +gst_midi_parse_perform_seek (GstMidiParse * midiparse, GstEvent * event) +{ + gboolean res = TRUE, tres; + gdouble rate; + GstFormat seek_format; + GstSeekFlags flags; + GstSeekType start_type, stop_type; + gint64 start, stop; + gboolean flush; + gboolean update; + GstSegment seeksegment; + guint32 seqnum; + GstEvent *tevent; + + GST_DEBUG_OBJECT (midiparse, "doing seek: %" GST_PTR_FORMAT, event); + + if (event) { + gst_event_parse_seek (event, &rate, &seek_format, &flags, + &start_type, &start, &stop_type, &stop); + + if (seek_format != GST_FORMAT_TIME) + goto invalid_format; + + flush = flags & GST_SEEK_FLAG_FLUSH; + seqnum = gst_event_get_seqnum (event); + } else { + flush = FALSE; + /* get next seqnum */ + seqnum = gst_util_seqnum_next (); + } + + /* send flush start */ + if (flush) { + tevent = gst_event_new_flush_start (); + gst_event_set_seqnum (tevent, seqnum); + gst_pad_push_event (midiparse->srcpad, tevent); + } else + gst_pad_pause_task (midiparse->srcpad); + + /* grab streaming lock, this should eventually be possible, either + * because the task is paused, our streaming thread stopped + * or because our peer is flushing. */ + GST_PAD_STREAM_LOCK (midiparse->sinkpad); + if (G_UNLIKELY (midiparse->seqnum == seqnum)) { + /* we have seen this event before, issue a warning for now */ + GST_WARNING_OBJECT (midiparse, "duplicate event found %" G_GUINT32_FORMAT, + seqnum); + } else { + midiparse->seqnum = seqnum; + GST_DEBUG_OBJECT (midiparse, "seek with seqnum %" G_GUINT32_FORMAT, seqnum); + } + + /* Copy the current segment info into the temp segment that we can actually + * attempt the seek with. We only update the real segment if the seek succeeds. */ + memcpy (&seeksegment, &midiparse->segment, sizeof (GstSegment)); + + /* now configure the final seek segment */ + if (event) { + gst_segment_do_seek (&seeksegment, rate, seek_format, flags, + start_type, start, stop_type, stop, &update); + } + /* Else, no seek event passed, so we're just (re)starting the + current segment. */ + GST_DEBUG_OBJECT (midiparse, "segment configured from %" G_GINT64_FORMAT + " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT, + seeksegment.start, seeksegment.stop, seeksegment.position); + + /* do the seek, segment.position contains the new position. */ + res = gst_midi_parse_do_seek (midiparse, &seeksegment); + + /* and prepare to continue streaming */ + if (flush) { + tevent = gst_event_new_flush_stop (TRUE); + gst_event_set_seqnum (tevent, seqnum); + /* send flush stop, peer will accept data and events again. We + * are not yet providing data as we still have the STREAM_LOCK. */ + gst_pad_push_event (midiparse->srcpad, tevent); + } + + /* if the seek was successful, we update our real segment and push + * out the new segment. */ + if (res) { + GST_OBJECT_LOCK (midiparse); + memcpy (&midiparse->segment, &seeksegment, sizeof (GstSegment)); + GST_OBJECT_UNLOCK (midiparse); + + if (seeksegment.flags & GST_SEGMENT_FLAG_SEGMENT) { + GstMessage *message; + + message = gst_message_new_segment_start (GST_OBJECT (midiparse), + seeksegment.format, seeksegment.position); + gst_message_set_seqnum (message, seqnum); + + gst_element_post_message (GST_ELEMENT (midiparse), message); + } + /* for deriving a stop position for the playback segment from the seek + * segment, we must take the duration when the stop is not set */ + if ((stop = seeksegment.stop) == -1) + stop = seeksegment.duration; + + midiparse->segment_pending = TRUE; + } + + midiparse->discont = TRUE; + /* and restart the task in case it got paused explicitly or by + * the FLUSH_START event we pushed out. */ + tres = + gst_pad_start_task (midiparse->sinkpad, + (GstTaskFunction) gst_midi_parse_loop, midiparse->sinkpad, NULL); + if (res && !tres) + res = FALSE; + + /* and release the lock again so we can continue streaming */ + GST_PAD_STREAM_UNLOCK (midiparse->sinkpad); + + return res; + + /* ERROR */ +invalid_format: + { + GST_DEBUG_OBJECT (midiparse, "Unsupported seek format %s", + gst_format_get_name (seek_format)); + return FALSE; + } +} + +static gboolean gst_midi_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event) { gboolean res = FALSE; + GstMidiParse *midiparse = GST_MIDI_PARSE (parent); GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event)); switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEEK: + res = gst_midi_parse_perform_seek (midiparse, event); + break; default: break; } @@ -239,7 +389,6 @@ gst_midi_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event) return res; } - static gboolean gst_midi_parse_activate (GstPad * sinkpad, GstObject * parent) { @@ -646,7 +795,7 @@ unhandled_event: } static void -reset_track (GstMidiParse * midiparse, GstMidiTrack * track) +reset_track (GstMidiTrack * track, GstMidiParse * midiparse) { GST_DEBUG_OBJECT (midiparse, "reset track"); track->offset = 0; @@ -669,7 +818,7 @@ parse_MTrk (GstMidiParse * midiparse, guint8 * data, guint size) track = g_slice_new (GstMidiTrack); track->data = data; track->size = size; - reset_track (midiparse, track); + reset_track (track, midiparse); midiparse->tracks = g_list_append (midiparse->tracks, track); midiparse->track_count++; @@ -688,7 +837,7 @@ parse_MTrk (GstMidiParse * midiparse, guint8 * data, guint size) if (duration > midiparse->segment.duration) midiparse->segment.duration = duration; - reset_track (midiparse, track); + reset_track (track, midiparse); return TRUE; } @@ -859,8 +1008,8 @@ gst_midi_parse_parse_song (GstMidiParse * midiparse) gst_pad_set_caps (midiparse->srcpad, outcaps); gst_caps_unref (outcaps); - gst_pad_push_event (midiparse->srcpad, - gst_event_new_segment (&midiparse->segment)); + midiparse->segment_pending = TRUE; + midiparse->discont = TRUE; GST_DEBUG_OBJECT (midiparse, "Parsing song done"); @@ -902,6 +1051,11 @@ play_push_func (GstMidiParse * midiparse, GstMidiTrack * track, GST_DEBUG_OBJECT (midiparse, "pushing %" GST_TIME_FORMAT, GST_TIME_ARGS (position)); + if (midiparse->discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + midiparse->discont = FALSE; + } + return gst_pad_push (midiparse->srcpad, outbuf); } @@ -917,6 +1071,12 @@ gst_midi_parse_do_play (GstMidiParse * midiparse) pulse = midiparse->pulse; position = midiparse->segment.position; + if (midiparse->segment_pending) { + gst_pad_push_event (midiparse->srcpad, + gst_event_new_segment (&midiparse->segment)); + midiparse->segment_pending = FALSE; + } + GST_DEBUG_OBJECT (midiparse, "pulse %" G_GUINT64_FORMAT ", position %" GST_TIME_FORMAT, pulse, GST_TIME_ARGS (position)); diff --git a/gst/midi/midiparse.h b/gst/midi/midiparse.h index 1381536bb..ba3a25ea1 100644 --- a/gst/midi/midiparse.h +++ b/gst/midi/midiparse.h @@ -72,6 +72,9 @@ struct _GstMidiParse /* output data */ gboolean discont; GstSegment segment; + gboolean segment_pending; + guint32 seqnum; + guint64 pulse; }; |