matroskademux: make max backtrack distance for keyframe search configurable

Add property instead of hardcoding it in the code.

In some scenarios such as CCTV variable fps and extra long GOPs are
used to minimise storage space, for example. In those cases there might
not be any keyframes for many minutes, so provide a property to override
the max allowed distance.

https://bugzilla.gnome.org/show_bug.cgi?id=790696
This commit is contained in:
Tim-Philipp Müller 2018-08-15 12:14:24 +01:00
parent 2990f0730a
commit 2d6efbbae2
2 changed files with 44 additions and 19 deletions

View file

@ -85,11 +85,13 @@ enum
PROP_0, PROP_0,
PROP_METADATA, PROP_METADATA,
PROP_STREAMINFO, PROP_STREAMINFO,
PROP_MAX_GAP_TIME PROP_MAX_GAP_TIME,
PROP_MAX_BACKTRACK_DISTANCE
}; };
#define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND) #define DEFAULT_MAX_GAP_TIME (2 * GST_SECOND)
#define INVALID_DATA_THRESHOLD (2 * 1024 * 1024) #define DEFAULT_MAX_BACKTRACK_DISTANCE 30
#define INVALID_DATA_THRESHOLD (2 * 1024 * 1024)
static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
@ -217,6 +219,15 @@ gst_matroska_demux_class_init (GstMatroskaDemuxClass * klass)
"gaps longer than this (0 = disabled).", 0, G_MAXUINT64, "gaps longer than this (0 = disabled).", 0, G_MAXUINT64,
DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); DEFAULT_MAX_GAP_TIME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_MAX_BACKTRACK_DISTANCE,
g_param_spec_uint ("max-backtrack-distance",
"Maximum backtrack distance",
"Maximum backtrack distance in seconds when seeking without "
"and index in pull mode and search for a keyframe "
"(0 = disable backtracking).",
0, G_MAXUINT, DEFAULT_MAX_BACKTRACK_DISTANCE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = gstelement_class->change_state =
GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state); GST_DEBUG_FUNCPTR (gst_matroska_demux_change_state);
gstelement_class->send_event = gstelement_class->send_event =
@ -264,6 +275,7 @@ gst_matroska_demux_init (GstMatroskaDemux * demux)
/* property defaults */ /* property defaults */
demux->max_gap_time = DEFAULT_MAX_GAP_TIME; demux->max_gap_time = DEFAULT_MAX_GAP_TIME;
demux->max_backtrack_distance = DEFAULT_MAX_BACKTRACK_DISTANCE;
GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE); GST_OBJECT_FLAG_SET (demux, GST_ELEMENT_FLAG_INDEXABLE);
@ -2082,12 +2094,6 @@ bit_reader_skip_ebml_num (GstBitReader * br)
* (random value, mostly for sanity checking) */ * (random value, mostly for sanity checking) */
#define MAX_CLUSTER_INFO_PROBE_LENGTH 256 #define MAX_CLUSTER_INFO_PROBE_LENGTH 256
/* Don't scan back more than this much in time from the cluster we originally
* landed on. This is mostly a sanity check in case a file always has keyframes
* in the middle of clusters and never at the beginning. Without this we would
* always scan back to the beginning of the file in that case. */
#define MAX_CLUSTER_BACKTRACK_SECS 30
static gboolean static gboolean
gst_matroska_demux_peek_cluster_info (GstMatroskaDemux * demux, gst_matroska_demux_peek_cluster_info (GstMatroskaDemux * demux,
ClusterInfo * cluster, guint64 offset) ClusterInfo * cluster, guint64 offset)
@ -2249,13 +2255,18 @@ gst_matroska_demux_scan_back_for_keyframe_cluster (GstMatroskaDemux * demux,
break; break;
} }
/* Don't scan back more than this much in time from the cluster we
* originally landed on. This is mostly a sanity check in case a file
* always has keyframes in the middle of clusters and never at the
* beginning. Without this we would always scan back to the beginning
* of the file in that case. */
if (cluster.time != GST_CLOCK_TIME_NONE) { if (cluster.time != GST_CLOCK_TIME_NONE) {
GstClockTimeDiff distance = GST_CLOCK_DIFF (cluster.time, *cluster_time); GstClockTimeDiff distance = GST_CLOCK_DIFF (cluster.time, *cluster_time);
if (distance < 0 || distance > MAX_CLUSTER_BACKTRACK_SECS * GST_SECOND) { if (distance < 0 || distance > demux->max_backtrack_distance * GST_SECOND) {
GST_DEBUG_OBJECT (demux, "Haven't found cluster with keyframe within " GST_DEBUG_OBJECT (demux, "Haven't found cluster with keyframe within "
"%u secs of original seek target cluster, stopping", "%u secs of original seek target cluster, stopping",
MAX_CLUSTER_BACKTRACK_SECS); demux->max_backtrack_distance);
break; break;
} }
} }
@ -2495,15 +2506,17 @@ retry:
/* If we have video and can easily backtrack, check if we landed on a cluster /* If we have video and can easily backtrack, check if we landed on a cluster
* that starts with a keyframe - and if not backtrack until we find one that * that starts with a keyframe - and if not backtrack until we find one that
* does. */ * does. */
if (demux->have_nonintraonly_v_streams && demux->seen_cluster_prevsize) { if (demux->have_nonintraonly_v_streams && demux->max_backtrack_distance > 0) {
if (gst_matroska_demux_scan_back_for_keyframe_cluster (demux, if (demux->seen_cluster_prevsize) {
&cluster_offset, &cluster_time)) { if (gst_matroska_demux_scan_back_for_keyframe_cluster (demux,
GST_INFO_OBJECT (demux, "Adjusted cluster to %" GST_TIME_FORMAT " @ " &cluster_offset, &cluster_time)) {
"%" G_GUINT64_FORMAT, GST_TIME_ARGS (cluster_time), cluster_offset); GST_INFO_OBJECT (demux, "Adjusted cluster to %" GST_TIME_FORMAT " @ "
"%" G_GUINT64_FORMAT, GST_TIME_ARGS (cluster_time), cluster_offset);
}
} else {
GST_FIXME_OBJECT (demux, "implement scanning back to prev cluster "
"without cluster prev size field");
} }
} else if (demux->have_nonintraonly_v_streams) {
GST_FIXME_OBJECT (demux, "implement scanning back to prev cluster without "
"cluster prev size field");
} }
entry = g_new0 (GstMatroskaIndex, 1); entry = g_new0 (GstMatroskaIndex, 1);
@ -6716,6 +6729,11 @@ gst_matroska_demux_set_property (GObject * object,
demux->max_gap_time = g_value_get_uint64 (value); demux->max_gap_time = g_value_get_uint64 (value);
GST_OBJECT_UNLOCK (demux); GST_OBJECT_UNLOCK (demux);
break; break;
case PROP_MAX_BACKTRACK_DISTANCE:
GST_OBJECT_LOCK (demux);
demux->max_backtrack_distance = g_value_get_uint (value);
GST_OBJECT_UNLOCK (demux);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -6737,6 +6755,11 @@ gst_matroska_demux_get_property (GObject * object,
g_value_set_uint64 (value, demux->max_gap_time); g_value_set_uint64 (value, demux->max_gap_time);
GST_OBJECT_UNLOCK (demux); GST_OBJECT_UNLOCK (demux);
break; break;
case PROP_MAX_BACKTRACK_DISTANCE:
GST_OBJECT_LOCK (demux);
g_value_set_uint (value, demux->max_backtrack_distance);
GST_OBJECT_UNLOCK (demux);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;

View file

@ -116,6 +116,8 @@ typedef struct _GstMatroskaDemux {
* cluster has it, all but * cluster has it, all but
* the first will have it. */ * the first will have it. */
guint max_backtrack_distance; /* in seconds (0 = don't backtrack) */
/* gap handling */ /* gap handling */
guint64 max_gap_time; guint64 max_gap_time;