mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-09-02 02:03:54 +00:00
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:
parent
3cb1180ff4
commit
88cda98939
2 changed files with 189 additions and 7 deletions
|
@ -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
|
useful to make sure muxers create readable files when a muxing pipeline is
|
||||||
shut down forcefully via Control-C.
|
shut down forcefully via Control-C.
|
||||||
.TP 8
|
.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
|
.B \-o FILE, \-\-output=FILE
|
||||||
Save XML representation of pipeline to FILE and exit (DEPRECATED, DO NOT USE)
|
Save XML representation of pipeline to FILE and exit (DEPRECATED, DO NOT USE)
|
||||||
.TP 8
|
.TP 8
|
||||||
|
|
|
@ -250,6 +250,159 @@ fault_setup (void)
|
||||||
}
|
}
|
||||||
#endif /* DISABLE_FAULT_HANDLER */
|
#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
|
static void
|
||||||
print_error_message (GstMessage * msg)
|
print_error_message (GstMessage * msg)
|
||||||
{
|
{
|
||||||
|
@ -741,6 +894,7 @@ main (int argc, char *argv[])
|
||||||
gboolean no_sigusr_handler = FALSE;
|
gboolean no_sigusr_handler = FALSE;
|
||||||
gboolean trace = FALSE;
|
gboolean trace = FALSE;
|
||||||
gboolean eos_on_shutdown = FALSE;
|
gboolean eos_on_shutdown = FALSE;
|
||||||
|
gboolean check_index = FALSE;
|
||||||
gchar *savefile = NULL;
|
gchar *savefile = NULL;
|
||||||
gchar *exclude_args = NULL;
|
gchar *exclude_args = NULL;
|
||||||
#ifndef GST_DISABLE_OPTION_PARSING
|
#ifndef GST_DISABLE_OPTION_PARSING
|
||||||
|
@ -767,12 +921,16 @@ main (int argc, char *argv[])
|
||||||
N_("Print alloc trace (if enabled at compile time)"), NULL},
|
N_("Print alloc trace (if enabled at compile time)"), NULL},
|
||||||
{"eos-on-shutdown", 'e', 0, G_OPTION_ARG_NONE, &eos_on_shutdown,
|
{"eos-on-shutdown", 'e', 0, G_OPTION_ARG_NONE, &eos_on_shutdown,
|
||||||
N_("Force EOS on sources before shutting the pipeline down"), NULL},
|
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,
|
GST_TOOLS_GOPTION_VERSION,
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
GOptionContext *ctx;
|
GOptionContext *ctx;
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
GstIndex *index;
|
||||||
|
GPtrArray *index_stats = NULL;
|
||||||
gchar **argvn;
|
gchar **argvn;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
gint res = 0;
|
gint res = 0;
|
||||||
|
@ -893,6 +1051,21 @@ main (int argc, char *argv[])
|
||||||
pipeline = real_pipeline;
|
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);
|
bus = gst_element_get_bus (pipeline);
|
||||||
gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) pipeline);
|
gst_bus_set_sync_handler (bus, bus_sync_handler, (gpointer) pipeline);
|
||||||
gst_object_unref (bus);
|
gst_object_unref (bus);
|
||||||
|
@ -985,6 +1158,11 @@ main (int argc, char *argv[])
|
||||||
gst_element_set_state (pipeline, GST_STATE_READY);
|
gst_element_set_state (pipeline, GST_STATE_READY);
|
||||||
gst_element_get_state (pipeline, &state, &pending, GST_CLOCK_TIME_NONE);
|
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:
|
end:
|
||||||
PRINT (_("Setting pipeline to NULL ...\n"));
|
PRINT (_("Setting pipeline to NULL ...\n"));
|
||||||
gst_element_set_state (pipeline, GST_STATE_NULL);
|
gst_element_set_state (pipeline, GST_STATE_NULL);
|
||||||
|
|
Loading…
Reference in a new issue