diff --git a/ext/vorbis/Makefile.am b/ext/vorbis/Makefile.am index 7c18ff1461..9678a31b9e 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 \ @@ -27,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 62be606a3f..a2d7957c14 100644 --- a/ext/vorbis/gstvorbisdec.c +++ b/ext/vorbis/gstvorbisdec.c @@ -65,37 +65,28 @@ GST_STATIC_PAD_TEMPLATE ("sink", ); #define gst_vorbis_dec_parent_class parent_class -G_DEFINE_TYPE (GST_VORBIS_DEC_GLIB_TYPE_NAME, gst_vorbis_dec, GST_TYPE_ELEMENT); +G_DEFINE_TYPE (GST_VORBIS_DEC_GLIB_TYPE_NAME, gst_vorbis_dec, + 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_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; gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&vorbis_dec_src_factory)); - gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&vorbis_dec_sink_factory)); @@ -104,51 +95,15 @@ gst_vorbis_dec_class_init (GstVorbisDecClass * klass) GST_VORBIS_DEC_DESCRIPTION, "Benjamin Otte , Chris Lord "); - 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) { - 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 @@ -162,7 +117,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); @@ -173,191 +127,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; + 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; - - res = gst_audio_info_convert (&dec->info, src_format, src_value, dest_format, - dest_value); - -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) { @@ -365,10 +172,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: @@ -418,87 +221,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_SEGMENT: - { - const GstSegment *segment; - - gst_event_parse_segment (event, &segment); - - /* we need time for now */ - if (segment->format != GST_FORMAT_TIME) - goto newseg_wrong_format; - - GST_DEBUG_OBJECT (dec, "segment: %" GST_SEGMENT_FORMAT, segment); - - /* now configure the values */ - gst_segment_copy_into (segment, &dec->segment); - 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_event_default (pad, 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) @@ -540,7 +263,7 @@ vorbis_handle_identification_packet (GstVorbisDec * vd) } caps = gst_audio_info_to_caps (&info); - gst_pad_set_caps (vd->srcpad, caps); + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (vd), caps); gst_caps_unref (caps); vd->info = info; @@ -612,8 +335,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. * @@ -628,7 +351,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); @@ -646,16 +368,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; @@ -686,7 +402,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))) @@ -709,6 +425,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 */ @@ -721,77 +441,82 @@ 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; + GstFlowReturn ret; - /* clip */ - if (!(buf = gst_audio_buffer_clip (buf, &dec->segment, dec->vi.rate, - dec->info.bpf))) { - GST_LOG_OBJECT (dec, "clipped buffer"); - return GST_FLOW_OK; - } + gst_ogg_packet_wrapper_map (&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; - } + ret = vorbis_handle_header_packet (vd, packet); - 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))); + gst_ogg_packet_wrapper_unmap (&packet_wrapper, buffer); - result = gst_pad_push (dec->srcpad, buf); - - return result; + return ret; } +#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_get_current_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 (caps) + gst_caps_unref (caps); -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; + if (array && (gst_value_array_get_size (array) >= MIN_NUM_HEADERS)) { + const GValue *value = NULL; + GstBuffer *buf = NULL; + gint i = 0; - /* take buffer timestamp, use interpolated timestamp otherwise */ - if (timestamp != -1) - vd->last_timestamp = timestamp; - else - timestamp = vd->last_timestamp; + 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; - /* interpolate forwards */ - if (vd->last_timestamp != -1 && duration != -1 && !reverse) - vd->last_timestamp += duration; +done: + return (result != GST_FLOW_OK ? GST_FLOW_NOT_NEGOTIATED : GST_FLOW_OK); - 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) { -#ifndef USE_TREMOLO +#ifdef USE_TREMELO + vorbis_sample_t *pcm; +#else vorbis_sample_t **pcm; #endif guint sample_count; @@ -800,8 +525,11 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, guint8 *data; gsize 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 @@ -810,8 +538,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))) @@ -829,8 +557,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->info.bpf; GST_LOG_OBJECT (vd, "%d samples ready for reading, size %" G_GSIZE_FORMAT, @@ -845,7 +573,7 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, if (G_UNLIKELY ((vorbis_dsp_pcmout (&vd->vd, data, 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; @@ -858,22 +586,10 @@ vorbis_handle_data_packet (GstVorbisDec * vd, ogg_packet * packet, GST_LOG_OBJECT (vd, "setting output size to %" G_GSIZE_FORMAT, size); gst_buffer_unmap (out, data, 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 @@ -887,7 +603,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: { @@ -911,102 +627,16 @@ wrong_samples: } static GstFlowReturn -vorbis_dec_handle_header_buffer (GstVorbisDec * vd, GstBuffer * buffer) -{ - ogg_packet *packet; - ogg_packet_wrapper packet_wrapper; - GstFlowReturn ret; - - gst_ogg_packet_wrapper_map (&packet_wrapper, buffer); - packet = gst_ogg_packet_from_wrapper (&packet_wrapper); - - ret = vorbis_handle_header_packet (vd, packet); - - gst_ogg_packet_wrapper_unmap (&packet_wrapper, buffer); - - return ret; -} - - -#define MIN_NUM_HEADERS 3 -static GstFlowReturn -vorbis_dec_handle_header_caps (GstVorbisDec * vd, GstBuffer * buffer) -{ - GstFlowReturn result = GST_FLOW_OK; - GstCaps *caps; - GstStructure *s; - const GValue *array; - const GValue *value = NULL; - GstBuffer *buf = NULL; - - if ((caps = gst_pad_get_current_caps (vd->sinkpad)) == NULL) - goto no_caps; - - if ((s = gst_caps_get_structure (caps, 0)) == NULL) - goto no_caps; - - array = gst_structure_get_value (s, "streamheader"); - - if (array == NULL || (gst_value_array_get_size (array) < MIN_NUM_HEADERS)) - goto array_error; - - /* 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); - if (result != GST_FLOW_OK) - goto buffer_error; - - /* comment header */ - 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); - if (result != GST_FLOW_OK) - goto buffer_error; - - /* bitstream codebook header */ - 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); - if (result != GST_FLOW_OK) - goto buffer_error; - - return result; - -no_caps: - { - GST_WARNING_OBJECT (vd, "no caps negotiated"); - return GST_FLOW_NOT_NEGOTIATED; - } -array_error: - { - GST_WARNING_OBJECT (vd, "streamheader array not found"); - return GST_FLOW_NOT_NEGOTIATED; - } -null_buffer: - { - GST_WARNING_OBJECT (vd, "streamheader with null buffer received"); - return GST_FLOW_NOT_NEGOTIATED; - } -buffer_error: - { - GST_WARNING_OBJECT (vd, "error handling buffer"); - return GST_FLOW_NOT_NEGOTIATED; - } -} - -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_map (&packet_wrapper, buffer); @@ -1038,13 +668,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); @@ -1070,253 +693,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 %" G_GSIZE_FORMAT - ", time %" GST_TIME_FORMAT ", dur %" GST_TIME_FORMAT, - buf, gst_buffer_get_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 = 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: - 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 d122a0e02d..721b666a54 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; GstAudioInfo info; - /* 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); diff --git a/ext/vorbis/gstvorbisenc.c b/ext/vorbis/gstvorbisenc.c index 54800bb26a..1bf7741758 100644 --- a/ext/vorbis/gstvorbisenc.c +++ b/ext/vorbis/gstvorbisenc.c @@ -88,28 +88,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 @@ -118,8 +96,17 @@ 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, + GstCaps * filter); +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); @@ -127,33 +114,21 @@ 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); #define gst_vorbis_enc_parent_class parent_class G_DEFINE_TYPE_WITH_CODE (GstVorbisEnc, gst_vorbis_enc, - GST_TYPE_ELEMENT, gst_vorbis_enc_add_interfaces (g_define_type_id)); - -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); -} + GST_TYPE_AUDIO_ENCODER, G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL)); 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; @@ -194,13 +169,39 @@ gst_vorbis_enc_class_init (GstVorbisEncClass * klass) gst_static_pad_template_get (&vorbis_enc_src_factory)); gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&vorbis_enc_sink_factory)); + gst_element_class_set_details_simple (gstelement_class, "Vorbis audio encoder", "Codec/Encoder/Audio", "Encodes audio in Vorbis format", "Monty , " "Wim Taymans "); - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_vorbis_enc_change_state); + 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); +} + +static void +gst_vorbis_enc_init (GstVorbisEnc * vorbisenc) +{ + 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 @@ -216,6 +217,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) { @@ -264,153 +294,14 @@ gst_vorbis_enc_generate_sink_caps (void) } static GstCaps * -gst_vorbis_enc_sink_getcaps (GstPad * pad, GstCaps * filter) +gst_vorbis_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter) { - 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 (); - if (filter) - return gst_caps_intersect_full (filter, vorbisenc->sinkcaps, - GST_CAPS_INTERSECT_FIRST); - else - return gst_caps_ref (vorbisenc->sinkcaps); -} - -static gboolean -gst_vorbis_enc_sink_setcaps (GstVorbisEnc * vorbisenc, GstCaps * caps) -{ - GstStructure *structure; - - 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 @@ -422,177 +313,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 (vorbisenc->sinkpad); + vorbisenc = GST_VORBISENC (enc); - switch (GST_QUERY_TYPE (query)) { - case GST_QUERY_POSITION: - { - GstFormat 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; - if (!(res = gst_pad_query_position (peerpad, GST_FORMAT_TIME, &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, GST_FORMAT_TIME, pos, req_fmt, - &val))) { - gst_query_set_position (query, req_fmt, val); - } - break; - } - case GST_QUERY_DURATION: - { - GstFormat 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; - } - - if (!(res = gst_pad_query_duration (peerpad, GST_FORMAT_TIME, &dur))) - break; - - if ((res = - gst_pad_query_convert (peerpad, GST_FORMAT_TIME, 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) -{ - 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_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 @@ -711,7 +451,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) { @@ -778,8 +519,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; @@ -794,6 +537,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; } @@ -802,47 +546,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); - gst_buffer_fill (outbuf, 0, 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); - 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_LOG_OBJECT (vorbisenc, "encoded buffer of %" G_GSIZE_FORMAT " bytes", - gst_buffer_get_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) @@ -856,106 +562,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_DEBUG ("created header packet buffer, %" G_GSIZE_FORMAT " bytes", + GST_DEBUG ("created header packet buffer, %d bytes", gst_buffer_get_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_get_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_CAPS: - { - GstCaps *caps; - - gst_event_parse_caps (event, &caps); - res = gst_vorbis_enc_sink_setcaps (vorbisenc, caps); - gst_event_unref (event); - break; - } - 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; @@ -966,93 +585,111 @@ 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_SEGMENT: - { - const GstSegment *segment; - - gst_event_parse_segment (event, &segment); - - if (segment->format == GST_FORMAT_TIME) { - gst_segment_copy_into (segment, &vorbisenc->segment); - } - } /* 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); +} - GST_DEBUG_OBJECT (vorbisenc, "exp %" GST_TIME_FORMAT " time %" GST_TIME_FORMAT - "dur %" GST_TIME_FORMAT, GST_TIME_ARGS (vorbisenc->expected_ts), - GST_TIME_ARGS (timestamp), GST_TIME_ARGS (duration)); +/* + * (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 (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; + 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); - 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; - } + 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_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; gfloat *data, *ptr; gulong size; + gsize bsize; 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; - gsize bsize; - 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; - GST_DEBUG_OBJECT (vorbisenc, " timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (timestamp)); + 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 @@ -1065,11 +702,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, @@ -1082,111 +714,34 @@ 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_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (vorbisenc), 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 - 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; - gsize size; + if (!buffer) + return gst_vorbis_enc_clear (vorbisenc); - /* 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)); - - size = gst_buffer_get_size (buffer); - - diff_bytes = - GST_CLOCK_TIME_TO_FRAMES (diff, - vorbisenc->frequency) * vorbisenc->channels * sizeof (gfloat); - if (diff_bytes >= size) { - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } - buffer = gst_buffer_make_writable (buffer); - gst_buffer_resize (buffer, diff_bytes, size - 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 */ data = gst_buffer_map (buffer, &bsize, NULL, GST_MAP_WRITE); - if (bsize == 0) { - gst_buffer_unmap (buffer, data, bsize); - gst_buffer_unref (buffer); - return GST_FLOW_OK; - } /* data to encode */ size = bsize / (vorbisenc->channels * sizeof (float)); @@ -1211,20 +766,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"); @@ -1233,7 +779,6 @@ failed_header_push: gst_buffer_unref (buf2); if (buf3) gst_buffer_unref (buf3); - gst_buffer_unref (buffer); return ret; } } @@ -1256,8 +801,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); + gst_buffer_fill (buf, 0, 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; @@ -1373,52 +926,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); - 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 840f2bdd3d..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,13 +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; GstTagList * tags; @@ -90,7 +84,7 @@ struct _GstVorbisEnc { }; struct _GstVorbisEncClass { - GstElementClass parent_class; + GstAudioEncoderClass parent_class; }; GType gst_vorbis_enc_get_type(void); diff --git a/gst-libs/gst/audio/gstaudiodecoder.c b/gst-libs/gst/audio/gstaudiodecoder.c index ec9f6d69ce..caf8d1ab08 100644 --- a/gst-libs/gst/audio/gstaudiodecoder.c +++ b/gst-libs/gst/audio/gstaudiodecoder.c @@ -1579,6 +1579,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)); @@ -1779,6 +1783,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); diff --git a/gst-libs/gst/audio/gstaudioencoder.c b/gst-libs/gst/audio/gstaudioencoder.c index 239fa48c64..99d9c77760 100644 --- a/gst-libs/gst/audio/gstaudioencoder.c +++ b/gst-libs/gst/audio/gstaudioencoder.c @@ -1504,6 +1504,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); diff --git a/gst/audiotestsrc/gstaudiotestsrc.c b/gst/audiotestsrc/gstaudiotestsrc.c index 01b7d8ac3a..5e41c0994b 100644 --- a/gst/audiotestsrc/gstaudiotestsrc.c +++ b/gst/audiotestsrc/gstaudiotestsrc.c @@ -787,9 +787,9 @@ gst_audio_test_src_create_red_noise_##type (GstAudioTestSrc * src, g##type * sam for (i = 0; i < src->generate_samples_per_buffer * channels; ) { \ for (c = 0; c < 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 */ \ @@ -958,6 +958,7 @@ gst_audio_test_src_change_wave (GstAudioTestSrc * src) src->gen = g_rand_new (); src->red.state = 0.0; src->process = violet_noise_funcs[idx]; + break; default: GST_ERROR ("invalid wave-form"); break; diff --git a/gst/encoding/gstencodebin.c b/gst/encoding/gstencodebin.c index 6f691918d6..bc0ca09e48 100644 --- a/gst/encoding/gstencodebin.c +++ b/gst/encoding/gstencodebin.c @@ -1090,45 +1090,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) { @@ -1139,7 +1100,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); @@ -1467,11 +1427,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"); diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c index 2d357af6c4..eeed34455d 100644 --- a/gst/playback/gstplaybin2.c +++ b/gst/playback/gstplaybin2.c @@ -2445,12 +2445,14 @@ stream_changed_data_probe (GstPad * pad, GstProbeType type, /* 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; @@ -2508,7 +2510,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; @@ -2521,20 +2523,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 diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index 9d75a82aac..cfd71f5e80 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -255,6 +255,8 @@ enum PROP_FRAME, PROP_AV_OFFSET, PROP_VIDEO_SINK, + PROP_AUDIO_SINK, + PROP_TEXT_SINK, PROP_LAST }; @@ -422,6 +424,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, @@ -3628,6 +3655,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; @@ -3670,6 +3705,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; diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c index 08f356e049..f84c5abbb9 100644 --- a/gst/playback/gstplaysinkaudioconvert.c +++ b/gst/playback/gstplaysinkaudioconvert.c @@ -339,10 +339,7 @@ gst_play_sink_audio_convert_getcaps (GstPad * pad, GstCaps * filter) 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 68a77b26c5..39d9aee26c 100644 --- a/gst/playback/gstplaysinkvideoconvert.c +++ b/gst/playback/gstplaysinkvideoconvert.c @@ -319,10 +319,7 @@ gst_play_sink_video_convert_getcaps (GstPad * pad, GstCaps * filter) 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); diff --git a/tests/check/elements/audiotestsrc.c b/tests/check/elements/audiotestsrc.c index 0fecc5db16..8151b992b1 100644 --- a/tests/check/elements/audiotestsrc.c +++ b/tests/check/elements/audiotestsrc.c @@ -87,6 +87,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,