Blame tests/icles/test-reverseplay.c

Packit 971217
/* Test example which plays a given file forward, then
Packit 971217
 * at EOS, plays the entire file in reverse
Packit 971217
 * and checks that reverse playback generates the same
Packit 971217
 * output as forward playback but reversed
Packit 971217
 */
Packit 971217
/* GStreamer
Packit 971217
 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
Packit 971217
 *
Packit 971217
 * This library is free software; you can redistribute it and/or
Packit 971217
 * modify it under the terms of the GNU Library General Public
Packit 971217
 * License as published by the Free Software Foundation; either
Packit 971217
 * version 2 of the License, or (at your option) any later version.
Packit 971217
 *
Packit 971217
 * This library is distributed in the hope that it will be useful,
Packit 971217
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 971217
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 971217
 * Library General Public License for more details.
Packit 971217
 *
Packit 971217
 * You should have received a copy of the GNU Library General Public
Packit 971217
 * License along with this library; if not, write to the
Packit 971217
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 971217
 * Boston, MA 02110-1301, USA.
Packit 971217
 */
Packit 971217
#include <gst/gst.h>
Packit 971217
#include <string.h>
Packit 971217
Packit 971217
typedef struct _PlayState PlayState;
Packit 971217
Packit 971217
typedef struct _StreamTSRange
Packit 971217
{
Packit 971217
  GstClockTime start;
Packit 971217
  GstClockTime end;
Packit 971217
} StreamTSRange;
Packit 971217
Packit 971217
typedef struct _StreamInfo
Packit 971217
{
Packit 971217
  PlayState *state;
Packit 971217
  GstPad *pad;
Packit 971217
Packit 971217
  GstSegment seg;
Packit 971217
Packit 971217
  GArray *fwd_times;
Packit 971217
  GArray *bkwd_times;
Packit 971217
} StreamInfo;
Packit 971217
Packit 971217
struct _PlayState
Packit 971217
{
Packit 971217
  GstElement *pipe;
Packit 971217
  GMainLoop *loop;
Packit 971217
  gboolean fwd_play;
Packit 971217
  gint n_sinks;
Packit 971217
Packit 971217
  GMutex output_lock;
Packit 971217
};
Packit 971217
Packit 971217
static void
Packit 971217
warning_cb (GstBus * bus, GstMessage * msg, gpointer foo)
Packit 971217
{
Packit 971217
  GError *err = NULL;
Packit 971217
  gchar *dbg = NULL;
Packit 971217
Packit 971217
  gst_message_parse_warning (msg, &err, &dbg;;
Packit 971217
Packit 971217
  g_printerr ("WARNING: %s (%s)\n", err->message, (dbg) ? dbg : "no details");
Packit 971217
Packit 971217
  g_error_free (err);
Packit 971217
  g_free (dbg);
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
error_cb (GstBus * bus, GstMessage * msg, PlayState * state)
Packit 971217
{
Packit 971217
  GError *err = NULL;
Packit 971217
  gchar *dbg = NULL;
Packit 971217
Packit 971217
  gst_message_parse_error (msg, &err, &dbg;;
Packit 971217
Packit 971217
  g_printerr ("ERROR: %s (%s)\n", err->message, (dbg) ? dbg : "no details");
Packit 971217
Packit 971217
  g_main_loop_quit (state->loop);
Packit 971217
Packit 971217
  g_error_free (err);
Packit 971217
  g_free (dbg);
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
eos_cb (GstBus * bus, GstMessage * msg, PlayState * state)
Packit 971217
{
Packit 971217
  if (state->fwd_play) {
Packit 971217
    g_print ("EOS - finished forward play. Starting reverse\n");
Packit 971217
    state->fwd_play = FALSE;
Packit 971217
    gst_element_seek (state->pipe, -1.0, GST_FORMAT_TIME,
Packit 971217
        GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
Packit 971217
        GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_END, 0);
Packit 971217
Packit 971217
    return;
Packit 971217
  }
Packit 971217
  g_print ("EOS - exiting\n");
Packit 971217
  g_main_loop_quit (state->loop);
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
state_cb (GstBus * bus, GstMessage * msg, PlayState * state)
Packit 971217
{
Packit 971217
  if (msg->src == GST_OBJECT (state->pipe)) {
Packit 971217
    GstState old_state, new_state, pending_state;
Packit 971217
Packit 971217
    gst_message_parse_state_changed (msg, &old_state, &new_state,
Packit 971217
        &pending_state);
Packit 971217
    if (new_state == GST_STATE_PLAYING)
Packit 971217
      g_print ("Decoding ...\n");
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
_destroy_stream_info (StreamInfo * si)
Packit 971217
{
Packit 971217
  g_array_free (si->fwd_times, TRUE);
Packit 971217
  g_array_free (si->bkwd_times, TRUE);
Packit 971217
  g_object_unref (si->pad);
Packit 971217
  g_free (si);
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
extend_times (StreamInfo * si, GstClockTime start, GstClockTime end)
Packit 971217
{
Packit 971217
  PlayState *state = si->state;
Packit 971217
  StreamTSRange *ts = NULL;
Packit 971217
  StreamTSRange tsn;
Packit 971217
  GArray *a;
Packit 971217
  guint i, n;
Packit 971217
Packit 971217
  /* Set up new entry, in case we need it */
Packit 971217
  tsn.start = start;
Packit 971217
  tsn.end = end;
Packit 971217
Packit 971217
  if (state->fwd_play) {
Packit 971217
    a = si->fwd_times;
Packit 971217
    n = a->len;
Packit 971217
    /* if playing forward, see if this new time extends the last entry */
Packit 971217
    i = n - 1;
Packit 971217
  } else {
Packit 971217
    a = si->bkwd_times;
Packit 971217
    n = a->len;
Packit 971217
    /* if playing backward, see if this new time extends the earliest entry */
Packit 971217
    i = 0;
Packit 971217
  }
Packit 971217
Packit 971217
  if (n > 0) {
Packit 971217
    ts = &g_array_index (a, StreamTSRange, i);
Packit 971217
    if (start > ts->start) {
Packit 971217
      /* This entry is after the most recent entry */
Packit 971217
      /* Tolerance of 1 millisecond allowed for imprecision */
Packit 971217
      if (ts->end + GST_MSECOND >= start) {
Packit 971217
        GST_LOG ("%p extending entry %d to %" GST_TIME_FORMAT,
Packit 971217
            si, i, GST_TIME_ARGS (end));
Packit 971217
        ts->end = end;
Packit 971217
        return;
Packit 971217
      }
Packit 971217
Packit 971217
      /* new start > ts->end, so this new entry goes after the first one */
Packit 971217
      GST_LOG ("%p inserting new entry %d %" GST_TIME_FORMAT
Packit 971217
          " to %" GST_TIME_FORMAT, si, i + 1, GST_TIME_ARGS (start),
Packit 971217
          GST_TIME_ARGS (end));
Packit 971217
      g_array_insert_val (a, i + 1, tsn);
Packit 971217
      return;
Packit 971217
    } else if (end + GST_MSECOND > ts->start) {
Packit 971217
      /* This entry precedes the current one, but overlaps it */
Packit 971217
      GST_LOG ("%p pre-extending entry %d to %" GST_TIME_FORMAT,
Packit 971217
          si, i, GST_TIME_ARGS (start));
Packit 971217
      ts->start = start;
Packit 971217
      return;
Packit 971217
    }
Packit 971217
  } else {
Packit 971217
    i = 0;
Packit 971217
  }
Packit 971217
Packit 971217
  /* otherwise insert a new entry before/at the start */
Packit 971217
  GST_LOG ("%p New entry %d - %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
Packit 971217
      si, i, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
Packit 971217
  g_array_insert_val (a, i, tsn);
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
dump_times (StreamInfo * si)
Packit 971217
{
Packit 971217
  PlayState *state = si->state;
Packit 971217
  guint i;
Packit 971217
  GArray *a;
Packit 971217
Packit 971217
  g_mutex_lock (&state->output_lock);
Packit 971217
  if (state->fwd_play)
Packit 971217
    a = si->fwd_times;
Packit 971217
  else
Packit 971217
    a = si->bkwd_times;
Packit 971217
Packit 971217
  g_print ("Pad %s times:\n", GST_PAD_NAME (si->pad));
Packit 971217
  for (i = 0; i < a->len; i++) {
Packit 971217
    StreamTSRange *ts = &g_array_index (a, StreamTSRange, i);
Packit 971217
Packit 971217
    g_print ("  %u %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n",
Packit 971217
        i, GST_TIME_ARGS (ts->start), GST_TIME_ARGS (ts->end));
Packit 971217
  }
Packit 971217
  g_mutex_unlock (&state->output_lock);
Packit 971217
}
Packit 971217
Packit 971217
static GstPadProbeReturn
Packit 971217
handle_output (GstPad * pad, GstPadProbeInfo * info, StreamInfo * si)
Packit 971217
{
Packit 971217
  GstClockTime start, end;
Packit 971217
  GstBuffer *buf;
Packit 971217
Packit 971217
  GST_LOG_OBJECT (pad, "Fired probe type 0x%x", info->type);
Packit 971217
Packit 971217
  if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
Packit 971217
    g_warning ("Buffer list handling not implemented");
Packit 971217
    return GST_PAD_PROBE_DROP;
Packit 971217
  }
Packit 971217
Packit 971217
  if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
Packit 971217
    GstEvent *event = gst_pad_probe_info_get_event (info);
Packit 971217
    switch (GST_EVENT_TYPE (event)) {
Packit 971217
      case GST_EVENT_SEGMENT:
Packit 971217
        gst_event_copy_segment (event, &si->seg);
Packit 971217
        break;
Packit 971217
      case GST_EVENT_EOS:
Packit 971217
        dump_times (si);
Packit 971217
        break;
Packit 971217
      default:
Packit 971217
        break;
Packit 971217
    }
Packit 971217
    return GST_PAD_PROBE_PASS;
Packit 971217
  }
Packit 971217
Packit 971217
  buf = gst_pad_probe_info_get_buffer (info);
Packit 971217
  if (!GST_BUFFER_PTS_IS_VALID (buf))
Packit 971217
    goto done;
Packit 971217
  end = start = GST_BUFFER_PTS (buf);
Packit 971217
Packit 971217
  if (GST_BUFFER_DURATION_IS_VALID (buf))
Packit 971217
    end += GST_BUFFER_DURATION (buf);
Packit 971217
Packit 971217
  gst_segment_clip (&si->seg, GST_FORMAT_TIME, start, end, &start, &end;;
Packit 971217
  start = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, start);
Packit 971217
  end = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, end);
Packit 971217
Packit 971217
  GST_DEBUG_OBJECT (pad, "new buffer %" GST_TIME_FORMAT
Packit 971217
      " to %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
Packit 971217
Packit 971217
  /* Now extend measured time range to include new times */
Packit 971217
  extend_times (si, start, end);
Packit 971217
Packit 971217
done:
Packit 971217
  return GST_PAD_PROBE_PASS;
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
pad_added_cb (GstElement * decodebin, GstPad * pad, PlayState * state)
Packit 971217
{
Packit 971217
  GstPadLinkReturn ret;
Packit 971217
  GstElement *fakesink;
Packit 971217
  GstPad *fakesink_pad;
Packit 971217
  StreamInfo *si;
Packit 971217
Packit 971217
  fakesink = gst_element_factory_make ("fakesink", NULL);
Packit 971217
#if 0
Packit 971217
  if (state->n_sinks == 1)
Packit 971217
    g_object_set (fakesink, "silent", FALSE, NULL);
Packit 971217
#endif
Packit 971217
Packit 971217
  si = g_new0 (StreamInfo, 1);
Packit 971217
  si->pad = g_object_ref (pad);
Packit 971217
  si->state = state;
Packit 971217
  si->fwd_times = g_array_new (FALSE, TRUE, sizeof (StreamTSRange));
Packit 971217
  si->bkwd_times = g_array_new (FALSE, TRUE, sizeof (StreamTSRange));
Packit 971217
Packit 971217
  gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
Packit 971217
      (GstPadProbeCallback) handle_output, si, (GDestroyNotify)
Packit 971217
      _destroy_stream_info);
Packit 971217
Packit 971217
  state->n_sinks++;
Packit 971217
  gst_bin_add (GST_BIN (state->pipe), fakesink);
Packit 971217
Packit 971217
  gst_element_sync_state_with_parent (fakesink);
Packit 971217
Packit 971217
  fakesink_pad = gst_element_get_static_pad (fakesink, "sink");
Packit 971217
Packit 971217
  ret = gst_pad_link (pad, fakesink_pad);
Packit 971217
  if (!GST_PAD_LINK_SUCCESSFUL (ret)) {
Packit 971217
    g_printerr ("Failed to link %s:%s to %s:%s (ret = %d)\n",
Packit 971217
        GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (fakesink_pad), ret);
Packit 971217
  } else {
Packit 971217
    GstCaps *caps = gst_pad_get_current_caps (pad);
Packit 971217
    gchar *s = gst_caps_to_string (caps);
Packit 971217
Packit 971217
    g_print ("Linked %s:%s to %s:%s caps %s\n", GST_DEBUG_PAD_NAME (pad),
Packit 971217
        GST_DEBUG_PAD_NAME (fakesink_pad), s);
Packit 971217
    gst_caps_unref (caps);
Packit 971217
    g_free (s);
Packit 971217
  }
Packit 971217
Packit 971217
  gst_object_unref (fakesink_pad);
Packit 971217
}
Packit 971217
Packit 971217
gint
Packit 971217
main (gint argc, gchar * argv[])
Packit 971217
{
Packit 971217
  PlayState state;
Packit 971217
  GstElement *decoder;
Packit 971217
  GstStateChangeReturn res;
Packit 971217
  GstBus *bus;
Packit 971217
Packit 971217
  gst_init (&argc, &argv);
Packit 971217
Packit 971217
  if (argc != 2) {
Packit 971217
    g_printerr ("Decode file from start to end.\n");
Packit 971217
    g_printerr ("Usage: %s URI\n\n", argv[0]);
Packit 971217
    return 1;
Packit 971217
  }
Packit 971217
  /* Start with zeroed-state */
Packit 971217
  memset (&state, 0, sizeof (PlayState));
Packit 971217
Packit 971217
  state.loop = g_main_loop_new (NULL, TRUE);
Packit 971217
  state.pipe = gst_pipeline_new ("pipeline");
Packit 971217
  state.fwd_play = TRUE;
Packit 971217
  g_mutex_init (&state.output_lock);
Packit 971217
Packit 971217
  bus = gst_pipeline_get_bus (GST_PIPELINE (state.pipe));
Packit 971217
  gst_bus_add_signal_watch (bus);
Packit 971217
Packit 971217
  g_signal_connect (bus, "message::eos", G_CALLBACK (eos_cb), &state);
Packit 971217
  g_signal_connect (bus, "message::error", G_CALLBACK (error_cb), &state);
Packit 971217
  g_signal_connect (bus, "message::warning", G_CALLBACK (warning_cb), NULL);
Packit 971217
  g_signal_connect (bus, "message::state-changed", G_CALLBACK (state_cb),
Packit 971217
      &state);
Packit 971217
Packit 971217
#if 0
Packit 971217
  g_signal_connect (state.pipe, "deep-notify",
Packit 971217
      G_CALLBACK (gst_object_default_deep_notify), NULL);
Packit 971217
#endif
Packit 971217
Packit 971217
  decoder = gst_element_factory_make ("uridecodebin", "decoder");
Packit 971217
  g_assert (decoder);
Packit 971217
  gst_bin_add (GST_BIN (state.pipe), decoder);
Packit 971217
Packit 971217
  if (argv[1] && strstr (argv[1], "://") != NULL) {
Packit 971217
    g_object_set (G_OBJECT (decoder), "uri", argv[1], NULL);
Packit 971217
  } else if (argv[1]) {
Packit 971217
    gchar *uri = g_strdup_printf ("file://%s", argv[1]);
Packit 971217
    g_object_set (G_OBJECT (decoder), "uri", uri, NULL);
Packit 971217
    g_free (uri);
Packit 971217
  } else {
Packit 971217
    g_print ("Usage: %s <filename|uri>\n", argv[0]);
Packit 971217
    return -1;
Packit 971217
  }
Packit 971217
Packit 971217
  g_signal_connect (decoder, "pad-added", G_CALLBACK (pad_added_cb), &state);
Packit 971217
Packit 971217
  res = gst_element_set_state (state.pipe, GST_STATE_PLAYING);
Packit 971217
  if (res == GST_STATE_CHANGE_FAILURE) {
Packit 971217
    g_print ("could not play\n");
Packit 971217
    return -1;
Packit 971217
  }
Packit 971217
Packit 971217
  g_main_loop_run (state.loop);
Packit 971217
Packit 971217
  /* tidy up */
Packit 971217
  gst_element_set_state (state.pipe, GST_STATE_NULL);
Packit 971217
  gst_object_unref (state.pipe);
Packit 971217
  gst_object_unref (bus);
Packit 971217
Packit 971217
  return 0;
Packit 971217
}