From 7ce811f1edb66ddf179b48d669f0978e5ed48caf Mon Sep 17 00:00:00 2001 From: Stefan Sauer Date: Tue, 4 Oct 2011 23:09:42 +0200 Subject: [PATCH 01/11] auditestsrc: indent fix --- gst/audiotestsrc/gstaudiotestsrc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gst/audiotestsrc/gstaudiotestsrc.c b/gst/audiotestsrc/gstaudiotestsrc.c index 91ace92a44..0304b8ade6 100644 --- a/gst/audiotestsrc/gstaudiotestsrc.c +++ b/gst/audiotestsrc/gstaudiotestsrc.c @@ -850,9 +850,9 @@ gst_audio_test_src_create_red_noise_##type (GstAudioTestSrc * src, g##type * sam for (i = 0; i < src->generate_samples_per_buffer * src->channels; ) { \ for (c = 0; c < src->channels; ++c) { \ while (TRUE) { \ - gdouble r = g_rand_double_range (src->gen, -1.0, 1.0); \ + gdouble r = g_rand_double_range (src->gen, -1.0, 1.0); \ state += r; \ - if (state<-8.0f || state>8.0f) state -= r; \ + if (state < -8.0f || state > 8.0f) state -= r; \ else break; \ } \ samples[i++] = (g##type) (amp * state * 0.0625f); /* /16.0 */ \ From ef4a4a0e9426cc4d1dbed8a8ec8f39b42fa5d476 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 5 Oct 2011 12:45:19 +0200 Subject: [PATCH 02/11] playsink: Add audio- and text-sink props --- gst/playback/gstplaysink.c | 43 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index 28a1324712..d642fa0a4b 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -252,6 +252,8 @@ enum PROP_FRAME, PROP_AV_OFFSET, PROP_VIDEO_SINK, + PROP_AUDIO_SINK, + PROP_TEXT_SINK, PROP_LAST }; @@ -419,6 +421,31 @@ gst_play_sink_class_init (GstPlaySinkClass * klass) g_param_spec_object ("video-sink", "Video Sink", "the video output element to use (NULL = default sink)", GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstPlaySink:audio-sink: + * + * Set the used audio sink element. NULL will use the default sink. playsink + * must be in %GST_STATE_NULL + * + * Since: 0.10.36 + */ + g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK, + g_param_spec_object ("audio-sink", "Audio Sink", + "the audio output element to use (NULL = default sink)", + GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * GstPlaySink:text-sink: + * + * Set the used text sink element. NULL will use the default sink. playsink + * must be in %GST_STATE_NULL + * + * Since: 0.10.36 + */ + g_object_class_install_property (gobject_klass, PROP_TEXT_SINK, + g_param_spec_object ("text-sink", "Text sink", + "the text output element to use (NULL = default textoverlay)", + GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass, @@ -3639,6 +3666,14 @@ gst_play_sink_set_property (GObject * object, guint prop_id, gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO, g_value_get_object (value)); break; + case PROP_AUDIO_SINK: + gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO, + g_value_get_object (value)); + break; + case PROP_TEXT_SINK: + gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT, + g_value_get_object (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); break; @@ -3681,6 +3716,14 @@ gst_play_sink_get_property (GObject * object, guint prop_id, g_value_take_object (value, gst_play_sink_get_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO)); break; + case PROP_AUDIO_SINK: + g_value_take_object (value, gst_play_sink_get_sink (playsink, + GST_PLAY_SINK_TYPE_AUDIO)); + break; + case PROP_TEXT_SINK: + g_value_take_object (value, gst_play_sink_get_sink (playsink, + GST_PLAY_SINK_TYPE_TEXT)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec); break; From 59f0b29c3f0c14c75faf9d0027eb36a85460da70 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Tue, 4 Oct 2011 21:17:37 -0300 Subject: [PATCH 03/11] encodebin: Re-enable parsers Re-enable parsers in encodebin to allow more passthrough scenarios to work. Specially the ones that require changing 'stream formats'. i.e. h264 in mkv to mpegts. --- gst/encoding/gstencodebin.c | 42 ------------------------------------- 1 file changed, 42 deletions(-) diff --git a/gst/encoding/gstencodebin.c b/gst/encoding/gstencodebin.c index 3ce67ff44c..d34d80636b 100644 --- a/gst/encoding/gstencodebin.c +++ b/gst/encoding/gstencodebin.c @@ -1095,45 +1095,6 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof, last = sgroup->outfilter; - /* FIXME : - * - * The usage of parsers in encoding/muxing scenarios is - * just too undefined to just use as-is. - * - * Take the use-case where you want to re-mux a stream of type - * "my/media". You create a StreamEncodingProfile with that type - * as the target (as-is). And you use decodebin2/uridecodebin - * upstream. - * - * * demuxer exposes "my/media" - * * a parser is available for "my/media" which has a source pad - * caps of "my/media,parsed=True" - * * decodebin2/uridecodebin exposes a new pad with the parsed caps - * * You request a new stream from encodebin, which will match the - * streamprofile and creates a group (i.e. going through this method) - * There is a matching parser (the same used in the decoder) whose - * source pad caps intersects with the stream profile caps, you - * therefore use it... - * * ... but that parser has a "my/media,parsed=False" sink pad caps - * * ... and you can't link your decodebin pad to encodebin. - * - * In the end, it comes down to parsers only taking into account the - * decoding use-cases. - * - * One way to solve that might be to : - * * Make parsers sink pad caps be "framed={False,True}" and the - * source pad caps be "framed=True" - * * Modify decodebin2 accordingly to avoid looping and chaining - * an infinite number of parsers - * - * Another way would be to have "well-known" caps properties to specify - * whether a stream has been parsed or not. - * * currently we fail. aacparse uses 'framed' and mp3parse uses 'parsed' - */ - /* FIXME : Re-enable once parser situation is un-$#*@(%$#ed */ -#if 0 - /* Parser. - * FIXME : identify smart parsers (used for re-encoding) */ sgroup->parser = _get_parser (ebin, sprof); if (sgroup->parser != NULL) { @@ -1144,7 +1105,6 @@ _create_stream_group (GstEncodeBin * ebin, GstEncodingProfile * sprof, goto parser_link_failure; last = sgroup->parser; } -#endif /* Stream combiner */ sgroup->combiner = g_object_new (GST_TYPE_STREAM_COMBINER, NULL); @@ -1462,11 +1422,9 @@ combiner_link_failure: GST_ERROR_OBJECT (ebin, "Failure linking to the combiner"); goto cleanup; -#if 0 parser_link_failure: GST_ERROR_OBJECT (ebin, "Failure linking the parser"); goto cleanup; -#endif converter_link_failure: GST_ERROR_OBJECT (ebin, "Failure linking the video converters"); From 76b29367e7b13cc1751724cb2678ebd9039678e1 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 6 Oct 2011 11:53:26 +0100 Subject: [PATCH 04/11] playbin2: fix mismatch between video/ and video/x-dvd-subpicture The new code was checking for a prefix, and would find video/ first. Check in two passes, first checking for a perfect match, and falling back to a prefix check if nothing was found. https://bugzilla.gnome.org/show_bug.cgi?id=657261 --- gst/playback/gstplaybin2.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c index c32a87f6f2..7c53864228 100644 --- a/gst/playback/gstplaybin2.c +++ b/gst/playback/gstplaybin2.c @@ -2442,12 +2442,14 @@ stream_changed_data_probe (GstPad * pad, GstMiniObject * object, gpointer data) /* helper function to lookup stuff in lists */ static gboolean -array_has_value (const gchar * values[], const gchar * value) +array_has_value (const gchar * values[], const gchar * value, gboolean exact) { gint i; for (i = 0; values[i]; i++) { - if (values[i] && g_str_has_prefix (value, values[i])) + if (exact && !strcmp (value, values[i])) + return TRUE; + if (!exact && g_str_has_prefix (value, values[i])) return TRUE; } return FALSE; @@ -2505,7 +2507,7 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group) GstPad *sinkpad; GstPadLinkReturn res; GstSourceSelect *select = NULL; - gint i; + gint i, pass; gboolean changed = FALSE; playbin = group->playbin; @@ -2518,20 +2520,24 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group) "pad %s:%s with caps %" GST_PTR_FORMAT " added in group %p", GST_DEBUG_PAD_NAME (pad), caps, group); - /* major type of the pad, this determines the selector to use */ - for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { - if (array_has_value (group->selector[i].media_list, name)) { - select = &group->selector[i]; - break; - } else if (group->selector[i].get_media_caps) { - GstCaps *media_caps = group->selector[i].get_media_caps (); - - if (media_caps && gst_caps_can_intersect (media_caps, caps)) { + /* major type of the pad, this determines the selector to use, + try exact match first so we don't prematurely match video/ + for video/x-dvd-subpicture */ + for (pass = 0; !select && pass < 2; pass++) { + for (i = 0; i < PLAYBIN_STREAM_LAST; i++) { + if (array_has_value (group->selector[i].media_list, name, pass == 0)) { select = &group->selector[i]; - gst_caps_unref (media_caps); break; + } else if (group->selector[i].get_media_caps) { + GstCaps *media_caps = group->selector[i].get_media_caps (); + + if (media_caps && gst_caps_can_intersect (media_caps, caps)) { + select = &group->selector[i]; + gst_caps_unref (media_caps); + break; + } + gst_caps_unref (media_caps); } - gst_caps_unref (media_caps); } } /* no selector found for the media type, don't bother linking it to a From 8d617f403769f4f2519de72c3d7e267ee850b123 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 6 Oct 2011 15:38:49 +0100 Subject: [PATCH 05/11] playsink: fix caps negotiation through the new convenience bins The bins' getcaps was bypassing the inner elements, and thus failing to account for the caps transformations they allow, which caused YUV video pipelines to fail with ximagesink, which does not support YUV, even though the convenience bin includes a colorspace converter for just this purpose. https://bugzilla.gnome.org/show_bug.cgi?id=660816 --- gst/playback/gstplaysinkaudioconvert.c | 5 +---- gst/playback/gstplaysinkvideoconvert.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index 8e405d2636..2c28c6eca0 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -351,10 +351,7 @@ gst_play_sink_audio_convert_getcaps (GstPad * pad) GstPad *otherpad, *peer; GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self); - if (pad == self->srcpad) - otherpad = gst_object_ref (self->sinkpad); - else - otherpad = gst_object_ref (self->srcpad); + otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self); peer = gst_pad_get_peer (otherpad); diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c index cf765ce969..8b22a644e6 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -331,10 +331,7 @@ gst_play_sink_video_convert_getcaps (GstPad * pad) GstPad *otherpad, *peer; GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self); - if (pad == self->srcpad) - otherpad = gst_object_ref (self->sinkpad); - else - otherpad = gst_object_ref (self->srcpad); + otherpad = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (pad)); GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self); peer = gst_pad_get_peer (otherpad); From 70239887e869f011ffa2b235422103d5fc626df3 Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 6 Oct 2011 18:20:32 +0100 Subject: [PATCH 06/11] audiotestsrc: add missing break And make violet noise usable https://bugzilla.gnome.org/show_bug.cgi?id=661105 --- gst/audiotestsrc/gstaudiotestsrc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/gst/audiotestsrc/gstaudiotestsrc.c b/gst/audiotestsrc/gstaudiotestsrc.c index 0304b8ade6..78a5d6b6ea 100644 --- a/gst/audiotestsrc/gstaudiotestsrc.c +++ b/gst/audiotestsrc/gstaudiotestsrc.c @@ -1004,6 +1004,7 @@ gst_audio_test_src_change_wave (GstAudioTestSrc * src) src->gen = g_rand_new (); src->red.state = 0.0; src->process = violet_noise_funcs[src->format]; + break; default: GST_ERROR ("invalid wave-form"); break; From be39ab28c3b040b10791ac5e9df7ac8fb2757eae Mon Sep 17 00:00:00 2001 From: Vincent Penquerc'h Date: Thu, 6 Oct 2011 18:21:29 +0100 Subject: [PATCH 07/11] tests: actually test what we said we would All tests were testing the default sine wave https://bugzilla.gnome.org/show_bug.cgi?id=661106 --- tests/check/elements/audiotestsrc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/check/elements/audiotestsrc.c b/tests/check/elements/audiotestsrc.c index e83dceb828..1d40a20eaa 100644 --- a/tests/check/elements/audiotestsrc.c +++ b/tests/check/elements/audiotestsrc.c @@ -89,6 +89,7 @@ GST_START_TEST (test_all_waves) while (values[j].value_name) { GST_DEBUG_OBJECT (audiotestsrc, "testing wave %s", values[j].value_name); + g_object_set (audiotestsrc, "wave", values[j].value, NULL); fail_unless (gst_element_set_state (audiotestsrc, GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, From f3cb93fc0c436e8c79e59e5bd0c66510f63b329d Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Wed, 5 Oct 2011 15:43:35 +0200 Subject: [PATCH 08/11] vorbisenc: port to audioencoder --- ext/vorbis/Makefile.am | 3 +- ext/vorbis/gstvorbisenc.c | 877 +++++++++----------------------------- ext/vorbis/gstvorbisenc.h | 19 +- 3 files changed, 201 insertions(+), 698 deletions(-) diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am index 7c18ff1461..59f57a2603 100644 --- a/ext/vorbis/Makefile.am +++ b/ext/vorbis/Makefile.am @@ -11,7 +11,8 @@ libgstvorbis_la_SOURCES = gstvorbis.c \ gstvorbistag.c \ gstvorbiscommon.c -libgstvorbis_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(VORBIS_CFLAGS) +libgstvorbis_la_CFLAGS = -DGST_USE_UNSTABLE_API \ + $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(VORBIS_CFLAGS) ## AM_PATH_VORBIS also sets VORBISENC_LIBS libgstvorbis_la_LIBADD = \ $(top_builddir)/gst-libs/gst/tag/libgsttag-@GST_MAJORMINOR@.la \ diff --git a/ext/vorbis/gstvorbisenc.c b/ext/vorbis/gstvorbisenc.c index 85d47c3249..6aac4ab6f5 100644 --- a/ext/vorbis/gstvorbisenc.c +++ b/ext/vorbis/gstvorbisenc.c @@ -89,28 +89,6 @@ enum static GstFlowReturn gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc); -/* this function takes into account the granulepos_offset and the subgranule - * time offset */ -static GstClockTime -granulepos_to_timestamp_offset (GstVorbisEnc * vorbisenc, - ogg_int64_t granulepos) -{ - if (granulepos >= 0) - return gst_util_uint64_scale ((guint64) granulepos - + vorbisenc->granulepos_offset, GST_SECOND, vorbisenc->frequency) - + vorbisenc->subgranule_offset; - return GST_CLOCK_TIME_NONE; -} - -/* this function does a straight granulepos -> timestamp conversion */ -static GstClockTime -granulepos_to_timestamp (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos) -{ - if (granulepos >= 0) - return gst_util_uint64_scale ((guint64) granulepos, - GST_SECOND, vorbisenc->frequency); - return GST_CLOCK_TIME_NONE; -} #define MAX_BITRATE_DEFAULT -1 #define BITRATE_DEFAULT -1 @@ -119,8 +97,16 @@ granulepos_to_timestamp (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos) #define LOWEST_BITRATE 6000 /* lowest allowed for a 8 kHz stream */ #define HIGHEST_BITRATE 250001 /* highest allowed for a 44 kHz stream */ -static gboolean gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer); +static gboolean gst_vorbis_enc_start (GstAudioEncoder * enc); +static gboolean gst_vorbis_enc_stop (GstAudioEncoder * enc); +static gboolean gst_vorbis_enc_set_format (GstAudioEncoder * enc, + GstAudioInfo * info); +static GstFlowReturn gst_vorbis_enc_handle_frame (GstAudioEncoder * enc, + GstBuffer * in_buf); +static GstCaps *gst_vorbis_enc_getcaps (GstAudioEncoder * enc); +static gboolean gst_vorbis_enc_sink_event (GstAudioEncoder * enc, + GstEvent * event); + static gboolean gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc); static void gst_vorbis_enc_dispose (GObject * object); @@ -128,36 +114,30 @@ static void gst_vorbis_enc_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_vorbis_enc_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static GstStateChangeReturn gst_vorbis_enc_change_state (GstElement * element, - GstStateChange transition); static void gst_vorbis_enc_add_interfaces (GType vorbisenc_type); -GST_BOILERPLATE_FULL (GstVorbisEnc, gst_vorbis_enc, GstElement, - GST_TYPE_ELEMENT, gst_vorbis_enc_add_interfaces); +GST_BOILERPLATE_FULL (GstVorbisEnc, gst_vorbis_enc, GstAudioEncoder, + GST_TYPE_AUDIO_ENCODER, gst_vorbis_enc_add_interfaces); static void gst_vorbis_enc_add_interfaces (GType vorbisenc_type) { static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL }; - static const GInterfaceInfo preset_info = { NULL, NULL, NULL }; g_type_add_interface_static (vorbisenc_type, GST_TYPE_TAG_SETTER, &tag_setter_info); - g_type_add_interface_static (vorbisenc_type, GST_TYPE_PRESET, &preset_info); } static void gst_vorbis_enc_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - GstPadTemplate *src_template, *sink_template; + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&vorbis_enc_src_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&vorbis_enc_sink_factory)); - src_template = gst_static_pad_template_get (&vorbis_enc_src_factory); - gst_element_class_add_pad_template (element_class, src_template); - - sink_template = gst_static_pad_template_get (&vorbis_enc_sink_factory); - gst_element_class_add_pad_template (element_class, sink_template); gst_element_class_set_details_simple (element_class, "Vorbis audio encoder", "Codec/Encoder/Audio", "Encodes audio in Vorbis format", @@ -168,15 +148,22 @@ static void gst_vorbis_enc_class_init (GstVorbisEncClass * klass) { GObjectClass *gobject_class; - GstElementClass *gstelement_class; + GstAudioEncoderClass *base_class; gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; + base_class = (GstAudioEncoderClass *) (klass); gobject_class->set_property = gst_vorbis_enc_set_property; gobject_class->get_property = gst_vorbis_enc_get_property; gobject_class->dispose = gst_vorbis_enc_dispose; + base_class->start = GST_DEBUG_FUNCPTR (gst_vorbis_enc_start); + base_class->stop = GST_DEBUG_FUNCPTR (gst_vorbis_enc_stop); + base_class->set_format = GST_DEBUG_FUNCPTR (gst_vorbis_enc_set_format); + base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_vorbis_enc_handle_frame); + base_class->getcaps = GST_DEBUG_FUNCPTR (gst_vorbis_enc_getcaps); + base_class->event = GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_event); + g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_BITRATE, g_param_spec_int ("max-bitrate", "Maximum Bitrate", "Specify a maximum bitrate (in bps). Useful for streaming " @@ -207,9 +194,27 @@ gst_vorbis_enc_class_init (GstVorbisEncClass * klass) g_param_spec_string ("last-message", "last-message", "The last status message", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); +} - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_vorbis_enc_change_state); +static void +gst_vorbis_enc_init (GstVorbisEnc * vorbisenc, GstVorbisEncClass * klass) +{ + GstAudioEncoder *enc = GST_AUDIO_ENCODER (vorbisenc); + + vorbisenc->channels = -1; + vorbisenc->frequency = -1; + + vorbisenc->managed = FALSE; + vorbisenc->max_bitrate = MAX_BITRATE_DEFAULT; + vorbisenc->bitrate = BITRATE_DEFAULT; + vorbisenc->min_bitrate = MIN_BITRATE_DEFAULT; + vorbisenc->quality = QUALITY_DEFAULT; + vorbisenc->quality_set = FALSE; + vorbisenc->last_message = NULL; + + /* arrange granulepos marking (and required perfect ts) */ + gst_audio_encoder_set_mark_granule (enc, TRUE); + gst_audio_encoder_set_perfect_timestamp (enc, TRUE); } static void @@ -225,6 +230,35 @@ gst_vorbis_enc_dispose (GObject * object) G_OBJECT_CLASS (parent_class)->dispose (object); } +static gboolean +gst_vorbis_enc_start (GstAudioEncoder * enc) +{ + GstVorbisEnc *vorbisenc = GST_VORBISENC (enc); + + GST_DEBUG_OBJECT (enc, "start"); + vorbisenc->tags = gst_tag_list_new (); + vorbisenc->header_sent = FALSE; + + return TRUE; +} + +static gboolean +gst_vorbis_enc_stop (GstAudioEncoder * enc) +{ + GstVorbisEnc *vorbisenc = GST_VORBISENC (enc); + + GST_DEBUG_OBJECT (enc, "stop"); + vorbis_block_clear (&vorbisenc->vb); + vorbis_dsp_clear (&vorbisenc->vd); + vorbis_info_clear (&vorbisenc->vi); + g_free (vorbisenc->last_message); + vorbisenc->last_message = NULL; + gst_tag_list_free (vorbisenc->tags); + vorbisenc->tags = NULL; + + return TRUE; +} + static GstCaps * gst_vorbis_enc_generate_sink_caps (void) { @@ -277,151 +311,14 @@ gst_vorbis_enc_generate_sink_caps (void) } static GstCaps * -gst_vorbis_enc_sink_getcaps (GstPad * pad) +gst_vorbis_enc_getcaps (GstAudioEncoder * enc) { - GstVorbisEnc *vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); + GstVorbisEnc *vorbisenc = GST_VORBISENC (enc); if (vorbisenc->sinkcaps == NULL) vorbisenc->sinkcaps = gst_vorbis_enc_generate_sink_caps (); - return gst_caps_ref (vorbisenc->sinkcaps); -} - -static gboolean -gst_vorbis_enc_sink_setcaps (GstPad * pad, GstCaps * caps) -{ - GstVorbisEnc *vorbisenc; - GstStructure *structure; - - vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); - vorbisenc->setup = FALSE; - - structure = gst_caps_get_structure (caps, 0); - gst_structure_get_int (structure, "channels", &vorbisenc->channels); - gst_structure_get_int (structure, "rate", &vorbisenc->frequency); - - gst_vorbis_enc_setup (vorbisenc); - - if (vorbisenc->setup) - return TRUE; - - return FALSE; -} - -static gboolean -gst_vorbis_enc_convert_src (GstPad * pad, GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - GstVorbisEnc *vorbisenc; - gint64 avg; - - vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); - - if (vorbisenc->samples_in == 0 || - vorbisenc->bytes_out == 0 || vorbisenc->frequency == 0) { - gst_object_unref (vorbisenc); - return FALSE; - } - - avg = (vorbisenc->bytes_out * vorbisenc->frequency) / (vorbisenc->samples_in); - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, avg); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = gst_util_uint64_scale_int (src_value, avg, GST_SECOND); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - gst_object_unref (vorbisenc); - return res; -} - -static gboolean -gst_vorbis_enc_convert_sink (GstPad * pad, GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value) -{ - gboolean res = TRUE; - guint scale = 1; - gint bytes_per_sample; - GstVorbisEnc *vorbisenc; - - vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); - - bytes_per_sample = vorbisenc->channels * 2; - - switch (src_format) { - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - if (bytes_per_sample == 0) - return FALSE; - *dest_value = src_value / bytes_per_sample; - break; - case GST_FORMAT_TIME: - { - gint byterate = bytes_per_sample * vorbisenc->frequency; - - if (byterate == 0) - return FALSE; - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, byterate); - break; - } - default: - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * bytes_per_sample; - break; - case GST_FORMAT_TIME: - if (vorbisenc->frequency == 0) - return FALSE; - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, - vorbisenc->frequency); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = bytes_per_sample; - /* fallthrough */ - case GST_FORMAT_DEFAULT: - *dest_value = - gst_util_uint64_scale_int (src_value, - scale * vorbisenc->frequency, GST_SECOND); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } - gst_object_unref (vorbisenc); - return res; + return gst_audio_encoder_proxy_getcaps (enc, vorbisenc->sinkcaps); } static gint64 @@ -433,177 +330,26 @@ gst_vorbis_enc_get_latency (GstVorbisEnc * vorbisenc) return 58 * GST_MSECOND; } -static const GstQueryType * -gst_vorbis_enc_get_query_types (GstPad * pad) -{ - static const GstQueryType gst_vorbis_enc_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_CONVERT, - 0 - }; - - return gst_vorbis_enc_src_query_types; -} - static gboolean -gst_vorbis_enc_src_query (GstPad * pad, GstQuery * query) +gst_vorbis_enc_set_format (GstAudioEncoder * enc, GstAudioInfo * info) { - gboolean res = TRUE; GstVorbisEnc *vorbisenc; - GstPad *peerpad; - vorbisenc = GST_VORBISENC (gst_pad_get_parent (pad)); - peerpad = gst_pad_get_peer (GST_PAD (vorbisenc->sinkpad)); + vorbisenc = GST_VORBISENC (enc); - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat fmt, req_fmt; - gint64 pos, val; + vorbisenc->channels = GST_AUDIO_INFO_CHANNELS (info); + vorbisenc->frequency = GST_AUDIO_INFO_RATE (info); - gst_query_parse_position (query, &req_fmt, NULL); - if ((res = gst_pad_query_position (peerpad, &req_fmt, &val))) { - gst_query_set_position (query, req_fmt, val); - break; - } + /* if re-configured, we were drained and cleared already */ + if (!gst_vorbis_enc_setup (vorbisenc)) + return FALSE; - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_position (peerpad, &fmt, &pos))) - break; + /* feedback to base class */ + gst_audio_encoder_set_latency (enc, + gst_vorbis_enc_get_latency (vorbisenc), + gst_vorbis_enc_get_latency (vorbisenc)); - if ((res = gst_pad_query_convert (peerpad, fmt, pos, &req_fmt, &val))) { - gst_query_set_position (query, req_fmt, val); - } - break; - } - case GST_QUERY_DURATION: - { - GstFormat fmt, req_fmt; - gint64 dur, val; - - gst_query_parse_duration (query, &req_fmt, NULL); - if ((res = gst_pad_query_duration (peerpad, &req_fmt, &val))) { - gst_query_set_duration (query, req_fmt, val); - break; - } - - fmt = GST_FORMAT_TIME; - if (!(res = gst_pad_query_duration (peerpad, &fmt, &dur))) - break; - - if ((res = gst_pad_query_convert (peerpad, fmt, dur, &req_fmt, &val))) { - gst_query_set_duration (query, req_fmt, val); - } - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - gst_vorbis_enc_convert_src (pad, src_fmt, src_val, &dest_fmt, - &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - case GST_QUERY_LATENCY: - { - gboolean live; - GstClockTime min_latency, max_latency; - gint64 latency; - - if ((res = gst_pad_query (peerpad, query))) { - gst_query_parse_latency (query, &live, &min_latency, &max_latency); - - latency = gst_vorbis_enc_get_latency (vorbisenc); - - /* add our latency */ - min_latency += latency; - if (max_latency != -1) - max_latency += latency; - - gst_query_set_latency (query, live, min_latency, max_latency); - } - break; - } - default: - res = gst_pad_query (peerpad, query); - break; - } - -error: - gst_object_unref (peerpad); - gst_object_unref (vorbisenc); - return res; -} - -static gboolean -gst_vorbis_enc_sink_query (GstPad * pad, GstQuery * query) -{ - gboolean res = TRUE; - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - gst_vorbis_enc_convert_sink (pad, src_fmt, src_val, &dest_fmt, - &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - -error: - return res; -} - -static void -gst_vorbis_enc_init (GstVorbisEnc * vorbisenc, GstVorbisEncClass * klass) -{ - vorbisenc->sinkpad = - gst_pad_new_from_static_template (&vorbis_enc_sink_factory, "sink"); - gst_pad_set_event_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_event)); - gst_pad_set_chain_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_chain)); - gst_pad_set_setcaps_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_setcaps)); - gst_pad_set_getcaps_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_getcaps)); - gst_pad_set_query_function (vorbisenc->sinkpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_sink_query)); - gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->sinkpad); - - vorbisenc->srcpad = - gst_pad_new_from_static_template (&vorbis_enc_src_factory, "src"); - gst_pad_set_query_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_src_query)); - gst_pad_set_query_type_function (vorbisenc->srcpad, - GST_DEBUG_FUNCPTR (gst_vorbis_enc_get_query_types)); - gst_element_add_pad (GST_ELEMENT (vorbisenc), vorbisenc->srcpad); - - vorbisenc->channels = -1; - vorbisenc->frequency = -1; - - vorbisenc->managed = FALSE; - vorbisenc->max_bitrate = MAX_BITRATE_DEFAULT; - vorbisenc->bitrate = BITRATE_DEFAULT; - vorbisenc->min_bitrate = MIN_BITRATE_DEFAULT; - vorbisenc->quality = QUALITY_DEFAULT; - vorbisenc->quality_set = FALSE; - vorbisenc->last_message = NULL; + return TRUE; } static void @@ -722,7 +468,8 @@ update_start_message (GstVorbisEnc * vorbisenc) static gboolean gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc) { - vorbisenc->setup = FALSE; + + GST_LOG_OBJECT (vorbisenc, "setup"); if (vorbisenc->bitrate < 0 && vorbisenc->min_bitrate < 0 && vorbisenc->max_bitrate < 0) { @@ -789,8 +536,10 @@ gst_vorbis_enc_setup (GstVorbisEnc * vorbisenc) vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi); vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb); - vorbisenc->next_ts = 0; + /* samples == granulepos start at 0 again */ + vorbisenc->samples_out = 0; + /* fresh encoder available */ vorbisenc->setup = TRUE; return TRUE; @@ -805,6 +554,7 @@ gst_vorbis_enc_clear (GstVorbisEnc * vorbisenc) vorbis_analysis_wrote (&vorbisenc->vd, 0); ret = gst_vorbis_enc_output_buffers (vorbisenc); + /* marked EOS to encoder, recreate if needed */ vorbisenc->setup = FALSE; } @@ -813,50 +563,9 @@ gst_vorbis_enc_clear (GstVorbisEnc * vorbisenc) vorbis_dsp_clear (&vorbisenc->vd); vorbis_info_clear (&vorbisenc->vi); - vorbisenc->header_sent = FALSE; - return ret; } -/* prepare a buffer for transmission by passing data through libvorbis */ -static GstBuffer * -gst_vorbis_enc_buffer_from_packet (GstVorbisEnc * vorbisenc, - ogg_packet * packet) -{ - GstBuffer *outbuf; - - outbuf = gst_buffer_new_and_alloc (packet->bytes); - memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes); - /* see ext/ogg/README; OFFSET_END takes "our" granulepos, OFFSET its - * time representation */ - GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos + - vorbisenc->granulepos_offset; - GST_BUFFER_OFFSET (outbuf) = granulepos_to_timestamp (vorbisenc, - GST_BUFFER_OFFSET_END (outbuf)); - GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts; - - /* update the next timestamp, taking granulepos_offset and subgranule offset - * into account */ - vorbisenc->next_ts = - granulepos_to_timestamp_offset (vorbisenc, packet->granulepos) + - vorbisenc->initial_ts; - GST_BUFFER_DURATION (outbuf) = - vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf); - - if (vorbisenc->next_discont) { - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - vorbisenc->next_discont = FALSE; - } - - gst_buffer_set_caps (outbuf, vorbisenc->srccaps); - - GST_LOG_OBJECT (vorbisenc, "encoded buffer of %d bytes", - GST_BUFFER_SIZE (outbuf)); - return outbuf; -} - -/* the same as above, but different logic for setting timestamp and granulepos - * */ static GstBuffer * gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc, ogg_packet * packet) @@ -870,99 +579,19 @@ gst_vorbis_enc_buffer_from_header_packet (GstVorbisEnc * vorbisenc, GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE; - gst_buffer_set_caps (outbuf, vorbisenc->srccaps); - GST_DEBUG ("created header packet buffer, %d bytes", GST_BUFFER_SIZE (outbuf)); return outbuf; } -/* push out the buffer and do internal bookkeeping */ -static GstFlowReturn -gst_vorbis_enc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer) -{ - vorbisenc->bytes_out += GST_BUFFER_SIZE (buffer); - - GST_DEBUG_OBJECT (vorbisenc, - "Pushing buffer with GP %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT, - GST_BUFFER_OFFSET_END (buffer), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); - return gst_pad_push (vorbisenc->srcpad, buffer); -} - -static GstFlowReturn -gst_vorbis_enc_push_packet (GstVorbisEnc * vorbisenc, ogg_packet * packet) -{ - GstBuffer *outbuf; - - outbuf = gst_vorbis_enc_buffer_from_packet (vorbisenc, packet); - return gst_vorbis_enc_push_buffer (vorbisenc, outbuf); -} - -/* Set a copy of these buffers as 'streamheader' on the caps. - * We need a copy to avoid these buffers ending up with (indirect) refs on - * themselves - */ -static GstCaps * -gst_vorbis_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, - GstBuffer * buf2, GstBuffer * buf3) -{ - GstBuffer *buf; - GstStructure *structure; - GValue array = { 0 }; - GValue value = { 0 }; - - caps = gst_caps_make_writable (caps); - structure = gst_caps_get_structure (caps, 0); - - /* mark buffers */ - GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS); - GST_BUFFER_FLAG_SET (buf3, GST_BUFFER_FLAG_IN_CAPS); - - /* put buffers in a fixed list */ - g_value_init (&array, GST_TYPE_ARRAY); - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf1); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf2); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - g_value_init (&value, GST_TYPE_BUFFER); - buf = gst_buffer_copy (buf3); - gst_value_set_buffer (&value, buf); - gst_buffer_unref (buf); - gst_value_array_append_value (&array, &value); - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&value); - g_value_unset (&array); - - return caps; -} - static gboolean -gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event) +gst_vorbis_enc_sink_event (GstAudioEncoder * enc, GstEvent * event) { - gboolean res = TRUE; GstVorbisEnc *vorbisenc; - vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); + vorbisenc = GST_VORBISENC (enc); switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - /* Tell the library we're at end of stream so that it can handle - * the last frame and mark end of stream in the output properly */ - GST_DEBUG_OBJECT (vorbisenc, "EOS, clearing state and sending event on"); - gst_vorbis_enc_clear (vorbisenc); - - res = gst_pad_push_event (vorbisenc->srcpad, event); - break; case GST_EVENT_TAG: if (vorbisenc->tags) { GstTagList *list; @@ -973,68 +602,86 @@ gst_vorbis_enc_sink_event (GstPad * pad, GstEvent * event) } else { g_assert_not_reached (); } - res = gst_pad_push_event (vorbisenc->srcpad, event); break; - case GST_EVENT_NEWSEGMENT: - { - gboolean update; - gdouble rate, applied_rate; - GstFormat format; - gint64 start, stop, position; - - gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate, - &format, &start, &stop, &position); - if (format == GST_FORMAT_TIME) { - gst_segment_set_newsegment (&vorbisenc->segment, update, rate, format, - start, stop, position); - if (vorbisenc->initial_ts == GST_CLOCK_TIME_NONE) { - GST_DEBUG_OBJECT (vorbisenc, "Initial segment %" GST_SEGMENT_FORMAT, - &vorbisenc->segment); - vorbisenc->initial_ts = start; - } - } - } /* fall through */ default: - res = gst_pad_push_event (vorbisenc->srcpad, event); break; } - return res; + + /* we only peeked, let base class handle it */ + return FALSE; } -static gboolean -gst_vorbis_enc_buffer_check_discontinuous (GstVorbisEnc * vorbisenc, - GstClockTime timestamp, GstClockTime duration) +/* push out the buffer and do internal bookkeeping */ +static GstFlowReturn +gst_vorbis_enc_push_header (GstVorbisEnc * vorbisenc, GstBuffer * buffer) { - gboolean ret = FALSE; + GST_DEBUG_OBJECT (vorbisenc, + "Pushing buffer with GP %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT, + GST_BUFFER_OFFSET_END (buffer), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); + return gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (vorbisenc), buffer); +} - if (timestamp != GST_CLOCK_TIME_NONE && - vorbisenc->expected_ts != GST_CLOCK_TIME_NONE && - timestamp + duration != vorbisenc->expected_ts) { - /* It turns out that a lot of elements don't generate perfect streams due - * to rounding errors. So, we permit small errors (< 3 samples) without - * causing a discont. - */ - int threesample = GST_SECOND / vorbisenc->frequency * 3; +/* + * (really really) FIXME: move into core (dixit tpm) + */ +/** + * _gst_caps_set_buffer_array: + * @caps: a #GstCaps + * @field: field in caps to set + * @buf: header buffers + * + * Adds given buffers to an array of buffers set as the given @field + * on the given @caps. List of buffer arguments must be NULL-terminated. + * + * Returns: input caps with a streamheader field added, or NULL if some error + */ +static GstCaps * +_gst_caps_set_buffer_array (GstCaps * caps, const gchar * field, + GstBuffer * buf, ...) +{ + GstStructure *structure = NULL; + va_list va; + GValue array = { 0 }; + GValue value = { 0 }; - if ((GstClockTimeDiff) (timestamp - vorbisenc->expected_ts) > threesample) { - GST_DEBUG_OBJECT (vorbisenc, "Expected TS %" GST_TIME_FORMAT - ", buffer TS %" GST_TIME_FORMAT, - GST_TIME_ARGS (vorbisenc->expected_ts), GST_TIME_ARGS (timestamp)); - ret = TRUE; - } + g_return_val_if_fail (caps != NULL, NULL); + g_return_val_if_fail (gst_caps_is_fixed (caps), NULL); + g_return_val_if_fail (field != NULL, NULL); + + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + + g_value_init (&array, GST_TYPE_ARRAY); + + va_start (va, buf); + /* put buffers in a fixed list */ + while (buf) { + g_assert (gst_buffer_is_metadata_writable (buf)); + + /* mark buffer */ + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + + g_value_init (&value, GST_TYPE_BUFFER); + buf = gst_buffer_copy (buf); + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); + gst_value_set_buffer (&value, buf); + gst_buffer_unref (buf); + gst_value_array_append_value (&array, &value); + g_value_unset (&value); + + buf = va_arg (va, GstBuffer *); } - if (timestamp != GST_CLOCK_TIME_NONE && duration != GST_CLOCK_TIME_NONE) { - vorbisenc->expected_ts = timestamp + duration; - } else - vorbisenc->expected_ts = GST_CLOCK_TIME_NONE; + gst_structure_set_value (structure, field, &array); + g_value_unset (&array); - return ret; + return caps; } static GstFlowReturn -gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer) +gst_vorbis_enc_handle_frame (GstAudioEncoder * enc, GstBuffer * buffer) { GstVorbisEnc *vorbisenc; GstFlowReturn ret = GST_FLOW_OK; @@ -1043,27 +690,22 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer) gulong i, j; float **vorbis_buffer; GstBuffer *buf1, *buf2, *buf3; - gboolean first = FALSE; - GstClockTime timestamp = GST_CLOCK_TIME_NONE; - GstClockTime running_time = GST_CLOCK_TIME_NONE; - vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); + vorbisenc = GST_VORBISENC (enc); - if (!vorbisenc->setup) - goto not_setup; - - buffer = gst_audio_buffer_clip (buffer, &vorbisenc->segment, - vorbisenc->frequency, 4 * vorbisenc->channels); - if (buffer == NULL) { - GST_DEBUG_OBJECT (vorbisenc, "Dropping buffer, out of segment"); - return GST_FLOW_OK; + if (G_UNLIKELY (!vorbisenc->setup)) { + if (buffer) { + GST_DEBUG_OBJECT (vorbisenc, "forcing setup"); + /* should not fail, as setup before same way */ + if (!gst_vorbis_enc_setup (vorbisenc)) + return GST_FLOW_ERROR; + } else { + /* end draining */ + GST_LOG_OBJECT (vorbisenc, "already drained"); + return GST_FLOW_OK; + } } - running_time = - gst_segment_to_running_time (&vorbisenc->segment, GST_FORMAT_TIME, - GST_BUFFER_TIMESTAMP (buffer)); - timestamp = running_time + vorbisenc->initial_ts; - GST_DEBUG_OBJECT (vorbisenc, "Initial ts is %" GST_TIME_FORMAT, - GST_TIME_ARGS (vorbisenc->initial_ts)); + if (!vorbisenc->header_sent) { /* Vorbis streams begin with three headers; the initial header (with most of the codec setup parameters) which is mandated by the Ogg @@ -1076,11 +718,6 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer) ogg_packet header_code; GstCaps *caps; - /* first, make sure header buffers get timestamp == 0 */ - vorbisenc->next_ts = 0; - vorbisenc->granulepos_offset = 0; - vorbisenc->subgranule_offset = 0; - GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets"); gst_vorbis_enc_set_metadata (vorbisenc); vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, @@ -1093,112 +730,36 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer) buf3 = gst_vorbis_enc_buffer_from_header_packet (vorbisenc, &header_code); /* mark and put on caps */ - vorbisenc->srccaps = gst_caps_new_simple ("audio/x-vorbis", NULL); - caps = vorbisenc->srccaps; - caps = gst_vorbis_enc_set_header_on_caps (caps, buf1, buf2, buf3); + caps = gst_caps_new_simple ("audio/x-vorbis", NULL); + caps = _gst_caps_set_buffer_array (caps, "streamheader", + buf1, buf2, buf3, NULL); /* negotiate with these caps */ - GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (vorbisenc->srcpad, caps); + GST_DEBUG_OBJECT (vorbisenc, "here are the caps: %" GST_PTR_FORMAT, caps); gst_buffer_set_caps (buf1, caps); gst_buffer_set_caps (buf2, caps); gst_buffer_set_caps (buf3, caps); + gst_caps_unref (caps); /* push out buffers */ /* push_buffer takes the reference even for failure */ - if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK) + if ((ret = gst_vorbis_enc_push_header (vorbisenc, buf1)) != GST_FLOW_OK) goto failed_header_push; - if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK) { + if ((ret = gst_vorbis_enc_push_header (vorbisenc, buf2)) != GST_FLOW_OK) { buf2 = NULL; goto failed_header_push; } - if ((ret = gst_vorbis_enc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK) { + if ((ret = gst_vorbis_enc_push_header (vorbisenc, buf3)) != GST_FLOW_OK) { buf3 = NULL; goto failed_header_push; } - /* now adjust starting granulepos accordingly if the buffer's timestamp is - nonzero */ - vorbisenc->next_ts = timestamp; - vorbisenc->expected_ts = timestamp; - vorbisenc->granulepos_offset = gst_util_uint64_scale - (running_time, vorbisenc->frequency, GST_SECOND); - vorbisenc->subgranule_offset = 0; - vorbisenc->subgranule_offset = - (vorbisenc->next_ts - vorbisenc->initial_ts) - - granulepos_to_timestamp_offset (vorbisenc, 0); - vorbisenc->header_sent = TRUE; - first = TRUE; } - if (vorbisenc->expected_ts != GST_CLOCK_TIME_NONE && - timestamp < vorbisenc->expected_ts) { - int threesample = GST_SECOND / vorbisenc->frequency * 3; - guint64 diff = vorbisenc->expected_ts - timestamp; - guint64 diff_bytes; - - /* Don't freak out on tiny jitters; use the same < 3 sample - tolerance as in the discontinuous detection */ - if ((GstClockTimeDiff) (vorbisenc->expected_ts - timestamp) > threesample) { - - GST_WARNING_OBJECT (vorbisenc, "Buffer is older than previous " - "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT - "), cannot handle. Clipping buffer.", - GST_TIME_ARGS (timestamp), GST_TIME_ARGS (vorbisenc->expected_ts)); - - diff_bytes = - GST_CLOCK_TIME_TO_FRAMES (diff, - vorbisenc->frequency) * vorbisenc->channels * sizeof (gfloat); - if (diff_bytes >= GST_BUFFER_SIZE (buffer)) { - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - buffer = gst_buffer_make_metadata_writable (buffer); - GST_BUFFER_DATA (buffer) += diff_bytes; - GST_BUFFER_SIZE (buffer) -= diff_bytes; - - if (GST_BUFFER_DURATION_IS_VALID (buffer)) - GST_BUFFER_DURATION (buffer) -= diff; - } - - /* adjust the input timestamp in either case */ - GST_BUFFER_TIMESTAMP (buffer) += diff; - } - - if (gst_vorbis_enc_buffer_check_discontinuous (vorbisenc, timestamp, - GST_BUFFER_DURATION (buffer)) && !first) { - GST_WARNING_OBJECT (vorbisenc, - "Buffer is discontinuous, flushing encoder " - "and restarting (Discont from %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT - ")", GST_TIME_ARGS (vorbisenc->next_ts), GST_TIME_ARGS (timestamp)); - /* Re-initialise encoder (there's unfortunately no API to flush it) */ - if ((ret = gst_vorbis_enc_clear (vorbisenc)) != GST_FLOW_OK) - return ret; - if (!gst_vorbis_enc_setup (vorbisenc)) - return GST_FLOW_ERROR; /* Should be impossible, we can only get here if - we successfully initialised earlier */ - - /* Now, set our granulepos offset appropriately. */ - vorbisenc->next_ts = timestamp; - /* We need to round to the nearest whole number of samples, not just do - * a truncating division here */ - vorbisenc->granulepos_offset = gst_util_uint64_scale - (running_time + GST_SECOND / vorbisenc->frequency / 2 - - vorbisenc->subgranule_offset, vorbisenc->frequency, GST_SECOND); - - vorbisenc->header_sent = TRUE; - - /* And our next output buffer must have DISCONT set on it */ - vorbisenc->next_discont = TRUE; - } - - /* Sending zero samples to libvorbis marks EOS, so we mustn't do that */ - if (GST_BUFFER_SIZE (buffer) == 0) { - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } + if (!buffer) + return gst_vorbis_enc_clear (vorbisenc); /* data to encode */ data = (gfloat *) GST_BUFFER_DATA (buffer); @@ -1221,20 +782,11 @@ gst_vorbis_enc_chain (GstPad * pad, GstBuffer * buffer) vorbisenc->samples_in += size; - gst_buffer_unref (buffer); - ret = gst_vorbis_enc_output_buffers (vorbisenc); return ret; /* error cases */ -not_setup: - { - gst_buffer_unref (buffer); - GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL), - ("encoder not initialized (input is not audio?)")); - return GST_FLOW_UNEXPECTED; - } failed_header_push: { GST_WARNING_OBJECT (vorbisenc, "Failed to push headers"); @@ -1243,7 +795,6 @@ failed_header_push: gst_buffer_unref (buf2); if (buf3) gst_buffer_unref (buf3); - gst_buffer_unref (buffer); return ret; } } @@ -1266,8 +817,16 @@ gst_vorbis_enc_output_buffers (GstVorbisEnc * vorbisenc) vorbis_bitrate_addblock (&vorbisenc->vb); while (vorbis_bitrate_flushpacket (&vorbisenc->vd, &op)) { + GstBuffer *buf; + GST_LOG_OBJECT (vorbisenc, "pushing out a data packet"); - ret = gst_vorbis_enc_push_packet (vorbisenc, &op); + buf = gst_buffer_new_and_alloc (op.bytes); + memcpy (GST_BUFFER_DATA (buf), op.packet, op.bytes); + /* tracking granulepos should tell us samples accounted for */ + ret = + gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER + (vorbisenc), buf, op.granulepos - vorbisenc->samples_out); + vorbisenc->samples_out = op.granulepos; if (ret != GST_FLOW_OK) return ret; @@ -1383,53 +942,3 @@ gst_vorbis_enc_set_property (GObject * object, guint prop_id, break; } } - -static GstStateChangeReturn -gst_vorbis_enc_change_state (GstElement * element, GstStateChange transition) -{ - GstVorbisEnc *vorbisenc = GST_VORBISENC (element); - GstStateChangeReturn res; - - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - vorbisenc->tags = gst_tag_list_new (); - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - vorbisenc->setup = FALSE; - vorbisenc->next_discont = FALSE; - vorbisenc->header_sent = FALSE; - gst_segment_init (&vorbisenc->segment, GST_FORMAT_TIME); - vorbisenc->initial_ts = GST_CLOCK_TIME_NONE; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - vorbis_block_clear (&vorbisenc->vb); - vorbis_dsp_clear (&vorbisenc->vd); - vorbis_info_clear (&vorbisenc->vi); - g_free (vorbisenc->last_message); - vorbisenc->last_message = NULL; - if (vorbisenc->srccaps) { - gst_caps_unref (vorbisenc->srccaps); - vorbisenc->srccaps = NULL; - } - break; - case GST_STATE_CHANGE_READY_TO_NULL: - gst_tag_list_free (vorbisenc->tags); - vorbisenc->tags = NULL; - default: - break; - } - - return res; -} diff --git a/ext/vorbis/gstvorbisenc.h b/ext/vorbis/gstvorbisenc.h index 9375a16c80..18d1e55f83 100644 --- a/ext/vorbis/gstvorbisenc.h +++ b/ext/vorbis/gstvorbisenc.h @@ -23,6 +23,7 @@ #include +#include #include @@ -48,14 +49,11 @@ typedef struct _GstVorbisEncClass GstVorbisEncClass; * Opaque data structure. */ struct _GstVorbisEnc { - GstElement element; + GstAudioEncoder element; - GstPad *sinkpad; - GstPad *srcpad; - - GstCaps *srccaps; GstCaps *sinkcaps; + /* codec */ vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ vorbis_comment vc; /* struct that stores all the user comments */ @@ -63,6 +61,7 @@ struct _GstVorbisEnc { vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ vorbis_block vb; /* local working space for packet->PCM decode */ + /* properties */ gboolean managed; gint bitrate; gint min_bitrate; @@ -74,14 +73,8 @@ struct _GstVorbisEnc { gint frequency; guint64 samples_in; + guint64 samples_out; guint64 bytes_out; - GstClockTime next_ts; - GstClockTime expected_ts; - gboolean next_discont; - guint64 granulepos_offset; - gint64 subgranule_offset; - GstSegment segment; - GstClockTime initial_ts; GstTagList * tags; @@ -91,7 +84,7 @@ struct _GstVorbisEnc { }; struct _GstVorbisEncClass { - GstElementClass parent_class; + GstAudioEncoderClass parent_class; }; GType gst_vorbis_enc_get_type(void); From 77069f01b15f5bcc9eddba1e8fb0bb2ab00e8446 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 7 Oct 2011 14:32:33 +0200 Subject: [PATCH 09/11] audiodecoder: make upstream queries and events MT-safe --- gst-libs/gst/audio/gstaudiodecoder.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/gst-libs/gst/audio/gstaudiodecoder.c b/gst-libs/gst/audio/gstaudiodecoder.c index 8ad677a098..3843f22050 100644 --- a/gst-libs/gst/audio/gstaudiodecoder.c +++ b/gst-libs/gst/audio/gstaudiodecoder.c @@ -1556,6 +1556,10 @@ gst_audio_decoder_src_event (GstPad * pad, GstEvent * event) gboolean res = FALSE; dec = GST_AUDIO_DECODER (gst_pad_get_parent (pad)); + if (G_UNLIKELY (dec == NULL)) { + gst_event_unref (event); + return FALSE; + } GST_DEBUG_OBJECT (dec, "received event %d, %s", GST_EVENT_TYPE (event), GST_EVENT_TYPE_NAME (event)); @@ -1754,6 +1758,9 @@ gst_audio_decoder_src_query (GstPad * pad, GstQuery * query) gboolean res = FALSE; dec = GST_AUDIO_DECODER (GST_PAD_PARENT (pad)); + if (G_UNLIKELY (dec == NULL)) + return FALSE; + peerpad = gst_pad_get_peer (GST_PAD (dec->sinkpad)); GST_LOG_OBJECT (dec, "handling query: %" GST_PTR_FORMAT, query); From 37c629fcc6c6862ccb01c75affc0d16f36bc7282 Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 7 Oct 2011 14:33:04 +0200 Subject: [PATCH 10/11] audioencoder: make upstream queries MT-safe --- gst-libs/gst/audio/gstaudioencoder.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gst-libs/gst/audio/gstaudioencoder.c b/gst-libs/gst/audio/gstaudioencoder.c index 383da3f3c0..5051efa3cc 100644 --- a/gst-libs/gst/audio/gstaudioencoder.c +++ b/gst-libs/gst/audio/gstaudioencoder.c @@ -1495,6 +1495,9 @@ gst_audio_encoder_src_query (GstPad * pad, GstQuery * query) gboolean res = FALSE; enc = GST_AUDIO_ENCODER (GST_PAD_PARENT (pad)); + if (G_UNLIKELY (enc == NULL)) + return FALSE; + peerpad = gst_pad_get_peer (GST_PAD (enc->sinkpad)); GST_LOG_OBJECT (enc, "handling query: %" GST_PTR_FORMAT, query); From f63f09483f22fd7e2672d3df58be8d8a69b4603c Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Fri, 7 Oct 2011 14:52:33 +0200 Subject: [PATCH 11/11] vorbisdec: port to audiodecoder --- ext/vorbis/Makefile.am | 3 +- ext/vorbis/gstvorbisdec.c | 899 ++++++-------------------------------- ext/vorbis/gstvorbisdec.h | 24 +- 3 files changed, 131 insertions(+), 795 deletions(-) diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am index 59f57a2603..9678a31b9e 100644 --- a/ext/vorbis/Makefile.am +++ b/ext/vorbis/Makefile.am @@ -28,7 +28,8 @@ plugin_LTLIBRARIES += libgstivorbisdec.la libgstivorbisdec_la_SOURCES = gstivorbisdec.c \ gstvorbisdec.c gstvorbisdeclib.c gstvorbiscommon.c -libgstivorbisdec_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ +libgstivorbisdec_la_CFLAGS = -DGST_USE_UNSTABLE_API \ + $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) \ -DTREMOR $(IVORBIS_CFLAGS) libgstivorbisdec_la_LIBADD = \ $(top_builddir)/gst-libs/gst/tag/libgsttag-@GST_MAJORMINOR@.la \ diff --git a/ext/vorbis/gstvorbisdec.c b/ext/vorbis/gstvorbisdec.c index ee90661489..6e6601a29c 100644 --- a/ext/vorbis/gstvorbisdec.c +++ b/ext/vorbis/gstvorbisdec.c @@ -64,26 +64,16 @@ GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_CAPS ("audio/x-vorbis") ); -GST_BOILERPLATE (GST_VORBIS_DEC_GLIB_TYPE_NAME, gst_vorbis_dec, GstElement, - GST_TYPE_ELEMENT); +GST_BOILERPLATE (GstVorbisDec, gst_vorbis_dec, GstAudioDecoder, + GST_TYPE_AUDIO_DECODER); static void vorbis_dec_finalize (GObject * object); -static gboolean vorbis_dec_sink_event (GstPad * pad, GstEvent * event); -static GstFlowReturn vorbis_dec_chain (GstPad * pad, GstBuffer * buffer); -static GstFlowReturn vorbis_dec_chain_forward (GstVorbisDec * vd, - gboolean discont, GstBuffer * buffer); -static GstFlowReturn vorbis_dec_chain_reverse (GstVorbisDec * vd, - gboolean discont, GstBuffer * buf); -static GstStateChangeReturn vorbis_dec_change_state (GstElement * element, - GstStateChange transition); -static gboolean vorbis_dec_src_event (GstPad * pad, GstEvent * event); -static gboolean vorbis_dec_src_query (GstPad * pad, GstQuery * query); -static gboolean vorbis_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value); - -static gboolean vorbis_dec_sink_query (GstPad * pad, GstQuery * query); +static gboolean vorbis_dec_start (GstAudioDecoder * dec); +static gboolean vorbis_dec_stop (GstAudioDecoder * dec); +static GstFlowReturn vorbis_dec_handle_frame (GstAudioDecoder * dec, + GstBuffer * buffer); +static void vorbis_dec_flush (GstAudioDecoder * dec, gboolean hard); static void gst_vorbis_dec_base_init (gpointer g_class) @@ -107,55 +97,19 @@ static void gst_vorbis_dec_class_init (GstVorbisDecClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass); gobject_class->finalize = vorbis_dec_finalize; - gstelement_class->change_state = GST_DEBUG_FUNCPTR (vorbis_dec_change_state); -} - -static const GstQueryType * -vorbis_get_query_types (GstPad * pad) -{ - static const GstQueryType vorbis_dec_src_query_types[] = { - GST_QUERY_POSITION, - GST_QUERY_DURATION, - GST_QUERY_CONVERT, - 0 - }; - - return vorbis_dec_src_query_types; + base_class->start = GST_DEBUG_FUNCPTR (vorbis_dec_start); + base_class->stop = GST_DEBUG_FUNCPTR (vorbis_dec_stop); + base_class->handle_frame = GST_DEBUG_FUNCPTR (vorbis_dec_handle_frame); + base_class->flush = GST_DEBUG_FUNCPTR (vorbis_dec_flush); } static void gst_vorbis_dec_init (GstVorbisDec * dec, GstVorbisDecClass * g_class) { - dec->sinkpad = gst_pad_new_from_static_template (&vorbis_dec_sink_factory, - "sink"); - - gst_pad_set_event_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_dec_sink_event)); - gst_pad_set_chain_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_dec_chain)); - gst_pad_set_query_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (vorbis_dec_sink_query)); - gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); - - dec->srcpad = gst_pad_new_from_static_template (&vorbis_dec_src_factory, - "src"); - - gst_pad_set_event_function (dec->srcpad, - GST_DEBUG_FUNCPTR (vorbis_dec_src_event)); - gst_pad_set_query_type_function (dec->srcpad, - GST_DEBUG_FUNCPTR (vorbis_get_query_types)); - gst_pad_set_query_function (dec->srcpad, - GST_DEBUG_FUNCPTR (vorbis_dec_src_query)); - gst_pad_use_fixed_caps (dec->srcpad); - gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - - dec->queued = NULL; - dec->pendingevents = NULL; - dec->taglist = NULL; } static void @@ -169,7 +123,6 @@ vorbis_dec_finalize (GObject * object) #ifndef USE_TREMOLO vorbis_block_clear (&vd->vb); #endif - vorbis_dsp_clear (&vd->vd); vorbis_comment_clear (&vd->vc); vorbis_info_clear (&vd->vi); @@ -180,232 +133,44 @@ vorbis_dec_finalize (GObject * object) static void gst_vorbis_dec_reset (GstVorbisDec * dec) { - dec->last_timestamp = GST_CLOCK_TIME_NONE; - dec->discont = TRUE; - dec->seqnum = gst_util_seqnum_next (); - gst_segment_init (&dec->segment, GST_FORMAT_TIME); - - g_list_foreach (dec->queued, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->queued); - dec->queued = NULL; - g_list_foreach (dec->gather, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->gather); - dec->gather = NULL; - g_list_foreach (dec->decode, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->decode); - dec->decode = NULL; - g_list_foreach (dec->pendingevents, (GFunc) gst_mini_object_unref, NULL); - g_list_free (dec->pendingevents); - dec->pendingevents = NULL; - if (dec->taglist) gst_tag_list_free (dec->taglist); dec->taglist = NULL; } - static gboolean -vorbis_dec_convert (GstPad * pad, - GstFormat src_format, gint64 src_value, - GstFormat * dest_format, gint64 * dest_value) +vorbis_dec_start (GstAudioDecoder * dec) { - gboolean res = TRUE; - GstVorbisDec *dec; - guint64 scale = 1; + GstVorbisDec *vd = GST_VORBIS_DEC (dec); - if (src_format == *dest_format) { - *dest_value = src_value; - return TRUE; - } + GST_DEBUG_OBJECT (dec, "start"); + vorbis_info_init (&vd->vi); + vorbis_comment_init (&vd->vc); + vd->initialized = FALSE; + gst_vorbis_dec_reset (vd); - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - if (!dec->initialized) - goto no_header; - - if (dec->sinkpad == pad && - (src_format == GST_FORMAT_BYTES || *dest_format == GST_FORMAT_BYTES)) - goto no_format; - - switch (src_format) { - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_BYTES: - scale = dec->width * dec->vi.channels; - case GST_FORMAT_DEFAULT: - *dest_value = - scale * gst_util_uint64_scale_int (src_value, dec->vi.rate, - GST_SECOND); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_BYTES: - *dest_value = src_value * dec->width * dec->vi.channels; - break; - case GST_FORMAT_TIME: - *dest_value = - gst_util_uint64_scale_int (src_value, GST_SECOND, dec->vi.rate); - break; - default: - res = FALSE; - } - break; - case GST_FORMAT_BYTES: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = src_value / (dec->width * dec->vi.channels); - break; - case GST_FORMAT_TIME: - *dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND, - dec->vi.rate * dec->width * dec->vi.channels); - break; - default: - res = FALSE; - } - break; - default: - res = FALSE; - } -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -no_header: - { - GST_DEBUG_OBJECT (dec, "no header packets received"); - res = FALSE; - goto done; - } -no_format: - { - GST_DEBUG_OBJECT (dec, "formats unsupported"); - res = FALSE; - goto done; - } + return TRUE; } static gboolean -vorbis_dec_src_query (GstPad * pad, GstQuery * query) +vorbis_dec_stop (GstAudioDecoder * dec) { - GstVorbisDec *dec; - gboolean res = FALSE; + GstVorbisDec *vd = GST_VORBIS_DEC (dec); - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - if (G_UNLIKELY (dec == NULL)) - return FALSE; + GST_DEBUG_OBJECT (dec, "stop"); + vd->initialized = FALSE; +#ifndef USE_TREMOLO + vorbis_block_clear (&vd->vb); +#endif + vorbis_dsp_clear (&vd->vd); + vorbis_comment_clear (&vd->vc); + vorbis_info_clear (&vd->vi); + gst_vorbis_dec_reset (vd); - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - gint64 value; - GstFormat format; - gint64 time; - - gst_query_parse_position (query, &format, NULL); - - /* we start from the last seen time */ - time = dec->last_timestamp; - /* correct for the segment values */ - time = gst_segment_to_stream_time (&dec->segment, GST_FORMAT_TIME, time); - - GST_LOG_OBJECT (dec, - "query %p: our time: %" GST_TIME_FORMAT, query, GST_TIME_ARGS (time)); - - /* and convert to the final format */ - if (!(res = - vorbis_dec_convert (pad, GST_FORMAT_TIME, time, &format, &value))) - goto error; - - gst_query_set_position (query, format, value); - - GST_LOG_OBJECT (dec, - "query %p: we return %" G_GINT64_FORMAT " (format %u)", query, value, - format); - - break; - } - case GST_QUERY_DURATION: - { - res = gst_pad_peer_query (dec->sinkpad, query); - if (!res) - goto error; - - break; - } - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -error: - { - GST_WARNING_OBJECT (dec, "error handling query"); - goto done; - } -} - -static gboolean -vorbis_dec_sink_query (GstPad * pad, GstQuery * query) -{ - GstVorbisDec *dec; - gboolean res; - - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_CONVERT: - { - GstFormat src_fmt, dest_fmt; - gint64 src_val, dest_val; - - gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val); - if (!(res = - vorbis_dec_convert (pad, src_fmt, src_val, &dest_fmt, &dest_val))) - goto error; - gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val); - break; - } - default: - res = gst_pad_query_default (pad, query); - break; - } - -done: - gst_object_unref (dec); - - return res; - - /* ERRORS */ -error: - { - GST_DEBUG_OBJECT (dec, "error converting value"); - goto done; - } + return TRUE; } +#if 0 static gboolean vorbis_dec_src_event (GstPad * pad, GstEvent * event) { @@ -413,10 +178,6 @@ vorbis_dec_src_event (GstPad * pad, GstEvent * event) GstVorbisDec *dec; dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - if (G_UNLIKELY (dec == NULL)) { - gst_event_unref (event); - return FALSE; - } switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: @@ -466,96 +227,7 @@ convert_error: goto done; } } - -static gboolean -vorbis_dec_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean ret = FALSE; - GstVorbisDec *dec; - - dec = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "handling event"); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_EOS: - if (dec->segment.rate < 0.0) - vorbis_dec_chain_reverse (dec, TRUE, NULL); - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_FLUSH_START: - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_FLUSH_STOP: - /* here we must clean any state in the decoder */ -#ifdef HAVE_VORBIS_SYNTHESIS_RESTART - vorbis_synthesis_restart (&dec->vd); #endif - gst_vorbis_dec_reset (dec); - ret = gst_pad_push_event (dec->srcpad, event); - break; - case GST_EVENT_NEWSEGMENT: - { - GstFormat format; - gdouble rate, arate; - gint64 start, stop, time; - gboolean update; - - gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, - &start, &stop, &time); - - /* we need time for now */ - if (format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - GST_DEBUG_OBJECT (dec, - "newsegment: update %d, rate %g, arate %g, start %" GST_TIME_FORMAT - ", stop %" GST_TIME_FORMAT ", time %" GST_TIME_FORMAT, - update, rate, arate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop), - GST_TIME_ARGS (time)); - - /* now configure the values */ - gst_segment_set_newsegment_full (&dec->segment, update, - rate, arate, format, start, stop, time); - dec->seqnum = gst_event_get_seqnum (event); - - if (dec->initialized) - /* and forward */ - ret = gst_pad_push_event (dec->srcpad, event); - else { - /* store it to send once we're initialized */ - dec->pendingevents = g_list_append (dec->pendingevents, event); - ret = TRUE; - } - break; - } - case GST_EVENT_TAG: - { - if (dec->initialized) - /* and forward */ - ret = gst_pad_push_event (dec->srcpad, event); - else { - /* store it to send once we're initialized */ - dec->pendingevents = g_list_append (dec->pendingevents, event); - ret = TRUE; - } - break; - } - default: - ret = gst_pad_push_event (dec->srcpad, event); - break; - } -done: - gst_object_unref (dec); - - return ret; - - /* ERRORS */ -newseg_wrong_format: - { - GST_DEBUG_OBJECT (dec, "received non TIME newsegment"); - goto done; - } -} static GstFlowReturn vorbis_handle_identification_packet (GstVorbisDec * vd) @@ -593,7 +265,7 @@ vorbis_handle_identification_packet (GstVorbisDec * vd) } /* negotiate width with downstream */ - caps = gst_pad_get_allowed_caps (vd->srcpad); + caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (vd)); if (caps) { if (!gst_caps_is_empty (caps)) { GstStructure *s; @@ -613,10 +285,11 @@ vorbis_handle_identification_packet (GstVorbisDec * vd) * for mono/stereo and avoid the depth switch in tremor case */ vd->copy_samples = get_copy_sample_func (vd->vi.channels, vd->width); - caps = gst_caps_copy (gst_pad_get_pad_template_caps (vd->srcpad)); - gst_caps_set_simple (caps, "rate", G_TYPE_INT, vd->vi.rate, - "channels", G_TYPE_INT, vd->vi.channels, - "width", G_TYPE_INT, width, NULL); + caps = + gst_caps_copy (gst_pad_get_pad_template_caps + (GST_AUDIO_DECODER_SRC_PAD (vd))); + gst_caps_set_simple (caps, "rate", G_TYPE_INT, vd->vi.rate, "channels", + G_TYPE_INT, vd->vi.channels, "width", G_TYPE_INT, width, NULL); if (pos) { gst_audio_set_channel_positions (gst_caps_get_structure (caps, 0), pos); @@ -626,7 +299,7 @@ vorbis_handle_identification_packet (GstVorbisDec * vd) g_free ((GstAudioChannelPosition *) pos); } - gst_pad_set_caps (vd->srcpad, caps); + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (vd), caps); gst_caps_unref (caps); return GST_FLOW_OK; @@ -694,8 +367,8 @@ vorbis_handle_comment_packet (GstVorbisDec * vd, ogg_packet * packet) } if (vd->initialized) { - gst_element_found_tags_for_pad (GST_ELEMENT_CAST (vd), vd->srcpad, - vd->taglist); + gst_element_found_tags_for_pad (GST_ELEMENT_CAST (vd), + GST_AUDIO_DECODER_SRC_PAD (vd), vd->taglist); vd->taglist = NULL; } else { /* Only post them as messages for the time being. * @@ -710,7 +383,6 @@ vorbis_handle_comment_packet (GstVorbisDec * vd, ogg_packet * packet) static GstFlowReturn vorbis_handle_type_packet (GstVorbisDec * vd) { - GList *walk; gint res; g_assert (vd->initialized == FALSE); @@ -728,16 +400,10 @@ vorbis_handle_type_packet (GstVorbisDec * vd) vd->initialized = TRUE; - if (vd->pendingevents) { - for (walk = vd->pendingevents; walk; walk = g_list_next (walk)) - gst_pad_push_event (vd->srcpad, GST_EVENT_CAST (walk->data)); - g_list_free (vd->pendingevents); - vd->pendingevents = NULL; - } - if (vd->taglist) { /* The tags have already been sent on the bus as messages. */ - gst_pad_push_event (vd->srcpad, gst_event_new_tag (vd->taglist)); + gst_pad_push_event (GST_AUDIO_DECODER_SRC_PAD (vd), + gst_event_new_tag (vd->taglist)); vd->taglist = NULL; } return GST_FLOW_OK; @@ -768,7 +434,7 @@ vorbis_handle_header_packet (GstVorbisDec * vd, ogg_packet * packet) /* Packetno = 0 if the first byte is exactly 0x01 */ packet->b_o_s = ((gst_ogg_packet_data (packet))[0] == 0x1) ? 1 : 0; -#ifdef USE_TREMOLO +#ifdef USE_TREMELO if ((ret = vorbis_dsp_headerin (&vd->vi, &vd->vc, packet))) #else if ((ret = vorbis_synthesis_headerin (&vd->vi, &vd->vc, packet))) @@ -791,6 +457,10 @@ vorbis_handle_header_packet (GstVorbisDec * vd, ogg_packet * packet) res = GST_FLOW_OK; break; } + + /* consumer header packet/frame */ + gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (vd), NULL, 1); + return res; /* ERRORS */ @@ -803,77 +473,72 @@ header_read_error: } static GstFlowReturn -vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf) +vorbis_dec_handle_header_buffer (GstVorbisDec * vd, GstBuffer * buffer) { - GstFlowReturn result; + ogg_packet *packet; + ogg_packet_wrapper packet_wrapper; - /* clip */ - if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate, - dec->vi.channels * dec->width))) { - GST_LOG_OBJECT (dec, "clipped buffer"); - return GST_FLOW_OK; - } + gst_ogg_packet_wrapper_from_buffer (&packet_wrapper, buffer); + packet = gst_ogg_packet_from_wrapper (&packet_wrapper); - if (dec->discont) { - GST_LOG_OBJECT (dec, "setting DISCONT"); - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT); - dec->discont = FALSE; - } - - GST_DEBUG_OBJECT (dec, - "pushing time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - - result = gst_pad_push (dec->srcpad, buf); - - return result; + return vorbis_handle_header_packet (vd, packet); } +#define MIN_NUM_HEADERS 3 static GstFlowReturn -vorbis_dec_push_reverse (GstVorbisDec * dec, GstBuffer * buf) +vorbis_dec_handle_header_caps (GstVorbisDec * vd) { GstFlowReturn result = GST_FLOW_OK; + GstCaps *caps; + GstStructure *s = NULL; + const GValue *array = NULL; - dec->queued = g_list_prepend (dec->queued, buf); + caps = GST_PAD_CAPS (GST_AUDIO_DECODER_SINK_PAD (vd)); + if (caps) + s = gst_caps_get_structure (caps, 0); + if (s) + array = gst_structure_get_value (s, "streamheader"); - return result; -} + if (array && (gst_value_array_get_size (array) >= MIN_NUM_HEADERS)) { + const GValue *value = NULL; + GstBuffer *buf = NULL; + gint i = 0; -static void -vorbis_do_timestamps (GstVorbisDec * vd, GstBuffer * buf, gboolean reverse, - GstClockTime timestamp, GstClockTime duration) -{ - /* interpolate reverse */ - if (vd->last_timestamp != -1 && duration != -1 && reverse) - vd->last_timestamp -= duration; + while (result == GST_FLOW_OK) { + value = gst_value_array_get_value (array, i); + buf = gst_value_get_buffer (value); + if (!buf) + goto null_buffer; + result = vorbis_dec_handle_header_buffer (vd, buf); + i++; + } + } else + goto array_error; - /* take buffer timestamp, use interpolated timestamp otherwise */ - if (timestamp != -1) - vd->last_timestamp = timestamp; - else - timestamp = vd->last_timestamp; +done: + return (result != GST_FLOW_OK ? GST_FLOW_NOT_NEGOTIATED : GST_FLOW_OK); - /* interpolate forwards */ - if (vd->last_timestamp != -1 && duration != -1 && !reverse) - vd->last_timestamp += duration; - - GST_LOG_OBJECT (vd, - "keeping timestamp %" GST_TIME_FORMAT " ts %" GST_TIME_FORMAT " dur %" - GST_TIME_FORMAT, GST_TIME_ARGS (vd->last_timestamp), - GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration)); - - if (buf) { - GST_BUFFER_TIMESTAMP (buf) = timestamp; - GST_BUFFER_DURATION (buf) = duration; + /* ERRORS */ +array_error: + { + GST_WARNING_OBJECT (vd, "streamheader array not found"); + result = GST_FLOW_ERROR; + goto done; + } +null_buffer: + { + GST_WARNING_OBJECT (vd, "streamheader with null buffer received"); + result = GST_FLOW_ERROR; + goto done; } } + static GstFlowReturn vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, GstClockTime timestamp, GstClockTime duration) { -#ifdef USE_TREMOLO +#ifdef USE_TREMELO vorbis_sample_t *pcm; #else vorbis_sample_t **pcm; @@ -883,8 +548,11 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, GstFlowReturn result; gint size; - if (G_UNLIKELY (!vd->initialized)) - goto not_initialized; + if (G_UNLIKELY (!vd->initialized)) { + result = vorbis_dec_handle_header_caps (vd); + if (result != GST_FLOW_OK) + goto not_initialized; + } /* normal data packet */ /* FIXME, we can skip decoding if the packet is outside of the @@ -893,8 +561,8 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, * throw away too much. For now we decode everything and clip right * before pushing data. */ -#ifdef USE_TREMOLO - if (G_UNLIKELY (vorbis_dsp_synthesis (&vd->vd, packet, 1))) +#ifdef USE_TREMELO + if (G_UNLIKELY (vorbis_dsp_synthesis (&vd->vb, packet, 1))) goto could_not_read; #else if (G_UNLIKELY (vorbis_synthesis (&vd->vb, packet))) @@ -912,8 +580,8 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, if ((sample_count = vorbis_dsp_pcmout (&vd->vd, NULL, 0)) == 0) #else if ((sample_count = vorbis_synthesis_pcmout (&vd->vd, NULL)) == 0) -#endif goto done; +#endif size = sample_count * vd->vi.channels * vd->width; GST_LOG_OBJECT (vd, "%d samples ready for reading, size %d", sample_count, @@ -921,18 +589,19 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, /* alloc buffer for it */ result = - gst_pad_alloc_buffer_and_set_caps (vd->srcpad, GST_BUFFER_OFFSET_NONE, - size, GST_PAD_CAPS (vd->srcpad), &out); + gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_DECODER_SRC_PAD (vd), + GST_BUFFER_OFFSET_NONE, size, + GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (vd)), &out); if (G_UNLIKELY (result != GST_FLOW_OK)) goto done; /* get samples ready for reading now, should be sample_count */ #ifdef USE_TREMOLO pcm = GST_BUFFER_DATA (out); - if (G_UNLIKELY ((vorbis_dsp_pcmout (&vd->vd, pcm, - sample_count)) != sample_count)) + if (G_UNLIKELY (vorbis_dsp_pcmout (&vd->vd, pcm, sample_count) != + sample_count)) #else - if (G_UNLIKELY ((vorbis_synthesis_pcmout (&vd->vd, &pcm)) != sample_count)) + if (G_UNLIKELY (vorbis_synthesis_pcmout (&vd->vd, &pcm) != sample_count)) #endif goto wrong_samples; @@ -945,22 +614,10 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, GST_LOG_OBJECT (vd, "setting output size to %d", size); GST_BUFFER_SIZE (out) = size; - /* this should not overflow */ - if (duration == -1) - duration = sample_count * GST_SECOND / vd->vi.rate; - - vorbis_do_timestamps (vd, out, FALSE, timestamp, duration); - - if (vd->segment.rate >= 0.0) - result = vorbis_dec_push_forward (vd, out); - else - result = vorbis_dec_push_reverse (vd, out); - done: - if (out == NULL) { - /* no output, still keep track of timestamps */ - vorbis_do_timestamps (vd, NULL, FALSE, timestamp, duration); - } + /* whether or not data produced, consume one frame and advance time */ + result = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (vd), out, 1); + #ifdef USE_TREMOLO vorbis_dsp_read (&vd->vd, sample_count); #else @@ -974,7 +631,7 @@ not_initialized: { GST_ELEMENT_ERROR (GST_ELEMENT (vd), STREAM, DECODE, (NULL), ("no header sent yet")); - return GST_FLOW_ERROR; + return GST_FLOW_NOT_NEGOTIATED; } could_not_read: { @@ -998,82 +655,16 @@ wrong_samples: } static GstFlowReturn -vorbis_dec_handle_header_buffer (GstVorbisDec * vd, GstBuffer * buffer) -{ - ogg_packet *packet; - ogg_packet_wrapper packet_wrapper; - - gst_ogg_packet_wrapper_from_buffer (&packet_wrapper, buffer); - packet = gst_ogg_packet_from_wrapper (&packet_wrapper); - - return vorbis_handle_header_packet (vd, packet); -} - - -#define MIN_NUM_HEADERS 3 -static GstFlowReturn -vorbis_dec_handle_header_caps (GstVorbisDec * vd, GstBuffer * buffer) -{ - GstFlowReturn result = GST_FLOW_OK; - GstCaps *caps = GST_PAD_CAPS (vd->sinkpad); - GstStructure *s = gst_caps_get_structure (caps, 0); - const GValue *array = gst_structure_get_value (s, "streamheader"); - - if (array && (gst_value_array_get_size (array) >= MIN_NUM_HEADERS)) { - const GValue *value = NULL; - GstBuffer *buf = NULL; - - /* initial header */ - value = gst_value_array_get_value (array, 0); - buf = gst_value_get_buffer (value); - if (!buf) - goto null_buffer; - result = vorbis_dec_handle_header_buffer (vd, buf); - - /* comment header */ - if (result == GST_FLOW_OK) { - value = gst_value_array_get_value (array, 1); - buf = gst_value_get_buffer (value); - if (!buf) - goto null_buffer; - result = vorbis_dec_handle_header_buffer (vd, buf); - } - - /* bitstream codebook header */ - if (result == GST_FLOW_OK) { - value = gst_value_array_get_value (array, 2); - buf = gst_value_get_buffer (value); - if (!buf) - goto null_buffer; - result = vorbis_dec_handle_header_buffer (vd, buf); - } - } else - goto array_error; - -done: - return (result != GST_FLOW_OK ? GST_FLOW_NOT_NEGOTIATED : GST_FLOW_OK); - -array_error: - { - GST_WARNING_OBJECT (vd, "streamheader array not found"); - result = GST_FLOW_ERROR; - goto done; - } - -null_buffer: - { - GST_WARNING_OBJECT (vd, "streamheader with null buffer received"); - result = GST_FLOW_ERROR; - goto done; - } -} - -static GstFlowReturn -vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer) +vorbis_dec_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer) { ogg_packet *packet; ogg_packet_wrapper packet_wrapper; GstFlowReturn result = GST_FLOW_OK; + GstVorbisDec *vd = GST_VORBIS_DEC (dec); + + /* no draining etc */ + if (G_UNLIKELY (!buffer)) + return GST_FLOW_OK; /* make ogg_packet out of the buffer */ gst_ogg_packet_wrapper_from_buffer (&packet_wrapper, buffer); @@ -1105,13 +696,6 @@ vorbis_dec_decode_buffer (GstVorbisDec * vd, GstBuffer * buffer) } else { GstClockTime timestamp, duration; - /* try to find header in caps so we can initialize the decoder */ - if (!vd->initialized) { - result = vorbis_dec_handle_header_caps (vd, buffer); - if (result != GST_FLOW_OK) - goto invalid_caps_header; - } - timestamp = GST_BUFFER_TIMESTAMP (buffer); duration = GST_BUFFER_DURATION (buffer); @@ -1135,252 +719,19 @@ empty_header: { GST_ELEMENT_ERROR (vd, STREAM, DECODE, (NULL), ("empty header received")); result = GST_FLOW_ERROR; - vd->discont = TRUE; - goto done; - } - -invalid_caps_header: - { - GST_ELEMENT_ERROR (vd, STREAM, DECODE, (NULL), - ("invalid streamheader in caps")); goto done; } } -/* - * Input: - * Buffer decoding order: 7 8 9 4 5 6 3 1 2 EOS - * Discont flag: D D D D - * - * - Each Discont marks a discont in the decoding order. - * - * for vorbis, each buffer is a keyframe when we have the previous - * buffer. This means that to decode buffer 7, we need buffer 6, which - * arrives out of order. - * - * we first gather buffers in the gather queue until we get a DISCONT. We - * prepend each incomming buffer so that they are in reversed order. - * - * gather queue: 9 8 7 - * decode queue: - * output queue: - * - * When a DISCONT is received (buffer 4), we move the gather queue to the - * decode queue. This is simply done be taking the head of the gather queue - * and prepending it to the decode queue. This yields: - * - * gather queue: - * decode queue: 7 8 9 - * output queue: - * - * Then we decode each buffer in the decode queue in order and put the output - * buffer in the output queue. The first buffer (7) will not produce any output - * because it needs the previous buffer (6) which did not arrive yet. This - * yields: - * - * gather queue: - * decode queue: 7 8 9 - * output queue: 9 8 - * - * Then we remove the consumed buffers from the decode queue. Buffer 7 is not - * completely consumed, we need to keep it around for when we receive buffer - * 6. This yields: - * - * gather queue: - * decode queue: 7 - * output queue: 9 8 - * - * Then we accumulate more buffers: - * - * gather queue: 6 5 4 - * decode queue: 7 - * output queue: - * - * prepending to the decode queue on DISCONT yields: - * - * gather queue: - * decode queue: 4 5 6 7 - * output queue: - * - * after decoding and keeping buffer 4: - * - * gather queue: - * decode queue: 4 - * output queue: 7 6 5 - * - * Etc.. - */ -static GstFlowReturn -vorbis_dec_flush_decode (GstVorbisDec * dec) +static void +vorbis_dec_flush (GstAudioDecoder * dec, gboolean hard) { - GstFlowReturn res = GST_FLOW_OK; - GList *walk; + GstVorbisDec *vd = GST_VORBIS_DEC (dec); - walk = dec->decode; - - GST_DEBUG_OBJECT (dec, "flushing buffers to decoder"); - - while (walk) { - GList *next; - GstBuffer *buf = GST_BUFFER_CAST (walk->data); - - GST_DEBUG_OBJECT (dec, "decoding buffer %p, ts %" GST_TIME_FORMAT, - buf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); - - next = g_list_next (walk); - - /* decode buffer, prepend to output queue */ - res = vorbis_dec_decode_buffer (dec, buf); - - /* if we generated output, we can discard the buffer, else we - * keep it in the queue */ - if (dec->queued) { - GST_DEBUG_OBJECT (dec, "decoded buffer to %p", dec->queued->data); - dec->decode = g_list_delete_link (dec->decode, walk); - gst_buffer_unref (buf); - } else { - GST_DEBUG_OBJECT (dec, "buffer did not decode, keeping"); - } - walk = next; - } - while (dec->queued) { - GstBuffer *buf = GST_BUFFER_CAST (dec->queued->data); - GstClockTime timestamp, duration; - - timestamp = GST_BUFFER_TIMESTAMP (buf); - duration = GST_BUFFER_DURATION (buf); - - vorbis_do_timestamps (dec, buf, TRUE, timestamp, duration); - res = vorbis_dec_push_forward (dec, buf); - - dec->queued = g_list_delete_link (dec->queued, dec->queued); - } - return res; -} - -static GstFlowReturn -vorbis_dec_chain_reverse (GstVorbisDec * vd, gboolean discont, GstBuffer * buf) -{ - GstFlowReturn result = GST_FLOW_OK; - - /* if we have a discont, move buffers to the decode list */ - if (G_UNLIKELY (discont)) { - GST_DEBUG_OBJECT (vd, "received discont"); - while (vd->gather) { - GstBuffer *gbuf; - - gbuf = GST_BUFFER_CAST (vd->gather->data); - /* remove from the gather list */ - vd->gather = g_list_delete_link (vd->gather, vd->gather); - /* copy to decode queue */ - vd->decode = g_list_prepend (vd->decode, gbuf); - } - /* flush and decode the decode queue */ - result = vorbis_dec_flush_decode (vd); - } - - if (G_LIKELY (buf)) { - GST_DEBUG_OBJECT (vd, - "gathering buffer %p of size %u, time %" GST_TIME_FORMAT - ", dur %" GST_TIME_FORMAT, buf, GST_BUFFER_SIZE (buf), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), - GST_TIME_ARGS (GST_BUFFER_DURATION (buf))); - - /* add buffer to gather queue */ - vd->gather = g_list_prepend (vd->gather, buf); - } - - return result; -} - -static GstFlowReturn -vorbis_dec_chain_forward (GstVorbisDec * vd, gboolean discont, - GstBuffer * buffer) -{ - GstFlowReturn result; - - result = vorbis_dec_decode_buffer (vd, buffer); - - gst_buffer_unref (buffer); - - return result; -} - -static GstFlowReturn -vorbis_dec_chain (GstPad * pad, GstBuffer * buffer) -{ - GstVorbisDec *vd; - GstFlowReturn result = GST_FLOW_OK; - gboolean discont; - - vd = GST_VORBIS_DEC (gst_pad_get_parent (pad)); - - discont = GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT); - - /* resync on DISCONT */ - if (G_UNLIKELY (discont)) { - GST_DEBUG_OBJECT (vd, "received DISCONT buffer"); - vd->last_timestamp = GST_CLOCK_TIME_NONE; #ifdef HAVE_VORBIS_SYNTHESIS_RESTART - vorbis_synthesis_restart (&vd->vd); -#endif - vd->discont = TRUE; - } - - if (vd->segment.rate >= 0.0) - result = vorbis_dec_chain_forward (vd, discont, buffer); - else - result = vorbis_dec_chain_reverse (vd, discont, buffer); - - gst_object_unref (vd); - - return result; -} - -static GstStateChangeReturn -vorbis_dec_change_state (GstElement * element, GstStateChange transition) -{ - GstVorbisDec *vd = GST_VORBIS_DEC (element); - GstStateChangeReturn res; - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - vorbis_info_init (&vd->vi); - vorbis_comment_init (&vd->vc); - vd->initialized = FALSE; - gst_vorbis_dec_reset (vd); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - res = parent_class->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - GST_DEBUG_OBJECT (vd, "PAUSED -> READY, clearing vorbis structures"); - vd->initialized = FALSE; - -#ifndef USE_TREMOLO - vorbis_block_clear (&vd->vb); + vorbis_synthesis_restart (&vd->vd); #endif - vorbis_dsp_clear (&vd->vd); - vorbis_comment_clear (&vd->vc); - vorbis_info_clear (&vd->vi); - gst_vorbis_dec_reset (vd); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return res; + if (hard) + gst_vorbis_dec_reset (vd); } diff --git a/ext/vorbis/gstvorbisdec.h b/ext/vorbis/gstvorbisdec.h index 04e4677410..56f9ca406a 100644 --- a/ext/vorbis/gstvorbisdec.h +++ b/ext/vorbis/gstvorbisdec.h @@ -27,6 +27,7 @@ #endif #include +#include #include "gstvorbisdeclib.h" G_BEGIN_DECLS @@ -51,15 +52,11 @@ typedef struct _GstVorbisDecClass GstVorbisDecClass; * Opaque data structure. */ struct _GstVorbisDec { - GstElement element; - - GstPad *sinkpad; - GstPad *srcpad; + GstAudioDecoder element; vorbis_dsp_state vd; vorbis_info vi; vorbis_comment vc; - #ifndef USE_TREMOLO vorbis_block vb; #endif @@ -67,26 +64,13 @@ struct _GstVorbisDec { gboolean initialized; guint width; - /* list of buffers that need timestamps */ - GList *queued; - /* gather/decode queues for reverse playback */ - GList *gather; - GList *decode; - - GstSegment segment; - gboolean discont; - guint32 seqnum; - - GstClockTime last_timestamp; - - GList *pendingevents; GstTagList *taglist; - + CopySampleFunc copy_samples; }; struct _GstVorbisDecClass { - GstElementClass parent_class; + GstAudioDecoderClass parent_class; }; GType gst_vorbis_dec_get_type(void);