ext/theora/: Mark discontinuities of > 3/4 of a frame, reinit encoder.

Original commit message from CVS:
* ext/theora/gsttheoraenc.h:
* ext/theora/theoraenc.c: (gst_theora_enc_init),
(theora_enc_reset), (theora_enc_clear), (theora_enc_sink_setcaps),
(theora_buffer_from_packet), (theora_enc_is_discontinuous),
(theora_enc_chain), (theora_enc_change_state):
Mark discontinuities of > 3/4 of a frame, reinit encoder.

* tests/check/pipelines/theoraenc.c: (check_buffer_granulepos),
(GST_START_TEST), (theoraenc_suite):
Enable discontinuity test, fix it.
This commit is contained in:
Michael Smith 2006-11-22 14:34:03 +00:00
parent 60ad667761
commit 32500268de
4 changed files with 93 additions and 10 deletions

View file

@ -1,3 +1,16 @@
2006-11-22 Michael Smith <msmith@fluendo.com>
* ext/theora/gsttheoraenc.h:
* ext/theora/theoraenc.c: (gst_theora_enc_init),
(theora_enc_reset), (theora_enc_clear), (theora_enc_sink_setcaps),
(theora_buffer_from_packet), (theora_enc_is_discontinuous),
(theora_enc_chain), (theora_enc_change_state):
Mark discontinuities of > 3/4 of a frame, reinit encoder.
* tests/check/pipelines/theoraenc.c: (check_buffer_granulepos),
(GST_START_TEST), (theoraenc_suite):
Enable discontinuity test, fix it.
2006-11-21 Tim-Philipp Müller <tim at centricular dot net>
* ext/pango/gsttextoverlay.c: (gst_text_overlay_init),

View file

@ -93,6 +93,9 @@ struct _GstTheoraEnc
gint fps_n, fps_d;
GstClockTime next_ts;
GstClockTime expected_ts;
gboolean next_discont;
guint packetno;
guint64 bytes_out;
guint64 granulepos_offset;

View file

@ -292,6 +292,7 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class)
GST_DEBUG_OBJECT (enc,
"keyframe_frequency_force is %d, granule shift is %d",
enc->info.keyframe_frequency_force, enc->granule_shift);
enc->expected_ts = GST_CLOCK_TIME_NONE;
}
static void
@ -307,6 +308,26 @@ theora_enc_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
theora_enc_reset (GstTheoraEnc * enc)
{
theora_clear (&enc->state);
theora_encode_init (&enc->state, &enc->info);
}
static void
theora_enc_clear (GstTheoraEnc * enc)
{
enc->packetno = 0;
enc->bytes_out = 0;
enc->granulepos_offset = 0;
enc->timestamp_offset = 0;
enc->next_ts = GST_CLOCK_TIME_NONE;
enc->next_discont = FALSE;
enc->expected_ts = GST_CLOCK_TIME_NONE;
}
static gboolean
theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
{
@ -374,7 +395,7 @@ theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
"keyframe_frequency_force is %d, granule shift is %d",
enc->info.keyframe_frequency_force, enc->granule_shift);
theora_encode_init (&enc->state, &enc->info);
theora_enc_reset (enc);
gst_object_unref (enc);
@ -418,6 +439,11 @@ theora_buffer_from_packet (GstTheoraEnc * enc, ogg_packet * packet,
GST_BUFFER_TIMESTAMP (buf) = timestamp + enc->timestamp_offset;
GST_BUFFER_DURATION (buf) = duration;
if (enc->next_discont) {
GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_DISCONT);
enc->next_discont = FALSE;
}
/* the second most significant bit of the first data byte is cleared
* for keyframes */
if ((packet->packet[0] & 0x40) == 0) {
@ -543,6 +569,34 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
return res;
}
static gboolean
theora_enc_is_discontinuous (GstTheoraEnc * enc, GstBuffer * buffer)
{
GstClockTime ts = GST_BUFFER_TIMESTAMP (buffer);
GstClockTimeDiff max_diff;
/* Allow 3/4 a frame off */
max_diff = (enc->info.fps_denominator * GST_SECOND * 3) /
(enc->info.fps_numerator * 4);
if (ts != GST_CLOCK_TIME_NONE && enc->expected_ts != GST_CLOCK_TIME_NONE) {
if ((GstClockTimeDiff) (ts - enc->expected_ts) > max_diff) {
GST_DEBUG_OBJECT (enc, "Incoming TS %" GST_TIME_FORMAT
" exceeds expected value %" GST_TIME_FORMAT
" by too much, marking discontinuity",
GST_TIME_ARGS (ts), GST_TIME_ARGS (enc->expected_ts));
return TRUE;
}
}
if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_DURATION (buffer)))
enc->expected_ts = ts + GST_BUFFER_DURATION (buffer);
else
enc->expected_ts = GST_CLOCK_TIME_NONE;
return FALSE;
}
static GstFlowReturn
theora_enc_chain (GstPad * pad, GstBuffer * buffer)
{
@ -784,6 +838,16 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
buffer = newbuf;
}
if (theora_enc_is_discontinuous (enc, buffer)) {
theora_enc_reset (enc);
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;
enc->next_discont = TRUE;
}
res = theora_encode_YUVin (&enc->state, &yuv);
ret = GST_FLOW_OK;
@ -863,6 +927,8 @@ theora_enc_change_state (GstElement * element, GstStateChange transition)
theora_clear (&enc->state);
theora_comment_clear (&enc->comment);
theora_info_clear (&enc->info);
theora_enc_clear (enc);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;

