mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-26 04:36:20 +00:00
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.
This commit is contained in:
parent
467b9f2861
commit
25896b3a93
4 changed files with 192 additions and 76 deletions
15
ChangeLog
15
ChangeLog
|
@ -1,3 +1,18 @@
|
||||||
|
2008-08-31 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
|
||||||
2008-08-31 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
2008-08-31 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||||
|
|
||||||
Patch by: Craig Keogh <cskeogh at adam dot com dot au>
|
Patch by: Craig Keogh <cskeogh at adam dot com dot au>
|
||||||
|
|
|
@ -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);
|
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 */
|
/* copy timestamp */
|
||||||
} else {
|
} else {
|
||||||
/* concealment data, pass NULL as the bits parameters */
|
/* 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->granulepos == -1) {
|
||||||
if (dec->segment.format != GST_FORMAT_TIME) {
|
if (dec->segment.format != GST_FORMAT_TIME) {
|
||||||
GST_WARNING_OBJECT (dec, "segment not initialized or not TIME format");
|
GST_WARNING_OBJECT (dec, "segment not initialized or not TIME format");
|
||||||
dec->granulepos = 0;
|
dec->granulepos = dec->frame_size;
|
||||||
} else {
|
} else {
|
||||||
dec->granulepos = gst_util_uint64_scale_int (dec->segment.last_stop,
|
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);
|
GST_DEBUG_OBJECT (dec, "granulepos=%" G_GINT64_FORMAT, dec->granulepos);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timestamp == -1) {
|
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_SECOND, dec->header->rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_BUFFER_OFFSET (outbuf) = dec->granulepos;
|
GST_BUFFER_OFFSET (outbuf) = dec->granulepos - dec->frame_size;
|
||||||
GST_BUFFER_OFFSET_END (outbuf) = dec->granulepos + dec->frame_size;
|
GST_BUFFER_OFFSET_END (outbuf) = dec->granulepos;
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
|
||||||
GST_BUFFER_DURATION (outbuf) = dec->frame_duration;
|
GST_BUFFER_DURATION (outbuf) = dec->frame_duration;
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <gst/gsttagsetter.h>
|
#include <gst/gsttagsetter.h>
|
||||||
#include <gst/tag/tag.h>
|
#include <gst/tag/tag.h>
|
||||||
|
#include <gst/audio/audio.h>
|
||||||
#include "gstspeexenc.h"
|
#include "gstspeexenc.h"
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_STATIC (speexenc_debug);
|
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,
|
static GstStateChangeReturn gst_speex_enc_change_state (GstElement * element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
|
||||||
|
static GstFlowReturn gst_speex_enc_encode (GstSpeexEnc * enc, gboolean flush);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_speex_enc_setup_interfaces (GType speexenc_type)
|
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)) {
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
case GST_EVENT_EOS:
|
case GST_EVENT_EOS:
|
||||||
enc->eos = TRUE;
|
gst_speex_enc_encode (enc, TRUE);
|
||||||
res = gst_pad_event_default (pad, event);
|
res = gst_pad_event_default (pad, event);
|
||||||
break;
|
break;
|
||||||
case GST_EVENT_TAG:
|
case GST_EVENT_TAG:
|
||||||
|
@ -847,6 +850,85 @@ gst_speex_enc_sinkevent (GstPad * pad, GstEvent * event)
|
||||||
return res;
|
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
|
static GstFlowReturn
|
||||||
gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
|
gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
|
||||||
|
@ -913,78 +995,80 @@ gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
|
||||||
enc->header_sent = TRUE;
|
enc->header_sent = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
/* Save the timestamp of the first buffer. This will be later
|
||||||
gint frame_size = enc->frame_size;
|
* used as offset for all following buffers */
|
||||||
gint bytes = frame_size * 2 * enc->channels;
|
if (enc->start_ts == GST_CLOCK_TIME_NONE) {
|
||||||
|
if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
|
||||||
GST_DEBUG_OBJECT (enc, "received buffer of %u bytes",
|
enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
|
||||||
GST_BUFFER_SIZE (buf));
|
enc->granulepos_offset = gst_util_uint64_scale
|
||||||
|
(GST_BUFFER_TIMESTAMP (buf), enc->rate, GST_SECOND);
|
||||||
/* push buffer to adapter */
|
} else {
|
||||||
gst_adapter_push (enc->adapter, buf);
|
enc->start_ts = 0;
|
||||||
buf = NULL;
|
enc->granulepos_offset = 0;
|
||||||
|
|
||||||
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++;
|
|
||||||
|
|
||||||
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) =
|
|
||||||
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);
|
|
||||||
|
|
||||||
ret = gst_speex_enc_push_buffer (enc, outbuf);
|
|
||||||
|
|
||||||
if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret))
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
GST_BUFFER_TIMESTAMP (buf) += diff;
|
||||||
|
if (GST_BUFFER_DURATION_IS_VALID (buf))
|
||||||
|
GST_BUFFER_DURATION (buf) -= diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
gst_speex_enc_encode (enc, TRUE);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf));
|
||||||
|
|
||||||
|
/* push buffer to adapter */
|
||||||
|
gst_adapter_push (enc->adapter, buf);
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
ret = gst_speex_enc_encode (enc, FALSE);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
|
||||||
if (buf)
|
if (buf)
|
||||||
|
@ -1104,7 +1188,11 @@ gst_speex_enc_change_state (GstElement * element, GstStateChange transition)
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
speex_bits_init (&enc->bits);
|
speex_bits_init (&enc->bits);
|
||||||
enc->frameno = 0;
|
enc->frameno = 0;
|
||||||
|
enc->frameno_out = 0;
|
||||||
enc->samples_in = 0;
|
enc->samples_in = 0;
|
||||||
|
enc->start_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
enc->next_ts = GST_CLOCK_TIME_NONE;
|
||||||
|
enc->granulepos_offset = 0;
|
||||||
break;
|
break;
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
|
@ -92,7 +92,6 @@ struct _GstSpeexEnc {
|
||||||
|
|
||||||
gboolean setup;
|
gboolean setup;
|
||||||
gboolean header_sent;
|
gboolean header_sent;
|
||||||
gboolean eos;
|
|
||||||
|
|
||||||
guint64 samples_in;
|
guint64 samples_in;
|
||||||
guint64 bytes_out;
|
guint64 bytes_out;
|
||||||
|
@ -103,11 +102,17 @@ struct _GstSpeexEnc {
|
||||||
|
|
||||||
gint frame_size;
|
gint frame_size;
|
||||||
guint64 frameno;
|
guint64 frameno;
|
||||||
|
guint64 frameno_out;
|
||||||
|
|
||||||
guint8 *comments;
|
guint8 *comments;
|
||||||
gint comment_len;
|
gint comment_len;
|
||||||
|
|
||||||
gfloat input[MAX_FRAME_SIZE];
|
gfloat input[MAX_FRAME_SIZE];
|
||||||
|
|
||||||
|
/* Timestamp and granulepos tracking */
|
||||||
|
GstClockTime start_ts;
|
||||||
|
GstClockTime next_ts;
|
||||||
|
guint64 granulepos_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstSpeexEncClass {
|
struct _GstSpeexEncClass {
|
||||||
|
|
Loading…
Reference in a new issue