mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 04:01:08 +00:00
tests/check/Makefile.am (check_vorbis): Add pipelines/vorbisenc.
Original commit message from CVS: 2006-01-30 Andy Wingo <wingo@pobox.com> * tests/check/Makefile.am (check_vorbis): Add pipelines/vorbisenc. * ext/vorbis/vorbisenc.c (gst_vorbisenc_buffer_from_packet): Logic updated to timestamp from the first sample, not the last. (gst_vorbisenc_buffer_from_header_packet): New function, takes special care of granulepos and timestamp for header packets. (gst_vorbisenc_chain): Reflow, fix some leaks, and handle the case when the first buffer has a nonzero timestamp. * ext/vorbis/vorbisenc.h (GstVorbisEnc.granulepos_offset) (GstVorbisEnc.subgranule_offset): New members. Take care of the case when the first audio buffer we get has a nonzero timestamp. (GstVorbisEnc.next_ts): Renamed from prev_ts, because now we properly timestamp vorbis buffers with the time of the first sample, not the last. * ext/vorbis/vorbisenc.c (granulepos_to_clocktime): Renamed from vorbis_granule_time_copy -- now it takes the granule/subgranule offset into account. * tests/check/pipelines/vorbisenc.c: New test for correctness of timestamps, durations, and granulepos on buffers produced by vorbisenc.
This commit is contained in:
parent
6757e87c91
commit
0ad84fae5d
5 changed files with 511 additions and 89 deletions
26
ChangeLog
26
ChangeLog
|
@ -1,3 +1,29 @@
|
||||||
|
2006-01-30 Andy Wingo <wingo@pobox.com>
|
||||||
|
|
||||||
|
* tests/check/Makefile.am (check_vorbis): Add pipelines/vorbisenc.
|
||||||
|
|
||||||
|
* ext/vorbis/vorbisenc.c (gst_vorbisenc_buffer_from_packet): Logic
|
||||||
|
updated to timestamp from the first sample, not the last.
|
||||||
|
(gst_vorbisenc_buffer_from_header_packet): New function, takes
|
||||||
|
special care of granulepos and timestamp for header packets.
|
||||||
|
(gst_vorbisenc_chain): Reflow, fix some leaks, and handle the case
|
||||||
|
when the first buffer has a nonzero timestamp.
|
||||||
|
|
||||||
|
* ext/vorbis/vorbisenc.h (GstVorbisEnc.granulepos_offset)
|
||||||
|
(GstVorbisEnc.subgranule_offset): New members. Take care of the
|
||||||
|
case when the first audio buffer we get has a nonzero timestamp.
|
||||||
|
(GstVorbisEnc.next_ts): Renamed from prev_ts, because now we
|
||||||
|
properly timestamp vorbis buffers with the time of the first
|
||||||
|
sample, not the last.
|
||||||
|
|
||||||
|
* ext/vorbis/vorbisenc.c (granulepos_to_clocktime): Renamed from
|
||||||
|
vorbis_granule_time_copy -- now it takes the granule/subgranule
|
||||||
|
offset into account.
|
||||||
|
|
||||||
|
* tests/check/pipelines/vorbisenc.c: New test for correctness of
|
||||||
|
timestamps, durations, and granulepos on buffers produced by
|
||||||
|
vorbisenc.
|
||||||
|
|
||||||
2006-01-30 Jan Schmidt <thaytan@mad.scientist.com>
|
2006-01-30 Jan Schmidt <thaytan@mad.scientist.com>
|
||||||
|
|
||||||
* gst/ffmpegcolorspace/gstffmpegcodecmap.c:
|
* gst/ffmpegcolorspace/gstffmpegcodecmap.c:
|
||||||
|
|
|
@ -98,17 +98,14 @@ enum
|
||||||
|
|
||||||
static GstFlowReturn gst_vorbisenc_output_buffers (GstVorbisEnc * vorbisenc);
|
static GstFlowReturn gst_vorbisenc_output_buffers (GstVorbisEnc * vorbisenc);
|
||||||
|
|
||||||
/* FIXME:
|
static GstClockTime
|
||||||
* vorbis_granule_time was added between 1.0 and 1.0.1; it's too silly
|
granulepos_to_clocktime (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos)
|
||||||
* to require a new version for such a simple function, but once we move
|
|
||||||
* beyond 1.0 for other reasons we can remove this copy */
|
|
||||||
|
|
||||||
static double
|
|
||||||
vorbis_granule_time_copy (vorbis_dsp_state * v, ogg_int64_t granulepos)
|
|
||||||
{
|
{
|
||||||
if (granulepos >= 0)
|
if (granulepos >= 0)
|
||||||
return ((double) granulepos / v->vi->rate);
|
return gst_util_uint64_scale ((guint64) granulepos
|
||||||
return (-1);
|
+ vorbisenc->granulepos_offset, GST_SECOND, vorbisenc->frequency)
|
||||||
|
+ vorbisenc->subgranule_offset;
|
||||||
|
return GST_CLOCK_TIME_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -549,7 +546,6 @@ gst_vorbisenc_init (GstVorbisEnc * vorbisenc)
|
||||||
vorbisenc->quality = QUALITY_DEFAULT;
|
vorbisenc->quality = QUALITY_DEFAULT;
|
||||||
vorbisenc->quality_set = FALSE;
|
vorbisenc->quality_set = FALSE;
|
||||||
vorbisenc->last_message = NULL;
|
vorbisenc->last_message = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -770,7 +766,7 @@ gst_vorbisenc_setup (GstVorbisEnc * vorbisenc)
|
||||||
vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi);
|
vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi);
|
||||||
vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb);
|
vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb);
|
||||||
|
|
||||||
vorbisenc->prev_ts = 0;
|
vorbisenc->next_ts = 0;
|
||||||
|
|
||||||
vorbisenc->setup = TRUE;
|
vorbisenc->setup = TRUE;
|
||||||
|
|
||||||
|
@ -808,18 +804,40 @@ gst_vorbisenc_buffer_from_packet (GstVorbisEnc * vorbisenc, ogg_packet * packet)
|
||||||
outbuf = gst_buffer_new_and_alloc (packet->bytes);
|
outbuf = gst_buffer_new_and_alloc (packet->bytes);
|
||||||
memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
|
memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
|
||||||
GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
|
GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
|
||||||
GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos;
|
GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos +
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) =
|
vorbisenc->granulepos_offset;
|
||||||
vorbis_granule_time_copy (&vorbisenc->vd,
|
GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts;
|
||||||
packet->granulepos) * GST_SECOND;
|
|
||||||
|
/* need to pass in an unadjusted granulepos here */
|
||||||
|
vorbisenc->next_ts = granulepos_to_clocktime (vorbisenc, packet->granulepos);
|
||||||
|
|
||||||
GST_BUFFER_DURATION (outbuf) =
|
GST_BUFFER_DURATION (outbuf) =
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) - vorbisenc->prev_ts;
|
vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf);
|
||||||
vorbisenc->prev_ts = GST_BUFFER_TIMESTAMP (outbuf);
|
|
||||||
|
|
||||||
GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf));
|
GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf));
|
||||||
return outbuf;
|
return outbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* the same as above, but different logic for setting timestamp and granulepos
|
||||||
|
* */
|
||||||
|
static GstBuffer *
|
||||||
|
gst_vorbisenc_buffer_from_header_packet (GstVorbisEnc * vorbisenc,
|
||||||
|
ogg_packet * packet)
|
||||||
|
{
|
||||||
|
GstBuffer *outbuf;
|
||||||
|
|
||||||
|
outbuf = gst_buffer_new_and_alloc (packet->bytes);
|
||||||
|
memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes);
|
||||||
|
GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out;
|
||||||
|
GST_BUFFER_OFFSET_END (outbuf) = 0;
|
||||||
|
GST_BUFFER_TIMESTAMP (outbuf) = GST_CLOCK_TIME_NONE;
|
||||||
|
GST_BUFFER_DURATION (outbuf) = GST_CLOCK_TIME_NONE;
|
||||||
|
|
||||||
|
GST_DEBUG ("created header packet buffer, %d bytes",
|
||||||
|
GST_BUFFER_SIZE (outbuf));
|
||||||
|
return outbuf;
|
||||||
|
}
|
||||||
|
|
||||||
/* push out the buffer and do internal bookkeeping */
|
/* push out the buffer and do internal bookkeeping */
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_vorbisenc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer)
|
gst_vorbisenc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer)
|
||||||
|
@ -913,97 +931,117 @@ gst_vorbisenc_sink_event (GstPad * pad, GstEvent * event)
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
gst_vorbisenc_chain (GstPad * pad, GstBuffer * buffer)
|
gst_vorbisenc_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
GstBuffer *buf = GST_BUFFER (buffer);
|
|
||||||
GstVorbisEnc *vorbisenc;
|
GstVorbisEnc *vorbisenc;
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
gfloat *data;
|
||||||
|
gulong size;
|
||||||
|
gulong i, j;
|
||||||
|
float **vorbis_buffer;
|
||||||
|
|
||||||
vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
|
vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad));
|
||||||
|
|
||||||
{
|
if (!vorbisenc->setup)
|
||||||
gfloat *data;
|
goto not_setup;
|
||||||
gulong size;
|
|
||||||
gulong i, j;
|
|
||||||
float **buffer;
|
|
||||||
|
|
||||||
if (!vorbisenc->setup) {
|
if (!vorbisenc->header_sent) {
|
||||||
gst_buffer_unref (buf);
|
/* Vorbis streams begin with three headers; the initial header (with
|
||||||
GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL),
|
most of the codec setup parameters) which is mandated by the Ogg
|
||||||
("encoder not initialized (input is not audio?)"));
|
bitstream spec. The second header holds any comment fields. The
|
||||||
return GST_FLOW_UNEXPECTED;
|
third header holds the bitstream codebook. We merely need to
|
||||||
}
|
make the headers, then pass them to libvorbis one at a time;
|
||||||
|
libvorbis handles the additional Ogg bitstream constraints */
|
||||||
|
ogg_packet header;
|
||||||
|
ogg_packet header_comm;
|
||||||
|
ogg_packet header_code;
|
||||||
|
GstBuffer *buf1, *buf2, *buf3;
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
if (!vorbisenc->header_sent) {
|
/* first, make sure header buffers get timestamp == 0 */
|
||||||
/* Vorbis streams begin with three headers; the initial header (with
|
vorbisenc->next_ts = 0;
|
||||||
most of the codec setup parameters) which is mandated by the Ogg
|
vorbisenc->granulepos_offset = 0;
|
||||||
bitstream spec. The second header holds any comment fields. The
|
vorbisenc->subgranule_offset = 0;
|
||||||
third header holds the bitstream codebook. We merely need to
|
|
||||||
make the headers, then pass them to libvorbis one at a time;
|
|
||||||
libvorbis handles the additional Ogg bitstream constraints */
|
|
||||||
ogg_packet header;
|
|
||||||
ogg_packet header_comm;
|
|
||||||
ogg_packet header_code;
|
|
||||||
GstBuffer *buf1, *buf2, *buf3;
|
|
||||||
GstCaps *caps;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets");
|
GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets");
|
||||||
gst_vorbisenc_set_metadata (vorbisenc);
|
gst_vorbisenc_set_metadata (vorbisenc);
|
||||||
vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header,
|
vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header,
|
||||||
&header_comm, &header_code);
|
&header_comm, &header_code);
|
||||||
|
|
||||||
/* create header buffers */
|
/* create header buffers */
|
||||||
buf1 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header);
|
buf1 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header);
|
||||||
buf2 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header_comm);
|
buf2 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header_comm);
|
||||||
buf3 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header_code);
|
buf3 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header_code);
|
||||||
|
|
||||||
/* mark and put on caps */
|
/* mark and put on caps */
|
||||||
caps = gst_pad_get_caps (vorbisenc->srcpad);
|
caps = gst_pad_get_caps (vorbisenc->srcpad);
|
||||||
caps = gst_vorbisenc_set_header_on_caps (caps, buf1, buf2, buf3);
|
caps = gst_vorbisenc_set_header_on_caps (caps, buf1, buf2, buf3);
|
||||||
|
|
||||||
/* negotiate with these caps */
|
/* negotiate with these caps */
|
||||||
GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
|
GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
|
||||||
gst_pad_set_caps (vorbisenc->srcpad, caps);
|
gst_pad_set_caps (vorbisenc->srcpad, caps);
|
||||||
|
|
||||||
gst_buffer_set_caps (buf1, caps);
|
gst_buffer_set_caps (buf1, caps);
|
||||||
gst_buffer_set_caps (buf2, caps);
|
gst_buffer_set_caps (buf2, caps);
|
||||||
gst_buffer_set_caps (buf3, caps);
|
gst_buffer_set_caps (buf3, caps);
|
||||||
|
|
||||||
/* push out buffers */
|
/* push out buffers */
|
||||||
if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK)
|
if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK)
|
||||||
goto done;
|
goto failed_header_push;
|
||||||
if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK)
|
if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK)
|
||||||
goto done;
|
goto failed_header_push;
|
||||||
if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK)
|
if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK)
|
||||||
goto done;
|
goto failed_header_push;
|
||||||
|
|
||||||
vorbisenc->header_sent = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* data to encode */
|
/* now adjust starting granulepos accordingly if the buffer's timestamp is
|
||||||
data = (gfloat *) GST_BUFFER_DATA (buf);
|
nonzero */
|
||||||
size = GST_BUFFER_SIZE (buf) / (vorbisenc->channels * sizeof (float));
|
vorbisenc->next_ts = GST_BUFFER_TIMESTAMP (buffer);
|
||||||
|
vorbisenc->granulepos_offset = gst_util_uint64_scale
|
||||||
|
(GST_BUFFER_TIMESTAMP (buffer), vorbisenc->frequency, GST_SECOND);
|
||||||
|
vorbisenc->subgranule_offset = 0;
|
||||||
|
vorbisenc->subgranule_offset =
|
||||||
|
vorbisenc->next_ts - granulepos_to_clocktime (vorbisenc, 0);
|
||||||
|
|
||||||
/* expose the buffer to submit data */
|
vorbisenc->header_sent = TRUE;
|
||||||
buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
|
|
||||||
|
|
||||||
/* uninterleave samples */
|
|
||||||
for (i = 0; i < size; i++) {
|
|
||||||
for (j = 0; j < vorbisenc->channels; j++) {
|
|
||||||
buffer[j][i] = *data++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tell the library how much we actually submitted */
|
|
||||||
vorbis_analysis_wrote (&vorbisenc->vd, size);
|
|
||||||
|
|
||||||
vorbisenc->samples_in += size;
|
|
||||||
|
|
||||||
gst_buffer_unref (buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* data to encode */
|
||||||
|
data = (gfloat *) GST_BUFFER_DATA (buffer);
|
||||||
|
size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float));
|
||||||
|
|
||||||
|
/* expose the buffer to submit data */
|
||||||
|
vorbis_buffer = vorbis_analysis_buffer (&vorbisenc->vd, size);
|
||||||
|
|
||||||
|
/* deinterleave samples, write the buffer data */
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
for (j = 0; j < vorbisenc->channels; j++) {
|
||||||
|
vorbis_buffer[j][i] = *data++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tell the library how much we actually submitted */
|
||||||
|
vorbis_analysis_wrote (&vorbisenc->vd, size);
|
||||||
|
|
||||||
|
vorbisenc->samples_in += size;
|
||||||
|
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
ret = gst_vorbisenc_output_buffers (vorbisenc);
|
ret = gst_vorbisenc_output_buffers (vorbisenc);
|
||||||
|
|
||||||
done:
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
/* error cases */
|
||||||
|
not_setup:
|
||||||
|
{
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL),
|
||||||
|
("encoder not initialized (input is not audio?)"));
|
||||||
|
return GST_FLOW_UNEXPECTED;
|
||||||
|
}
|
||||||
|
failed_header_push:
|
||||||
|
{
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
|
|
|
@ -69,7 +69,9 @@ struct _GstVorbisEnc {
|
||||||
|
|
||||||
guint64 samples_in;
|
guint64 samples_in;
|
||||||
guint64 bytes_out;
|
guint64 bytes_out;
|
||||||
GstClockTime prev_ts;
|
GstClockTime next_ts;
|
||||||
|
guint64 granulepos_offset;
|
||||||
|
gint64 subgranule_offset;
|
||||||
|
|
||||||
GstTagList * tags;
|
GstTagList * tags;
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ $(CHECK_REGISTRY):
|
||||||
TESTS = $(check_PROGRAMS)
|
TESTS = $(check_PROGRAMS)
|
||||||
|
|
||||||
if USE_VORBIS
|
if USE_VORBIS
|
||||||
check_vorbis = elements/vorbisdec
|
check_vorbis = elements/vorbisdec pipelines/vorbisenc
|
||||||
else
|
else
|
||||||
check_vorbis =
|
check_vorbis =
|
||||||
endif
|
endif
|
||||||
|
|
356
tests/check/pipelines/vorbisenc.c
Normal file
356
tests/check/pipelines/vorbisenc.c
Normal file
|
@ -0,0 +1,356 @@
|
||||||
|
/* GStreamer
|
||||||
|
*
|
||||||
|
* unit test for vorbisenc
|
||||||
|
*
|
||||||
|
* Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Library General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Library General Public
|
||||||
|
* License along with this library; if not, write to the
|
||||||
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
* Boston, MA 02111-1307, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <gst/check/gstcheck.h>
|
||||||
|
|
||||||
|
#define TIMESTAMP_OFFSET G_GUINT64_CONSTANT(3249870963)
|
||||||
|
|
||||||
|
static GCond *cond = NULL;
|
||||||
|
static GMutex *lock = NULL;
|
||||||
|
static GstBuffer *buf = NULL;
|
||||||
|
static gulong id;
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
buffer_probe (GstPad * pad, GstBuffer * buffer, gpointer unused)
|
||||||
|
{
|
||||||
|
g_mutex_lock (lock);
|
||||||
|
|
||||||
|
while (buf != NULL)
|
||||||
|
g_cond_wait (cond, lock);
|
||||||
|
|
||||||
|
buf = gst_buffer_ref (buffer);
|
||||||
|
|
||||||
|
g_cond_signal (cond);
|
||||||
|
|
||||||
|
g_mutex_unlock (lock);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
start_pipeline (GstElement * bin, GstPad * pad)
|
||||||
|
{
|
||||||
|
id = gst_pad_add_buffer_probe (pad, G_CALLBACK (buffer_probe), NULL);
|
||||||
|
|
||||||
|
cond = g_cond_new ();
|
||||||
|
lock = g_mutex_new ();
|
||||||
|
|
||||||
|
gst_element_set_state (bin, GST_STATE_PLAYING);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstBuffer *
|
||||||
|
get_buffer (GstElement * bin, GstPad * pad)
|
||||||
|
{
|
||||||
|
GstBuffer *ret;
|
||||||
|
|
||||||
|
g_mutex_lock (lock);
|
||||||
|
|
||||||
|
while (buf == NULL)
|
||||||
|
g_cond_wait (cond, lock);
|
||||||
|
|
||||||
|
ret = buf;
|
||||||
|
buf = NULL;
|
||||||
|
|
||||||
|
g_cond_signal (cond);
|
||||||
|
|
||||||
|
g_mutex_unlock (lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stop_pipeline (GstElement * bin, GstPad * pad)
|
||||||
|
{
|
||||||
|
g_mutex_lock (lock);
|
||||||
|
if (buf)
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
buf = NULL;
|
||||||
|
gst_pad_remove_buffer_probe (pad, (guint) id);
|
||||||
|
id = 0;
|
||||||
|
g_cond_signal (cond);
|
||||||
|
g_mutex_unlock (lock);
|
||||||
|
|
||||||
|
gst_element_set_state (bin, GST_STATE_NULL);
|
||||||
|
|
||||||
|
g_mutex_lock (lock);
|
||||||
|
if (buf)
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
buf = NULL;
|
||||||
|
g_mutex_unlock (lock);
|
||||||
|
|
||||||
|
g_mutex_free (lock);
|
||||||
|
g_cond_free (cond);
|
||||||
|
|
||||||
|
lock = NULL;
|
||||||
|
cond = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_buffer_timestamp (GstBuffer * buffer, GstClockTime timestamp)
|
||||||
|
{
|
||||||
|
fail_unless (GST_BUFFER_TIMESTAMP (buffer) == timestamp,
|
||||||
|
"expected timestamp %" GST_TIME_FORMAT
|
||||||
|
", but got timestamp %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (timestamp), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_buffer_duration (GstBuffer * buffer, GstClockTime duration)
|
||||||
|
{
|
||||||
|
fail_unless (GST_BUFFER_DURATION (buffer) == duration,
|
||||||
|
"expected duration %" GST_TIME_FORMAT
|
||||||
|
", but got duration %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (duration), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_buffer_granulepos (GstBuffer * buffer, GstClockTime granulepos)
|
||||||
|
{
|
||||||
|
fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
|
||||||
|
"expected granulepos %" G_GUINT64_FORMAT
|
||||||
|
", but got granulepos %" G_GUINT64_FORMAT,
|
||||||
|
granulepos, GST_BUFFER_OFFSET_END (buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* this check is here to check that the granulepos we derive from the timestamp
|
||||||
|
is about correct. This is "about correct" because you can't precisely go from
|
||||||
|
timestamp to granulepos due to the downward-rounding characteristics of
|
||||||
|
gst_util_uint64_scale, so you check if granulepos is equal to the number, or
|
||||||
|
the number plus one. */
|
||||||
|
static void
|
||||||
|
check_buffer_granulepos_from_endtime (GstBuffer * buffer, GstClockTime endtime)
|
||||||
|
{
|
||||||
|
GstClockTime granulepos, expected;
|
||||||
|
|
||||||
|
granulepos = GST_BUFFER_OFFSET_END (buffer);
|
||||||
|
expected = gst_util_uint64_scale (endtime, 44100, GST_SECOND);
|
||||||
|
|
||||||
|
fail_unless (granulepos == expected || granulepos == expected + 1,
|
||||||
|
"expected granulepos %" G_GUINT64_FORMAT
|
||||||
|
" or %" G_GUINT64_FORMAT
|
||||||
|
", but got granulepos %" G_GUINT64_FORMAT,
|
||||||
|
expected, expected + 1, granulepos);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_START_TEST (test_granulepos_offset)
|
||||||
|
{
|
||||||
|
GstElement *bin;
|
||||||
|
GstPad *pad;
|
||||||
|
gchar *pipe_str;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
GError *error = NULL;
|
||||||
|
GstClockTime timestamp;
|
||||||
|
|
||||||
|
pipe_str = g_strdup_printf ("audiotestsrc timestamp-offset=%" G_GUINT64_FORMAT
|
||||||
|
" ! audio/x-raw-int,rate=44100"
|
||||||
|
" ! audioconvert ! vorbisenc ! fakesink", TIMESTAMP_OFFSET);
|
||||||
|
|
||||||
|
bin = gst_parse_launch (pipe_str, &error);
|
||||||
|
fail_unless (bin != NULL, "Error parsing pipeline: %s",
|
||||||
|
error ? error->message : "(invalid error)");
|
||||||
|
g_free (pipe_str);
|
||||||
|
|
||||||
|
/* get the pad */
|
||||||
|
{
|
||||||
|
GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fakesink0");
|
||||||
|
|
||||||
|
fail_unless (sink != NULL, "Could not get fakesink out of bin");
|
||||||
|
pad = gst_element_get_pad (sink, "sink");
|
||||||
|
fail_unless (pad != NULL, "Could not get pad out of fakesink");
|
||||||
|
gst_object_unref (sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pipeline (bin, pad);
|
||||||
|
|
||||||
|
/* header packets should have timestamp == NONE, granulepos 0 */
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_granulepos (buffer, 0);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_granulepos (buffer, 0);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_granulepos (buffer, 0);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
{
|
||||||
|
GstClockTime next_timestamp, last_granulepos;
|
||||||
|
|
||||||
|
/* first buffer should have timestamp of TIMESTAMP_OFFSET, granulepos to
|
||||||
|
* match the timestamp of the end of the last sample in the output buffer.
|
||||||
|
* Note that one cannot go timestamp->granulepos->timestamp and get the same
|
||||||
|
* value due to loss of precision with granulepos. vorbisenc does take care
|
||||||
|
* to timestamp correctly based on the offset of the input data however, so
|
||||||
|
* it does do sub-granulepos timestamping. */
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
last_granulepos = GST_BUFFER_OFFSET_END (buffer);
|
||||||
|
check_buffer_timestamp (buffer, TIMESTAMP_OFFSET);
|
||||||
|
/* don't really have a good way of checking duration... */
|
||||||
|
check_buffer_granulepos_from_endtime (buffer,
|
||||||
|
TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer));
|
||||||
|
|
||||||
|
next_timestamp = TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer);
|
||||||
|
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
/* check continuity with the next buffer */
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, next_timestamp);
|
||||||
|
check_buffer_duration (buffer,
|
||||||
|
gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
|
||||||
|
44100)
|
||||||
|
- gst_util_uint64_scale (last_granulepos, GST_SECOND, 44100));
|
||||||
|
check_buffer_granulepos_from_endtime (buffer,
|
||||||
|
next_timestamp + GST_BUFFER_DURATION (buffer));
|
||||||
|
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_pipeline (bin, pad);
|
||||||
|
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_object_unref (bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
GST_START_TEST (test_timestamps)
|
||||||
|
{
|
||||||
|
GstElement *bin;
|
||||||
|
GstPad *pad;
|
||||||
|
gchar *pipe_str;
|
||||||
|
GstBuffer *buffer;
|
||||||
|
GError *error = NULL;
|
||||||
|
GstClockTime timestamp;
|
||||||
|
|
||||||
|
pipe_str = g_strdup_printf ("audiotestsrc"
|
||||||
|
" ! audio/x-raw-int,rate=44100" " ! audioconvert ! vorbisenc ! fakesink");
|
||||||
|
|
||||||
|
bin = gst_parse_launch (pipe_str, &error);
|
||||||
|
fail_unless (bin != NULL, "Error parsing pipeline: %s",
|
||||||
|
error ? error->message : "(invalid error)");
|
||||||
|
g_free (pipe_str);
|
||||||
|
|
||||||
|
/* get the pad */
|
||||||
|
{
|
||||||
|
GstElement *sink = gst_bin_get_by_name (GST_BIN (bin), "fakesink0");
|
||||||
|
|
||||||
|
fail_unless (sink != NULL, "Could not get fakesink out of bin");
|
||||||
|
pad = gst_element_get_pad (sink, "sink");
|
||||||
|
fail_unless (pad != NULL, "Could not get pad out of fakesink");
|
||||||
|
gst_object_unref (sink);
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pipeline (bin, pad);
|
||||||
|
|
||||||
|
/* check header packets */
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_granulepos (buffer, 0);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_granulepos (buffer, 0);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_duration (buffer, GST_CLOCK_TIME_NONE);
|
||||||
|
check_buffer_granulepos (buffer, 0);
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
{
|
||||||
|
GstClockTime next_timestamp, last_granulepos;
|
||||||
|
|
||||||
|
/* first buffer has timestamp 0 */
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
last_granulepos = GST_BUFFER_OFFSET_END (buffer);
|
||||||
|
check_buffer_timestamp (buffer, 0);
|
||||||
|
/* don't really have a good way of checking duration... */
|
||||||
|
check_buffer_granulepos_from_endtime (buffer, GST_BUFFER_DURATION (buffer));
|
||||||
|
|
||||||
|
next_timestamp = GST_BUFFER_DURATION (buffer);
|
||||||
|
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
|
||||||
|
/* check continuity with the next buffer */
|
||||||
|
buffer = get_buffer (bin, pad);
|
||||||
|
check_buffer_timestamp (buffer, next_timestamp);
|
||||||
|
check_buffer_duration (buffer,
|
||||||
|
gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
|
||||||
|
44100)
|
||||||
|
- gst_util_uint64_scale (last_granulepos, GST_SECOND, 44100));
|
||||||
|
check_buffer_granulepos_from_endtime (buffer,
|
||||||
|
next_timestamp + GST_BUFFER_DURATION (buffer));
|
||||||
|
|
||||||
|
gst_buffer_unref (buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_pipeline (bin, pad);
|
||||||
|
|
||||||
|
gst_object_unref (pad);
|
||||||
|
gst_object_unref (bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
|
Suite *
|
||||||
|
vorbisenc_suite (void)
|
||||||
|
{
|
||||||
|
Suite *s = suite_create ("vorbisenc");
|
||||||
|
TCase *tc_chain = tcase_create ("general");
|
||||||
|
|
||||||
|
suite_add_tcase (s, tc_chain);
|
||||||
|
tcase_add_test (tc_chain, test_granulepos_offset);
|
||||||
|
tcase_add_test (tc_chain, test_timestamps);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char **argv)
|
||||||
|
{
|
||||||
|
int nf;
|
||||||
|
|
||||||
|
Suite *s = vorbisenc_suite ();
|
||||||
|
SRunner *sr = srunner_create (s);
|
||||||
|
|
||||||
|
gst_check_init (&argc, &argv);
|
||||||
|
|
||||||
|
srunner_run_all (sr, CK_NORMAL);
|
||||||
|
nf = srunner_ntests_failed (sr);
|
||||||
|
srunner_free (sr);
|
||||||
|
|
||||||
|
return nf;
|
||||||
|
}
|
Loading…
Reference in a new issue