gst/wavparse/gstwavparse.*: Implement seek-query. Refactor duration calculations. Appropriate use of uint64_scale_int...

Original commit message from CVS:
* gst/wavparse/gstwavparse.c:
* gst/wavparse/gstwavparse.h:
Implement seek-query. Refactor duration calculations. Appropriate use
of uint64_scale_int and uint64_scale. Move repeadedly calculated stuff
out of loops.
This commit is contained in:
Stefan Kost 2007-09-04 07:58:36 +00:00
parent c1b2242e77
commit 43b18b3f43
3 changed files with 159 additions and 70 deletions

View file

@ -1,3 +1,11 @@
2007-09-04 Stefan Kost <ensonic@users.sf.net>
* gst/wavparse/gstwavparse.c:
* gst/wavparse/gstwavparse.h:
Implement seek-query. Refactor duration calculations. Appropriate use
of uint64_scale_int and uint64_scale. Move repeadedly calculated stuff
out of loops.
2007-09-03 Stefan Kost <ensonic@users.sf.net> 2007-09-03 Stefan Kost <ensonic@users.sf.net>
* gst/avi/gstavidemux.c: * gst/avi/gstavidemux.c:

View file

@ -175,6 +175,7 @@ gst_wavparse_reset (GstWavParse * wavparse)
wavparse->dataleft = 0; wavparse->dataleft = 0;
wavparse->datasize = 0; wavparse->datasize = 0;
wavparse->datastart = 0; wavparse->datastart = 0;
wavparse->duration = 0;
wavparse->got_fmt = FALSE; wavparse->got_fmt = FALSE;
wavparse->first = TRUE; wavparse->first = TRUE;
@ -243,9 +244,21 @@ gst_wavparse_create_sourcepad (GstWavParse * wavparse)
#define uint64_scale_modulo(val, nom, denom) \ #define uint64_scale_modulo(val, nom, denom) \
((val % denom) * (nom % denom) % denom) ((val % denom) * (nom % denom) % denom)
/* Like gst_util_uint64_scale_int, but performs ceiling division. */ /* Like gst_util_uint64_scale, but performs ceiling division. */
static guint64 static guint64
uint64_ceiling_scale_int (guint64 val, gint num, gint denom) uint64_ceiling_scale_int (guint64 val, gint num, gint denom)
{
guint64 result = gst_util_uint64_scale (val, num, denom);
if (uint64_scale_modulo (val, num, denom) == 0)
return result;
else
return result + 1;
}
/* Like gst_util_uint64_scale, but performs ceiling division. */
static guint64
uint64_ceiling_scale (guint64 val, guint64 num, guint64 denom)
{ {
guint64 result = gst_util_uint64_scale_int (val, num, denom); guint64 result = gst_util_uint64_scale_int (val, num, denom);
@ -768,6 +781,7 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
} else { } else {
GST_DEBUG_OBJECT (wav, "doing seek without event"); GST_DEBUG_OBJECT (wav, "doing seek without event");
flags = 0; flags = 0;
rate = 1.0;
cur_type = GST_SEEK_TYPE_SET; cur_type = GST_SEEK_TYPE_SET;
stop_type = GST_SEEK_TYPE_SET; stop_type = GST_SEEK_TYPE_SET;
} }
@ -821,11 +835,16 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
/* bring offset to bytes, if the bps is 0, we have the segment in BYTES and /* bring offset to bytes, if the bps is 0, we have the segment in BYTES and
* we can just copy the last_stop. If not, we use the bps to convert TIME to * we can just copy the last_stop. If not, we use the bps to convert TIME to
* bytes. */ * bytes. */
if (wav->bps) if (wav->bps > 0)
wav->offset = wav->offset =
uint64_ceiling_scale_int (seeksegment.last_stop, wav->bps, uint64_ceiling_scale (seeksegment.last_stop, (guint64) wav->bps,
GST_SECOND); GST_SECOND);
else else if (wav->fact) {
guint64 bps =
gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
wav->offset =
uint64_ceiling_scale (seeksegment.last_stop, bps, GST_SECOND);
} else
wav->offset = seeksegment.last_stop; wav->offset = seeksegment.last_stop;
GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset); GST_LOG_OBJECT (wav, "offset=%" G_GUINT64_FORMAT, wav->offset);
wav->offset -= (wav->offset % wav->bytes_per_sample); wav->offset -= (wav->offset % wav->bytes_per_sample);
@ -838,9 +857,14 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
} }
if (stop_type != GST_SEEK_TYPE_NONE) { if (stop_type != GST_SEEK_TYPE_NONE) {
if (wav->bps) if (wav->bps > 0)
wav->end_offset = uint64_ceiling_scale_int (stop, wav->bps, GST_SECOND); wav->end_offset =
else uint64_ceiling_scale (stop, (guint64) wav->bps, GST_SECOND);
else if (wav->fact) {
guint64 bps =
gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
wav->end_offset = uint64_ceiling_scale (stop, bps, GST_SECOND);
} else
wav->end_offset = stop; wav->end_offset = stop;
GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset); GST_LOG_OBJECT (wav, "end_offset=%" G_GUINT64_FORMAT, wav->end_offset);
wav->end_offset -= (wav->end_offset % wav->bytes_per_sample); wav->end_offset -= (wav->end_offset % wav->bytes_per_sample);
@ -863,9 +887,9 @@ gst_wavparse_perform_seek (GstWavParse * wav, GstEvent * event)
wav->dataleft = wav->end_offset - wav->offset; wav->dataleft = wav->end_offset - wav->offset;
GST_DEBUG_OBJECT (wav, GST_DEBUG_OBJECT (wav,
"seek: offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT ", segment %" "seek: rate %lf, offset %" G_GUINT64_FORMAT ", end %" G_GUINT64_FORMAT
GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, wav->offset, wav->end_offset, ", segment %" GST_TIME_FORMAT " -- %" GST_TIME_FORMAT, rate, wav->offset,
GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop)); wav->end_offset, GST_TIME_ARGS (seeksegment.start), GST_TIME_ARGS (stop));
/* prepare for streaming again */ /* prepare for streaming again */
if (wav->srcpad) { if (wav->srcpad) {
@ -1002,19 +1026,34 @@ gst_wavparse_peek_chunk (GstWavParse * wav, guint32 * tag, guint32 * size)
} }
} }
/*
* gst_wavparse_calculate_duration:
* @wav Wavparse object
*
* Calculate duration on demand and store in @wav. Prefer bps, but use fact as a
* fallback.
*
* Returns: %TRUE if duration is available.
*/
static gboolean static gboolean
gst_wavparse_get_upstream_size (GstWavParse * wav, gint64 * len) gst_wavparse_calculate_duration (GstWavParse * wav)
{ {
gboolean res = FALSE; if (wav->duration > 0)
GstFormat fmt = GST_FORMAT_BYTES; return TRUE;
GstPad *peer;
if ((peer = gst_pad_get_peer (wav->sinkpad))) { if (wav->bps > 0) {
res = gst_pad_query_duration (peer, &fmt, len); wav->duration =
gst_object_unref (peer); uint64_ceiling_scale (wav->datasize, GST_SECOND, (guint64) wav->bps);
GST_INFO_OBJECT (wav, "Got duration (bps) %" GST_TIME_FORMAT,
GST_TIME_ARGS (wav->duration));
return TRUE;
} else if (wav->fact) {
wav->duration = uint64_ceiling_scale_int (GST_SECOND, wav->fact, wav->rate);
GST_INFO_OBJECT (wav, "Got duration (fact) %" GST_TIME_FORMAT,
GST_TIME_ARGS (wav->duration));
return TRUE;
} }
return FALSE;
return res;
} }
static GstFlowReturn static GstFlowReturn
@ -1026,9 +1065,10 @@ gst_wavparse_stream_headers (GstWavParse * wav)
guint32 tag, size; guint32 tag, size;
gboolean gotdata = FALSE; gboolean gotdata = FALSE;
GstCaps *caps; GstCaps *caps;
gint64 duration;
gchar *codec_name = NULL; gchar *codec_name = NULL;
GstEvent **event_p; GstEvent **event_p;
GstFormat bformat;
gint64 upstream_size = 0;
while (!wav->got_fmt) { while (!wav->got_fmt) {
GstBuffer *extra; GstBuffer *extra;
@ -1162,10 +1202,12 @@ gst_wavparse_stream_headers (GstWavParse * wav)
} }
bformat = GST_FORMAT_BYTES;
gst_pad_query_peer_duration (wav->sinkpad, &bformat, &upstream_size);
GST_DEBUG_OBJECT (wav, "upstream size %" G_GUINT64_FORMAT, upstream_size);
/* loop headers until we get data */ /* loop headers until we get data */
while (!gotdata) { while (!gotdata) {
gint64 upstream_size = 0;
if (wav->streaming) { if (wav->streaming) {
if (!gst_wavparse_peek_chunk_info (wav, &tag, &size)) if (!gst_wavparse_peek_chunk_info (wav, &tag, &size))
return GST_FLOW_OK; return GST_FLOW_OK;
@ -1181,8 +1223,6 @@ gst_wavparse_stream_headers (GstWavParse * wav)
GST_DEBUG_OBJECT (wav, "Got TAG: %" GST_FOURCC_FORMAT, GST_DEBUG_OBJECT (wav, "Got TAG: %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (tag)); GST_FOURCC_ARGS (tag));
gst_wavparse_get_upstream_size (wav, &upstream_size);
/* wav is a st00pid format, we don't know for sure where data starts. /* wav is a st00pid format, we don't know for sure where data starts.
* So we have to go bit by bit until we find the 'data' header * So we have to go bit by bit until we find the 'data' header
*/ */
@ -1264,19 +1304,19 @@ gst_wavparse_stream_headers (GstWavParse * wav)
GST_DEBUG_OBJECT (wav, "Finished parsing headers"); GST_DEBUG_OBJECT (wav, "Finished parsing headers");
if (wav->bps <= 0 && wav->fact) { if (wav->bps <= 0 && wav->fact) {
#if 0
/* not a good idea, as for embedded mp2/mp3 we set bps to 0 earlier */
wav->bps = wav->bps =
(guint32) gst_util_uint64_scale ((guint64) wav->rate, wav->datasize, (guint32) gst_util_uint64_scale ((guint64) wav->rate, wav->datasize,
(guint64) wav->fact); (guint64) wav->fact);
GST_DEBUG_OBJECT (wav, "calculated bps : %d", wav->bps); GST_INFO_OBJECT (wav, "calculated bps : %d, enabling VBR", wav->bps);
#endif
wav->vbr = TRUE; wav->vbr = TRUE;
} }
if (wav->bps > 0) { if (gst_wavparse_calculate_duration (wav)) {
duration = uint64_ceiling_scale_int (wav->datasize, GST_SECOND, wav->bps);
GST_DEBUG_OBJECT (wav, "Got duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (duration));
gst_segment_init (&wav->segment, GST_FORMAT_TIME); gst_segment_init (&wav->segment, GST_FORMAT_TIME);
gst_segment_set_duration (&wav->segment, GST_FORMAT_TIME, duration); gst_segment_set_duration (&wav->segment, GST_FORMAT_TIME, wav->duration);
} else { } else {
/* no bitrate, let downstream peer do the math, we'll feed it bytes. */ /* no bitrate, let downstream peer do the math, we'll feed it bytes. */
gst_segment_init (&wav->segment, GST_FORMAT_BYTES); gst_segment_init (&wav->segment, GST_FORMAT_BYTES);
@ -1563,23 +1603,21 @@ iterate_adapter:
GST_BUFFER_OFFSET_END (buf) = nextpos / wav->bytes_per_sample; GST_BUFFER_OFFSET_END (buf) = nextpos / wav->bytes_per_sample;
if (wav->bps > 0) { if (wav->bps > 0) {
/* and timestamps if we have a bitrate, be carefull for overflows */ /* and timestamps if we have a bitrate, be careful for overflows */
timestamp = uint64_ceiling_scale_int (pos, GST_SECOND, wav->bps); timestamp = uint64_ceiling_scale (pos, GST_SECOND, (guint64) wav->bps);
next_timestamp = uint64_ceiling_scale_int (nextpos, GST_SECOND, wav->bps); next_timestamp =
uint64_ceiling_scale (nextpos, GST_SECOND, (guint64) wav->bps);
duration = next_timestamp - timestamp; duration = next_timestamp - timestamp;
/* update current running segment position */ /* update current running segment position */
gst_segment_set_last_stop (&wav->segment, GST_FORMAT_TIME, next_timestamp); gst_segment_set_last_stop (&wav->segment, GST_FORMAT_TIME, next_timestamp);
} else if (wav->fact) {
if (wav->discont) { guint64 bps =
GST_DEBUG_OBJECT (wav, "marking DISCONT"); gst_util_uint64_scale_int (wav->datasize, wav->rate, wav->fact);
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); /* and timestamps if we have a bitrate, be careful for overflows */
wav->discont = FALSE; timestamp = uint64_ceiling_scale (pos, GST_SECOND, bps);
} else if (wav->vbr) { next_timestamp = uint64_ceiling_scale (nextpos, GST_SECOND, bps);
/* don't set timestamps for VBR files if it's not the first buffer */ duration = next_timestamp - timestamp;
timestamp = GST_CLOCK_TIME_NONE;
duration = GST_CLOCK_TIME_NONE;
}
} else { } else {
/* no bitrate, all we know is that the first sample has timestamp 0, all /* no bitrate, all we know is that the first sample has timestamp 0, all
* other positions and durations have unknown timestamp. */ * other positions and durations have unknown timestamp. */
@ -1591,6 +1629,16 @@ iterate_adapter:
/* update current running segment position with byte offset */ /* update current running segment position with byte offset */
gst_segment_set_last_stop (&wav->segment, GST_FORMAT_BYTES, nextpos); gst_segment_set_last_stop (&wav->segment, GST_FORMAT_BYTES, nextpos);
} }
if ((pos > 0) && wav->vbr) {
/* don't set timestamps for VBR files if it's not the first buffer */
timestamp = GST_CLOCK_TIME_NONE;
duration = GST_CLOCK_TIME_NONE;
}
if (wav->discont) {
GST_DEBUG_OBJECT (wav, "marking DISCONT");
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
wav->discont = FALSE;
}
GST_BUFFER_TIMESTAMP (buf) = timestamp; GST_BUFFER_TIMESTAMP (buf) = timestamp;
GST_BUFFER_DURATION (buf) = duration; GST_BUFFER_DURATION (buf) = duration;
@ -1641,8 +1689,10 @@ pull_error:
} }
push_error: push_error:
{ {
GST_WARNING_OBJECT (wav, "Error pushing on srcpad %p, is linked? = %d", GST_INFO_OBJECT (wav,
wav->srcpad, gst_pad_is_linked (wav->srcpad)); "Error pushing on srcpad %s:%s, reason %s, is linked? = %d",
GST_DEBUG_PAD_NAME (wav->srcpad), gst_flow_get_name (res),
gst_pad_is_linked (wav->srcpad));
return res; return res;
} }
} }
@ -1798,8 +1848,8 @@ gst_wavparse_pad_convert (GstPad * pad,
return TRUE; return TRUE;
} }
if (wavparse->bps == 0) if ((wavparse->bps == 0) && !wavparse->fact)
goto no_bps; goto no_bps_fact;
switch (src_format) { switch (src_format) {
case GST_FORMAT_BYTES: case GST_FORMAT_BYTES:
@ -1810,8 +1860,16 @@ gst_wavparse_pad_convert (GstPad * pad,
*dest_value -= *dest_value % wavparse->bytes_per_sample; *dest_value -= *dest_value % wavparse->bytes_per_sample;
break; break;
case GST_FORMAT_TIME: case GST_FORMAT_TIME:
*dest_value = if (wavparse->bps > 0)
gst_util_uint64_scale_int (src_value, GST_SECOND, wavparse->bps); *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
(guint64) wavparse->bps);
else {
guint64 bps = gst_util_uint64_scale_int (wavparse->datasize,
wavparse->rate, wavparse->fact);
*dest_value =
gst_util_uint64_scale_int (src_value, GST_SECOND, bps);
}
break; break;
default: default:
res = FALSE; res = FALSE;
@ -1825,8 +1883,8 @@ gst_wavparse_pad_convert (GstPad * pad,
*dest_value = src_value * wavparse->bytes_per_sample; *dest_value = src_value * wavparse->bytes_per_sample;
break; break;
case GST_FORMAT_TIME: case GST_FORMAT_TIME:
*dest_value = *dest_value = gst_util_uint64_scale (src_value, GST_SECOND,
gst_util_uint64_scale_int (src_value, GST_SECOND, wavparse->rate); (guint64) wavparse->rate);
break; break;
default: default:
res = FALSE; res = FALSE;
@ -1837,14 +1895,21 @@ gst_wavparse_pad_convert (GstPad * pad,
case GST_FORMAT_TIME: case GST_FORMAT_TIME:
switch (*dest_format) { switch (*dest_format) {
case GST_FORMAT_BYTES: case GST_FORMAT_BYTES:
if (wavparse->bps > 0)
*dest_value = gst_util_uint64_scale (src_value,
(guint64) wavparse->bps, GST_SECOND);
else {
guint64 bps = gst_util_uint64_scale_int (wavparse->datasize,
wavparse->rate, wavparse->fact);
*dest_value = gst_util_uint64_scale (src_value, bps, GST_SECOND);
}
/* make sure we end up on a sample boundary */ /* make sure we end up on a sample boundary */
*dest_value =
gst_util_uint64_scale_int (src_value, wavparse->bps, GST_SECOND);
*dest_value -= *dest_value % wavparse->blockalign; *dest_value -= *dest_value % wavparse->blockalign;
break; break;
case GST_FORMAT_DEFAULT: case GST_FORMAT_DEFAULT:
*dest_value = *dest_value = gst_util_uint64_scale (src_value,
gst_util_uint64_scale_int (src_value, wavparse->rate, GST_SECOND); (guint64) wavparse->rate, GST_SECOND);
break; break;
default: default:
res = FALSE; res = FALSE;
@ -1863,9 +1928,9 @@ done:
return res; return res;
/* ERRORS */ /* ERRORS */
no_bps: no_bps_fact:
{ {
GST_DEBUG_OBJECT (wavparse, "bps 0, cannot convert"); GST_DEBUG_OBJECT (wavparse, "bps 0 or no fact chunk, cannot convert");
res = FALSE; res = FALSE;
goto done; goto done;
} }
@ -1878,6 +1943,7 @@ gst_wavparse_get_query_types (GstPad * pad)
GST_QUERY_POSITION, GST_QUERY_POSITION,
GST_QUERY_DURATION, GST_QUERY_DURATION,
GST_QUERY_CONVERT, GST_QUERY_CONVERT,
GST_QUERY_SEEKING,
0 0
}; };
@ -1907,8 +1973,7 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
switch (format) { switch (format) {
case GST_FORMAT_TIME: case GST_FORMAT_TIME:
res &= res = gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, curb,
gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, curb,
&format, &cur); &format, &cur);
break; break;
default: default:
@ -1922,31 +1987,27 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
} }
case GST_QUERY_DURATION: case GST_QUERY_DURATION:
{ {
gint64 endb; gint64 duration;
gint64 end;
GstFormat format; GstFormat format;
endb = wav->datasize;
gst_query_parse_duration (query, &format, NULL); gst_query_parse_duration (query, &format, NULL);
switch (format) { switch (format) {
case GST_FORMAT_TIME:{ case GST_FORMAT_TIME:{
if (wav->fact) { if (gst_wavparse_calculate_duration (wav)) {
end = GST_SECOND * wav->fact / wav->rate; duration = wav->duration;
} else { } else {
res &= format = GST_FORMAT_BYTES;
gst_wavparse_pad_convert (pad, GST_FORMAT_BYTES, endb, duration = wav->datasize;
&format, &end);
} }
break; break;
} }
default: default:
format = GST_FORMAT_BYTES; format = GST_FORMAT_BYTES;
end = endb; duration = wav->datasize;
break; break;
} }
if (res) gst_query_set_duration (query, format, duration);
gst_query_set_duration (query, format, end);
break; break;
} }
case GST_QUERY_CONVERT: case GST_QUERY_CONVERT:
@ -1963,6 +2024,24 @@ gst_wavparse_pad_query (GstPad * pad, GstQuery * query)
gst_query_set_convert (query, srcformat, srcvalue, dstformat, dstvalue); gst_query_set_convert (query, srcformat, srcvalue, dstformat, dstvalue);
break; break;
} }
case GST_QUERY_SEEKING:{
GstFormat fmt;
gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL);
if (fmt == GST_FORMAT_TIME) {
gboolean seekable = TRUE;
if ((wav->bps == 0) && !wav->fact) {
seekable = FALSE;
} else if (!gst_wavparse_calculate_duration (wav)) {
seekable = FALSE;
}
gst_query_set_seeking (query, GST_FORMAT_TIME, seekable,
0, wav->duration);
res = TRUE;
}
break;
}
default: default:
res = gst_pad_query_default (pad, query); res = gst_pad_query_default (pad, query);
break; break;

View file

@ -97,6 +97,8 @@ struct _GstWavParse {
/* offset/length of data part */ /* offset/length of data part */
guint64 datastart; guint64 datastart;
guint64 datasize; guint64 datasize;
/* duration in time */
guint64 duration;
/* pending seek */ /* pending seek */
GstEvent *seek_event; GstEvent *seek_event;