2010-06-19 6 views
10

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

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

स्वीकार किए जाते हैं जवाब है, खिलाया व्यापक चम्मच किया जाएगा, और

उत्तर

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

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

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

बेशक आपको इसे परिप्रेक्ष्य में रखना होगा। इन भयानक दौड़-परिस्थितियों के लिए खिड़की मैं वास्तव में बहुत छोटी है, और केवल आपके प्रोग्राम को चलाने के लिए हर 1000 वें या 10.000 वें समय हो सकती है। लेकिन एक पेशेवर आवेदन के लिए यह निश्चित रूप से स्वीकार्य नहीं है। http://cgit.freedesktop.org/gstreamer/gstreamer/tree/docs/design/part-block.txt : http: // cgit मैं एक बात है जहाँ मैं इस चीज से कुछ here

2

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

मैं #gstreamer आईआरसी चैनल पर पूछकर लिंक का पालन किया गया: http://cgit.freedesktop.org/gstreamer/gstreamer/tree/docs/design/part-dynamic.txt

हो सकता है कि सही दिशा के लिए एक संकेत

कृपया मुझे पता है जब आप अन्य ढूँढ़ने दें। । प्रलेखन ...

+0

आगे लिंक कवर किया .freedesktop.org/gstreamer/gstreamer/पेड़/docs/डिजाइन/अंशकालिक segments.txt – gue

2

इस पोस्ट में पहली जब मैं गतिशील किसी भी gstreamer पाइप लाइन को संशोधित मिले कुछ लिंक्स के लिए देखा से पता चला है, लेकिन अब यह अच्छी तरह से मैनुअल में प्रलेखित है: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html

4

मैं आउटपुट-चयनकर्ता या इनपुट चयनकर्ता डिब्बे पैड अवरुद्ध जटिलता की बजाय स्थिति के आधार पर डिब्बे का उपयोग करने के लिए उपयोग करता हूं (मैंने http://gstreamer-devel.966125.n4.nabble.com/Dynamically-adding-and-removing-branches-of-a-tee-td973635.html#a4656812 पर किसी अन्य पोस्ट में पैड अवरुद्ध करने का उत्तर दिया है)। और उपयोग में नहीं होने पर चयनकर्ता को fakesrc या fakesink डिब्बे से कनेक्ट करें। नीचे दिए गए उदाहरण में यदि कोई जीटीके का उपयोग कर रहा है तो कोई g_timeout_add (SWITCH_TIMEOUT, switch_cb, osel);gtk_toggle_button के साथ लाइन को प्रतिस्थापित कर सकता है और वर्तमान में switch_cb फ़ंक्शन में टॉगल बटन कॉलबैक फ़ंक्शन में सभी कोड डाल सकता है। इस कोड में कोई दो छवियों के बीच स्विच कर सकता है। मैं पाइपलाइन चलाने के लिए fakesink के साथ एक छवि सिंक को प्रतिस्थापित करूँगा, अगर मैं भविष्य में एक फाइल को एक फाइलिंक के साथ जोड़ना चाहता हूं जहां मैं वीडियो रिकॉर्ड करना चाहता हूं फिर भी खिलाड़ी को चालू करने का विकल्प प्रदान करता है (इमेजिंग पर चयनकर्ता)/बंद (चयनकर्ता 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; 
} 
0

मैं प्राप्त नहीं किया है multifilesink या उत्पादन-चयनकर्ता के शीर्ष पर के लिए पूर्ण सुपाठ्य muxed फाइलों बनाने के लिए GStreamer 0.10http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html

जांच समारोह API, 1 करने के लिए 0.10 से थोड़ा बदल दिया गया है:

विकल्प के बहुत सारे विश्लेषण करने के बाद मेरी समाधान कोड आधार के रूप में उदाहरण में दिखाया गया लेता है।0 लेकिन समाधान नीचे हर एन सेकंड विभिन्न MP4 फ़ाइलों को बनाने के लिए काम करता है:

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

संबंधित मुद्दे