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.
This commit is contained in:
Mark Nauwelaerts 2011-06-06 12:48:23 +02:00
parent de1f593537
commit 99944a5a81
2 changed files with 115 additions and 1 deletions

View file

@ -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;

View file

@ -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