mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-24 02:31:03 +00:00
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:
parent
ba42031248
commit
80c1e3d27c
3 changed files with 107 additions and 6 deletions
|
@ -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>
|
2007-06-05 Jan Schmidt <thaytan@mad.scientist.com>
|
||||||
|
|
||||||
* configure.ac:
|
* configure.ac:
|
||||||
|
|
72
docs/design/draft-keyframe-force.txt
Normal file
72
docs/design/draft-keyframe-force.txt
Normal 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.
|
|
@ -565,8 +565,31 @@ theora_enc_sink_event (GstPad * pad, GstEvent * event)
|
||||||
}
|
}
|
||||||
res = gst_pad_push_event (enc->srcpad, event);
|
res = gst_pad_push_event (enc->srcpad, event);
|
||||||
break;
|
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:
|
default:
|
||||||
res = gst_pad_push_event (enc->srcpad, event);
|
res = gst_pad_push_event (enc->srcpad, event);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -697,9 +720,8 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
enc->granulepos_offset =
|
enc->granulepos_offset =
|
||||||
gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buffer), enc->fps_n,
|
gst_util_uint64_scale (in_time, enc->fps_n, GST_SECOND * enc->fps_d);
|
||||||
GST_SECOND * enc->fps_d);
|
enc->timestamp_offset = in_time;
|
||||||
enc->timestamp_offset = GST_BUFFER_TIMESTAMP (buffer);
|
|
||||||
enc->next_ts = 0;
|
enc->next_ts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -855,9 +877,8 @@ theora_enc_chain (GstPad * pad, GstBuffer * buffer)
|
||||||
if (theora_enc_is_discontinuous (enc, buffer)) {
|
if (theora_enc_is_discontinuous (enc, buffer)) {
|
||||||
theora_enc_reset (enc);
|
theora_enc_reset (enc);
|
||||||
enc->granulepos_offset =
|
enc->granulepos_offset =
|
||||||
gst_util_uint64_scale (GST_BUFFER_TIMESTAMP (buffer), enc->fps_n,
|
gst_util_uint64_scale (in_time, enc->fps_n, GST_SECOND * enc->fps_d);
|
||||||
GST_SECOND * enc->fps_d);
|
enc->timestamp_offset = in_time;
|
||||||
enc->timestamp_offset = GST_BUFFER_TIMESTAMP (buffer);
|
|
||||||
enc->next_ts = 0;
|
enc->next_ts = 0;
|
||||||
enc->next_discont = TRUE;
|
enc->next_discont = TRUE;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue