qtdemux: Improve framerate calculation/guessing

Change the way the output framerate is calculated
to ignore the first sample (which is sometimes truncated
in my testing) and use the new gst_video_guess_framerate()
function to recognise common standard framerates better.

Remove the code that was sorting the first 20 sample
durations and then ignoring the result.
This commit is contained in:
Jan Schmidt 2014-08-15 01:12:20 +10:00
parent ce1d4d9f21
commit ca068865c3
2 changed files with 31 additions and 41 deletions

View file

@ -10,7 +10,7 @@ libgstisomp4_la_LIBADD = \
-lgstrtp-@GST_API_VERSION@ \ -lgstrtp-@GST_API_VERSION@ \
-lgsttag-@GST_API_VERSION@ \ -lgsttag-@GST_API_VERSION@ \
-lgstpbutils-@GST_API_VERSION@ \ -lgstpbutils-@GST_API_VERSION@ \
$(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS) $(LIBM) $(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS)
libgstisomp4_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS} libgstisomp4_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
libgstisomp4_la_SOURCES = isomp4-plugin.c gstrtpxqtdepay.c \ libgstisomp4_la_SOURCES = isomp4-plugin.c gstrtpxqtdepay.c \
qtdemux.c qtdemux_types.c qtdemux_dump.c qtdemux_lang.c \ qtdemux.c qtdemux_types.c qtdemux_dump.c qtdemux_lang.c \

View file

@ -233,7 +233,7 @@ struct _QtDemuxStream
guint32 n_samples; guint32 n_samples;
QtDemuxSample *samples; QtDemuxSample *samples;
gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */ gboolean all_keyframe; /* TRUE when all samples are keyframes (no stss) */
guint32 min_duration; /* duration in timescale of first sample, used for figuring out guint32 first_duration; /* duration in timescale of first sample, used for figuring out
the framerate, in timescale units */ the framerate, in timescale units */
guint32 offset_in_sample; guint32 offset_in_sample;
guint32 max_buffer_size; guint32 max_buffer_size;
@ -5815,26 +5815,37 @@ static gboolean
gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream) gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
{ {
if (stream->subtype == FOURCC_vide) { if (stream->subtype == FOURCC_vide) {
/* fps is calculated base on the duration of the first frames since /* fps is calculated base on the duration of the average framerate since
* qt does not have a fixed framerate. */ * qt does not have a fixed framerate. */
if ((stream->n_samples == 1) && (stream->min_duration == 0)) { if ((stream->n_samples == 1) && (stream->first_duration == 0)) {
/* still frame */ /* still frame */
stream->fps_n = 0; stream->fps_n = 0;
stream->fps_d = 1; stream->fps_d = 1;
} else { } else {
/* we might need to scale the timescale to get precise framerate */ if (stream->duration == 0 || stream->n_samples < 2) {
const int required_scale = rint (log (10000) / 2.303); /* divide to get log10 */ stream->fps_n = stream->timescale;
int current_scale = rint (log (stream->timescale) / 2.303); stream->fps_d = 1;
int factor = pow (10.0, MAX (0, required_scale - current_scale)); } else {
/* Calculate a framerate, ignoring the first sample which is sometimes truncated */
/* stream->duration is guint64, timescale, n_samples are guint32 */
GstClockTime avg_duration =
gst_util_uint64_scale_round (stream->duration -
stream->first_duration, GST_SECOND,
(guint64) (stream->timescale) * (stream->n_samples - 1));
stream->fps_n = stream->timescale * factor; GST_LOG_OBJECT (qtdemux,
"Calculating avg sample duration based on stream duration %"
G_GUINT64_FORMAT
" minus first sample %u, leaving %d samples gives %"
GST_TIME_FORMAT, stream->duration, stream->first_duration,
stream->n_samples - 1, GST_TIME_ARGS (avg_duration));
if (stream->duration == 0 || stream->n_samples == 0) gst_video_guess_framerate (avg_duration, &stream->fps_n,
stream->fps_d = factor; &stream->fps_d);
else }
stream->fps_d = GST_DEBUG_OBJECT (qtdemux,
gst_util_uint64_scale_int_round (stream->duration, factor, "Calculating framerate, timescale %u gave fps_n %d fps_d %d",
stream->n_samples); stream->timescale, stream->fps_n, stream->fps_d);
} }
if (stream->caps) { if (stream->caps) {
@ -7091,14 +7102,6 @@ qtdemux_get_rtsp_uri_from_hndl (GstQTDemux * qtdemux, GNode * minf)
return uri; return uri;
} }
static gint
less_than (gconstpointer a, gconstpointer b)
{
const guint32 *av = a, *bv = b;
return *av - *bv;
}
#define AMR_NB_ALL_MODES 0x81ff #define AMR_NB_ALL_MODES 0x81ff
#define AMR_WB_ALL_MODES 0x83ff #define AMR_WB_ALL_MODES 0x83ff
static guint static guint
@ -9019,8 +9022,6 @@ qtdemux_prepare_streams (GstQTDemux * qtdemux)
for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) { for (i = 0; ret == GST_FLOW_OK && i < qtdemux->n_streams; i++) {
QtDemuxStream *stream = qtdemux->streams[i]; QtDemuxStream *stream = qtdemux->streams[i];
guint32 sample_num = 0; guint32 sample_num = 0;
guint samples = 20;
GArray *durations;
GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT, GST_DEBUG_OBJECT (qtdemux, "stream %d, id %d, fourcc %" GST_FOURCC_FORMAT,
i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc)); i, stream->track_id, GST_FOURCC_ARGS (stream->fourcc));
@ -9051,26 +9052,15 @@ qtdemux_prepare_streams (GstQTDemux * qtdemux)
continue; continue;
} }
/* parse number of initial sample to set frame rate cap */ /* parse the initial sample for use in setting the frame rate cap */
while (sample_num < stream->n_samples && sample_num < samples) { while (sample_num == 0) {
if (!qtdemux_parse_samples (qtdemux, stream, sample_num)) if (!qtdemux_parse_samples (qtdemux, stream, sample_num))
break; break;
++sample_num; ++sample_num;
} }
/* collect and sort durations */ stream->first_duration = stream->samples[0].duration;
samples = MIN (stream->stbl_index + 1, samples); GST_LOG_OBJECT (qtdemux, "stream %d first duration %u",
GST_DEBUG_OBJECT (qtdemux, "%d samples for framerate", samples); stream->track_id, stream->first_duration);
if (samples) {
durations = g_array_sized_new (FALSE, FALSE, sizeof (guint32), samples);
sample_num = 0;
while (sample_num < samples) {
g_array_append_val (durations, stream->samples[sample_num].duration);
sample_num++;
}
g_array_sort (durations, less_than);
stream->min_duration = g_array_index (durations, guint32, samples / 2);
g_array_free (durations, TRUE);
}
} }
return ret; return ret;