Add draft design for forcing keyframes in encoders and implement in theoraenc.

Original commit message from CVS:
* docs/design/draft-keyframe-force.txt:
* ext/theora/theoraenc.c: (theora_enc_sink_event),
(theora_enc_chain):
Add draft design for forcing keyframes in encoders and implement in
theoraenc.
This commit is contained in:
Wim Taymans 2007-06-05 15:59:00 +00:00
parent ba42031248
commit 80c1e3d27c
3 changed files with 107 additions and 6 deletions

View file

@ -1,3 +1,11 @@
2007-06-05 Wim Taymans <wim@fluendo.com>
* docs/design/draft-keyframe-force.txt:
* ext/theora/theoraenc.c: (theora_enc_sink_event),
(theora_enc_chain):
Add draft design for forcing keyframes in encoders and implement in
theoraenc.
2007-06-05 Jan Schmidt <thaytan@mad.scientist.com>
* configure.ac:

View file

@ -0,0 +1,72 @@
Forcing keyframes
-----------------
Consider the following use case:
We have a pipeline that performs video and audio capture from a live source,
compesses and muxes the streams and writes the resulting data into a file.
Inside the uncompressed video data we have a specific pattern inserted at
specific moments that should trigger a switch to a new file, meaning, we close
the existing file we are writing to and start writing to a new file.
We want the new file to start with a keyframe so that one can start decoding
the file immediatly.
Components:
1) We need an element that is able to detect the pattern in the video stream.
2) We need to inform the video encoder that it should start encoding a keyframe
starting from exactly the frame with the pattern.
3) We need to inform the demuxer that it should flush out any pending data and
start creating the start of a new file with the keyframe as a first video
frame.
4) We need to inform the sink element that it should start writing to the next
file. This requires application interaction to instruct the sink of the new
filename. The application should also be free to ignore the boundary and
continue to write to the existing file. The application will typically use
an event pad probe to detect the custom event.
Implementation:
The implementation would consist of generating a GST_EVENT_CUSTOM_DOWNSTREAM
event that marks the keyframe boundary. This event is inserted into the
pipeline by the application upon a certain trigger. In the above use case this
trigger would be given by the element that detects the pattern, in the form of
an element message.
The custom event would travel further downstream to instruct encoder, muxer and
sink about the possible switch.
The information passed in the event consists of:
name: GstForceKeyUnit
(G_TYPE_UINT64)"timestamp" : the timestamp of the buffer that
triggered the event.
(G_TYPE_UINT64)"stream-time" : the stream position that triggered the
event.
(G_TYPE_UINT64)"running_time" : the running time of the stream when the
event was triggered.
.... : optional other data fields.
Note that this event is purely informational, no element is required to
perform an action but it should forward the event downstream, just like any
other event it does not handle.
Elements understanding the event should behave as follows:
1) The video encoder receives the event before the next frame. Upon reception
of the event it schedules to encode the next frame as a keyframe.
Before pushing out the encoded keyframe it must push the GstForceKeyUnit
event downstream.
2) The muxer receives the GstForceKeyUnit event and flushes out its current state,
preparing to produce data that can be used as a keyunit. Before pushing out
the new data it pushes the GstForceKeyUnit event downstream.
3) The application receives the GstForceKeyUnit on a sink padprobe of the sink
and reconfigures the sink to make it perform new actions after receiving
the next buffer.

View file

@ -565,8 +565,31 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
}
res = gst_pad_push_event (enc->srcpad, event);
break;
case GST_EVENT_CUSTOM_DOWNSTREAM:
{
const GstStructure *s;
s = gst_event_get_structure (event);
if (gst_structure_has_name (s, "GstForceKeyUnit")) {
GstClockTime next_ts;
/* make sure timestamps increment after reseting the decoder */
next_ts = enc->next_ts + enc->timestamp_offset;
theora_enc_reset (enc);
enc->granulepos_offset =
gst_util_uint64_scale (next_ts, enc->fps_n,
GST_SECOND * enc->fps_d);
enc->timestamp_offset = next_ts;
enc->next_ts = 0;
}
res = gst_pad_push_event (enc->srcpad, event);
break;
}
default:
res = gst_pad_push_event (enc->srcpad, event);
break;
}
return res;
}
@ -697,9 +720,8 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
}
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);
gst_util_uint64_scale (in_time, enc->fps_n, GST_SECOND * enc->fps_d);
enc->timestamp_offset = in_time;
enc->next_ts = 0;
}
@ -855,9 +877,8 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
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);
gst_util_uint64_scale (in_time, enc->fps_n, GST_SECOND * enc->fps_d);
enc->timestamp_offset = in_time;
enc->next_ts = 0;
enc->next_discont = TRUE;
}