gst-launch: add index support

When option "-i" is given, set an index object on the pipeline and compute
statistics for all index writers. Print a sumary when shutting down the
pipeline.
This commit is contained in:
Stefan Kost 2011-02-21 11:24:45 +02:00
parent 3cb1180ff4
commit 88cda98939
2 changed files with 189 additions and 7 deletions

View file

@ -4,7 +4,7 @@ gst\-launch \- build and run a GStreamer pipeline
.SH "SYNOPSIS"
\fBgst\-launch\fR \fI[OPTION...]\fR PIPELINE\-DESCRIPTION
.SH "DESCRIPTION"
.LP
.LP
\fIgst\-launch\fP is a tool that builds and runs basic
\fIGStreamer\fP pipelines.
@ -44,6 +44,10 @@ Force an EOS event on sources before shutting the pipeline down. This is
useful to make sure muxers create readable files when a muxing pipeline is
shut down forcefully via Control-C.
.TP 8
.B \-i, \-\-index
Gather and print index statistics. This is mostly useful for playback or
recording pipelines.
.TP 8
.B \-o FILE, \-\-output=FILE
Save XML representation of pipeline to FILE and exit (DEPRECATED, DO NOT USE)
.TP 8
@ -114,7 +118,7 @@ plugins to preload is to use the environment variable GST_PLUGIN_PATH
.SH "PIPELINE DESCRIPTION"
A pipeline consists \fIelements\fR and \fIlinks\fR. \fIElements\fR can be put
A pipeline consists \fIelements\fR and \fIlinks\fR. \fIElements\fR can be put
into \fIbins\fR of different sorts. \fIElements\fR, \fIlinks\fR and \fIbins\fR
can be specified in a pipeline description in any order.
@ -138,7 +142,7 @@ Enumeration properties can be set by name, nick or value.
\fI[BINTYPE.]\fR ( \fI[PROPERTY1 ...]\fR PIPELINE-DESCRIPTION )
.br
Specifies that a bin of type BINTYPE is created and the given properties are
Specifies that a bin of type BINTYPE is created and the given properties are
set. Every element between the braces is put into the bin. Please note the dot
that has to be used after the BINTYPE. You will almost never need this
functionality, it is only really useful for applications using the
@ -194,11 +198,11 @@ and the type can have the following case-insensitive values:
.br
- \fBl\fR or \fBlist\fR for lists
.br
If no type was given, the following order is tried: integer, float, boolean,
If no type was given, the following order is tried: integer, float, boolean,
string.
.br
Integer values must be parsable by \fBstrtol()\fP, floats by \fBstrtod()\fP. FOURCC values may
either be integers or strings. Boolean values are (case insensitive) \fIyes\fR,
either be integers or strings. Boolean values are (case insensitive) \fIyes\fR,
\fIno\fR, \fItrue\fR or \fIfalse\fR and may like strings be escaped with " or '.
.br
Ranges are in this format: [ VALUE, VALUE ]
@ -390,7 +394,7 @@ automatically. To make this even easier, you can use the playbin element:
.B
gst\-launch playbin uri=file:///home/joe/foo.avi
.br
.B Filtered connections
@ -438,7 +442,7 @@ Specifies a list of directories to scan for additional plugins.
These take precedence over the system plugins.
.TP
\fBGST_PLUGIN_SYSTEM_PATH\fR
Specifies a list of plugins that are always loaded by default. If not set,
Specifies a list of plugins that are always loaded by default. If not set,
this defaults to the system-installed path, and the plugins installed in the
user's home directory
.TP

View file

