audio गतिशील(संयुक्त राष्ट्र) चल रहे(जीस्ट्र्रीम) पाइपलाइन में लिंक तत्व?




gstreamer (4)

स्थैतिक पाइपलाइनों के निर्माण और चलाने के लिए gstreamer प्रलेखन में बहुत सारे उदाहरण हैं हालांकि, जीवित पाइपलाइन में तत्वों को बदलने / रिंकिंग करने के बारे में बहुत कुछ नहीं है - जबकि मीडिया वास्तव में बह रहा है। यह निश्चित रूप से संभव है, तो सवाल यह है:

  1. क्या प्रयास करने से पहले मैं gstreamer अवधारणाओं / यांत्रिकी को समझ सकता हूँ?
  2. क्या किसी भी नुकसान के लिए बाहर देखने के लिए?
  3. मूल प्रक्रिया क्या है, या एक अच्छा उदाहरण है?

स्वीकृत जवाब चम्मच खिलाया जाएगा, व्यापक, और स्रोत कोड के साथ


मैं आउटपुट-चयनकर्ता या इनपुट चयनकर्ता डिब्बे का उपयोग पैड अवरुद्ध करने की जटिलता के बजाय स्थिति पर निर्भर करता हूं (मैंने किसी अन्य पोस्ट में पैड अवरुद्ध करने का उत्तर दिया है http://gstreamer-devel.966125.n4.nabble.com/Dynamically-adding- ए-टी-टीडी 973635.html # ए 4656812 ) और निकालने वाली शाखाएं और उपयोग में नहीं होने पर चयनकर्ता को फ़ैक्सरक या फ़ैक्सिंक डिब्बे से कनेक्ट करें। नीचे दिए गए उदाहरण में अगर कोई जीटीके का प्रयोग कर रहा है तो कोई लाइन g_timeout_add (SWITCH_TIMEOUT, switch_cb, osel); gtk_toggle_button साथ और वर्तमान में सभी कोड को switch_cb फ़ंक्शन में टॉगल बटन कॉलबैक फ़ंक्शन में डाल दिया। इस कोड में एक दो छवियों के बीच स्विच हो सकता है। मैं एक छवि सिंक के साथ पाइपलाइन चलाना रखने के लिए फ़ैक्सिंक का स्थान लेगा, अगर मैं भविष्य में एक फाइल सिंक के साथ एक टी जोड़ना चाहता हूं, जहां मैं वीडियो रिकॉर्ड करना चाहता हूं, फिर भी खिलाड़ी को विकल्प प्रदान करने के विकल्प (इमेडिक पर चयनकर्ता) / बंद (चयनकर्ता fakesink पर) प्रदर्शन यह चयनकर्ता के द्वारा रनटाइम पर डिब्बे को जोड़ने / हटाने की अनुमति देता है

#include <gst/gst.h>

#define SWITCH_TIMEOUT 1000
#define NUM_VIDEO_BUFFERS 500

static GMainLoop *loop;

/* Output selector src pads */
static GstPad *osel_src1 = NULL;
static GstPad *osel_src2 = NULL;

static gboolean
my_bus_callback (GstBus * bus, GstMessage * message, gpointer data)
{
  g_print ("Got %s message\n", GST_MESSAGE_TYPE_NAME (message));

  switch (GST_MESSAGE_TYPE (message)) {
    case GST_MESSAGE_ERROR:{
      GError *err;
      gchar *debug;

      gst_message_parse_error (message, &err, &debug);
      g_print ("Error: %s\n", err->message);
      g_error_free (err);
      g_free (debug);

      g_main_loop_quit (loop);
      break;
    }
    case GST_MESSAGE_EOS:
      /* end-of-stream */
      g_main_loop_quit (loop);
      break;
    default:
      /* unhandled message */
      break;
  }
  /* we want to be notified again the next time there is a message
   * on the bus, so returning TRUE (FALSE means we want to stop watching
   * for messages on the bus and our callback should not be called again)
   */
  return TRUE;
}

static gboolean
switch_cb (gpointer user_data)
{
  GstElement *sel = GST_ELEMENT (user_data);
  GstPad *old_pad, *new_pad = NULL;

  g_object_get (G_OBJECT (sel), "active-pad", &old_pad, NULL);

  if (old_pad == osel_src1)
    new_pad = osel_src2;
  else
    new_pad = osel_src1;

  g_object_set (G_OBJECT (sel), "active-pad", new_pad, NULL);

  g_print ("switched from %s:%s to %s:%s\n", GST_DEBUG_PAD_NAME (old_pad),
      GST_DEBUG_PAD_NAME (new_pad));

  gst_object_unref (old_pad);

  return TRUE;

}

gint
main (gint argc, gchar * argv[])
{
  GstElement *pipeline, *src, *toverlay, *osel, *sink1, *sink2, *convert;
  GstPad *sinkpad1;
  GstPad *sinkpad2;
  GstBus *bus;

  /* init GStreamer */
  gst_init (&argc, &argv);
  loop = g_main_loop_new (NULL, FALSE);

  /* create elements */
  pipeline = gst_element_factory_make ("pipeline", "pipeline");
  src = gst_element_factory_make ("videotestsrc", "src");
  toverlay = gst_element_factory_make ("timeoverlay", "timeoverlay");
  osel = gst_element_factory_make ("output-selector", "osel");
  convert = gst_element_factory_make ("ffmpegcolorspace", "convert");
  sink1 = gst_element_factory_make ("xvimagesink", "sink1");
  sink2 = gst_element_factory_make ("ximagesink", "sink2");

  if (!pipeline || !src || !toverlay || !osel || !convert || !sink1 || !sink2) {
    g_print ("missing element\n");
    return -1;
  }

  /* add them to bin */
  gst_bin_add_many (GST_BIN (pipeline), src, toverlay, osel, convert, sink1,
      sink2, NULL);

  /* set properties */
  g_object_set (G_OBJECT (src), "is-live", TRUE, NULL);
  g_object_set (G_OBJECT (src), "do-timestamp", TRUE, NULL);
  g_object_set (G_OBJECT (src), "num-buffers", NUM_VIDEO_BUFFERS, NULL);
  g_object_set (G_OBJECT (sink1), "sync", FALSE, "async", FALSE, NULL);
  g_object_set (G_OBJECT (sink2), "sync", FALSE, "async", FALSE, NULL);
  g_object_set (G_OBJECT (osel), "resend-latest", TRUE, NULL);

  /* link src ! timeoverlay ! osel */
  if (!gst_element_link_many (src, toverlay, osel, NULL)) {
    g_print ("linking failed\n");
    return -1;
  }

  /* link output 1 */
  sinkpad1 = gst_element_get_static_pad (sink1, "sink");
  osel_src1 = gst_element_get_request_pad (osel, "src%d");
  if (gst_pad_link (osel_src1, sinkpad1) != GST_PAD_LINK_OK) {
    g_print ("linking output 1 failed\n");
    return -1;
  }
  gst_object_unref (sinkpad1);

  /* link output 2 */
  sinkpad2 = gst_element_get_static_pad (convert, "sink");
  osel_src2 = gst_element_get_request_pad (osel, "src%d");
  if (gst_pad_link (osel_src2, sinkpad2) != GST_PAD_LINK_OK) {
    g_print ("linking output 2 failed\n");
    return -1;
  }
  gst_object_unref (sinkpad2);

  if (!gst_element_link (convert, sink2)) {
    g_print ("linking output 2 failed\n");
    return -1;
  }

  /* add switch callback */
  g_timeout_add (SWITCH_TIMEOUT, switch_cb, osel);

  /* change to playing */
  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
  gst_bus_add_watch (bus, my_bus_callback, loop);
  gst_object_unref (bus);

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* now run */
  g_main_loop_run (loop);

  /* also clean up */
  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_element_release_request_pad (osel, osel_src1);
  gst_element_release_request_pad (osel, osel_src2);
  gst_object_unref (GST_OBJECT (pipeline));

  return 0;
}

असल में मैं ऐसा करने की कोशिश कर रहा हूं। अभी तक बहुत ज्यादा किस्मत नहीं है :(

मुझे # जीस्ट्रीमर आईआरसी चैनल पर पूछकर लिंक मिला है: http://cgit.freedesktop.org/gstreamer/gstreamer/tree/docs/design/part-dynamic.txt

शायद सही दिशा का संकेत।

कृपया मुझे बताएं जब आपको अन्य दस्तावेज मिलेगा ...


  1. लिंकिंग (और गतिशील लिंकिंग) को समझने के लिए मेरी पसंदीदा "अवधारणा", पाइपलाइन को इसके माध्यम से जल प्रवाह के साथ वास्तविक पाइप के रूप में सोच रही है। एक बार जब आप ऐसा करते हैं, तो कुछ चीजें बहुत स्पष्ट हो जाएंगी। जैसे, "क्या आप तत्व को जोड़ने से पहले खेलने के लिए स्रोत सेट करते हैं?", "क्या आप नली को जोड़ने से पहले पानी चालू करते हैं?", गतिशील लिंकिंग के साथ भी, आप यह सुनिश्चित कैसे करेंगे कि कोई भी पानी "लीक" (यह बुरा नहीं है, जीएसटी्रीमर में "लीक" एक GST_FLOW_NOT_LINKED प्राप्त करने के बराबर है, और आपका स्रोत और मज़ा बंद कर देगा) या चिपक जाता है (हो सकता है छोड़ने या पैकेट के भीड़ का कारण)।

  2. हाँ। अनेक। थोड़ी अस्वीकृति के साथ कि मैं अभी भी 0.10 पर कार्य करता हूं और इनमें से कुछ 1.0 के साथ तय हो सकते हैं, दुर्भाग्य से बहुत ही मुश्किल है, जीएसटी्रीमर 0.10 के साथ गतिशील लिंकिंग और अनलिंक करना। मुझे समझाएं: मान लीजिए कि आप टी प्रयोग कर रहे हैं, और आप एक शाखा को अनलिंक करना चाहते हैं आप Tees srcpad (इसे अनलिंक कभी नहीं, जो पैड जारी होने के हिस्से के रूप में होता है) को छोड़कर शुरू करते हैं, और अब आपको सुरक्षित रूप से उस पैड से नीचे के तत्वों को तोड़ने में सक्षम होना चाहिए। (पानी का समतुल्य है कि आप टी के बाद एक वाल्व बंद कर देते हैं, और अब वाल्व के बाद पाइप को खत्म करने में सक्षम हो सकते हैं, आप पहले वाल्व को बंद किए बिना पाइप को खत्म नहीं करना शुरू करेंगे जब तक कि आप गीला नहीं करना चाहते ...) यह ज्यादातर समय काम करेंगे, लेकिन यहां एक दौड़ है। क्योंकि आपने पैड जारी करने के बाद, उस पैड पर अपने रास्ते पर अभी भी एक धक्का या पैड-एलोक हो सकता है, और यदि अब आप अपने कोड में डाउनस्ट्रीम तत्वों को फाड़ डालने लगते हैं, तो यह अब मौजूद दौड़ की वजह से क्रैश हो सकता है कुछ तत्वों में अगर उन्हें धक्का या पैड-एलोॉक मिलता है, या आपको GST_FLOW_WRONG_STATE या GST_FLOW_NOT_LINKED मिलता है और वे सभी के लिए स्ट्रीम को रोकने वाले स्रोत पर वापस जाएंगे ...

  3. मैंने इसके साथ बहुत सारे प्रयोग किए, और पाया कि अगर आपको स्थिरता की आवश्यकता है, और कभी-कभी क्रैशिंग / फ्रीजिंग एक विकल्प नहीं है तो आपको एक तत्व की जरूरत है जो आपके गतिशील सुरक्षा-नेट के रूप में काम करेगी। एक तत्व जो गारंटी देगा कि आपके द्वारा रिलीज़ / अनलिंक किए जाने के बाद पैड पर कोई भी गतिविधि नहीं होगी ऐसा करने का एकमात्र तरीका एक ताला लगाते हुए धक्का न देने के एक और जीस्ट्रीमर प्रतिमान को तोड़ना है: ईवेंट / पैड-आबंटन को भेजने / भेजने / भेजने के दौरान आपको लॉक को पकड़ना होगा। मैंने थोड़ी देर पहले ऐसी चीज बनाई थी (टेस्ट-केस बिल्कुल सबसे महत्वपूर्ण बात है, क्योंकि यह आपको अपने स्वयं के / अन्य तत्वों को उनके सुरक्षितता के लिए परीक्षण करने की अनुमति देता है) आप एक लॉक-मुक्त तत्व की कल्पना भी कर सकते हैं जो कि सभी खराब फ्लो रिटरन को निगल लेते हैं, और केवल एक सुंदर चित्र इसके ऊपर की ओर, लेकिन फिर आपको पूरी तरह से यह सुनिश्चित करने की आवश्यकता होगी कि आपके सभी डाउनस्ट्रीम-तत्व "शट डाउन करते समय पुश या पैड-एलोक प्राप्त हों" -साफ, क्योंकि आपका तत्व गारंटी नहीं दे पाएगा कि एक बार "प्रवाह रोकें" (रिहाई / अनलिंक) निष्पादित की गई है, एक छोटी बूंद पिछले नहीं निचोड़ होगा

बेशक आपको इसे कुछ परिप्रेक्ष्य में रखना होगा इन भयानक दौड़-शर्तों के लिए खिड़की जो मैं बोल रहा हूं वास्तव में बहुत ही बहुत छोटी है, और केवल 1000 या 10.000 वें समय में आपका कार्यक्रम चला सकते हैं। लेकिन एक पेशेवर आवेदन के लिए यह निश्चित रूप से स्वीकार्य नहीं है। मैंने एक ऐसी बात की थी जहां मैंने इस सामान को कुछ कवर किया था


मैं मल्टीफीइलिंक या आउटपुट-चयनकर्ता के शीर्ष पर Gstreamer 0.10 के लिए पूर्ण सुपाठ्य muxed फ़ाइलों को बनाने के लिए प्राप्त नहीं किया।

कई विकल्पों का विश्लेषण करने के बाद मेरा समाधान कोड आधार जैसा दिखता है: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html

जांच फ़ंक्शन एपीआई को 0.10 से 1.0 तक थोड़ा बदल दिया गया है लेकिन नीचे दिए गए समाधान हर एन सेकंड्स को विभिन्न एमपी 4 फाइल बनाने के लिए काम करता है:

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_after_sinkpad||elem_after
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;
}