View file

@ -62,6 +62,7 @@ static void
check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
{
GstClockTime clocktime;
int framecount;
fail_unless (GST_BUFFER_OFFSET_END (buffer) == granulepos,
"expected granulepos %" G_GUINT64_FORMAT
@ -70,8 +71,10 @@ check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
/* contrary to what we record as TIMESTAMP, we can use OFFSET to check
* the granulepos correctly here */
clocktime = gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND,
FRAMERATE);
framecount = GST_BUFFER_OFFSET_END (buffer);
framecount = granulepos >> GRANULEPOS_SHIFT;
framecount += granulepos & ((1 << GRANULEPOS_SHIFT) - 1);
clocktime = gst_util_uint64_scale (framecount, GST_SECOND, FRAMERATE);
fail_unless (clocktime == GST_BUFFER_OFFSET (buffer),
"expected OFFSET set to clocktime %" GST_TIME_FORMAT
@ -287,7 +290,6 @@ GST_START_TEST (test_continuity)
GST_END_TEST;
#if 0
static gboolean
drop_second_data_buffer (GstPad * droppad, GstBuffer * buffer, gpointer unused)
{
@ -301,7 +303,6 @@ GST_START_TEST (test_discontinuity)
gchar *pipe_str;
GstBuffer *buffer;
GError *error = NULL;
GstClockTime timestamp;
guint drop_id;
pipe_str = g_strdup_printf ("videotestsrc"
@ -336,7 +337,8 @@ GST_START_TEST (test_discontinuity)
gst_object_unref (sink);
}
drop_id = gst_pad_add_buffer_probe (droppad, drop_second_data_buffer, NULL);
drop_id = gst_pad_add_buffer_probe (droppad,
G_CALLBACK (drop_second_data_buffer), NULL);
gst_buffer_straw_start_pipeline (bin, pad);
/* header packets should have timestamp == NONE, granulepos 0 */
@ -374,7 +376,9 @@ GST_START_TEST (test_discontinuity)
/* check discontinuity with the next buffer */
buffer = gst_buffer_straw_get_buffer (bin, pad);
check_buffer_duration (buffer, GST_SECOND / 10);
check_buffer_granulepos (buffer, 2);
/* After a discont, we'll always get a keyframe, so this one should be
* 2<<GRANULEPOS_SHIFT */
check_buffer_granulepos (buffer, 2 << GRANULEPOS_SHIFT);
check_buffer_is_header (buffer, FALSE);
fail_unless (GST_BUFFER_IS_DISCONT (buffer),
"expected discontinuous buffer yo");
@ -390,7 +394,6 @@ GST_START_TEST (test_discontinuity)
}
GST_END_TEST;
#endif /* 0 */
#endif /* #ifndef GST_DISABLE_PARSE */
@ -405,9 +408,7 @@ theoraenc_suite (void)
#ifndef GST_DISABLE_PARSE
tcase_add_test (tc_chain, test_granulepos_offset);
tcase_add_test (tc_chain, test_continuity);
#if 0
tcase_add_test (tc_chain, test_discontinuity);
#endif /* 0 */
#endif
return s;