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
This commit is contained in:
Michael Smith 2008-01-11 15:48:11 +00:00
parent 3feb4bc8c5
commit 57fab036b1
6 changed files with 125 additions and 29 deletions

View file

@ -1,3 +1,26 @@
2008-01-11 Michael Smith <msmith@fluendo.com>
* 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 <tim at centricular dot net> 2008-01-10 Tim-Philipp Müller <tim at centricular dot net>
* gst-libs/gst/audio/gstbaseaudiosink.c: * gst-libs/gst/audio/gstbaseaudiosink.c:

2
common

@ -1 +1 @@
Subproject commit 49c2fc5c9bff0e9858e89978bd98164a386de51d Subproject commit bd02d788384b40ff511cac0e32aa77f51a68912d

View file

@ -51,6 +51,15 @@
#define GST_CAT_DEFAULT theoradec_debug #define GST_CAT_DEFAULT theoradec_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); 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 #define THEORA_DEF_CROP TRUE
enum enum
{ {
@ -141,6 +150,8 @@ gst_theora_dec_class_init (GstTheoraDecClass * klass)
gstelement_class->change_state = theora_dec_change_state; gstelement_class->change_state = theora_dec_change_state;
GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder"); GST_DEBUG_CATEGORY_INIT (theoradec_debug, "theoradec", 0, "Theora decoder");
use_old_granulepos = (theora_version_number () <= 0x00030200);
} }
static void static void
@ -212,11 +223,13 @@ _theora_ilog (unsigned int v)
return (ret); return (ret);
} }
/* Return the frame number (starting from zero) corresponding to this
* granulepos */
static gint64 static gint64
_theora_granule_frame (GstTheoraDec * dec, gint64 granulepos) _theora_granule_frame (GstTheoraDec * dec, gint64 granulepos)
{ {
guint ilog; guint ilog;
gint framecount; gint framenum;
if (granulepos == -1) if (granulepos == -1)
return -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 /* granulepos is last ilog bits for counting pframes since last iframe and
* bits in front of that for the framenumber of the last iframe. */ * bits in front of that for the framenumber of the last iframe. */
framecount = granulepos >> ilog; framenum = granulepos >> ilog;
framecount += granulepos - (framecount << 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 static GstClockTime
_theora_granule_time (GstTheoraDec * dec, gint64 granulepos) _theora_granule_start_time (GstTheoraDec * dec, gint64 granulepos)
{ {
gint64 framecount; gint64 framecount;
@ -407,7 +425,7 @@ theora_dec_sink_convert (GstPad * pad,
case GST_FORMAT_DEFAULT: case GST_FORMAT_DEFAULT:
switch (*dest_format) { switch (*dest_format) {
case GST_FORMAT_TIME: case GST_FORMAT_TIME:
*dest_value = _theora_granule_time (dec, src_value); *dest_value = _theora_granule_start_time (dec, src_value);
break; break;
default: default:
res = FALSE; res = FALSE;
@ -1224,9 +1242,9 @@ theora_dec_decode_buffer (GstTheoraDec * dec, GstBuffer * buf)
if (dec->have_header) { if (dec->have_header) {
if (packet.granulepos != -1) { if (packet.granulepos != -1) {
dec->granulepos = packet.granulepos; 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) { } 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)) if (dec->last_timestamp == -1 && GST_BUFFER_TIMESTAMP_IS_VALID (buf))
dec->last_timestamp = GST_BUFFER_TIMESTAMP (buf); dec->last_timestamp = GST_BUFFER_TIMESTAMP (buf);

View file

@ -69,6 +69,15 @@
#define GST_CAT_DEFAULT theoraenc_debug #define GST_CAT_DEFAULT theoraenc_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); 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()) #define GST_TYPE_BORDER_MODE (gst_border_mode_get_type())
static GType static GType
gst_border_mode_get_type (void) 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; gstelement_class->change_state = theora_enc_change_state;
GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder"); GST_DEBUG_CATEGORY_INIT (theoraenc_debug, "theoraenc", 0, "Theora encoder");
use_old_granulepos = (theora_version_number () <= 0x00030200);
} }
static void static void
@ -540,6 +551,25 @@ theora_set_header_on_caps (GstCaps * caps, GstBuffer * buf1,
return caps; 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 static gboolean
theora_enc_sink_event (GstPad * pad, GstEvent * event) theora_enc_sink_event (GstPad * pad, GstEvent * event)
{ {
@ -554,10 +584,8 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
if (enc->initialised) { if (enc->initialised) {
/* push last packet with eos flag */ /* push last packet with eos flag */
while (theora_encode_packetout (&enc->state, 1, &op)) { while (theora_encode_packetout (&enc->state, 1, &op)) {
/* See comment in the chain function */ GstClockTime next_time =
GstClockTime next_time = theora_granule_time (&enc->state, theora_enc_get_ogg_packet_end_time (enc, &op);
granulepos_add (op.granulepos, 1, enc->granule_shift)) *
GST_SECOND;
theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts); theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts);
enc->next_ts = next_time; enc->next_ts = next_time;
@ -574,7 +602,7 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
if (gst_structure_has_name (s, "GstForceKeyUnit")) { if (gst_structure_has_name (s, "GstForceKeyUnit")) {
GstClockTime next_ts; 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; next_ts = enc->next_ts + enc->timestamp_offset;
theora_enc_reset (enc); theora_enc_reset (enc);
@ -887,14 +915,8 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
ret = GST_FLOW_OK; ret = GST_FLOW_OK;
while (theora_encode_packetout (&enc->state, 0, &op)) { while (theora_encode_packetout (&enc->state, 0, &op)) {
/* This is where we hack around theora's broken idea of what granulepos GstClockTime next_time = theora_enc_get_ogg_packet_end_time (enc, &op);
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;
next_time = theora_granule_time (&enc->state,
granulepos_add (op.granulepos, 1, enc->granule_shift)) * GST_SECOND;
ret = ret =
theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts); theora_push_packet (enc, &op, enc->next_ts, next_time - enc->next_ts);
enc->next_ts = next_time; enc->next_ts = next_time;

View file

@ -288,6 +288,9 @@ pipelines_vorbisdec_CFLAGS = $(AM_CFLAGS)
pipelines_oggmux_LDADD = $(LDADD) $(OGG_LIBS) pipelines_oggmux_LDADD = $(LDADD) $(OGG_LIBS)
pipelines_oggmux_CFLAGS = $(AM_CFLAGS) $(OGG_CFLAGS) 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 = \ pipelines_simple_launch_lines_CFLAGS = \
$(GST_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) \
$(AM_CFLAGS) $(AM_CFLAGS)

View file

@ -23,6 +23,8 @@
#include <gst/check/gstcheck.h> #include <gst/check/gstcheck.h>
#include <gst/check/gstbufferstraw.h> #include <gst/check/gstbufferstraw.h>
#include <theora/theora.h>
#ifndef GST_DISABLE_PARSE #ifndef GST_DISABLE_PARSE
#define TIMESTAMP_OFFSET G_GINT64_CONSTANT(3249870963) #define TIMESTAMP_OFFSET G_GINT64_CONSTANT(3249870963)
@ -50,12 +52,29 @@
", but got duration %" GST_TIME_FORMAT, \ ", but got duration %" GST_TIME_FORMAT, \
GST_TIME_ARGS (duration), GST_TIME_ARGS (GST_BUFFER_DURATION (buffer))) 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 static void
check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos) check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
{ {
GstClockTime clocktime; GstClockTime clocktime;
int framecount; 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, fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
"expected granulepos %" G_GUINT64_FORMAT "expected granulepos %" G_GUINT64_FORMAT
", but got 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; gint64 granulepos, expected, framecount;
granulepos = GST_BUFFER_OFFSET_END (buffer); 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 >> GRANULEPOS_SHIFT;
framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1); framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1);
expected = gst_util_uint64_scale (starttime, FRAMERATE, GST_SECOND); 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 "expected frame count %" G_GUINT64_FORMAT
" or %" G_GUINT64_FORMAT " or %" G_GUINT64_FORMAT
", but got frame count %" G_GUINT64_FORMAT, ", but got frame count %" G_GUINT64_FORMAT,
expected, expected + 1, granulepos); expected, expected + 1, framecount);
} }
GST_START_TEST (test_granulepos_offset) GST_START_TEST (test_granulepos_offset)
@ -257,7 +285,7 @@ GST_START_TEST (test_continuity)
check_buffer_timestamp (buffer, 0); check_buffer_timestamp (buffer, 0);
/* plain division because I know the answer is exact */ /* plain division because I know the answer is exact */
check_buffer_duration (buffer, GST_SECOND / 10); 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); check_buffer_is_header (buffer, FALSE);
next_timestamp = GST_BUFFER_DURATION (buffer); next_timestamp = GST_BUFFER_DURATION (buffer);
@ -268,7 +296,7 @@ GST_START_TEST (test_continuity)
buffer = gst_buffer_straw_get_buffer (bin, pad); buffer = gst_buffer_straw_get_buffer (bin, pad);
check_buffer_timestamp (buffer, next_timestamp); check_buffer_timestamp (buffer, next_timestamp);
check_buffer_duration (buffer, GST_SECOND / 10); 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); check_buffer_is_header (buffer, FALSE);
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
@ -360,7 +388,7 @@ GST_START_TEST (test_discontinuity)
check_buffer_timestamp (buffer, 0); check_buffer_timestamp (buffer, 0);
/* plain division because I know the answer is exact */ /* plain division because I know the answer is exact */
check_buffer_duration (buffer, GST_SECOND / 10); 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); check_buffer_is_header (buffer, FALSE);
fail_if (GST_BUFFER_IS_DISCONT (buffer), "expected continuous buffer yo"); fail_if (GST_BUFFER_IS_DISCONT (buffer), "expected continuous buffer yo");
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
@ -369,8 +397,8 @@ GST_START_TEST (test_discontinuity)
buffer = gst_buffer_straw_get_buffer (bin, pad); buffer = gst_buffer_straw_get_buffer (bin, pad);
check_buffer_duration (buffer, GST_SECOND / 10); check_buffer_duration (buffer, GST_SECOND / 10);
/* After a discont, we'll always get a keyframe, so this one should be /* After a discont, we'll always get a keyframe, so this one should be
* 2<<GRANULEPOS_SHIFT */ * 3<<GRANULEPOS_SHIFT */
check_buffer_granulepos (buffer, 2 << GRANULEPOS_SHIFT); check_buffer_granulepos (buffer, 3 << GRANULEPOS_SHIFT);
check_buffer_is_header (buffer, FALSE); check_buffer_is_header (buffer, FALSE);
fail_unless (GST_BUFFER_IS_DISCONT (buffer), fail_unless (GST_BUFFER_IS_DISCONT (buffer),
"expected discontinuous buffer yo"); "expected discontinuous buffer yo");
@ -381,7 +409,7 @@ GST_START_TEST (test_discontinuity)
fail_if (GST_BUFFER_IS_DISCONT (buffer), "expected continuous buffer yo"); fail_if (GST_BUFFER_IS_DISCONT (buffer), "expected continuous buffer yo");
/* plain division because I know the answer is exact */ /* plain division because I know the answer is exact */
check_buffer_duration (buffer, GST_SECOND / 10); check_buffer_duration (buffer, GST_SECOND / 10);
check_buffer_granulepos (buffer, (2 << GRANULEPOS_SHIFT) + 1); check_buffer_granulepos (buffer, (3 << GRANULEPOS_SHIFT) | 1);
check_buffer_is_header (buffer, FALSE); check_buffer_is_header (buffer, FALSE);
gst_buffer_unref (buffer); gst_buffer_unref (buffer);
} }
@ -406,6 +434,8 @@ theoraenc_suite (void)
suite_add_tcase (s, tc_chain); suite_add_tcase (s, tc_chain);
check_libtheora ();
#ifndef GST_DISABLE_PARSE #ifndef GST_DISABLE_PARSE
tcase_add_test (tc_chain, test_granulepos_offset); tcase_add_test (tc_chain, test_granulepos_offset);
tcase_add_test (tc_chain, test_continuity); tcase_add_test (tc_chain, test_continuity);