Commit 99944a5a authored by Mark Nauwelaerts's avatar Mark Nauwelaerts
Browse files

oggmux: determine granulepos metadata using stream mapper whenever possible

... which unfortunately is not the case for all types, but at least so for
most common ones.
parent de1f5935
......@@ -82,11 +82,13 @@ enum
/* set to 0.5 seconds by default */
#define DEFAULT_MAX_DELAY G_GINT64_CONSTANT(500000000)
#define DEFAULT_MAX_PAGE_DELAY G_GINT64_CONSTANT(500000000)
#define DEFAULT_MAX_TOLERANCE G_GINT64_CONSTANT(40000000)
enum
{
ARG_0,
ARG_MAX_DELAY,
ARG_MAX_PAGE_DELAY,
ARG_MAX_TOLERANCE
};
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
......@@ -204,6 +206,11 @@ gst_ogg_mux_class_init (GstOggMuxClass * klass)
"Maximum delay for sending out a page", 0, G_MAXUINT64,
DEFAULT_MAX_PAGE_DELAY,
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, ARG_MAX_TOLERANCE,
g_param_spec_uint64 ("max-tolerance", "Max time tolerance",
"Maximum timestamp difference for maintaining perfect granules",
0, G_MAXUINT64, DEFAULT_MAX_TOLERANCE,
(GParamFlags) G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gstelement_class->change_state = gst_ogg_mux_change_state;
......@@ -257,6 +264,7 @@ gst_ogg_mux_init (GstOggMux * ogg_mux)
ogg_mux->max_delay = DEFAULT_MAX_DELAY;
ogg_mux->max_page_delay = DEFAULT_MAX_PAGE_DELAY;
ogg_mux->max_tolerance = DEFAULT_MAX_TOLERANCE;
gst_ogg_mux_clear (ogg_mux);
}
......@@ -446,6 +454,8 @@ gst_ogg_mux_request_new_pad (GstElement * element,
oggpad->pagebuffers = g_queue_new ();
oggpad->map.headers = NULL;
oggpad->map.queued = NULL;
oggpad->next_granule = 0;
oggpad->keyframe_granule = -1;
gst_segment_init (&oggpad->segment, GST_FORMAT_TIME);
......@@ -774,6 +784,13 @@ gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
GstBuffer * buf)
{
GstClockTime time;
gint64 duration, granule, limit;
GstClockTime next_time;
GstClockTimeDiff diff;
ogg_packet packet;
/* ensure messing with metadata is ok */
buf = gst_buffer_make_metadata_writable (buf);
/* convert time to running time, so we need no longer bother about that */
time = GST_BUFFER_TIMESTAMP (buf);
......@@ -783,12 +800,90 @@ gst_ogg_mux_decorate_buffer (GstOggMux * ogg_mux, GstOggPadData * pad,
gst_buffer_unref (buf);
return NULL;
} else {
buf = gst_buffer_make_metadata_writable (buf);
GST_BUFFER_TIMESTAMP (buf) = time;
}
}
/* now come up with granulepos stuff corresponding to time */
if (!pad->have_type ||
pad->map.granulerate_n <= 0 || pad->map.granulerate_d <= 0)
goto no_granule;
packet.packet = GST_BUFFER_DATA (buf);
packet.bytes = GST_BUFFER_SIZE (buf);
duration = gst_ogg_stream_get_packet_duration (&pad->map, &packet);
/* give up if no duration can be determined, relying on upstream */
if (G_UNLIKELY (duration < 0)) {
/* well, if some day we really could handle sparse input ... */
if (pad->map.is_sparse) {
limit = 1;
diff = 2;
goto resync;
}
GST_WARNING_OBJECT (pad->collect.pad,
"failed to determine packet duration");
goto no_granule;
}
GST_LOG_OBJECT (pad->collect.pad, "buffer ts %" GST_TIME_FORMAT
", duration %" GST_TIME_FORMAT ", granule duration %" G_GINT64_FORMAT,
GST_TIME_ARGS (time), GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
duration);
/* determine granule corresponding to time,
* using the inverse of oggdemux' granule -> time */
/* see if interpolated granule matches good enough */
granule = pad->next_granule;
next_time = gst_ogg_stream_granule_to_time (&pad->map, pad->next_granule);
diff = GST_CLOCK_DIFF (next_time, time);
/* we tolerate deviation up to configured or within granule granularity */
limit = gst_ogg_stream_granule_to_time (&pad->map, 1) / 2;
limit = MAX (limit, ogg_mux->max_tolerance);
GST_LOG_OBJECT (pad->collect.pad, "expected granule %" G_GINT64_FORMAT " == "
"time %" GST_TIME_FORMAT " --> ts diff %" GST_TIME_FORMAT
" < tolerance %" GST_TIME_FORMAT " (?)",
granule, GST_TIME_ARGS (next_time), GST_TIME_ARGS (ABS (diff)),
GST_TIME_ARGS (limit));
resync:
/* if not good enough, determine granule based on time */
if (diff > limit || diff < -limit) {
granule = gst_util_uint64_scale_round (time, pad->map.granulerate_n,
GST_SECOND * pad->map.granulerate_d);
GST_DEBUG_OBJECT (pad->collect.pad,
"resyncing to determined granule %" G_GINT64_FORMAT, granule);
}
if (pad->map.is_ogm || pad->map.is_sparse) {
pad->next_granule = granule;
} else {
granule += duration;
pad->next_granule = granule;
}
/* track previous keyframe */
if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
pad->keyframe_granule = granule;
/* determine corresponding time and granulepos */
GST_BUFFER_OFFSET (buf) = gst_ogg_stream_granule_to_time (&pad->map, granule);
GST_BUFFER_OFFSET_END (buf) =
gst_ogg_stream_granule_to_granulepos (&pad->map, granule,
pad->keyframe_granule);
return buf;
/* ERRORS */
no_granule:
{
GST_DEBUG_OBJECT (pad->collect.pad, "could not determine granulepos, "
"falling back to upstream provided metadata");
return buf;
}
}
......@@ -894,6 +989,15 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux)
"got data buffer in control state, switching to data mode");
/* this is a data buffer so switch to data state */
pad->state = GST_OGG_PAD_STATE_DATA;
/* check if this type of stream allows generating granulepos
* metadata here, if not, upstream will have to provide */
if (gst_ogg_stream_granule_to_granulepos (&pad->map, 1, 1) < 0) {
GST_WARNING_OBJECT (data->pad, "can not generate metadata; "
"relying on upstream");
/* disable metadata code path, otherwise not used anyway */
pad->map.granulerate_n = 0;
}
}
}
......@@ -1683,6 +1787,9 @@ gst_ogg_mux_get_property (GObject * object,
case ARG_MAX_PAGE_DELAY:
g_value_set_uint64 (value, ogg_mux->max_page_delay);
break;
case ARG_MAX_TOLERANCE:
g_value_set_uint64 (value, ogg_mux->max_tolerance);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -1704,6 +1811,9 @@ gst_ogg_mux_set_property (GObject * object,
case ARG_MAX_PAGE_DELAY:
ogg_mux->max_page_delay = g_value_get_uint64 (value);
break;
case ARG_MAX_TOLERANCE:
ogg_mux->max_tolerance = g_value_get_uint64 (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......
......@@ -82,6 +82,9 @@ typedef struct
gboolean prev_delta; /* was the previous buffer a delta frame */
gboolean data_pushed; /* whether we pushed data already */
gint64 next_granule; /* expected granule of next buffer ts */
gint64 keyframe_granule; /* granule of last preceding keyframe */
GstPadEventFunction collect_event;
gboolean always_flush_page;
......@@ -123,6 +126,7 @@ struct _GstOggMux
guint64 max_delay;
guint64 max_page_delay;
guint64 max_tolerance;
GstOggPadData *delta_pad; /* when a delta frame is detected on a stream, we mark
pages as delta frames up to the page that has the
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment