GST_DEBUG_CATEGORY_STATIC (flacenc_debug);
#define GST_CAT_DEFAULT flacenc_debug
-
-#define _do_init(type) \
- G_STMT_START{ \
- static const GInterfaceInfo tag_setter_info = { \
- NULL, \
- NULL, \
- NULL \
- }; \
- g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, \
- &tag_setter_info); \
- }G_STMT_END
-
-GST_BOILERPLATE_FULL (GstFlacEnc, gst_flac_enc, GstAudioEncoder,
- GST_TYPE_AUDIO_ENCODER, _do_init);
+#define gst_flac_enc_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstFlacEnc, gst_flac_enc, GST_TYPE_ELEMENT,
- G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL);
- G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
++ G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL));
+
+ static gboolean gst_flac_enc_start (GstAudioEncoder * enc);
+ static gboolean gst_flac_enc_stop (GstAudioEncoder * enc);
+ static gboolean gst_flac_enc_set_format (GstAudioEncoder * enc,
+ GstAudioInfo * info);
+ static GstFlowReturn gst_flac_enc_handle_frame (GstAudioEncoder * enc,
+ GstBuffer * in_buf);
-static GstCaps *gst_flac_enc_getcaps (GstAudioEncoder * enc);
++static GstCaps *gst_flac_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter);
+ static gboolean gst_flac_enc_sink_event (GstAudioEncoder * enc,
+ GstEvent * event);
static void gst_flac_enc_finalize (GObject * object);
gst_flac_enc_class_init (GstFlacEncClass * klass)
{
GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+ GstAudioEncoderClass *base_class;
gobject_class = (GObjectClass *) klass;
+ gstelement_class = (GstElementClass *) klass;
+ base_class = (GstAudioEncoderClass *) (klass);
+ GST_DEBUG_CATEGORY_INIT (flacenc_debug, "flacenc", 0,
+ "Flac encoding element");
+
gobject_class->set_property = gst_flac_enc_set_property;
gobject_class->get_property = gst_flac_enc_get_property;
gobject_class->finalize = gst_flac_enc_finalize;
-G_MAXINT, G_MAXINT,
DEFAULT_SEEKPOINTS,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
- gstelement_class->change_state = gst_flac_enc_change_state;
-
+
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&src_factory));
+ gst_element_class_add_pad_template (gstelement_class,
+ gst_static_pad_template_get (&sink_factory));
+
+ gst_element_class_set_details_simple (gstelement_class, "FLAC audio encoder",
+ "Codec/Encoder/Audio",
+ "Encodes audio with the FLAC lossless audio encoder",
+ "Wim Taymans <wim.taymans@chello.be>");
++
++ base_class->start = GST_DEBUG_FUNCPTR (gst_flac_enc_start);
++ base_class->stop = GST_DEBUG_FUNCPTR (gst_flac_enc_stop);
++ base_class->set_format = GST_DEBUG_FUNCPTR (gst_flac_enc_set_format);
++ base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_flac_enc_handle_frame);
++ base_class->getcaps = GST_DEBUG_FUNCPTR (gst_flac_enc_getcaps);
++ base_class->event = GST_DEBUG_FUNCPTR (gst_flac_enc_sink_event);
}
static void
-gst_flac_enc_init (GstFlacEnc * flacenc, GstFlacEncClass * klass)
+gst_flac_enc_init (GstFlacEnc * flacenc)
{
- flacenc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
- gst_pad_set_chain_function (flacenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flac_enc_chain));
- gst_pad_set_event_function (flacenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flac_enc_sink_event));
- gst_pad_set_getcaps_function (flacenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flac_enc_sink_getcaps));
- gst_pad_set_setcaps_function (flacenc->sinkpad,
- GST_DEBUG_FUNCPTR (gst_flac_enc_sink_setcaps));
- gst_element_add_pad (GST_ELEMENT (flacenc), flacenc->sinkpad);
-
- flacenc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
- gst_pad_use_fixed_caps (flacenc->srcpad);
- gst_element_add_pad (GST_ELEMENT (flacenc), flacenc->srcpad);
+ GstAudioEncoder *enc = GST_AUDIO_ENCODER (flacenc);
flacenc->encoder = FLAC__stream_encoder_new ();
-
- flacenc->offset = 0;
- flacenc->samples_written = 0;
- flacenc->channels = 0;
gst_flac_enc_update_quality (flacenc, DEFAULT_QUALITY);
- flacenc->tags = gst_tag_list_new ();
- flacenc->got_headers = FALSE;
- flacenc->headers = NULL;
- flacenc->last_flow = GST_FLOW_OK;
+
+ /* 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
if (n_images + n_preview_images > 0) {
GstBuffer *buffer;
++#if 0
GstCaps *caps;
GstStructure *structure;
GstTagImageType image_type = GST_TAG_IMAGE_TYPE_NONE;
++#endif
gint i;
+ guint8 *data;
+ gsize size;
for (i = 0; i < n_images + n_preview_images; i++) {
if (i < n_images) {
flacenc->meta[entries] =
FLAC__metadata_object_new (FLAC__METADATA_TYPE_PICTURE);
++#if 0
caps = gst_buffer_get_caps (buffer);
structure = gst_caps_get_structure (caps, 0);
image_type = (i < n_images) ? 0x00 : 0x01;
else
image_type = image_type + 2;
++#endif
+ data = gst_buffer_map (buffer, &size, NULL, GST_MAP_READ);
FLAC__metadata_object_picture_set_data (flacenc->meta[entries],
- GST_BUFFER_DATA (buffer), GST_BUFFER_SIZE (buffer), TRUE);
+ data, size, TRUE);
+ gst_buffer_unmap (buffer, data, size);
+
++#if 0
/* FIXME: There's no way to set the picture type in libFLAC */
flacenc->meta[entries]->data.picture.type = image_type;
FLAC__metadata_object_picture_set_mime_type (flacenc->meta[entries],
(char *) gst_structure_get_name (structure), TRUE);
--
gst_caps_unref (caps);
++#endif
++
gst_buffer_unref (buffer);
entries++;
}
}
static GstCaps *
- gst_flac_enc_sink_getcaps (GstPad * pad)
-gst_flac_enc_getcaps (GstAudioEncoder * enc)
++gst_flac_enc_getcaps (GstAudioEncoder * enc, GstCaps * filter)
{
- GstCaps *ret = NULL;
+ GstCaps *ret = NULL, *caps = NULL;
+ GstPad *pad;
+
+ pad = GST_AUDIO_ENCODER_SINK_PAD (enc);
GST_OBJECT_LOCK (pad);
-- if (GST_PAD_CAPS (pad)) {
-- ret = gst_caps_ref (GST_PAD_CAPS (pad));
++ if (gst_pad_has_current_caps (pad)) {
++ ret = gst_pad_get_current_caps (pad);
} else {
gint i, c;
static guint64
gst_flac_enc_query_peer_total_samples (GstFlacEnc * flacenc, GstPad * pad)
{
-- GstFormat fmt = GST_FORMAT_DEFAULT;
gint64 duration;
GST_DEBUG_OBJECT (flacenc, "querying peer for DEFAULT format duration");
-- if (gst_pad_query_peer_duration (pad, &fmt, &duration)
-- && fmt == GST_FORMAT_DEFAULT && duration != GST_CLOCK_TIME_NONE)
++ if (gst_pad_query_peer_duration (pad, GST_FORMAT_DEFAULT, &duration)
++ && duration != GST_CLOCK_TIME_NONE)
goto done;
-- fmt = GST_FORMAT_TIME;
GST_DEBUG_OBJECT (flacenc, "querying peer for TIME format duration");
-- if (gst_pad_query_peer_duration (pad, &fmt, &duration) &&
-- fmt == GST_FORMAT_TIME && duration != GST_CLOCK_TIME_NONE) {
++ if (gst_pad_query_peer_duration (pad, GST_FORMAT_TIME, &duration)
++ && duration != GST_CLOCK_TIME_NONE) {
GST_DEBUG_OBJECT (flacenc, "peer reported duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (duration));
duration = GST_CLOCK_TIME_TO_FRAMES (duration, flacenc->sample_rate);
GstFlacEnc *flacenc;
GstEvent *event;
GstPad *peerpad;
++ GstSegment seg;
flacenc = GST_FLAC_ENC (client_data);
if (flacenc->stopped)
return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
-- event = gst_event_new_new_segment (TRUE, 1.0, GST_FORMAT_BYTES,
-- absolute_byte_offset, GST_BUFFER_OFFSET_NONE, 0);
++ gst_segment_init (&seg, GST_FORMAT_BYTES);
++ seg.start = absolute_byte_offset;
++ seg.stop = GST_BUFFER_OFFSET_NONE;
++ seg.time = 0;
++ event = gst_event_new_segment (&seg);
- if ((peerpad = gst_pad_get_peer (flacenc->srcpad))) {
+ if ((peerpad = gst_pad_get_peer (GST_AUDIO_ENCODER_SRC_PAD (flacenc)))) {
gboolean ret = gst_pad_send_event (peerpad, event);
gst_object_unref (peerpad);
GstBuffer *buf;
buf = GST_BUFFER (l->data);
-- gst_buffer_set_caps (buf, caps);
GST_LOG_OBJECT (enc, "Pushing header buffer, size %u bytes",
- GST_BUFFER_SIZE (buf));
+ gst_buffer_get_size (buf));
+#if 0
GST_MEMDUMP_OBJECT (enc, "header buffer", GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf));
- (void) gst_pad_push (enc->srcpad, buf);
+#endif
+ ret = gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), buf);
l->data = NULL;
}
g_list_free (enc->headers);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
outbuf = gst_buffer_new_and_alloc (bytes);
- memcpy (GST_BUFFER_DATA (outbuf), buffer, bytes);
+ gst_buffer_fill (outbuf, 0, buffer, bytes);
- if (samples > 0 && flacenc->samples_written != (guint64) - 1) {
- guint64 granulepos;
-
- GST_BUFFER_TIMESTAMP (outbuf) = flacenc->start_ts +
- GST_FRAMES_TO_CLOCK_TIME (flacenc->samples_written,
- flacenc->sample_rate);
- GST_BUFFER_DURATION (outbuf) =
- GST_FRAMES_TO_CLOCK_TIME (samples, flacenc->sample_rate);
- /* offset_end = granulepos for ogg muxer */
- granulepos =
- flacenc->granulepos_offset + flacenc->samples_written + samples;
- GST_BUFFER_OFFSET_END (outbuf) = granulepos;
- /* offset = timestamp corresponding to granulepos for ogg muxer
- * (see vorbisenc for a much more elaborate version of this) */
- GST_BUFFER_OFFSET (outbuf) =
- GST_FRAMES_TO_CLOCK_TIME (granulepos, flacenc->sample_rate);
- } else {
- GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
- GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
- GST_BUFFER_OFFSET (outbuf) =
- flacenc->samples_written * flacenc->width * flacenc->channels;
- GST_BUFFER_OFFSET_END (outbuf) = 0;
- GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_IN_CAPS);
- }
-
/* we assume libflac passes us stuff neatly framed */
if (!flacenc->got_headers) {
if (samples == 0) {
flacenc->got_headers = TRUE;
}
} else if (flacenc->got_headers && samples == 0) {
+ /* header fixup, push downstream directly */
GST_DEBUG_OBJECT (flacenc, "Fixing up headers at pos=%" G_GUINT64_FORMAT
", size=%u", flacenc->offset, (guint) bytes);
+#if 0
GST_MEMDUMP_OBJECT (flacenc, "Presumed header fragment",
GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf));
- gst_buffer_set_caps (outbuf,
- GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (flacenc)));
+#endif
+ ret = gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (flacenc), outbuf);
} else {
+ /* regular frame data, pass to base class */
GST_LOG ("Pushing buffer: ts=%" GST_TIME_FORMAT ", samples=%u, size=%u, "
"pos=%" G_GUINT64_FORMAT, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
samples, (guint) bytes, flacenc->offset);
GST_DEBUG ("Received %s event on sinkpad", GST_EVENT_TYPE_NAME (event));
switch (GST_EVENT_TYPE (event)) {
-- case GST_EVENT_NEWSEGMENT:{
-- GstFormat format;
++ case GST_EVENT_SEGMENT:{
++ GstSegment seg;
gint64 start, stream_time;
if (flacenc->offset == 0) {
-- gst_event_parse_new_segment (event, NULL, NULL, &format, &start, NULL,
-- &stream_time);
++ gst_event_copy_segment (event, &seg);
++ start = seg.start;
++ stream_time = seg.time;
} else {
start = -1;
stream_time = -1;
else
GST_DEBUG ("Not handling newsegment event with non-zero start");
} else {
-- GstEvent *e = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
-- 0, -1, 0);
++ GstEvent *e;
- ret = gst_pad_push_event (flacenc->srcpad, e);
++ gst_segment_init (&seg, GST_FORMAT_BYTES);
++ e = gst_event_new_segment (&seg);
+ ret = gst_pad_push_event (GST_AUDIO_ENCODER_SRC_PAD (enc), e);
}
if (stream_time > 0) {
gint samples, width;
gulong i;
FLAC__bool res;
+ gpointer bdata;
- flacenc = GST_FLAC_ENC (GST_PAD_PARENT (pad));
+ flacenc = GST_FLAC_ENC (enc);
- /* make sure setcaps has been called and the encoder is set up */
- if (G_UNLIKELY (flacenc->depth == 0))
- return GST_FLOW_NOT_NEGOTIATED;
+ /* base class ensures configuration */
+ g_return_val_if_fail (flacenc->depth != 0, GST_FLOW_NOT_NEGOTIATED);
width = flacenc->width;
- /* Save the timestamp of the first buffer. This will be later
- * used as offset for all following buffers */
- if (flacenc->start_ts == GST_CLOCK_TIME_NONE) {
- if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
- flacenc->start_ts = GST_BUFFER_TIMESTAMP (buffer);
- flacenc->granulepos_offset = gst_util_uint64_scale
- (GST_BUFFER_TIMESTAMP (buffer), flacenc->sample_rate, GST_SECOND);
+ if (G_UNLIKELY (!buffer)) {
+ if (flacenc->eos) {
+ FLAC__stream_encoder_finish (flacenc->encoder);
} else {
- flacenc->start_ts = 0;
- flacenc->granulepos_offset = 0;
+ /* can't handle intermittent draining/resyncing */
+ GST_ELEMENT_WARNING (flacenc, STREAM, FORMAT, (NULL),
+ ("Stream discontinuity detected. "
+ "The output may have wrong timestamps, "
+ "consider using audiorate to handle discontinuities"));
}
+ return flacenc->last_flow;
}
- /* Check if we have a continous stream, if not drop some samples or the buffer or
- * insert some silence samples */
- if (flacenc->next_ts != GST_CLOCK_TIME_NONE
- && GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
- gst_flac_enc_check_discont (flacenc, flacenc->next_ts,
- GST_BUFFER_TIMESTAMP (buffer));
- }
-
- if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)
- && GST_BUFFER_DURATION_IS_VALID (buffer))
- flacenc->next_ts =
- GST_BUFFER_TIMESTAMP (buffer) + GST_BUFFER_DURATION (buffer);
- else
- flacenc->next_ts = GST_CLOCK_TIME_NONE;
-
- insize = GST_BUFFER_SIZE (buffer);
- samples = insize / (width >> 3);
+ bdata = gst_buffer_map (buffer, &bsize, NULL, GST_MAP_READ);
+ samples = bsize / (width >> 3);
data = g_malloc (samples * sizeof (FLAC__int32));
} else {
g_assert_not_reached ();
}
- gst_buffer_unref (buffer);
+ gst_buffer_unmap (buffer, bdata, bsize);
res = FLAC__stream_encoder_process_interleaved (flacenc->encoder,
(const FLAC__int32 *) data, samples / flacenc->channels);