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:
Andy Wingo 2006-01-30 15:01:28 +00:00
parent 6757e87c91
commit 0ad84fae5d
5 changed files with 511 additions and 89 deletions

View file

@ -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:

View file

@ -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

View file

@ -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;

View file

@ -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

View 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;
}