diff --git a/ChangeLog b/ChangeLog index 0faf96dd48..4c866153a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2008-08-08 Sebastian Dröge + + * ext/flac/gstflacenc.c: (gst_flac_enc_write_callback), + (gst_flac_enc_check_discont), (gst_flac_enc_chain), + (gst_flac_enc_change_state): + * ext/flac/gstflacenc.h: + Handle non-zero start timestamps correctly, mark header packets as + IN_CAPS and print a warning and suggest using audiorate if stream + discontinuities are detected. When FLAC supports flushing the encoder + somehow this should be done for discontinuities instead. + + Remove some unused variables from the instance struct. + 2008-08-07 Sebastian Dröge * ext/flac/gstflacenc.c: (gst_flac_enc_seek_callback): diff --git a/ext/flac/gstflacenc.c b/ext/flac/gstflacenc.c index 4f290c4ad5..4440fcca71 100644 --- a/ext/flac/gstflacenc.c +++ b/ext/flac/gstflacenc.c @@ -17,10 +17,9 @@ * Boston, MA 02111-1307, USA. */ -/* FIXME: - * - we assume timestamps start from 0 and that we get a perfect stream; we - * don't handle non-zero starts and mid-stream discontinuities, esp. not if - * we're muxing into ogg +/* TODO: - We currently don't handle discontinuities in the stream in a useful + * way and instead rely on the developer plugging in audiorate if + * the stream contains discontinuities. */ #ifdef HAVE_CONFIG_H @@ -966,13 +965,14 @@ gst_flac_enc_write_callback (const FLAC__StreamEncoder * encoder, if (samples > 0 && flacenc->samples_written != (guint64) - 1) { guint64 granulepos; - GST_BUFFER_TIMESTAMP (outbuf) = + 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->samples_written + samples; + 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) */ @@ -981,6 +981,10 @@ gst_flac_enc_write_callback (const FLAC__StreamEncoder * encoder, } 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 */ @@ -1110,6 +1114,27 @@ gst_flac_enc_sink_event (GstPad * pad, GstEvent * event) return ret; } +static gboolean +gst_flac_enc_check_discont (GstFlacEnc * flacenc, GstClockTime expected, + GstClockTime timestamp) +{ + guint allowed_diff = GST_SECOND / flacenc->sample_rate / 2; + + if ((timestamp + allowed_diff < expected) + || (timestamp > expected + allowed_diff)) { + GST_ELEMENT_WARNING (flacenc, STREAM, FORMAT, (NULL), + ("Stream discontinuity detected (wanted %" GST_TIME_FORMAT " got %" + GST_TIME_FORMAT "). The output will have wrong timestamps," + " consider using audiorate to handle discontinuities")); + return TRUE; + } + + /* TODO: Do something to handle discontinuities in the stream. The FLAC encoder + * unfortunately doesn't have any way to flush it's internal buffers */ + + return FALSE; +} + static GstFlowReturn gst_flac_enc_chain (GstPad * pad, GstBuffer * buffer) { @@ -1128,6 +1153,34 @@ gst_flac_enc_chain (GstPad * pad, GstBuffer * buffer) 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); + } else { + flacenc->start_ts = 0; + flacenc->granulepos_offset = 0; + } + } + + /* 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); @@ -1449,6 +1502,9 @@ gst_flac_enc_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_NULL_TO_READY: case GST_STATE_CHANGE_READY_TO_PAUSED: flacenc->stopped = FALSE; + flacenc->start_ts = GST_CLOCK_TIME_NONE; + flacenc->next_ts = GST_CLOCK_TIME_NONE; + flacenc->granulepos_offset = 0; break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: default: diff --git a/ext/flac/gstflacenc.h b/ext/flac/gstflacenc.h index 1c6a6c66bb..0e8e4be805 100644 --- a/ext/flac/gstflacenc.h +++ b/ext/flac/gstflacenc.h @@ -54,19 +54,14 @@ struct _GstFlacEnc { * correct flow return upstream in case the push * fails for some reason */ - gboolean first; - GstBuffer *first_buf; guint64 offset; guint64 samples_written; - gboolean eos; gint channels; gint width; gint depth; gint sample_rate; - gboolean negotiated; gint quality; gboolean stopped; - FLAC__int32 *data; #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8 FLAC__SeekableStreamEncoder *encoder; @@ -80,6 +75,11 @@ struct _GstFlacEnc { /* queue headers until we have them all so we can add streamheaders to caps */ gboolean got_headers; GList *headers; + + /* Timestamp and granulepos tracking */ + GstClockTime start_ts; + GstClockTime next_ts; + guint64 granulepos_offset; }; struct _GstFlacEncClass {