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> 2006-11-21 Tim-Philipp Müller <tim at centricular dot net>
* ext/pango/gsttextoverlay.c: (gst_text_overlay_init), * ext/pango/gsttextoverlay.c: (gst_text_overlay_init),

View file

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

View file

@ -292,6 +292,7 @@ gst_theora_enc_init (GstTheoraEnc * enc, GstTheoraEncClass * g_class)
GST_DEBUG_OBJECT (enc, GST_DEBUG_OBJECT (enc,
"keyframe_frequency_force is %d, granule shift is %d", "keyframe_frequency_force is %d, granule shift is %d",
enc->info.keyframe_frequency_force, enc->granule_shift); enc->info.keyframe_frequency_force, enc->granule_shift);
enc->expected_ts = GST_CLOCK_TIME_NONE;
} }
static void static void
@ -307,6 +308,26 @@ theora_enc_finalize (GObject * object)
G_OBJECT_CLASS (parent_class)->finalize (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 static gboolean
theora_enc_sink_setcaps (GstPad * pad, GstCaps * caps) 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", "keyframe_frequency_force is %d, granule shift is %d",
enc->info.keyframe_frequency_force, enc->granule_shift); enc->info.keyframe_frequency_force, enc->granule_shift);
theora_encode_init (&enc->state, &enc->info); theora_enc_reset (enc);
gst_object_unref (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_TIMESTAMP (buf) = timestamp + enc->timestamp_offset;
GST_BUFFER_DURATION (buf) = duration; 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 /* the second most significant bit of the first data byte is cleared
* for keyframes */ * for keyframes */
if ((packet->packet[0] & 0x40) == 0) { if ((packet->packet[0] & 0x40) == 0) {
@ -543,6 +569,34 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
return res; 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 static GstFlowReturn
theora_enc_chain (GstPad * pad, GstBuffer * buffer) theora_enc_chain (GstPad * pad, GstBuffer * buffer)
{ {
@ -784,6 +838,16 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
buffer = newbuf; 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); res = theora_encode_YUVin (&enc->state, &yuv);
ret = GST_FLOW_OK; ret = GST_FLOW_OK;
@ -863,6 +927,8 @@ theora_enc_change_state (GstElement * element, GstStateChange transition)
theora_clear (&enc->state); theora_clear (&enc->state);
theora_comment_clear (&enc->comment); theora_comment_clear (&enc->comment);
theora_info_clear (&enc->info); theora_info_clear (&enc->info);
theora_enc_clear (enc);
break; break;
case GST_STATE_CHANGE_READY_TO_NULL: case GST_STATE_CHANGE_READY_TO_NULL:
break; break;

View file

@ -62,6 +62,7 @@ static void
check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos) check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
{ {
GstClockTime clocktime; GstClockTime clocktime;
int framecount;
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
@ -70,8 +71,10 @@ check_buffer_granulepos (GstBuffer * buffer, gint64 granulepos)
/* contrary to what we record as TIMESTAMP, we can use OFFSET to check /* contrary to what we record as TIMESTAMP, we can use OFFSET to check
* the granulepos correctly here */ * the granulepos correctly here */
clocktime = gst_util_uint64_scale (GST_BUFFER_OFFSET_END (buffer), GST_SECOND, framecount = GST_BUFFER_OFFSET_END (buffer);
FRAMERATE); 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), fail_unless (clocktime == GST_BUFFER_OFFSET (buffer),
"expected OFFSET set to clocktime %" GST_TIME_FORMAT "expected OFFSET set to clocktime %" GST_TIME_FORMAT
@ -287,7 +290,6 @@ GST_START_TEST (test_continuity)
GST_END_TEST; GST_END_TEST;
#if 0
static gboolean static gboolean
drop_second_data_buffer (GstPad * droppad, GstBuffer * buffer, gpointer unused) drop_second_data_buffer (GstPad * droppad, GstBuffer * buffer, gpointer unused)
{ {
@ -301,7 +303,6 @@ GST_START_TEST (test_discontinuity)
gchar *pipe_str; gchar *pipe_str;
GstBuffer *buffer; GstBuffer *buffer;
GError *error = NULL; GError *error = NULL;
GstClockTime timestamp;
guint drop_id; guint drop_id;
pipe_str = g_strdup_printf ("videotestsrc" pipe_str = g_strdup_printf ("videotestsrc"
@ -336,7 +337,8 @@ GST_START_TEST (test_discontinuity)
gst_object_unref (sink); 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); gst_buffer_straw_start_pipeline (bin, pad);
/* header packets should have timestamp == NONE, granulepos 0 */ /* header packets should have timestamp == NONE, granulepos 0 */
@ -374,7 +376,9 @@ GST_START_TEST (test_discontinuity)
/* check discontinuity with the next buffer */ /* check discontinuity with the next buffer */
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);
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); 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");
@ -390,7 +394,6 @@ GST_START_TEST (test_discontinuity)
} }
GST_END_TEST; GST_END_TEST;
#endif /* 0 */
#endif /* #ifndef GST_DISABLE_PARSE */ #endif /* #ifndef GST_DISABLE_PARSE */
@ -405,9 +408,7 @@ theoraenc_suite (void)
#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);
#if 0
tcase_add_test (tc_chain, test_discontinuity); tcase_add_test (tc_chain, test_discontinuity);
#endif /* 0 */
#endif #endif
return s; return s;