From 57fab036b19f014930c08a1bc4602327987ae907 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Fri, 11 Jan 2008 15:48:11 +0000 Subject: [PATCH] ext/theora/theoradec.c: Adapt for post-alpha meaning of granulepos, when we have a newer version of libtheora. Original commit message from CVS: * ext/theora/theoradec.c: (gst_theora_dec_class_init), (_theora_granule_frame), (_theora_granule_start_time), (theora_dec_sink_convert), (theora_dec_decode_buffer): Adapt for post-alpha meaning of granulepos, when we have a newer version of libtheora. * ext/theora/theoraenc.c: (gst_theora_enc_class_init), (theora_enc_get_ogg_packet_end_time), (theora_enc_sink_event), (theora_enc_is_discontinuous), (theora_enc_chain): Likewise. * tests/check/Makefile.am: Link libtheora into theoraenc test so we can check which version of libtheora we're testing against. * tests/check/pipelines/theoraenc.c: (check_libtheora), (check_buffer_granulepos), (check_buffer_granulepos_from_starttime), (GST_START_TEST), (theoraenc_suite): Adapt tests to check the values that are now defined for theora; make the tests backwards-adapt the passed values if we're running against an old libtheora. Fixes #497964 --- ChangeLog | 23 ++++++++++++++++ common | 2 +- ext/theora/theoradec.c | 36 ++++++++++++++++++------ ext/theora/theoraenc.c | 46 +++++++++++++++++++++++-------- tests/check/Makefile.am | 3 ++ tests/check/pipelines/theoraenc.c | 44 ++++++++++++++++++++++++----- 6 files changed, 125 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index 24bdd84f75..9368c680fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2008-01-11 Michael Smith + + * ext/theora/theoradec.c: (gst_theora_dec_class_init), + (_theora_granule_frame), (_theora_granule_start_time), + (theora_dec_sink_convert), (theora_dec_decode_buffer): + Adapt for post-alpha meaning of granulepos, when we + have a newer version of libtheora. + * ext/theora/theoraenc.c: (gst_theora_enc_class_init), + (theora_enc_get_ogg_packet_end_time), (theora_enc_sink_event), + (theora_enc_is_discontinuous), (theora_enc_chain): + Likewise. + * tests/check/Makefile.am: + Link libtheora into theoraenc test so we can check which version of + libtheora we're testing against. + * tests/check/pipelines/theoraenc.c: (check_libtheora), + (check_buffer_granulepos), + (check_buffer_granulepos_from_starttime), (GST_START_TEST), + (theoraenc_suite): + Adapt tests to check the values that are now defined for theora; make + the tests backwards-adapt the passed values if we're running against an + old libtheora. + Fixes #497964 + 2008-01-10 Tim-Philipp Müller * gst-libs/gst/audio/gstbaseaudiosink.c: diff --git a/common b/common index 49c2fc5c9b..bd02d78838 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 49c2fc5c9bff0e9858e89978bd98164a386de51d +Subproject commit bd02d788384b40ff511cac0e32aa77f51a68912d diff --git a/ext/theora/theoradec.c b/ext/theora/theoradec.c index 84b2f57668..6d43d29184 100644 --- a/ext/theora/theoradec.c +++ b/ext/theora/theoradec.c @@ -51,6 +51,15 @@ #define GST_CAT_DEFAULT theoradec_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); +/* With libtheora-1.0beta1 the granulepos scheme was changed: + * where earlier the granulepos refered to the index/beginning + * of a frame, it now refers to the end, which matches the use + * in vorbis/speex. There don't seem to be defines for the + * theora version we're compiling against, so we'll just use + * a run-time check for now. See theora_enc_get_ogg_packet_end_time(). + */ +static gboolean use_old_granulepos; + #define THEORA_DEF_CROP TRUE enum { @@ -141,6 +150,8 @@ gst_theora_dec_class_init (GstTheoraDecClass * klass) gstelement_class->change_state = theora_dec_change_state; GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder"); + + use_old_granulepos = (theora_version_number () <= 0x00030200); } static void @@ -212,11 +223,13 @@ _theora_ilog (unsigned int v) return (ret); } +/* Return the frame number (starting from zero) corresponding to this + * granulepos */ static gint64 _theora_granule_frame (GstTheoraDec * dec, gint64 granulepos) { guint ilog; - gint framecount; + gint framenum; if (granulepos == -1) return -1; @@ -225,16 +238,21 @@ _theora_granule_frame (GstTheoraDec * dec, gint64 granulepos) /* granulepos is last ilog bits for counting pframes since last iframe and * bits in front of that for the framenumber of the last iframe. */ - framecount = granulepos >> ilog; - framecount += granulepos - (framecount << ilog); + framenum = granulepos >> ilog; + framenum += granulepos - (framenum << ilog); - GST_DEBUG_OBJECT (dec, "framecount=%d, ilog=%u", framecount, ilog); + /* This is 1-based for current libtheora, 0 based for old. Fix up. */ + if (!use_old_granulepos) + framenum -= 1; - return framecount; + GST_DEBUG_OBJECT (dec, "framecount=%d, ilog=%u", framenum, ilog); + + return framenum; } +/* Return the frame start time corresponding to this granulepos */ static GstClockTime -_theora_granule_time (GstTheoraDec * dec, gint64 granulepos) +_theora_granule_start_time (GstTheoraDec * dec, gint64 granulepos) { gint64 framecount; @@ -407,7 +425,7 @@ theora_dec_sink_convert (GstPad * pad, case GST_FORMAT_DEFAULT: switch (*dest_format) { case GST_FORMAT_TIME: - *dest_value = _theora_granule_time (dec, src_value); + *dest_value = _theora_granule_start_time (dec, src_value); break; default: res = FALSE; @@ -1224,9 +1242,9 @@ theora_dec_decode_buffer (GstTheoraDec * dec, GstBuffer * buf) if (dec->have_header) { if (packet.granulepos != -1) { dec->granulepos = packet.granulepos; - dec->last_timestamp = _theora_granule_time (dec, packet.granulepos); + dec->last_timestamp = _theora_granule_start_time (dec, packet.granulepos); } else if (dec->last_timestamp != -1) { - dec->last_timestamp = _theora_granule_time (dec, dec->granulepos); + dec->last_timestamp = _theora_granule_start_time (dec, dec->granulepos); } if (dec->last_timestamp == -1 && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) dec->last_timestamp = GST_BUFFER_TIMESTAMP (buf); diff --git a/ext/theora/theoraenc.c b/ext/theora/theoraenc.c index 66b3389d45..40de70e9fb 100644 --- a/ext/theora/theoraenc.c +++ b/ext/theora/theoraenc.c @@ -69,6 +69,15 @@ #define GST_CAT_DEFAULT theoraenc_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); +/* With libtheora-1.0beta1 the granulepos scheme was changed: + * where earlier the granulepos refered to the index/beginning + * of a frame, it now refers to the end, which matches the use + * in vorbis/speex. There don't seem to be defines for the + * theora version we're compiling against, so we'll just use + * a run-time check for now. See theora_enc_get_ogg_packet_end_time(). + */ +static gboolean use_old_granulepos; + #define GST_TYPE_BORDER_MODE (gst_border_mode_get_type()) static GType gst_border_mode_get_type (void) @@ -258,6 +267,8 @@ gst_theora_enc_class_init (GstTheoraEncClass * klass) gstelement_class->change_state = theora_enc_change_state; GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder"); + + use_old_granulepos = (theora_version_number () <= 0x00030200); } static void @@ -540,6 +551,25 @@ theora_set_header_on_caps (GstCaps * caps, GstBuffer * buf1, return caps; } +static GstClockTime +theora_enc_get_ogg_packet_end_time (GstTheoraEnc * enc, ogg_packet * op) +{ + ogg_int64_t end_granule; + + /* FIXME: remove this hack once we depend on libtheora >= 1.0beta1 */ + if (G_UNLIKELY (use_old_granulepos)) { + /* 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... (update: this only applies to old + * bitstream/theora versions, this is fixed with bitstream version 3.2.1) */ + end_granule = granulepos_add (op->granulepos, 1, enc->granule_shift); + } else { + end_granule = op->granulepos; + } + return theora_granule_time (&enc->state, end_granule) * GST_SECOND; +} + static gboolean theora_enc_sink_event (GstPad * pad, GstEvent * event) { @@ -554,10 +584,8 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event) if (enc->initialised) { /* push last packet with eos flag */ while (theora_encode_packetout (&enc->state, 1, &op)) { - /* See comment in the chain function */ - GstClockTime next_time = theora_granule_time (&enc->state, - granulepos_add (op.granulepos, 1, enc->granule_shift)) * - GST_SECOND; + GstClockTime next_time = + theora_enc_get_ogg_packet_end_time (enc, &op); theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts); enc->next_ts = next_time; @@ -574,7 +602,7 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event) if (gst_structure_has_name (s, "GstForceKeyUnit")) { GstClockTime next_ts; - /* make sure timestamps increment after reseting the decoder */ + /* make sure timestamps increment after resetting the decoder */ next_ts = enc->next_ts + enc->timestamp_offset; theora_enc_reset (enc); @@ -887,14 +915,8 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer) ret = GST_FLOW_OK; while (theora_encode_packetout (&enc->state, 0, &op)) { - /* 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; + GstClockTime next_time = theora_enc_get_ogg_packet_end_time (enc, &op); - next_time = theora_granule_time (&enc->state, - granulepos_add (op.granulepos, 1, enc->granule_shift)) * GST_SECOND; ret = theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts); enc->next_ts = next_time; diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 4049ed6729..82e54818fa 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -288,6 +288,9 @@ pipelines_vorbisdec_CFLAGS = $(AM_CFLAGS) pipelines_oggmux_LDADD = $(LDADD) $(OGG_LIBS) pipelines_oggmux_CFLAGS = $(AM_CFLAGS) $(OGG_CFLAGS) +pipelines_theoraenc_CFLAGS = $(GST_CFLAGS) $(THEORA_CFLAGS) +pipelines_theoraenc_LDADD = $(LDADD) $(THEORA_LIBS) + pipelines_simple_launch_lines_CFLAGS = \ $(GST_BASE_CFLAGS) \ $(AM_CFLAGS) diff --git a/tests/check/pipelines/theoraenc.c b/tests/check/pipelines/theoraenc.c index dfb7ea11ac..673fa01cfb 100644 --- a/tests/check/pipelines/theoraenc.c +++ b/tests/check/pipelines/theoraenc.c @@ -23,6 +23,8 @@ #include #include +#include + #ifndef GST_DISABLE_PARSE #define TIMESTAMP_OFFSET G_GINT64_CONSTANT(3249870963) @@ -50,12 +52,29 @@ ", but got duration %" GST_TIME_FORMAT, \ GST_TIME_ARGS (duration), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))) +static gboolean old_libtheora; + +static void +check_libtheora (void) +{ + old_libtheora = (theora_version_number () <= 0x00030200); +} + static void check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos) { GstClockTime clocktime; int framecount; + /* With old versions of libtheora, the granulepos represented the + * start time, not end time. Adapt for that. */ + if (old_libtheora) { + if (granulepos >> GRANULEPOS_SHIFT) + granulepos -= 1 << GRANULEPOS_SHIFT; + else if (granulepos) + granulepos -= 1; + } + fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos, "expected granulepos %" G_GUINT64_FORMAT ", but got granulepos %" G_GUINT64_FORMAT, @@ -87,6 +106,15 @@ check_buffer_granulepos_from_starttime (GstBuffer * buffer, gint64 granulepos, expected, framecount; granulepos = GST_BUFFER_OFFSET_END (buffer); + /* Now convert to 'granulepos for start time', depending on libtheora + * version */ + if (!old_libtheora) { + if (granulepos & ((1 << GRANULEPOS_SHIFT) - 1)) + granulepos -= 1; + else if (granulepos) + granulepos -= 1 << GRANULEPOS_SHIFT; + } + framecount = granulepos >> GRANULEPOS_SHIFT; framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1); expected = gst_util_uint64_scale (starttime, FRAMERATE, GST_SECOND); @@ -95,7 +123,7 @@ check_buffer_granulepos_from_starttime (GstBuffer * buffer, "expected frame count %" G_GUINT64_FORMAT " or %" G_GUINT64_FORMAT ", but got frame count %" G_GUINT64_FORMAT, - expected, expected + 1, granulepos); + expected, expected + 1, framecount); } GST_START_TEST (test_granulepos_offset) @@ -257,7 +285,7 @@ GST_START_TEST (test_continuity) check_buffer_timestamp (buffer, 0); /* plain division because I know the answer is exact */ check_buffer_duration (buffer, GST_SECOND / 10); - check_buffer_granulepos (buffer, 0); + check_buffer_granulepos (buffer, 1 << GRANULEPOS_SHIFT); check_buffer_is_header (buffer, FALSE); next_timestamp = GST_BUFFER_DURATION (buffer); @@ -268,7 +296,7 @@ GST_START_TEST (test_continuity) buffer = gst_buffer_straw_get_buffer (bin, pad); check_buffer_timestamp (buffer, next_timestamp); check_buffer_duration (buffer, GST_SECOND / 10); - check_buffer_granulepos (buffer, 1); + check_buffer_granulepos (buffer, (1 << GRANULEPOS_SHIFT) | 1); check_buffer_is_header (buffer, FALSE); gst_buffer_unref (buffer); @@ -360,7 +388,7 @@ GST_START_TEST (test_discontinuity) check_buffer_timestamp (buffer, 0); /* plain division because I know the answer is exact */ check_buffer_duration (buffer, GST_SECOND / 10); - check_buffer_granulepos (buffer, 0); + check_buffer_granulepos (buffer, 1 << GRANULEPOS_SHIFT); check_buffer_is_header (buffer, FALSE); fail_if (GST_BUFFER_IS_DISCONT (buffer), "expected continuous buffer yo"); gst_buffer_unref (buffer); @@ -369,8 +397,8 @@ GST_START_TEST (test_discontinuity) buffer = gst_buffer_straw_get_buffer (bin, pad); check_buffer_duration (buffer, GST_SECOND / 10); /* After a discont, we'll always get a keyframe, so this one should be - * 2<