Changing filesink location after a mux with many inputs.

Angel Martin amartin at vicomtech.org
Thu Jun 13 04:39:38 PDT 2013


And finally the solution for 2 inputs, Video and Audio

I have to highlight that the option of place 2 buffer probes (1 for video
and 1 for audio) have lots of problems meaning callbacks are called just
the first time.

Video new file needs to start with a key frame but Audio is more open to
random access.

So, the solution:
1) place a probe in the video
2) once the key frame come blocks the audio pad and send an "EOS" ALSO
through the audio pad
3) unblock the pad when the EOS has been processed by the muxer.

// Pipeline -> src                      -> dynamic pipeline
//
//             capsfilter(f264file)
// Pipeline ->    ->                    mp4mux(mux0)               ->
             filesink(fsink0)
// Pipeline -> capsfilter(fmpafile)
//
// Pipeline -> velem_before||vblockpad| -> |velem_cur_sinkpad|
// Pipeline ->
 |elem_cur||elem_cur_srcpad -> |elem_after_sinkpad||elem_after
// Pipeline -> aelem_before||ablockpad| -> |aelem_cur_sinkpad|

// Block audio source callback and launch EOS to MUXER to achieve a full
muxed file
static void pad_blocked_cb(GstPad * pad, gboolean blocked, gpointer
userdata)
{
  GstPad *sinkpad = (GstPad *)userdata;
  /* push EOS into the element, the probe will be fired when the
   * EOS leaves the effect and it has thus drained all of its data */
  if(blocked && (sinkpad != NULL))
  gst_pad_send_event (sinkpad, gst_event_new_eos ());
}

// Block source and launch EOS to MUXER to achieve a full muxed file
static gboolean
pad_probe_cb (GstPad * pad, GstBuffer * buffer, gpointer user_data)
{
  g_print ("\n\tINSIDE pad_probe_cb:%d %s %s\n",probe_id,
(newfile?"newfile":"thesame"),
  (is_sync_frame (buffer)?"KEYframe":"frame"));
  GST_DEBUG_OBJECT (pad, "pad is blocked now");

  last_ts = GST_BUFFER_TIMESTAMP(buffer);
  if(!GST_CLOCK_TIME_IS_VALID(last_ts))
  last_ts=0;

  if((newfile==NO_NEW_FILE) || !is_sync_frame (buffer))
return TRUE;

  /* remove the video probe first */
  gst_pad_remove_buffer_probe (pad, probe_id);

  /* BLOCK AUDIO */
  gst_pad_set_blocked_async(ablockpad, true, pad_blocked_cb,
aelem_cur_sinkpad);

  /* install new probe for EOS */
  probe_id = gst_pad_add_event_probe (elem_after_sinkpad,
G_CALLBACK(event_probe_cb), user_data);

  /* push EOS into the element, the probe will be fired when the
   * EOS leaves the effect and it has thus drained all of its data */
  gst_pad_send_event (velem_cur_sinkpad, gst_event_new_eos ());

  // Wait til the EOS have been processed the Buffer with the Key frame
will be the FIRST
  while(newfile != NO_NEW_FILE)
  Sleep(1);

  /* UNBLOCK AUDIO */
  gst_pad_set_blocked_async(ablockpad, false, pad_blocked_cb, NULL);

  // Push the buffer in the pipe flow (false DROP)
  return TRUE;
}

// this timeout is periodically run as part of the mainloop
static gboolean timeout (gpointer user_data)
{
  g_print ("TIMEOUT\n");
  if(!playing)
  return false;
  if(counter >= 3)
newfile = NO_FILE;
  else
newfile = NEW_FILE;
  /* install new probe for video Keyframe and New File */
  probe_id = gst_pad_add_buffer_probe (vblockpad, G_CALLBACK(pad_probe_cb),
pipeline);
  return true;
}


2013/6/5 Angel Martin <amartin at vicomtech.org>

