ext/flac/gstflacenc.*: Handle non-zero start timestamps correctly, mark header packets as

Original commit message from CVS:
* 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.
This commit is contained in:
Sebastian Dröge 2008-08-08 15:20:24 +00:00
parent 415ea1455f
commit c046872132
3 changed files with 80 additions and 11 deletions

View file

@ -1,3 +1,16 @@
2008-08-08 Sebastian Dröge <sebastian.droege@collabora.co.uk>
* 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 <sebastian.droege@collabora.co.uk> 2008-08-07 Sebastian Dröge <sebastian.droege@collabora.co.uk>
* ext/flac/gstflacenc.c: (gst_flac_enc_seek_callback): * ext/flac/gstflacenc.c: (gst_flac_enc_seek_callback):

View file

@ -17,10 +17,9 @@
* Boston, MA 02111-1307, USA. * Boston, MA 02111-1307, USA.
*/ */
/* FIXME: /* TODO: - We currently don't handle discontinuities in the stream in a useful
* - we assume timestamps start from 0 and that we get a perfect stream; we * way and instead rely on the developer plugging in audiorate if
* don't handle non-zero starts and mid-stream discontinuities, esp. not if * the stream contains discontinuities.
* we're muxing into ogg
*/ */
#ifdef HAVE_CONFIG_H #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) { if (samples > 0 && flacenc->samples_written != (guint64) - 1) {
guint64 granulepos; guint64 granulepos;
GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (outbuf) = flacenc->start_ts +
GST_FRAMES_TO_CLOCK_TIME (flacenc->samples_written, GST_FRAMES_TO_CLOCK_TIME (flacenc->samples_written,
flacenc->sample_rate); flacenc->sample_rate);
GST_BUFFER_DURATION (outbuf) = GST_BUFFER_DURATION (outbuf) =
GST_FRAMES_TO_CLOCK_TIME (samples, flacenc->sample_rate); GST_FRAMES_TO_CLOCK_TIME (samples, flacenc->sample_rate);
/* offset_end = granulepos for ogg muxer */ /* 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; GST_BUFFER_OFFSET_END (outbuf) = granulepos;
/* offset = timestamp corresponding to granulepos for ogg muxer /* offset = timestamp corresponding to granulepos for ogg muxer
* (see vorbisenc for a much more elaborate version of this) */ * (see vorbisenc for a much more elaborate version of this) */
@ -981,6 +981,10 @@ gst_flac_enc_write_callback (const FLAC__StreamEncoder * encoder,
} else { } else {
GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE; GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
GST_BUFFER_DURATION (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 */ /* we assume libflac passes us stuff neatly framed */
@ -1110,6 +1114,27 @@ gst_flac_enc_sink_event (GstPad * pad, GstEvent * event)
return ret; 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 static GstFlowReturn
gst_flac_enc_chain (GstPad * pad, GstBuffer * buffer) gst_flac_enc_chain (GstPad * pad, GstBuffer * buffer)
{ {
@ -1128,6 +1153,34 @@ gst_flac_enc_chain (GstPad * pad, GstBuffer * buffer)
width = flacenc->width; 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); insize = GST_BUFFER_SIZE (buffer);
samples = insize / (width >> 3); 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_NULL_TO_READY:
case GST_STATE_CHANGE_READY_TO_PAUSED: case GST_STATE_CHANGE_READY_TO_PAUSED:
flacenc->stopped = FALSE; flacenc->stopped = FALSE;
flacenc->start_ts = GST_CLOCK_TIME_NONE;
flacenc->next_ts = GST_CLOCK_TIME_NONE;
flacenc->granulepos_offset = 0;
break; break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING: case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
default: default:

View file

@ -54,19 +54,14 @@ struct _GstFlacEnc {
* correct flow return upstream in case the push * correct flow return upstream in case the push
* fails for some reason */ * fails for some reason */
gboolean first;
GstBuffer *first_buf;
guint64 offset; guint64 offset;
guint64 samples_written; guint64 samples_written;
gboolean eos;
gint channels; gint channels;
gint width; gint width;
gint depth; gint depth;
gint sample_rate; gint sample_rate;
gboolean negotiated;
gint quality; gint quality;
gboolean stopped; gboolean stopped;
FLAC__int32 *data;
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8 #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8
FLAC__SeekableStreamEncoder *encoder; FLAC__SeekableStreamEncoder *encoder;
@ -80,6 +75,11 @@ struct _GstFlacEnc {
/* queue headers until we have them all so we can add streamheaders to caps */ /* queue headers until we have them all so we can add streamheaders to caps */
gboolean got_headers; gboolean got_headers;
GList *headers; GList *headers;
/* Timestamp and granulepos tracking */
GstClockTime start_ts;
GstClockTime next_ts;
guint64 granulepos_offset;
}; };
struct _GstFlacEncClass { struct _GstFlacEncClass {