public bool seek_simple (Format format, SeekFlags seek_flags, int64 seek_pos)

Simple API to perform a seek on the given element, meaning it just seeks to the given position relative to the start of the stream.

For more complex operations like segment seeks (e.g. for looping) or changing the playback rate or seeking relative to the last configured playback segment you should use seek.

In a completely prerolled PAUSED or PLAYING pipeline, seeking is always guaranteed to return true on a seekable media type or false when the media type is certainly not seekable (such as a live stream).

Some elements allow for seeking in the READY state, in this case they will store the seek event and execute it when they are put to PAUSED. If the element supports seek in READY, it will always return true when it receives the event in the READY state.

Example: Time management:

// See
// for a detailed description

public class Main {

// TODO: GST_CLOCK_TIME_IS_VALID(time) isn't bound (Tue Aug 20, 2013)
private static inline bool GST_CLOCK_TIME_IS_VALID (Gst.ClockTime time) {
return ((time) != Gst.CLOCK_TIME_NONE);

// Our one and only element
private Gst.Element playbin2;

// Are we in the PLAYING state?
private bool playing;

// Should we terminate execution?
private bool terminate;

// Is seeking enabled for this media?
private bool seek_enabled;

// Have we performed the seek already?
private bool seek_done;

// How long does this media last, in nanoseconds
private Gst.ClockTime duration;

// Forward definition of the message processing function
private void handle_message (Gst.Message msg) {
switch (msg.type) {
case Gst.MessageType.ERROR:
GLib.Error err;
string debug_info;

msg.parse_error (out err, out debug_info);
print ("Error received from element %s: %s\n",, err.message);
print ("Debugging information: %s\n", (debug_info != null)? debug_info : "none");
this.terminate = true;

case Gst.MessageType.EOS:
print ("End-Of-Stream reached.\n");
this.terminate = true;

case Gst.MessageType.DURATION_CHANGED :
// The duration has changed, mark the current one as invalid:
this.duration = Gst.CLOCK_TIME_NONE;

case Gst.MessageType.STATE_CHANGED:
Gst.State old_state;
Gst.State new_state;
Gst.State pending_state;

msg.parse_state_changed (out old_state, out new_state, out pending_state);
if (msg.src == this.playbin2) {
print ("Pipeline state changed from %s to %s:\n",
Gst.Element.state_get_name (old_state),
Gst.Element.state_get_name (new_state));

// Remember whether we are in the PLAYING state or not:
this.playing = (new_state == Gst.State.PLAYING);

if (this.playing) {
// We just moved to PLAYING. Check if seeking is possible:
Gst.Query query = new Gst.Query.seeking (Gst.Format.TIME);
int64 start;
int64 end;

if (this.playbin2.query (query)) {
query.parse_seeking (null, out this.seek_enabled, out start, out end);
if (this.seek_enabled) {
// GST_TIME_ARGS isn't available (Tue Aug 20, 2013)
//print ("Seeking is ENABLED from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n",
// GST_TIME_ARGS (start), GST_TIME_ARGS (end));
print ("Seeking is ENABLED from %" + int64.FORMAT + " to %" + int64.FORMAT + "\n", start, end);
} else {
print ("Seeking is DISABLED for this stream.\n");
} else {
print ("Seeking query failed.\n");

// We should not reach here:
assert_not_reached ();

public int run () {
// init:
this.playing = false;
this.terminate = false;
this.seek_enabled = false;
this.seek_done = false;
this.duration = Gst.CLOCK_TIME_NONE;

// Create the elements:
this.playbin2 = Gst.ElementFactory.make ("playbin", "playbin");

if (this.playbin2 == null) {
print ("Not all elements could be created.\n");
return -1;

// Set the URI to play:
this.playbin2.set ("uri", "");

// Start playing:
Gst.StateChangeReturn ret = this.playbin2.set_state (Gst.State.PLAYING);
if (ret == Gst.StateChangeReturn.FAILURE) {
stderr.puts ("Unable to set the pipeline to the playing state.\n");
this.playbin2 = null;
return -1;

// Listen to the bus:
Gst.Bus bus = this.playbin2.get_bus ();
do {
Gst.Message msg = bus.timed_pop_filtered (100 * Gst.MSECOND,
Gst.MessageType.STATE_CHANGED | Gst.MessageType.ERROR | Gst.MessageType.EOS | Gst.MessageType.DURATION_CHANGED);

// Parse message:
if (msg != null) {
handle_message (msg);
} else {
// We got no message, this means the timeout expired:
if (this.playing) {
Gst.Format fmt = Gst.Format.TIME;
int64 current = -1;

// Query the current position of the stream:
if (!this.playbin2.query_position (fmt, out current)) {
stderr.puts ("Could not query current position.\n");

// If we didn't know it yet, query the stream duration:
if (!GST_CLOCK_TIME_IS_VALID (this.duration)) {
if (!this.playbin2.query_duration (fmt, out this.duration)) {
stderr.puts ("Could not query current duration.\n");

// Print current position and total duration:
// GST_TIME_ARGS isn't available (Tue Aug 20, 2013)
// print ("Position %" + Gst.TIME_FORMAT + " / %" + Gst.TIME_FORMAT + "\r",
// GST_TIME_ARGS (current), GST_TIME_ARGS (this.duration));
print ("Position %" + int64.FORMAT + " / %" + int64.FORMAT + "\r",
current, this.duration);

// If seeking is enabled, we have not done it yet, and the time is right, seek:
if (this.seek_enabled && !this.seek_done && current > 10 * Gst.SECOND) {
print ("\nReached 10s, performing seek...\n");
this.playbin2.seek_simple (Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 30 * Gst.SECOND);
this.seek_done = true;
} while (!this.terminate);

// Free resources:
this.playbin2.set_state (Gst.State.NULL);
this.playbin2 = null;

return 0;

public static int main (string[] args) {
// Initialize GStreamer:
Gst.init (ref args);

return new Main ().run ();

valac --pkg gstreamer-1.0 --enable-experimental time-management.vala



a Element to seek on


a Format to execute the seek in, such as TIME


seek options; playback applications will usually want to use GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT here


position to seek to (relative to the start); if you are doing a seek in TIME this value is in nanoseconds - multiply with SECOND to convert seconds to nanoseconds or with MSECOND to convert milliseconds to nanoseconds.


true if the seek operation succeeded. Flushing seeks will trigger a preroll, which will emit ASYNC_DONE.