From 9d2a2750c2e69f1c687352e6b5b758add82dc77f Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Sat, 19 Nov 2011 16:06:09 +0000 Subject: [PATCH 1/8] ogg: add opus support --- ext/ogg/gstoggmux.c | 2 +- ext/ogg/gstoggstream.c | 85 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index 186e87d55f..5341d6ca9a 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -107,7 +107,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", "audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; " "application/x-ogm-video; application/x-ogm-audio; video/x-dirac; " "video/x-smoke; video/x-vp8; text/x-cmml, encoded = (boolean) TRUE; " - "subtitle/x-kate; application/x-kate") + "subtitle/x-kate; application/x-kate; audio/x-opus") ); static void gst_ogg_mux_base_init (gpointer g_class); diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index a26791d261..08cfca76e0 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -1845,6 +1845,79 @@ extract_tags_kate (GstOggStream * pad, ogg_packet * packet) } } +/* opus */ + +static gboolean +setup_opus_mapper (GstOggStream * pad, ogg_packet * packet) +{ + if (packet->bytes < 19) + return FALSE; + + pad->granulerate_n = 48000; + pad->granulerate_d = 1; + pad->granuleshift = 0; + pad->n_header_packets = 2; + + pad->caps = gst_caps_new_simple ("audio/x-opus", NULL); + + return TRUE; +} + +static gint64 +packet_duration_opus (GstOggStream * pad, ogg_packet * packet) +{ + static const guint64 durations[32] = { + 10000, 20000, 40000, 60000, /* Silk NB */ + 10000, 20000, 40000, 60000, /* Silk MB */ + 10000, 20000, 40000, 60000, /* Silk WB */ + 10000, 20000, /* Hybrid SWB */ + 10000, 20000, /* Hybrid FB */ + 2500, 5000, 10000, 20000, /* CELT NB */ + 2500, 5000, 10000, 20000, /* CELT NB */ + 2500, 5000, 10000, 20000, /* CELT NB */ + 2500, 5000, 10000, 20000, /* CELT NB */ + }; + + gint64 duration; + gint64 frame_duration; + gint nframes; + guint8 toc; + + if (packet->bytes < 1) + return 0; + + toc = packet->packet[0]; + + frame_duration = durations[toc >> 3] * 1000; + switch (toc & 3) { + case 0: + nframes = 1; + break; + case 1: + nframes = 2; + break; + case 2: + nframes = 2; + break; + case 3: + if (packet->bytes < 2) { + GST_WARNING ("Code 3 Opus packet has less than 2 bytes"); + return 0; + } + nframes = packet->packet[1] & 63; + break; + } + + duration = nframes * frame_duration; + if (duration > 120 * GST_MSECOND) { + 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 / 1000000.f, nframes, duration / 1000000.f); + return (duration + 24000) / 48000; +} + /* *INDENT-OFF* */ /* indent hates our freedoms */ @@ -2016,6 +2089,18 @@ const GstOggMap mappers[] = { granulepos_to_key_granule_vp8, extract_tags_vp8 }, + { + "OpusHead", 8, 0, + "audio/x-opus", + setup_opus_mapper, + granulepos_to_granule_default, + granule_to_granulepos_default, + NULL, + is_header_count, + packet_duration_opus, + NULL, + extract_tags_count + }, { "\001audio\0\0\0", 9, 53, "application/x-ogm-audio", From da69993a49e7e0009daad929c394caa512cae57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Stadler?= Date: Tue, 22 Nov 2011 10:04:12 +0100 Subject: [PATCH 2/8] playsinkconvertbin: avoid removing children from bin twice GstBin base class removes children in dispose, so we need to do the same. --- gst/playback/gstplaysinkconvertbin.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index bade4ab9a7..99a2e380bb 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -413,12 +413,20 @@ gst_play_sink_convert_bin_remove_elements (GstPlaySinkConvertBin * self) } static void -gst_play_sink_convert_bin_finalize (GObject * object) +gst_play_sink_convert_bin_dispose (GObject * object) { GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (object); gst_play_sink_convert_bin_remove_elements (self); + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_play_sink_convert_bin_finalize (GObject * object) +{ + GstPlaySinkConvertBin *self = GST_PLAY_SINK_CONVERT_BIN_CAST (object); + gst_object_unref (self->sink_proxypad); g_mutex_free (self->lock); @@ -521,6 +529,7 @@ gst_play_sink_convert_bin_class_init (GstPlaySinkConvertBinClass * klass) gobject_class = (GObjectClass *) klass; gstelement_class = (GstElementClass *) klass; + gobject_class->dispose = gst_play_sink_convert_bin_dispose; gobject_class->finalize = gst_play_sink_convert_bin_finalize; gst_element_class_add_pad_template (gstelement_class, From e05f1df04b6a29fe0d38bee4e041977ff94a3865 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Mon, 21 Nov 2011 17:03:21 +0000 Subject: [PATCH 3/8] oggstream: account for opus pre-skip in granpos/time mapping --- ext/ogg/gstoggstream.c | 9 +++++++++ ext/ogg/gstoggstream.h | 1 + 2 files changed, 10 insertions(+) diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index 08cfca76e0..e56e561181 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -127,6 +127,10 @@ gst_ogg_stream_granule_to_time (GstOggStream * pad, gint64 granule) if (granule == 0 || pad->granulerate_n == 0 || pad->granulerate_d == 0) return 0; + granule += pad->granule_offset; + if (granule < 0) + return 0; + return gst_util_uint64_scale (granule, GST_SECOND * pad->granulerate_d, pad->granulerate_n); } @@ -1858,6 +1862,11 @@ setup_opus_mapper (GstOggStream * pad, ogg_packet * packet) pad->granuleshift = 0; pad->n_header_packets = 2; + /* pre-skip is in samples at 48000 Hz, which matches granule one for one */ + pad->granule_offset = -GST_READ_UINT16_LE (packet->packet + 10); + GST_INFO ("Opus has a pre-skip of %" G_GINT64_FORMAT " samples", + -pad->granule_offset); + pad->caps = gst_caps_new_simple ("audio/x-opus", NULL); return TRUE; diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h index d6c4de61c1..a66a78c34d 100644 --- a/ext/ogg/gstoggstream.h +++ b/ext/ogg/gstoggstream.h @@ -60,6 +60,7 @@ struct _GstOggStream gboolean have_fisbone; gint granulerate_n; gint granulerate_d; + gint64 granule_offset; guint32 preroll; guint granuleshift; gint n_header_packets; From ceee36195ae6835631ca93fbdfc5f2553dae4aca Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 12:00:58 +0000 Subject: [PATCH 4/8] oggstream: early out on headers when determining packet duration --- ext/ogg/gstoggstream.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index e56e561181..ac4ae34d0d 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -1895,6 +1895,10 @@ packet_duration_opus (GstOggStream * pad, ogg_packet * packet) if (packet->bytes < 1) return 0; + /* headers */ + if (packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4)) + return 0; + toc = packet->packet[0]; frame_duration = durations[toc >> 3] * 1000; From 2a87d7c8ce25001fe4ea35a5be0c907cd5070c56 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 12:55:56 +0000 Subject: [PATCH 5/8] oggstream: fix opus duration calculation --- ext/ogg/gstoggstream.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index ac4ae34d0d..e0dde03254 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -1876,15 +1876,15 @@ static gint64 packet_duration_opus (GstOggStream * pad, ogg_packet * packet) { static const guint64 durations[32] = { - 10000, 20000, 40000, 60000, /* Silk NB */ - 10000, 20000, 40000, 60000, /* Silk MB */ - 10000, 20000, 40000, 60000, /* Silk WB */ - 10000, 20000, /* Hybrid SWB */ - 10000, 20000, /* Hybrid FB */ - 2500, 5000, 10000, 20000, /* CELT NB */ - 2500, 5000, 10000, 20000, /* CELT NB */ - 2500, 5000, 10000, 20000, /* CELT NB */ - 2500, 5000, 10000, 20000, /* CELT NB */ + 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; @@ -1901,7 +1901,7 @@ packet_duration_opus (GstOggStream * pad, ogg_packet * packet) toc = packet->packet[0]; - frame_duration = durations[toc >> 3] * 1000; + frame_duration = durations[toc >> 3]; switch (toc & 3) { case 0: nframes = 1; @@ -1922,13 +1922,13 @@ packet_duration_opus (GstOggStream * pad, ogg_packet * packet) } duration = nframes * frame_duration; - if (duration > 120 * GST_MSECOND) { + 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 / 1000000.f, nframes, duration / 1000000.f); - return (duration + 24000) / 48000; + frame_duration / 48.f, nframes, duration / 48.f); + return duration; } From 9d4989395cd6494b3e1f4ea4f8cdaf1e821b168f Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 13:01:35 +0000 Subject: [PATCH 6/8] oggdemux: add some more debug info when determining start time --- ext/ogg/gstoggdemux.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index 998efb98d4..2426b7275f 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -914,9 +914,12 @@ gst_ogg_pad_submit_packet (GstOggPad * pad, ogg_packet * packet) pad->start_time = gst_ogg_stream_granule_to_time (&pad->map, start_granule); GST_DEBUG_OBJECT (ogg, - "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s", + "start time %" GST_TIME_FORMAT " (%" GST_TIME_FORMAT ") for %s " + "from granpos %" G_GINT64_FORMAT " (granule %" G_GINT64_FORMAT ", " + "accumulated granule %" G_GINT64_FORMAT, GST_TIME_ARGS (pad->start_time), GST_TIME_ARGS (pad->start_time), - gst_ogg_stream_get_media_type (&pad->map)); + gst_ogg_stream_get_media_type (&pad->map), packet->granulepos, + granule, pad->map.accumulated_granule); } else { packet->granulepos = gst_ogg_stream_granule_to_granulepos (&pad->map, pad->map.accumulated_granule, pad->keyframe_granule); From bf73491077d31db9032e13a0640a5c4552f5aee9 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 13:15:33 +0000 Subject: [PATCH 7/8] oggstream: recognize opus headers from data, not packet count Opus streams outside of Ogg may not have headers, and oggstream may be used by oggmux to mux an Opus stream which does not come from Ogg - thus without headers. Determining headerness by packet count would strip the first two packets from such an Opus stream, leading to a very small amount of audio being clipped at the beginning of the stream. --- ext/ogg/gstoggstream.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index e0dde03254..79b67b230c 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -1872,6 +1872,12 @@ setup_opus_mapper (GstOggStream * pad, ogg_packet * packet) return TRUE; } +static gboolean +is_header_opus (GstOggStream * pad, ogg_packet * packet) +{ + return packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4); +} + static gint64 packet_duration_opus (GstOggStream * pad, ogg_packet * packet) { @@ -1896,7 +1902,7 @@ packet_duration_opus (GstOggStream * pad, ogg_packet * packet) return 0; /* headers */ - if (packet->bytes >= 8 && !memcmp (packet->packet, "Opus", 4)) + if (is_header_opus (pad, packet)) return 0; toc = packet->packet[0]; @@ -2109,7 +2115,7 @@ const GstOggMap mappers[] = { granulepos_to_granule_default, granule_to_granulepos_default, NULL, - is_header_count, + is_header_opus, packet_duration_opus, NULL, extract_tags_count From 042b4f9a294bf0ecaea85de13175b45d54602309 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Tue, 22 Nov 2011 13:29:10 +0000 Subject: [PATCH 8/8] oggstream: extract opus comments if available --- ext/ogg/gstoggstream.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index 79b67b230c..96cd581a92 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -1937,6 +1937,15 @@ packet_duration_opus (GstOggStream * pad, ogg_packet * packet) return duration; } +static void +extract_tags_opus (GstOggStream * pad, ogg_packet * packet) +{ + if (packet->bytes >= 8 && memcmp (packet->packet, "OpusTags", 8) == 0) { + tag_list_from_vorbiscomment_packet (packet, + (const guint8 *) "OpusTags", 8, &pad->taglist); + } +} + /* *INDENT-OFF* */ /* indent hates our freedoms */ @@ -2118,7 +2127,7 @@ const GstOggMap mappers[] = { is_header_opus, packet_duration_opus, NULL, - extract_tags_count + extract_tags_opus }, { "\001audio\0\0\0", 9, 53,