@ -250,6 +250,159 @@ fault_setup (void)
}
#endif /* DISABLE_FAULT_HANDLER */
typedef struct _GstIndexStats
{
gint id;
gchar *desc;
guint num_frames;
guint num_keyframes;
guint num_dltframes;
GstClockTime last_keyframe;
GstClockTime last_dltframe;
GstClockTime min_keyframe_gap;
GstClockTime max_keyframe_gap;
GstClockTime avg_keyframe_gap;
} GstIndexStats;
static void
entry_added (GstIndex * index, GstIndexEntry * entry, gpointer user_data)
{
GPtrArray *index_stats = (GPtrArray *) user_data;
GstIndexStats *s;
switch (entry->type) {
case GST_INDEX_ENTRY_ID:
/* we have a new writer */
GST_DEBUG_OBJECT (index, "id %d: describes writer %s", entry->id,
GST_INDEX_ID_DESCRIPTION (entry));
if (entry->id >= index_stats->len) {
g_ptr_array_set_size (index_stats, entry->id + 1);
}
s = g_new (GstIndexStats, 1);
s->id = entry->id;
s->desc = g_strdup (GST_INDEX_ID_DESCRIPTION (entry));
s->num_frames = s->num_keyframes = s->num_dltframes = 0;
s->last_keyframe = s->last_dltframe = GST_CLOCK_TIME_NONE;
s->min_keyframe_gap = s->max_keyframe_gap = s->avg_keyframe_gap =
GST_CLOCK_TIME_NONE;
g_ptr_array_index (index_stats, entry->id) = s;
break;
case GST_INDEX_ENTRY_FORMAT:
/* have not found any code calling this */
GST_DEBUG_OBJECT (index, "id %d: registered format %d for %s\n",
entry->id, GST_INDEX_FORMAT_FORMAT (entry),
GST_INDEX_FORMAT_KEY (entry));
break;
case GST_INDEX_ENTRY_ASSOCIATION:
{
gint64 ts;
GstAssocFlags flags = GST_INDEX_ASSOC_FLAGS (entry);
s = g_ptr_array_index (index_stats, entry->id);
gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &ts);
if (flags & GST_ASSOCIATION_FLAG_KEY_UNIT) {
s->num_keyframes++;
if (GST_CLOCK_TIME_IS_VALID (ts)) {
if (GST_CLOCK_TIME_IS_VALID (s->last_keyframe)) {
GstClockTimeDiff d = GST_CLOCK_DIFF (s->last_keyframe, ts);
if (G_UNLIKELY (d < 0)) {
GST_WARNING ("received out-of-order keyframe at %"
GST_TIME_FORMAT, GST_TIME_ARGS (ts));
/* FIXME: does it still make sense to use that for the statistics */
d = GST_CLOCK_DIFF (ts, s->last_keyframe);
}
if (GST_CLOCK_TIME_IS_VALID (s->min_keyframe_gap)) {
if (d < s->min_keyframe_gap)
s->min_keyframe_gap = d;
} else {
s->min_keyframe_gap = d;
}
if (GST_CLOCK_TIME_IS_VALID (s->max_keyframe_gap)) {
if (d > s->max_keyframe_gap)
s->max_keyframe_gap = d;
} else {
s->max_keyframe_gap = d;
}
if (GST_CLOCK_TIME_IS_VALID (s->avg_keyframe_gap)) {
s->avg_keyframe_gap = (d + s->num_frames * s->avg_keyframe_gap) /
(s->num_frames + 1);
} else {
s->avg_keyframe_gap = d;
}
}
s->last_keyframe = ts;
}
}
if (flags & GST_ASSOCIATION_FLAG_DELTA_UNIT) {
s->num_dltframes++;
if (GST_CLOCK_TIME_IS_VALID (ts)) {
s->last_dltframe = ts;
}
}
s->num_frames++;
break;
}
default:
break;
}
}
/* print statistics from the entry_added callback, free the entries */
static void
print_index_stats (GPtrArray * index_stats)
{
gint i;
if (index_stats->len) {
g_print (_("Index statistics\n"));
}
for (i = 0; i < index_stats->len; i++) {
GstIndexStats *s = g_ptr_array_index (index_stats, i);
if (s) {
g_print ("id %d, %s\n", s->id, s->desc);
if (s->num_frames) {
GstClockTime last_frame = s->last_keyframe;
if (GST_CLOCK_TIME_IS_VALID (s->last_dltframe)) {
if (!GST_CLOCK_TIME_IS_VALID (last_frame) ||
(s->last_dltframe > last_frame))
last_frame = s->last_dltframe;
}
if (GST_CLOCK_TIME_IS_VALID (last_frame)) {
g_print (" total time = %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (last_frame));
}
g_print (" frame/keyframe rate = %u / %u = ", s->num_frames,
s->num_keyframes);
if (s->num_keyframes)
g_print ("%lf\n", s->num_frames / (gdouble) s->num_keyframes);
else
g_print ("-\n");
if (s->num_keyframes) {
g_print (" min/avg/max keyframe gap = %" GST_TIME_FORMAT ", %"
GST_TIME_FORMAT ", %" GST_TIME_FORMAT "\n",
GST_TIME_ARGS (s->min_keyframe_gap),
GST_TIME_ARGS (s->avg_keyframe_gap),
GST_TIME_ARGS (s->max_keyframe_gap));
}
} else {
g_print (" no stats\n");
}
g_free (s->desc);
g_free (s);
}
}
}
static void
print_error_message (GstMessage * msg)
{
@ -741,6 +894,7 @@ main (int argc, char *argv[])
gboolean no_sigusr_handler = FALSE;
gboolean trace = FALSE;
gboolean eos_on_shutdown = FALSE;
gboolean check_index = FALSE;
gchar *savefile = NULL;
gchar *exclude_args = NULL;
#ifndef GST_DISABLE_OPTION_PARSING
@ -767,12 +921,16 @@ main (int argc, char *argv[])
N_("Print alloc trace (if enabled at compile time)"), NULL},
{"eos-on-shutdown", 'e', 0, G_OPTION_ARG_NONE, &eos_on_shutdown,
N_("Force EOS on sources before shutting the pipeline down"), NULL},
{"index", 'i', 0, G_OPTION_ARG_NONE, &check_index,
N_("Gather and print index statistics"), NULL},
GST_TOOLS_GOPTION_VERSION,
{NULL}
};
GOptionContext *ctx;
GError *err = NULL;
#endif
GstIndex *index;
GPtrArray *index_stats = NULL;
gchar **argvn;
GError *error = NULL;
gint res = 0;
@ -893,6 +1051,21 @@ main (int argc, char *argv[])
pipeline = real_pipeline;
}
if (check_index) {
/* gst_index_new() creates a null-index, it does not store anything, but
* the entry-added signal works and this is what we use to build the
* statistics */
index = gst_index_new ();
if (index) {
index_stats = g_ptr_array_new ();
g_signal_connect (G_OBJECT (index), "entry-added",
G_CALLBACK (entry_added), index_stats);
g_object_set (G_OBJECT (index), "resolver", GST_INDEX_RESOLVER_GTYPE,
NULL);
gst_element_set_index (pipeline, index);
}
}
bus = gst_element_get_bus (pipeline);
gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) pipeline);
gst_object_unref (bus);
@ -985,6 +1158,11 @@ main (int argc, char *argv[])
gst_element_set_state (pipeline, GST_STATE_READY);
gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
if (check_index) {
print_index_stats (index_stats);
g_ptr_array_free (index_stats, TRUE);
}
end:
PRINT (_("Setting pipeline to NULL ...\n"));
gst_element_set_state (pipeline, GST_STATE_NULL);