> Hi,
>
> To fully understand the elements/pads structure the right schedule is:
>
> // Pipeline -> src                    -> dynamic pipeline
> // Pipeline -> capsfilter(f264file)   -> mp4mux(mux0)
>             -> filesink(fsink0)
> // Pipeline -> elem_BEFORE||blockpad| ->
> |elem_CUR_sinkpad||elem_CUR||elem_CUR_srcpad ->
> |elem_AFTER_sinkpad||elem_AFTER
>
> Best,
>
> Angel
>
> 2013/6/4 Angel Martin <amartin at vicomtech.org>
>
>> Dear all,
>>
>> I did not achieve to create full legible muxed files for Gstreamer 0.10
>> on top of multifilesink or output-selector.
>>
>> After analysing lots of alternatives my solution takes as code base the
>> example depicted in:
>>
>> http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html
>>
>> The probes function API has been changed a bit but the solution below
>> works to create every N seconds different MP4 files:
>>
>> static GstElement *pipeline = NULL;
>>
>> // Pipeline -> src                    -> dynamic pipeline
>> // Pipeline -> capsfilter(f264file)   -> mp4mux(mux0)
>>             -> filesink(fsink0)
>> // Pipeline -> elem_before||blockpad| ->
>> |elem_cur_sinkpad||elem_cur||elem_cur_srcpad -> |elem_cur_sinkpad||elem_cur
>> static gulong probe_id; // probe ID
>> static GstElement *elem_before; // SRC of dynamic pipeline
>> static GstElement *elem_after; // SINK of dynamic pipeline
>> static GstElement *elem_cur; // Main element of dynamic pipeline
>> static GstPad *blockpad; // SRC pad to be blocked
>> static GstPad *elem_cur_srcpad; // SRC pad where check EOS
>> static GstPad *elem_cur_sinkpad; // SINK of dynamic pipeline
>> static GstPad *elem_after_sinkpad;  // SINK of SINK element
>>
>> // Last Buffer Timestamp
>> static GstClockTime last_ts = 0;
>>
>> typedef enum {
>>   NO_NEW_FILE, // Keep current file destination
>>   NEW_FILE, // Switch file destination
>> } NewFileStatus;
>> static NewFileStatus newfile = NO_NEW_FILE; // Switch File Flag
>>
>> static int counter = 1; // Index filename
>>
>> // EOS listener to switch to other file destination
>> static gboolean
>> event_probe_cb (GstPad * pad, GstEvent * event, gpointer user_data)
>> {
>>   g_print ("INSIDE event_probe_cb:%d type:%s\n",probe_id,
>>   GST_EVENT_TYPE (event)==GST_EVENT_EOS?"EOS":GST_EVENT_TYPE
>> (event)==GST_EVENT_NEWSEGMENT?"NEWSEGMENT":"OTHER");
>>
>>   if (GST_EVENT_TYPE (event) != GST_EVENT_EOS)
>>   {
>>     // Push the event in the pipe flow (false DROP)
>>     return TRUE;
>>   }
>>    // remove the probe first
>>   gst_pad_remove_event_probe (pad, probe_id);
>>
>>   gst_object_unref (elem_cur_srcpad);
>>   gst_object_unref (elem_after_sinkpad);
>>   gst_element_release_request_pad(elem_cur, elem_cur_sinkpad);
>>
>>   gst_element_set_state (elem_cur, GST_STATE_NULL);
>>   gst_element_set_state (elem_after, GST_STATE_NULL);
>>
>>   // remove unlinks automatically
>>   GST_DEBUG_OBJECT (pipeline, "removing %" GST_PTR_FORMAT, elem_cur);
>>   gst_bin_remove (GST_BIN (pipeline), elem_cur);
>>   GST_DEBUG_OBJECT (pipeline, "removing %" GST_PTR_FORMAT, elem_after);
>>   gst_bin_remove (GST_BIN (pipeline), elem_after);
>>
>>   GstElement * mux0 = gst_element_factory_make("mp4mux", "mux0");
>>   GstElement * fsink0 = gst_element_factory_make("filesink", "fsink0");
>>   elem_cur = mux0;
>>   elem_after = fsink0;
>>
>>   if(!mux0 || !fsink0)
>>   {
>> printf("mising elements\n");
>>   }
>>
>>   GST_DEBUG_OBJECT (pipeline, "adding   %" GST_PTR_FORMAT, elem_cur);
>>   gst_bin_add (GST_BIN (pipeline), elem_cur);
>>   GST_DEBUG_OBJECT (pipeline, "adding   %" GST_PTR_FORMAT, elem_after);
>>   gst_bin_add (GST_BIN (pipeline), elem_after);
>>
>>   char buffer[128];
>>   sprintf(buffer, "test_%d.mp4", counter++);
>>   g_print ("File Switching %s\n", buffer);
>>   g_object_set(G_OBJECT(elem_after), "location", buffer, NULL);
>>
>>   GST_DEBUG_OBJECT (pipeline, "linking..");
>>   elem_cur_srcpad = gst_element_get_static_pad (elem_cur, "src");
>>   elem_cur_sinkpad = gst_element_get_request_pad (elem_cur, "video_%d");
>>   elem_after_sinkpad = gst_element_get_static_pad (elem_after, "sink");
>>
>>   if(gst_pad_link(blockpad, elem_cur_sinkpad) != GST_PAD_LINK_OK)
>>   {
>> printf("linking output 0 failed\n");
>> return -1;
>>   }
>>   if(gst_pad_link(elem_cur_srcpad, elem_after_sinkpad) != GST_PAD_LINK_OK)
>>   {
>> printf("linking output 1 failed\n");
>>  return -1;
>>   }
>>
>>   g_print ("Moving to PLAYING\n");
>>   gst_element_set_state (elem_cur, GST_STATE_PLAYING);
>>   gst_element_set_state (elem_after, GST_STATE_PLAYING);
>>
>>   GST_DEBUG_OBJECT (pipeline, "done");
>>
>>   newfile = NO_NEW_FILE;
>>   // Push the event in the pipe flow (false DROP)
>>   return TRUE;
>> }
>>
>> // Check if Buffer contains a KEY FRAME
>> static gboolean
>> is_sync_frame (GstBuffer * buffer)
>> {
>>   if (GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT))
>>   {
>>     return FALSE;
>>   }
>>   else if (!GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_IN_CAPS))
>>   {
>>     return TRUE;
>>   }
>> }
>>
>> // Block source and launch EOS to MUXER to achieve a full muxed file
>> static gboolean
>> pad_probe_cb (GstPad * pad, GstBuffer * buffer, gpointer user_data)
>> {
>>   g_print ("\n\tINSIDE pad_probe_cb:%d %s %s\n",probe_id,
>> (newfile?"newfile":"thesame"),
>>    (is_sync_frame (buffer)?"KEYframe":"frame"));
>>   GST_DEBUG_OBJECT (pad, "pad is blocked now");
>>
>>   last_ts = GST_BUFFER_TIMESTAMP(buffer);
>>   if(!GST_CLOCK_TIME_IS_VALID(last_ts))
>>   last_ts=0;
>>
>>   if((newfile==NO_NEW_FILE) || !is_sync_frame (buffer))
>>  return TRUE;
>>
>>   /* remove the probe first */
>>   gst_pad_remove_buffer_probe (pad, probe_id);
>>
>>   /* install new probe for EOS */
>>   probe_id = gst_pad_add_event_probe (elem_after_sinkpad,
>> G_CALLBACK(event_probe_cb), user_data);
>>
>>   /* push EOS into the element, the probe will be fired when the
>>    * EOS leaves the effect and it has thus drained all of its data */
>>   gst_pad_send_event (elem_cur_sinkpad, gst_event_new_eos ());
>>
>>   // Wait til the EOS have been processed the Buffer with the Key frame
>> will be the FIRST
>>   while(newfile != NO_NEW_FILE)
>>    Sleep(1);
>>
>>   // Push the buffer in the pipe flow (false DROP)
>>   return TRUE;
>> }
>>
>> // this timeout is periodically run as part of the mainloop
>> static gboolean timeout (gpointer user_data)
>> {
>>   g_print ("TIMEOUT\n");
>>   if(!playing)
>>   return false;
>>   newfile = NEW_FILE;
>>   /* install new probe for Keyframe and New File */
>>   probe_id = gst_pad_add_buffer_probe (blockpad,
>> G_CALLBACK(pad_probe_cb), pipeline);
>>   return true;
>> }
>>
>> Best,
>>
>> Angel
>>
>> 2012/10/19 Paddy <pat.blanchon at gmail.com>
>>
>>> Paddy wrote
>>> > More progress ...
>>> >
>>> > I've recovered the 1.3sec of missing video. This is what I now do when
>>> I
>>> > want to change the filesink location on the fly:
>>> > 1)  install/enable a handoff function on an identity element inserted
>>> > between the x264enc & videoQ elements which simply looks for keyframes
>>> > (see above pipeline).
>>> > 2)  when a keyframe is detected block both queue src pads & disable the
>>> > handoff
>>> > 3)  in the first blocked callback, unlink the mux/sink bin from both
>>> > queues & send an EOS event to the audio & video sink pads in the
>>> mux/sink
>>> > bin. I have to do this - without the EOS I never see the second
>>> callback.
>>> > I can also confirm that both EOS's find their way to the mux/sink bin
>>> - I
>>> > missed them in the yards of debug
>>> > 4)  on the second callback a new mux/sink bin is created, added &
>>> synced
>>> > to the pipeline & the queues are unblocked
>>> > 5)  the mux/file bin EOS handler function sets the retiring bin state
>>> to
>>> > NULL & removes it from the pipeline
>>> >
>>> > Hopefully this may help others who want to try this.
>>> >
>>> > Meanwhile I still have a couple of questions:
>>> >
>>> > Is the missing second block callback expected behaviour or a bug ? I
>>> never
>>> > see the second callback unless I send the EOS event.
>>> >
>>> > Bug or not: is the above procedure sound ? I'm new to gstreamer & don't
>>> > have much of a grip on its architecture, design & implementation; so
>>> > there's everything chance what I'm doing is fundamentally flawed.
>>> >
>>> > Cheer again
>>>
>>>
>>> Bumping this up so hopefully my questions can be considered.
>>>
>>> I'll dig some sample code out that shows the pertinent steps for those
>>> gents
>>> that have asked
>>>
>>>
>>>
>>> --
>>> View this message in context:
>>> http://gstreamer-devel.966125.n4.nabble.com/Changing-filesink-location-after-a-mux-with-many-inputs-tp4656116p4656654.html
>>> Sent from the GStreamer-devel mailing list archive at Nabble.com.
>>> _______________________________________________
>>> gstreamer-devel mailing list
>>> gstreamer-devel at lists.freedesktop.org
>>> http://lists.freedesktop.org/mailman/listinfo/gstreamer-devel
>>>
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freedesktop.org/archives/gstreamer-devel/attachments/20130613/5d85a469/attachment-0001.html>


More information about the gstreamer-devel mailing list