opusdec: handle missing buffers with no duration

If buffer duration is missing, it is parsed from the packet data.
This is not foolproof, since Opus can change durations on the
fly.

https://bugzilla.gnome.org/show_bug.cgi?id=767826
This commit is contained in:
Vincent Penquerc'h 2016-06-20 12:42:28 +01:00
parent 72879c3639
commit 6bc6b6ebbf
2 changed files with 79 additions and 0 deletions

View file

@ -166,6 +166,7 @@ gst_opus_dec_reset (GstOpusDec * dec)
dec->sample_rate = 0;
dec->n_channels = 0;
dec->leftover_plc_duration = 0;
dec->last_known_buffer_duration = GST_CLOCK_TIME_NONE;
}
static void
@ -383,6 +384,66 @@ gst_opus_dec_parse_comments (GstOpusDec * dec, GstBuffer * buf)
return GST_FLOW_OK;
}
/* adapted from ext/ogg/gstoggstream.c */
static gint64
packet_duration_opus (const unsigned char *data, size_t bytes)
{
static const guint64 durations[32] = {
480, 960, 1920, 2880, /* Silk NB */
480, 960, 1920, 2880, /* Silk MB */
480, 960, 1920, 2880, /* Silk WB */
480, 960, /* Hybrid SWB */
480, 960, /* Hybrid FB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
120, 240, 480, 960, /* CELT NB */
};
gint64 duration;
gint64 frame_duration;
gint nframes = 0;
guint8 toc;
if (bytes < 1)
return 0;
/* headers */
if (bytes >= 8 && !memcmp (data, "Opus", 4))
return 0;
toc = data[0];
frame_duration = durations[toc >> 3];
switch (toc & 3) {
case 0:
nframes = 1;
break;
case 1:
nframes = 2;
break;
case 2:
nframes = 2;
break;
case 3:
if (bytes < 2) {
GST_WARNING ("Code 3 Opus packet has less than 2 bytes");
return 0;
}
nframes = data[1] & 63;
break;
}
duration = nframes * frame_duration;
if (duration > 5760) {
GST_WARNING ("Opus packet duration > 120 ms, invalid");
return 0;
}
GST_LOG ("Opus packet: frame size %.1f ms, %d frames, duration %.1f ms",
frame_duration / 48.f, nframes, duration / 48.f);
return duration / 48.f * 1000000;
}
static GstFlowReturn
opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer)
{
@ -479,6 +540,19 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer)
GstClockTime aligned_missing_duration;
GstClockTime missing_duration = GST_BUFFER_DURATION (bufd);
if (!GST_CLOCK_TIME_IS_VALID (missing_duration)) {
if (GST_CLOCK_TIME_IS_VALID (dec->last_known_buffer_duration)) {
missing_duration = dec->last_known_buffer_duration;
GST_WARNING_OBJECT (dec,
"Missing duration, using last duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (missing_duration));
} else {
GST_WARNING_OBJECT (dec,
"Missing buffer, but unknown duration, and no previously known duration, assuming 20 ms");
missing_duration = 20 * GST_MSECOND;
}
}
GST_DEBUG_OBJECT (dec,
"missing buffer, doing PLC duration %" GST_TIME_FORMAT
" plus leftover %" GST_TIME_FORMAT, GST_TIME_ARGS (missing_duration),
@ -531,6 +605,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buffer)
goto buffer_failed;
}
if (size > 0)
dec->last_known_buffer_duration = packet_duration_opus (data, size);
gst_buffer_map (outbuf, &omap, GST_MAP_WRITE);
out_data = (gint16 *) omap.data;

View file

@ -73,6 +73,8 @@ struct _GstOpusDec {
gboolean primed;
guint64 leftover_plc_duration;
GstClockTime last_known_buffer_duration;
};
struct _GstOpusDecClass {