From: <wi...@fr...> - 2006-01-30 17:18:53
|
CVS Root: /cvs/gstreamer Module: gst-plugins-base Changes by: wingo Date: Mon Jan 30 2006 09:02:07 PST Log message: 2006-01-30 Andy Wingo <wi...@po...> * ext/theora/gsttheoraenc.h: * ext/theora/theoraenc.c: Same changes as were done to vorbisenc, although theoraenc was timestamping correctly. Added handling of streams that start with nonzero timestamps. * tests/check/Makefile.am: * tests/check/pipelines/theoraenc.c: New file, basically does same tests as vorbisenc. * tests/check/pipelines/vorbisenc.c: I claim these bugs. Modified files: . : ChangeLog ext/theora : gsttheoraenc.h theoraenc.c tests/check : Makefile.am tests/check/pipelines: vorbisenc.c Added files: tests/check/pipelines: theoraenc.c Links: http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-base/ChangeLog.diff?r1=1.2313&r2=1.2314 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-base/ext/theora/gsttheoraenc.h.diff?r1=1.1&r2=1.2 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-base/ext/theora/theoraenc.c.diff?r1=1.43&r2=1.44 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-base/tests/check/Makefile.am.diff?r1=1.25&r2=1.26 http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-base/tests/check/pipelines/theoraenc.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://freedesktop.org/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins-base/tests/check/pipelines/vorbisenc.c.diff?r1=1.1&r2=1.2 ====Begin Diffs==== Index: ChangeLog =================================================================== RCS file: /cvs/gstreamer/gst-plugins-base/ChangeLog,v retrieving revision 1.2313 retrieving revision 1.2314 diff -u -d -r1.2313 -r1.2314 --- ChangeLog 30 Jan 2006 16:19:32 -0000 1.2313 +++ ChangeLog 30 Jan 2006 17:01:54 -0000 1.2314 @@ -1,3 +1,16 @@ +2006-01-30 Andy Wingo <wi...@po...> + + * ext/theora/gsttheoraenc.h: + * ext/theora/theoraenc.c: Same changes as were done to vorbisenc, + although theoraenc was timestamping correctly. Added handling of + streams that start with nonzero timestamps. + * tests/check/Makefile.am: + * tests/check/pipelines/theoraenc.c: New file, basically does same + tests as vorbisenc. + * tests/check/pipelines/vorbisenc.c: I claim these bugs. 2006-01-30 Wim Taymans <wi...@fl...> * gst-libs/gst/audio/gstaudiosink.c: Index: gsttheoraenc.h RCS file: /cvs/gstreamer/gst-plugins-base/ext/theora/gsttheoraenc.h,v retrieving revision 1.1 retrieving revision 1.2 diff -u -d -r1.1 -r1.2 --- gsttheoraenc.h 15 Sep 2005 15:28:36 -0000 1.1 +++ gsttheoraenc.h 30 Jan 2006 17:01:54 -0000 1.2 @@ -77,11 +77,13 @@ gint info_width, info_height; gint width, height; gint offset_x, offset_y; - gdouble fps; + gint fps_n, fps_d; + GstClockTime next_ts; guint packetno; guint64 bytes_out; - guint64 initial_delay; + guint64 granulepos_offset; + guint64 timestamp_offset; }; struct _GstTheoraEncClass Index: theoraenc.c RCS file: /cvs/gstreamer/gst-plugins-base/ext/theora/theoraenc.c,v retrieving revision 1.43 retrieving revision 1.44 diff -u -d -r1.43 -r1.44 --- theoraenc.c 6 Dec 2005 19:41:56 -0000 1.43 +++ theoraenc.c 30 Jan 2006 17:01:54 -0000 1.44 @@ -271,8 +271,8 @@ enc->info.offset_x = enc->offset_x; enc->info.offset_y = enc->offset_y; - enc->info.fps_numerator = fps_n; - enc->info.fps_denominator = fps_d; + enc->info.fps_numerator = enc->fps_n = fps_n; + enc->info.fps_denominator = enc->fps_d = fps_d; if (par) { enc->info.aspect_numerator = gst_value_get_fraction_numerator (par); enc->info.aspect_denominator = gst_value_get_fraction_denominator (par); @@ -318,8 +318,8 @@ memcpy (GST_BUFFER_DATA (buf), packet->packet, packet->bytes); GST_BUFFER_OFFSET (buf) = enc->bytes_out; - GST_BUFFER_OFFSET_END (buf) = packet->granulepos; - GST_BUFFER_TIMESTAMP (buf) = timestamp; + GST_BUFFER_OFFSET_END (buf) = packet->granulepos + enc->granulepos_offset; + GST_BUFFER_TIMESTAMP (buf) = timestamp + enc->timestamp_offset; GST_BUFFER_DURATION (buf) = duration; /* the second most significant bit of the first data byte is cleared @@ -417,10 +417,12 @@ case GST_EVENT_EOS: /* push last packet with eos flag */ while (theora_encode_packetout (&enc->state, 1, &op)) { - GstClockTime out_time = - theora_granule_time (&enc->state, op.granulepos) * GST_SECOND; + /* See comment in the chain function */ + GstClockTime next_time = + theora_granule_time (&enc->state, op.granulepos + 1) * GST_SECOND; - theora_push_packet (enc, &op, out_time, GST_SECOND / enc->fps); + theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts); + enc->next_ts = next_time; } res = gst_pad_push_event (enc->srcpad, event); break; @@ -447,6 +449,9 @@ GstCaps *caps; GstBuffer *buf1, *buf2, *buf3; + enc->granulepos_offset = 0; + enc->timestamp_offset = 0; /* Theora 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 @@ -456,7 +461,8 @@ /* first packet will get its own page automatically */ theora_encode_header (&enc->state, &op); - ret = theora_buffer_from_packet (enc, &op, 0, 0, &buf1); + ret = theora_buffer_from_packet (enc, &op, GST_CLOCK_TIME_NONE, + GST_CLOCK_TIME_NONE, &buf1); if (ret != GST_FLOW_OK) { goto header_buffer_alloc; } @@ -467,14 +473,16 @@ * portably work around it. Leaks ~50 bytes per encoder instance, so not a * huge problem. */ theora_encode_comment (&enc->comment, &op); - ret = theora_buffer_from_packet (enc, &op, 0, 0, &buf2); + GST_CLOCK_TIME_NONE, &buf2); gst_buffer_unref (buf1); theora_encode_tables (&enc->state, &op); - ret = theora_buffer_from_packet (enc, &op, 0, 0, &buf3); + GST_CLOCK_TIME_NONE, &buf3); gst_buffer_unref (buf2); @@ -504,6 +512,12 @@ if ((ret = theora_push_buffer (enc, buf3)) != GST_FLOW_OK) { goto header_push; + enc->granulepos_offset = + gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buffer), enc->fps_n, + GST_SECOND * enc->fps_d); + enc->timestamp_offset = GST_BUFFER_TIMESTAMP (buffer); + enc->next_ts = 0; } { @@ -655,11 +669,18 @@ ret = GST_FLOW_OK; while (theora_encode_packetout (&enc->state, 0, &op)) { - GstClockTime out_time; + /* This is where we hack around theora's broken idea of what granulepos + is -- normally we wouldn't need to add the 1, because granulepos + should be the presentation time of the last sample in the packet, but + theora starts with 0 instead of 1... */ + GstClockTime next_time; - out_time = theora_granule_time (&enc->state, op.granulepos) * GST_SECOND; - if ((ret = theora_push_packet (enc, &op, out_time, GST_SECOND / enc->fps)) - != GST_FLOW_OK) + next_time = + theora_granule_time (&enc->state, op.granulepos + 1) * GST_SECOND; + ret = + theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts); + enc->next_ts = next_time; + if (ret != GST_FLOW_OK) goto data_push; gst_buffer_unref (buffer); @@ -705,7 +726,6 @@ theora_info_init (&enc->info); theora_comment_init (&enc->comment); enc->packetno = 0; - enc->initial_delay = 0; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: Index: Makefile.am RCS file: /cvs/gstreamer/gst-plugins-base/tests/check/Makefile.am,v retrieving revision 1.25 retrieving revision 1.26 diff -u -d -r1.25 -r1.26 --- Makefile.am 30 Jan 2006 15:01:28 -0000 1.25 +++ Makefile.am 30 Jan 2006 17:01:54 -0000 1.26 @@ -27,7 +27,14 @@ check_vorbis = endif +if USE_THEORA +check_theora = pipelines/theoraenc +else +check_theora = +endif check_PROGRAMS = $(check_vorbis) \ + $(check_theora) \ elements/audioconvert \ elements/audioresample \ elements/audiotestsrc \ --- NEW FILE: theoraenc.c --- /* GStreamer * * unit test for theoraenc * Copyright (C) 2006 Andy Wingo <wingo at pobox.com> * 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) #define FRAMERATE 10 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; while (buf == NULL) ret = buf; buf = NULL; return ret; stop_pipeline (GstElement * bin, GstPad * pad) if (buf) gst_buffer_unref (buf); gst_pad_remove_buffer_probe (pad, (guint) id); id = 0; gst_element_set_state (bin, GST_STATE_NULL); g_mutex_free (lock); g_cond_free (cond); lock = NULL; cond = NULL; 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))); 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))); 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. */ /* should be from_endtime, but theora's granulepos mapping is "special" */ check_buffer_granulepos_from_starttime (GstBuffer * buffer, GstClockTime starttime) GstClockTime granulepos, expected; granulepos = GST_BUFFER_OFFSET_END (buffer); expected = gst_util_uint64_scale (starttime, FRAMERATE, GST_SECOND); fail_unless (granulepos == expected || granulepos == expected + 1, " or %" 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 ("videotestsrc timestamp-offset=%" G_GUINT64_FORMAT " ! video/x-raw-yuv,format=(fourcc)I420,framerate=10/1" " ! theoraenc ! 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); 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. theoraenc 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_starttime (buffer, TIMESTAMP_OFFSET); next_timestamp = TIMESTAMP_OFFSET + GST_BUFFER_DURATION (buffer); gst_buffer_unref (buffer); /* check continuity with the next buffer */ check_buffer_timestamp (buffer, next_timestamp); check_buffer_duration (buffer, gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND, FRAMERATE) - gst_util_uint64_scale (last_granulepos, GST_SECOND, FRAMERATE)); check_buffer_granulepos_from_starttime (buffer, next_timestamp); stop_pipeline (bin, pad); gst_object_unref (pad); gst_object_unref (bin); GST_END_TEST; GST_START_TEST (test_continuity) pipe_str = g_strdup_printf ("videotestsrc" " ! theoraenc ! fakesink"); check_buffer_timestamp (buffer, 0); check_buffer_duration (buffer, GST_SECOND / 10); /* plain division because I know the answer is exact */ check_buffer_granulepos (buffer, 0); next_timestamp = GST_BUFFER_DURATION (buffer); check_buffer_duration (buffer, GST_SECOND / 10); check_buffer_granulepos (buffer, 1); Suite * theoraenc_suite (void) Suite *s = suite_create ("theoraenc"); 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_continuity); return s; int main (int argc, char **argv) int nf; Suite *s = theoraenc_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; Index: vorbisenc.c RCS file: /cvs/gstreamer/gst-plugins-base/tests/check/pipelines/vorbisenc.c,v --- vorbisenc.c 30 Jan 2006 15:01:28 -0000 1.1 +++ vorbisenc.c 30 Jan 2006 17:01:54 -0000 1.2 @@ -2,7 +2,7 @@ * * unit test for vorbisenc - * Copyright (C) <2005> Thomas Vander Stichele <thomas at apestaart dot org> + * Copyright (C) 2006 Andy Wingo <wingo at pobox.com> * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public |