summaryrefslogtreecommitdiffstats
path: root/ext/speex
diff options
context:
space:
mode:
authorSebastian Dröge <slomo@circular-chaos.org>2008-08-31 14:39:57 +0000
committerSebastian Dröge <slomo@circular-chaos.org>2008-08-31 14:39:57 +0000
commit25896b3a93da2cc7a8b2c5418d874bdb0065c5ef (patch)
treeeef8dca6d7544e9a2af0784955eeced545064f0d /ext/speex
parent467b9f2861846accbc3cfbe726d163cf1be312a1 (diff)
ext/speex/gstspeexdec.c: Correctly take the granulepos from upstream if possible and correctly handle the granulepos ...
Original commit message from CVS: * ext/speex/gstspeexdec.c: (speex_dec_chain_parse_data): Correctly take the granulepos from upstream if possible and correctly handle the granulepos in various calculations: the granulepos is the sample number of the _last_ sample in a frame, not the first. * ext/speex/gstspeexenc.c: (gst_speex_enc_sinkevent), (gst_speex_enc_encode), (gst_speex_enc_chain), (gst_speex_enc_change_state): * ext/speex/gstspeexenc.h: Handle non-zero start timestamps in the encoder and detect/handle stream discontinuities. Fixes bug #547075.
Diffstat (limited to 'ext/speex')
-rw-r--r--ext/speex/gstspeexdec.c18
-rw-r--r--ext/speex/gstspeexenc.c212
-rw-r--r--ext/speex/gstspeexenc.h7
3 files changed, 169 insertions, 68 deletions
diff --git a/ext/speex/gstspeexdec.c b/ext/speex/gstspeexdec.c
index a42de618..a3881451 100644
--- a/ext/speex/gstspeexdec.c
+++ b/ext/speex/gstspeexdec.c
@@ -664,6 +664,14 @@ speex_dec_chain_parse_data (GstSpeexDec * dec, GstBuffer * buf,
GST_DEBUG_OBJECT (dec, "received buffer of size %u, fpp %d", size, fpp);
+ if (!GST_BUFFER_TIMESTAMP_IS_VALID (buf)
+ && GST_BUFFER_OFFSET_END_IS_VALID (buf)) {
+ dec->granulepos = GST_BUFFER_OFFSET_END (buf);
+ GST_DEBUG_OBJECT (dec,
+ "Taking granulepos from upstream: %" G_GUINT64_FORMAT,
+ dec->granulepos);
+ }
+
/* copy timestamp */
} else {
/* concealment data, pass NULL as the bits parameters */
@@ -722,21 +730,21 @@ speex_dec_chain_parse_data (GstSpeexDec * dec, GstBuffer * buf,
if (dec->granulepos == -1) {
if (dec->segment.format != GST_FORMAT_TIME) {
GST_WARNING_OBJECT (dec, "segment not initialized or not TIME format");
- dec->granulepos = 0;
+ dec->granulepos = dec->frame_size;
} else {
dec->granulepos = gst_util_uint64_scale_int (dec->segment.last_stop,
- dec->header->rate, GST_SECOND);
+ dec->header->rate, GST_SECOND) + dec->frame_size;
}
GST_DEBUG_OBJECT (dec, "granulepos=%" G_GINT64_FORMAT, dec->granulepos);
}
if (timestamp == -1) {
- timestamp = gst_util_uint64_scale_int (dec->granulepos,
+ timestamp = gst_util_uint64_scale_int (dec->granulepos - dec->frame_size,
GST_SECOND, dec->header->rate);
}
- GST_BUFFER_OFFSET (outbuf) = dec->granulepos;
- GST_BUFFER_OFFSET_END (outbuf) = dec->granulepos + dec->frame_size;
+ GST_BUFFER_OFFSET (outbuf) = dec->granulepos - dec->frame_size;
+ GST_BUFFER_OFFSET_END (outbuf) = dec->granulepos;
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
GST_BUFFER_DURATION (outbuf) = dec->frame_duration;
diff --git a/ext/speex/gstspeexenc.c b/ext/speex/gstspeexenc.c
index e0beb53d..b2653b3a 100644
--- a/ext/speex/gstspeexenc.c
+++ b/ext/speex/gstspeexenc.c
@@ -30,6 +30,7 @@
#include <gst/gsttagsetter.h>
#include <gst/tag/tag.h>
+#include <gst/audio/audio.h>
#include "gstspeexenc.h"
GST_DEBUG_CATEGORY_STATIC (speexenc_debug);
@@ -135,6 +136,8 @@ static void gst_speex_enc_set_property (GObject * object, guint prop_id,
static GstStateChangeReturn gst_speex_enc_change_state (GstElement * element,
GstStateChange transition);
+static GstFlowReturn gst_speex_enc_encode (GstSpeexEnc * enc, gboolean flush);
+
static void
gst_speex_enc_setup_interfaces (GType speexenc_type)
{
@@ -822,7 +825,7 @@ gst_speex_enc_sinkevent (GstPad * pad, GstEvent * event)
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
- enc->eos = TRUE;
+ gst_speex_enc_encode (enc, TRUE);
res = gst_pad_event_default (pad, event);
break;
case GST_EVENT_TAG:
@@ -847,6 +850,85 @@ gst_speex_enc_sinkevent (GstPad * pad, GstEvent * event)
return res;
}
+static GstFlowReturn
+gst_speex_enc_encode (GstSpeexEnc * enc, gboolean flush)
+{
+ gint frame_size = enc->frame_size;
+ gint bytes = frame_size * 2 * enc->channels;
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if (flush && gst_adapter_available (enc->adapter) % bytes != 0) {
+ guint diff = gst_adapter_available (enc->adapter) % bytes;
+ GstBuffer *buf = gst_buffer_new_and_alloc (diff);
+
+ memset (GST_BUFFER_DATA (buf), 0, diff);
+ gst_adapter_push (enc->adapter, buf);
+ }
+
+ while (gst_adapter_available (enc->adapter) >= bytes) {
+ gint16 *data;
+ gint i;
+ gint outsize, written;
+ GstBuffer *outbuf;
+
+ data = (gint16 *) gst_adapter_peek (enc->adapter, bytes);
+
+ for (i = 0; i < frame_size * enc->channels; i++) {
+ enc->input[i] = (gfloat) data[i];
+ }
+ gst_adapter_flush (enc->adapter, bytes);
+
+ enc->samples_in += frame_size;
+
+ GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", frame_size, bytes);
+
+ if (enc->channels == 2) {
+ speex_encode_stereo (enc->input, frame_size, &enc->bits);
+ }
+ speex_encode (enc->state, enc->input, &enc->bits);
+
+ enc->frameno++;
+ enc->frameno_out++;
+
+ if ((enc->frameno % enc->nframes) != 0)
+ continue;
+
+ speex_bits_insert_terminator (&enc->bits);
+ outsize = speex_bits_nbytes (&enc->bits);
+
+ ret = gst_pad_alloc_buffer_and_set_caps (enc->srcpad,
+ GST_BUFFER_OFFSET_NONE, outsize, GST_PAD_CAPS (enc->srcpad), &outbuf);
+
+ if ((GST_FLOW_OK != ret))
+ goto done;
+
+ written = speex_bits_write (&enc->bits,
+ (gchar *) GST_BUFFER_DATA (outbuf), outsize);
+ g_assert (written == outsize);
+ speex_bits_reset (&enc->bits);
+
+ GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts +
+ gst_util_uint64_scale_int (enc->frameno_out * frame_size -
+ enc->lookahead, GST_SECOND, enc->rate);
+ GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int (frame_size,
+ GST_SECOND, enc->rate);
+ /* set gp time and granulepos; see gst-plugins-base/ext/ogg/README */
+ GST_BUFFER_OFFSET_END (outbuf) = enc->granulepos_offset +
+ ((enc->frameno_out + 1) * frame_size - enc->lookahead);
+ GST_BUFFER_OFFSET (outbuf) =
+ gst_util_uint64_scale_int (GST_BUFFER_OFFSET_END (outbuf), GST_SECOND,
+ enc->rate);
+
+ ret = gst_speex_enc_push_buffer (enc, outbuf);
+
+ if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret))
+ goto done;
+ }
+
+done:
+
+ return ret;
+}
static GstFlowReturn
gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
@@ -913,77 +995,79 @@ gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
enc->header_sent = TRUE;
}
- {
- gint frame_size = enc->frame_size;
- gint bytes = frame_size * 2 * enc->channels;
-
- GST_DEBUG_OBJECT (enc, "received buffer of %u bytes",
- GST_BUFFER_SIZE (buf));
-
- /* push buffer to adapter */
- gst_adapter_push (enc->adapter, buf);
- buf = NULL;
-
- while (gst_adapter_available (enc->adapter) >= bytes) {
- gint16 *data;
- gint i;
- gint outsize, written;
- GstBuffer *outbuf;
-
- data = (gint16 *) gst_adapter_peek (enc->adapter, bytes);
-
- for (i = 0; i < frame_size * enc->channels; i++) {
- enc->input[i] = (gfloat) data[i];
- }
- gst_adapter_flush (enc->adapter, bytes);
-
- enc->samples_in += frame_size;
-
- GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", frame_size,
- bytes);
+ /* Save the timestamp of the first buffer. This will be later
+ * used as offset for all following buffers */
+ if (enc->start_ts == GST_CLOCK_TIME_NONE) {
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+ enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
+ enc->granulepos_offset = gst_util_uint64_scale
+ (GST_BUFFER_TIMESTAMP (buf), enc->rate, GST_SECOND);
+ } else {
+ enc->start_ts = 0;
+ enc->granulepos_offset = 0;
+ }
+ }
- if (enc->channels == 2) {
- speex_encode_stereo (enc->input, frame_size, &enc->bits);
- }
- speex_encode (enc->state, enc->input, &enc->bits);
+ /* Check if we have a continous stream, if not drop some samples or the buffer or
+ * insert some silence samples */
+ if (enc->next_ts != GST_CLOCK_TIME_NONE &&
+ GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) {
+ guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf);
+ guint64 diff_bytes;
+
+ GST_WARNING_OBJECT (enc, "Buffer is older than previous "
+ "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
+ "), cannot handle. Clipping buffer.",
+ GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
+ GST_TIME_ARGS (enc->next_ts));
+
+ diff_bytes = GST_CLOCK_TIME_TO_FRAMES (diff, enc->rate) * enc->channels * 2;
+ if (diff_bytes >= GST_BUFFER_SIZE (buf)) {
+ gst_buffer_unref (buf);
+ return GST_FLOW_OK;
+ }
+ buf = gst_buffer_make_metadata_writable (buf);
+ GST_BUFFER_DATA (buf) += diff_bytes;
+ GST_BUFFER_SIZE (buf) -= diff_bytes;
- enc->frameno++;
+ GST_BUFFER_TIMESTAMP (buf) += diff;
+ if (GST_BUFFER_DURATION_IS_VALID (buf))
+ GST_BUFFER_DURATION (buf) -= diff;
+ }
- if ((enc->frameno % enc->nframes) != 0)
- continue;
+ if (enc->next_ts != GST_CLOCK_TIME_NONE
+ && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
+ guint64 max_diff =
+ gst_util_uint64_scale (enc->frame_size, GST_SECOND, enc->rate);
- speex_bits_insert_terminator (&enc->bits);
- outsize = speex_bits_nbytes (&enc->bits);
+ if (GST_BUFFER_TIMESTAMP (buf) != enc->next_ts &&
+ GST_BUFFER_TIMESTAMP (buf) - enc->next_ts > max_diff) {
+ GST_WARNING_OBJECT (enc,
+ "Discontinuity detected: %" G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT,
+ GST_BUFFER_TIMESTAMP (buf) - enc->next_ts, max_diff);
- ret = gst_pad_alloc_buffer_and_set_caps (enc->srcpad,
- GST_BUFFER_OFFSET_NONE, outsize, GST_PAD_CAPS (enc->srcpad), &outbuf);
+ gst_speex_enc_encode (enc, TRUE);
- if ((GST_FLOW_OK != ret))
- goto done;
+ enc->frameno_out = 0;
+ enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
+ enc->granulepos_offset = gst_util_uint64_scale
+ (GST_BUFFER_TIMESTAMP (buf), enc->rate, GST_SECOND);
+ }
+ }
- written = speex_bits_write (&enc->bits,
- (gchar *) GST_BUFFER_DATA (outbuf), outsize);
- g_assert (written == outsize);
- speex_bits_reset (&enc->bits);
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)
+ && GST_BUFFER_DURATION_IS_VALID (buf))
+ enc->next_ts = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
+ else
+ enc->next_ts = GST_CLOCK_TIME_NONE;
- GST_BUFFER_TIMESTAMP (outbuf) =
- gst_util_uint64_scale_int (enc->frameno * frame_size -
- enc->lookahead, GST_SECOND, enc->rate);
- GST_BUFFER_DURATION (outbuf) = gst_util_uint64_scale_int (frame_size,
- GST_SECOND, enc->rate);
- /* set gp time and granulepos; see gst-plugins-base/ext/ogg/README */
- GST_BUFFER_OFFSET_END (outbuf) =
- ((enc->frameno + 1) * frame_size - enc->lookahead);
- GST_BUFFER_OFFSET (outbuf) =
- gst_util_uint64_scale_int (GST_BUFFER_OFFSET_END (outbuf), GST_SECOND,
- enc->rate);
+ GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf));
- ret = gst_speex_enc_push_buffer (enc, outbuf);
+ /* push buffer to adapter */
+ gst_adapter_push (enc->adapter, buf);
+ buf = NULL;
- if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret))
- goto done;
- }
- }
+ ret = gst_speex_enc_encode (enc, FALSE);
done:
@@ -1104,7 +1188,11 @@ gst_speex_enc_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_READY_TO_PAUSED:
speex_bits_init (&enc->bits);
enc->frameno = 0;
+ enc->frameno_out = 0;
enc->samples_in = 0;
+ enc->start_ts = GST_CLOCK_TIME_NONE;
+ enc->next_ts = GST_CLOCK_TIME_NONE;
+ enc->granulepos_offset = 0;
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
/* fall through */
diff --git a/ext/speex/gstspeexenc.h b/ext/speex/gstspeexenc.h
index a5de5110..a5bfb366 100644
--- a/ext/speex/gstspeexenc.h
+++ b/ext/speex/gstspeexenc.h
@@ -92,7 +92,6 @@ struct _GstSpeexEnc {
gboolean setup;
gboolean header_sent;
- gboolean eos;
guint64 samples_in;
guint64 bytes_out;
@@ -103,11 +102,17 @@ struct _GstSpeexEnc {
gint frame_size;
guint64 frameno;
+ guint64 frameno_out;
guint8 *comments;
gint comment_len;
gfloat input[MAX_FRAME_SIZE];
+
+ /* Timestamp and granulepos tracking */
+ GstClockTime start_ts;
+ GstClockTime next_ts;
+ guint64 granulepos_offset;
};
struct _GstSpeexEncClass {