GST_STATIC_CAPS ("audio/x-vorbis")
);
-GST_BOILERPLATE (GstVorbisDec, gst_vorbis_dec, GstAudioDecoder,
+#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_base_init (gpointer g_class)
+gst_vorbis_dec_class_init (GstVorbisDecClass * klass)
{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- GstPadTemplate *src_template, *sink_template;
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
++ GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
- src_template = gst_static_pad_template_get (&vorbis_dec_src_factory);
- gst_element_class_add_pad_template (element_class, src_template);
+ gobject_class->finalize = vorbis_dec_finalize;
- sink_template = gst_static_pad_template_get (&vorbis_dec_sink_factory);
- gst_element_class_add_pad_template (element_class, sink_template);
+ 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));
- gst_element_class_set_details_simple (element_class,
+ gst_element_class_set_details_simple (gstelement_class,
"Vorbis audio decoder", "Codec/Decoder/Audio",
GST_VORBIS_DEC_DESCRIPTION,
"Benjamin Otte <otte@gnome.org>, Chris Lord <chris@openedhand.com>");
-}
-
-static void
-gst_vorbis_dec_class_init (GstVorbisDecClass * klass)
-{
- GObjectClass *gobject_class = G_OBJECT_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)
+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
}
}
- /* negotiate width with downstream */
- caps = gst_pad_get_allowed_caps (GST_AUDIO_DECODER_SRC_PAD (vd));
- if (caps) {
- if (!gst_caps_is_empty (caps)) {
- GstStructure *s;
-
- s = gst_caps_get_structure (caps, 0);
- /* template ensures 16 or 32 */
- gst_structure_get_int (s, "width", &width);
-
- GST_INFO_OBJECT (vd, "using %s with %d channels and %d bit audio depth",
- gst_structure_get_name (s), vd->vi.channels, width);
- }
- gst_caps_unref (caps);
- }
- vd->width = width >> 3;
+ 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;
/* select a copy_samples function, this way we can have specialized versions
* 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
- (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);
- }
-
- if (vd->vi.channels > 8) {
- g_free ((GstAudioChannelPosition *) pos);
- }
-
- gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (vd), caps);
- gst_caps_unref (caps);
+ vd->copy_samples = get_copy_sample_func (info.channels);
return GST_FLOW_OK;
}
}
static GstFlowReturn
- vorbis_dec_push_forward (GstVorbisDec * dec, GstBuffer * buf)
+ vorbis_dec_handle_header_buffer (GstVorbisDec * vd, GstBuffer * buffer)
{
- GstFlowReturn result;
-
- /* 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;
- }
+ ogg_packet *packet;
+ ogg_packet_wrapper packet_wrapper;
++ GstFlowReturn ret;
- if (dec->discont) {
- GST_LOG_OBJECT (dec, "setting DISCONT");
- GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
- dec->discont = FALSE;
- }
- gst_ogg_packet_wrapper_from_buffer (&packet_wrapper, buffer);
++ gst_ogg_packet_wrapper_map (&packet_wrapper, buffer);
+ packet = gst_ogg_packet_from_wrapper (&packet_wrapper);
- 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)));
- return vorbis_handle_header_packet (vd, packet);
++ ret = vorbis_handle_header_packet (vd, packet);
+
- result = gst_pad_push (dec->srcpad, buf);
++ gst_ogg_packet_wrapper_unmap (&packet_wrapper, buffer);
+
- 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;
- caps = GST_PAD_CAPS (GST_AUDIO_DECODER_SINK_PAD (vd));
+ GstCaps *caps;
+ GstStructure *s = NULL;
+ const GValue *array = NULL;
+
++ 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");
+
++ if (caps)
++ gst_caps_unref (caps);
++
+ if (array && (gst_value_array_get_size (array) >= MIN_NUM_HEADERS)) {
+ const GValue *value = NULL;
+ GstBuffer *buf = NULL;
+ gint i = 0;
+
+ 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;
- dec->queued = g_list_prepend (dec->queued, buf);
-
- return result;
- }
+ done:
+ return (result != GST_FLOW_OK ? GST_FLOW_NOT_NEGOTIATED : GST_FLOW_OK);
- 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;
-
- /* take buffer timestamp, use interpolated timestamp otherwise */
- if (timestamp != -1)
- vd->last_timestamp = timestamp;
- else
- timestamp = vd->last_timestamp;
-
- /* 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;
}
}
guint sample_count;
GstBuffer *out = NULL;
GstFlowReturn result;
- gint size;
+ 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
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,
- size);
+ size = sample_count * vd->info.bpf;
+ GST_LOG_OBJECT (vd, "%d samples ready for reading, size %" G_GSIZE_FORMAT,
+ sample_count, size);
/* alloc buffer for it */
- result =
- 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;
+ out = gst_buffer_new_and_alloc (size);
+ data = gst_buffer_map (out, NULL, NULL, GST_MAP_WRITE);
/* 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, 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;
#ifndef USE_TREMOLO
/* copy samples in buffer */
- vd->copy_samples ((vorbis_sample_t *) GST_BUFFER_DATA (out), pcm,
- sample_count, vd->vi.channels, vd->width);
+ vd->copy_samples ((vorbis_sample_t *) data, pcm,
+ sample_count, vd->info.channels);
#endif
- GST_LOG_OBJECT (vd, "setting output size to %d", size);
- GST_BUFFER_SIZE (out) = size;
+ 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
{
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;
+ GstVorbisDec *vd = GST_VORBIS_DEC (dec);
- 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)
- {
- ogg_packet *packet;
- ogg_packet_wrapper packet_wrapper;
- GstFlowReturn result = GST_FLOW_OK;
+ /* 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);
+ gst_ogg_packet_wrapper_map (&packet_wrapper, buffer);
packet = gst_ogg_packet_from_wrapper (&packet_wrapper);
/* set some more stuff */
packet->granulepos = -1;
#endif
gboolean initialized;
- guint width;
+ 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;
};
#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 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);
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, 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 };
-
- g_type_add_interface_static (vorbisenc_type, GST_TYPE_TAG_SETTER,
- &tag_setter_info);
-}
-
-static void
-gst_vorbis_enc_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
-
- 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));
-
- gst_element_class_set_details_simple (element_class,
- "Vorbis audio encoder", "Codec/Encoder/Audio",
- "Encodes audio in Vorbis format",
- "Monty <monty@xiph.org>, " "Wim Taymans <wim@fluendo.com>");
-}
+#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;
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);
+
+ gst_element_class_add_pad_template (gstelement_class,
+ 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 <monty@xiph.org>, " "Wim Taymans <wim@fluendo.com>");
+
-gst_vorbis_enc_init (GstVorbisEnc * vorbisenc, GstVorbisEncClass * klass)
++ 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
}
static GstCaps *
- gst_vorbis_enc_sink_getcaps (GstPad * pad, GstCaps * filter)
-gst_vorbis_enc_getcaps (GstAudioEncoder * enc)
++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 ();
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_SIZE (outbuf));
+ gst_buffer_get_size (outbuf));
return outbuf;
}
} 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, "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));
-
- 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;
-
- 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;
- }
+ 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);
+ }
+
+ /*
+ * (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 };
+
+ 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));
++ 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
{
GstVorbisEnc *vorbisenc;
GstFlowReturn ret = GST_FLOW_OK;
- gfloat *data;
+ gfloat *data, *ptr;
gulong size;
++ gsize bsize;
gulong i, j;
float **vorbis_buffer;
GstBuffer *buf1, *buf2, *buf3;
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);
++ gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (vorbisenc), caps);
/* push out buffers */
/* push_buffer takes the reference even for failure */
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;
-
- /* 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;
- }
+ if (!buffer)
+ return gst_vorbis_enc_clear (vorbisenc);
- /* 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 */
- data = (gfloat *) GST_BUFFER_DATA (buffer);
- size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float));
+ size = bsize / (vorbisenc->channels * sizeof (float));
+
+ ptr = data;
/* expose the buffer to submit data */
vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
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);
++ 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;
gint i, c; \
gdouble amp = (src->volume * scale); \
gdouble state = src->red.state; \
+ gint channels = GST_AUDIO_INFO_CHANNELS (&src->info); \
\
- for (i = 0; i < src->generate_samples_per_buffer * src->channels; ) { \
- for (c = 0; c < src->channels; ++c) { \
+ 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 */ \
if (!(src->gen))
src->gen = g_rand_new ();
src->red.state = 0.0;
- src->process = violet_noise_funcs[src->format];
+ src->process = violet_noise_funcs[idx];
+ break;
default:
GST_ERROR ("invalid wave-form");
break;