diff --git a/ChangeLog b/ChangeLog index e1a95845fa..a30d21537d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2007-06-05 Wim Taymans + + * 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 * configure.ac: diff --git a/docs/design/draft-keyframe-force.txt b/docs/design/draft-keyframe-force.txt new file mode 100644 index 0000000000..28417e49f4 --- /dev/null +++ b/docs/design/draft-keyframe-force.txt @@ -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. diff --git a/ext/theora/theoraenc.c b/ext/theora/theoraenc.c index d80838c2d4..66b3389d45 100644 --- a/ext/theora/theoraenc.c +++ b/ext/theora/theoraenc.c @@ -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; }