diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index b0907baea6..ad5ab26ed8 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -891,9 +891,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); diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index 283505c6fd..922409b9cd 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -115,7 +115,7 @@ static GstStaticPadTemplate audio_sink_factory = GST_PAD_REQUEST, GST_STATIC_CAPS ("audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; " - "application/x-ogm-audio; ") + "application/x-ogm-audio; audio/x-opus") ); static GstStaticPadTemplate subtitle_sink_factory = diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index f09f02874f..49fe2555e8 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); } @@ -1820,6 +1824,103 @@ 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; + + /* 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; +} + +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) +{ + 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; + guint8 toc; + + if (packet->bytes < 1) + return 0; + + /* headers */ + if (is_header_opus (pad, packet)) + return 0; + + toc = packet->packet[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 (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 > 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; +} + +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 */ @@ -1991,6 +2092,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_opus, + packet_duration_opus, + NULL, + extract_tags_opus + }, { "\001audio\0\0\0", 9, 53, "application/x-ogm-audio", 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; diff --git a/gst/playback/gstplaysinkconvertbin.c b/gst/playback/gstplaysinkconvertbin.c index dbd233b9c1..ba5efaa463 100644 --- a/gst/playback/gstplaysinkconvertbin.c +++ b/gst/playback/gstplaysinkconvertbin.c @@ -446,12 +446,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); @@ -548,6 +556,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,