From: Andy Wingo Date: Mon, 30 Jan 2006 15:01:28 +0000 (+0000) Subject: tests/check/Makefile.am (check_vorbis): Add pipelines/vorbisenc. X-Git-Tag: 1.19.3~511^2~12269 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0ad84fae5db8b486fec816f8905e200ded7fb241;p=platform%2Fupstream%2Fgstreamer.git tests/check/Makefile.am (check_vorbis): Add pipelines/vorbisenc. Original commit message from CVS: 2006-01-30 Andy Wingo * 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. --- diff --git a/ChangeLog b/ChangeLog index 7df40d0..012e5a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2006-01-30 Andy Wingo + + * 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 * gst/ffmpegcolorspace/gstffmpegcodecmap.c: diff --git a/ext/vorbis/vorbisenc.c b/ext/vorbis/vorbisenc.c index 463cd55..b211f4e 100644 --- a/ext/vorbis/vorbisenc.c +++ b/ext/vorbis/vorbisenc.c @@ -98,17 +98,14 @@ enum static GstFlowReturn gst_vorbisenc_output_buffers (GstVorbisEnc * vorbisenc); -/* FIXME: - * vorbis_granule_time was added between 1.0 and 1.0.1; it's too silly - * 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) +static GstClockTime +granulepos_to_clocktime (GstVorbisEnc * vorbisenc, ogg_int64_t granulepos) { if (granulepos >= 0) - return ((double) granulepos / v->vi->rate); - return (-1); + return gst_util_uint64_scale ((guint64) granulepos + + vorbisenc->granulepos_offset, GST_SECOND, vorbisenc->frequency) + + vorbisenc->subgranule_offset; + return GST_CLOCK_TIME_NONE; } #if 0 @@ -549,7 +546,6 @@ gst_vorbisenc_init (GstVorbisEnc * vorbisenc) vorbisenc->quality = QUALITY_DEFAULT; vorbisenc->quality_set = FALSE; vorbisenc->last_message = NULL; - } @@ -770,7 +766,7 @@ gst_vorbisenc_setup (GstVorbisEnc * vorbisenc) vorbis_analysis_init (&vorbisenc->vd, &vorbisenc->vi); vorbis_block_init (&vorbisenc->vd, &vorbisenc->vb); - vorbisenc->prev_ts = 0; + vorbisenc->next_ts = 0; 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); memcpy (GST_BUFFER_DATA (outbuf), packet->packet, packet->bytes); GST_BUFFER_OFFSET (outbuf) = vorbisenc->bytes_out; - GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos; - GST_BUFFER_TIMESTAMP (outbuf) = - vorbis_granule_time_copy (&vorbisenc->vd, - packet->granulepos) * GST_SECOND; + GST_BUFFER_OFFSET_END (outbuf) = packet->granulepos + + vorbisenc->granulepos_offset; + GST_BUFFER_TIMESTAMP (outbuf) = vorbisenc->next_ts; + + /* need to pass in an unadjusted granulepos here */ + vorbisenc->next_ts = granulepos_to_clocktime (vorbisenc, packet->granulepos); + GST_BUFFER_DURATION (outbuf) = - GST_BUFFER_TIMESTAMP (outbuf) - vorbisenc->prev_ts; - vorbisenc->prev_ts = GST_BUFFER_TIMESTAMP (outbuf); + vorbisenc->next_ts - GST_BUFFER_TIMESTAMP (outbuf); GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (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 */ static GstFlowReturn gst_vorbisenc_push_buffer (GstVorbisEnc * vorbisenc, GstBuffer * buffer) @@ -913,97 +931,117 @@ gst_vorbisenc_sink_event (GstPad * pad, GstEvent * event) static GstFlowReturn gst_vorbisenc_chain (GstPad * pad, GstBuffer * buffer) { - GstBuffer *buf = GST_BUFFER (buffer); GstVorbisEnc *vorbisenc; GstFlowReturn ret = GST_FLOW_OK; + gfloat *data; + gulong size; + gulong i, j; + float **vorbis_buffer; vorbisenc = GST_VORBISENC (GST_PAD_PARENT (pad)); - { - gfloat *data; - gulong size; - gulong i, j; - float **buffer; - - if (!vorbisenc->setup) { - gst_buffer_unref (buf); - GST_ELEMENT_ERROR (vorbisenc, CORE, NEGOTIATION, (NULL), - ("encoder not initialized (input is not audio?)")); - return GST_FLOW_UNEXPECTED; - } - - if (!vorbisenc->header_sent) { - /* Vorbis streams begin with three headers; the initial header (with - most of the codec setup parameters) which is mandated by the Ogg - bitstream spec. The second header holds any comment fields. The - 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_vorbisenc_set_metadata (vorbisenc); - vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, - &header_comm, &header_code); - - /* create header buffers */ - buf1 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header); - buf2 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header_comm); - buf3 = gst_vorbisenc_buffer_from_packet (vorbisenc, &header_code); - - /* mark and put on caps */ - caps = gst_pad_get_caps (vorbisenc->srcpad); - caps = gst_vorbisenc_set_header_on_caps (caps, buf1, buf2, buf3); - - /* negotiate with these caps */ - GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); - gst_pad_set_caps (vorbisenc->srcpad, caps); - - gst_buffer_set_caps (buf1, caps); - gst_buffer_set_caps (buf2, caps); - gst_buffer_set_caps (buf3, caps); - - /* push out buffers */ - if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK) - goto done; - if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK) - goto done; - if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK) - goto done; - - vorbisenc->header_sent = TRUE; - } + if (!vorbisenc->setup) + goto not_setup; + + if (!vorbisenc->header_sent) { + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + 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; + + /* first, make sure header buffers get timestamp == 0 */ + vorbisenc->next_ts = 0; + vorbisenc->granulepos_offset = 0; + vorbisenc->subgranule_offset = 0; + + GST_DEBUG_OBJECT (vorbisenc, "creating and sending header packets"); + gst_vorbisenc_set_metadata (vorbisenc); + vorbis_analysis_headerout (&vorbisenc->vd, &vorbisenc->vc, &header, + &header_comm, &header_code); + + /* create header buffers */ + buf1 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header); + buf2 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header_comm); + buf3 = gst_vorbisenc_buffer_from_header_packet (vorbisenc, &header_code); + + /* mark and put on caps */ + caps = gst_pad_get_caps (vorbisenc->srcpad); + caps = gst_vorbisenc_set_header_on_caps (caps, buf1, buf2, buf3); + + /* negotiate with these caps */ + GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps); + gst_pad_set_caps (vorbisenc->srcpad, caps); + + gst_buffer_set_caps (buf1, caps); + gst_buffer_set_caps (buf2, caps); + gst_buffer_set_caps (buf3, caps); + + /* push out buffers */ + if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf1)) != GST_FLOW_OK) + goto failed_header_push; + if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf2)) != GST_FLOW_OK) + goto failed_header_push; + if ((ret = gst_vorbisenc_push_buffer (vorbisenc, buf3)) != GST_FLOW_OK) + goto failed_header_push; + + + /* now adjust starting granulepos accordingly if the buffer's timestamp is + nonzero */ + 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); + + vorbisenc->header_sent = TRUE; + } - /* data to encode */ - data = (gfloat *) GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf) / (vorbisenc->channels * sizeof (float)); + /* data to encode */ + data = (gfloat *) GST_BUFFER_DATA (buffer); + size = GST_BUFFER_SIZE (buffer) / (vorbisenc->channels * sizeof (float)); - /* expose the buffer to submit data */ - buffer = vorbis_analysis_buffer (&vorbisenc->vd, size); + /* expose the buffer to submit data */ + vorbis_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++; - } + /* 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); + /* tell the library how much we actually submitted */ + vorbis_analysis_wrote (&vorbisenc->vd, size); - vorbisenc->samples_in += size; + vorbisenc->samples_in += size; - gst_buffer_unref (buf); - } + gst_buffer_unref (buffer); ret = gst_vorbisenc_output_buffers (vorbisenc); -done: 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 diff --git a/ext/vorbis/vorbisenc.h b/ext/vorbis/vorbisenc.h index 613d43a..b8e41d2 100644 --- a/ext/vorbis/vorbisenc.h +++ b/ext/vorbis/vorbisenc.h @@ -69,7 +69,9 @@ struct _GstVorbisEnc { guint64 samples_in; guint64 bytes_out; - GstClockTime prev_ts; + GstClockTime next_ts; + guint64 granulepos_offset; + gint64 subgranule_offset; GstTagList * tags; diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index b3b3556..6c3ab24 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -22,7 +22,7 @@ $(CHECK_REGISTRY): TESTS = $(check_PROGRAMS) if USE_VORBIS -check_vorbis = elements/vorbisdec +check_vorbis = elements/vorbisdec pipelines/vorbisenc else check_vorbis = endif diff --git a/tests/check/pipelines/vorbisenc.c b/tests/check/pipelines/vorbisenc.c new file mode 100644 index 0000000..36ae66b --- /dev/null +++ b/tests/check/pipelines/vorbisenc.c @@ -0,0 +1,356 @@ +/* GStreamer + * + * unit test for vorbisenc + * + * Copyright (C) <2005> Thomas Vander Stichele + * + * 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 + +#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; +}