From 6bc6b6ebbfb35796e3dfd22f15ebabb685bd4576 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 20 Jun 2016 12:42:28 +0100 Subject: [PATCH] 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 --- ext/opus/gstopusdec.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++ ext/opus/gstopusdec.h | 2 ++ 2 files changed, 79 insertions(+) diff --git a/ext/opus/gstopusdec.c b/ext/opus/gstopusdec.c index 8f03da3..7e99478 100644 --- a/ext/opus/gstopusdec.c +++ b/ext/opus/gstopusdec.c @@ -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; diff --git a/ext/opus/gstopusdec.h b/ext/opus/gstopusdec.h index df52cfb..de0dd17 100644 --- a/ext/opus/gstopusdec.h +++ b/ext/opus/gstopusdec.h @@ -73,6 +73,8 @@ struct _GstOpusDec { gboolean primed; guint64 leftover_plc_duration; + + GstClockTime last_known_buffer_duration; }; struct _GstOpusDecClass { -- 2.7.4