2010-06-19 17 views
10

hay muchos ejemplos en la documentación de gstreamer sobre la construcción y ejecución de tuberías estáticas. Sin embargo, no hay mucho sobre cambiar/volver a unir elementos en una tubería live - mientras que el medio fluye realmente. es definitivamente posible, por lo que la pregunta es:dinámicamente (un) elementos de enlace en una tubería en ejecución (gstreamer)?

  1. lo gstreamer conceptos/i mecánicos deben entender antes de hacer esto?
  2. ¿Hay alguna dificultad de la que tenga cuidado?
  3. ¿cuál es el procedimiento básico, o un buen ejemplo?

respuesta aceptada será alimentados con cuchara, integral y con el código fuente

Respuesta

6
  1. Mi favorito "concepto" para comprender la vinculación (y la vinculación dinámica), está pensando en la tubería como una tubería de bienes con agua fluyendo a través de él. Una vez que hagas esto, algunas cosas serán muy obvias. Como, "¿configura la fuente para JUGAR antes de vincular el elemento?", Se convierte en "¿enciende el agua antes de conectar la manguera?", Y en cierto modo se responde a sí misma. Aún más con los enlaces dinámicos, ¿cómo se aseguraría de que no "gotee" el agua (que es malo, "goteos" en GStreamer es equivalente a obtener un GST_FLOW_NOT_LINKED, y detendrá su fuente y la diversión) o se obstruya (puede causar caída o congestión de paquetes).

  2. Sí. Muchos. Con un pequeño descargo de responsabilidad de que todavía trabajo con 0.10 y algo de esto podría haber sido corregido con 1.0, desafortunadamente, es muy, muy difícil de hacer enlaces dinámicos y desvinculación con GStreamer 0.10. Déjame que te explique: Digamos que estás usando una camiseta, y quieres desvincular una rama. Comenzarías por soltar el Tees srcpad (sin importar desvincularlo, eso sucede como parte del lanzamiento del pad), y ahora deberías ser capaz de derribar los elementos aguas abajo de ese pad. (El equivalente de agua es que cierra una válvula después de la T, y ahora debería poder desmontar las tuberías después de la válvula, no comenzaría a desmantelar las tuberías sin cerrar la válvula primero a menos que quisiera mojarse ...) Esto funcionará la mayor parte del tiempo, pero hay una carrera aquí. Porque después de que hayas liberado el bloc, puede que aún haya un push o un pad-alloc en su camino en ese pad, y si ahora en tu código comienzas a derribar los elementos downstream, esto podría ahora colapsar debido a la raza que existe en algunos elementos si reciben un push o pad-alloc mientras lo derriban, o si obtiene un GST_FLOW_WRONG_STATE o GST_FLOW_NOT_LINKED y volverán a la fuente que detiene la transmisión para todos ...

  3. Hice muchos experimentos con esto, y descubrió que si necesita estabilidad y el bloqueo/congelamiento ocasionalmente no es una opción, necesita un elemento que le sirva como su red de seguridad dinámica. Un elemento que garantizará que no ocurra absolutamente ninguna actividad en un panel después de que lo suelte/desenlace. La única forma de hacerlo es romper otro paradigma de GStreamer de no presionar mientras se mantiene un bloqueo: es necesario mantener un bloqueo mientras se empujan/envían eventos/pad-allocing. Hice una cosa así hace un tiempo here. (el caso de prueba es lo más importante, por supuesto, ya que le permite probar sus propios/otros elementos para su seguridad) También podría imaginarse un elemento libre de bloqueo que se tragaría todos los malos FlowReturns, y simplemente pinta una imagen bonita para su flujo ascendente, pero luego tendría que estar absolutamente seguro de que todos sus elementos en sentido descendente serían "push o pad-alloc recibidos mientras se apaga" -seguro, ya que su elemento no podría garantizar que una vez "detenga el flujo" "(liberar/desvincular) se ha ejecutado, una pequeña gota no pasará.

Por supuesto, tiene que poner algo de esto en perspectiva. La ventana para estas terribles condiciones de carrera de las que estoy hablando es de hecho muy, muy pequeña, y podría suceder solo cada 1000 o 10.000 vez que ejecutas tu programa. Pero para una aplicación profesional esto por supuesto no es aceptable. Hice una charla donde he cubierto algunas de estas cosas here

2

En realidad, yo estoy tratando de hacer lo mismo. No demasiada suerte aún :(

Me siguiente enlace preguntando en el canal de IRC#gstreamer: http://cgit.freedesktop.org/gstreamer/gstreamer/tree/docs/design/part-dynamic.txt

Tal vez un toque a la dirección correcta

Por favor, hágamelo saber cuando encuentre otra. documentación ...

+0

otros enlaces: http://cgit.freedesktop.org/gstreamer/gstreamer/tree/docs/design/part-block.txt http: // CGIT .freedesktop.org/gstreamer/gstreamer/tree/docs/design/part-segments.txt – gue

4

Tiendo a utilizar selector de salida o selector de entrada bins dependiendo de la situación en lugar de la complejidad de bloqueo de bloc (he respondido el bloqueo de bloc en otra publicación http://gstreamer-devel.966125.n4.nabble.com/Dynamically-adding-and-removing-branches-of-a-tee-td973635.html#a4656812). Y conecte el selector a los contenedores fakesrc o fakesink cuando no esté en uso. En el ejemplo siguiente si uno está utilizando GTK, entonces uno puede reemplazar la línea g_timeout_add (SWITCH_TIMEOUT, switch_cb, osel); con gtk_toggle_button y poner todo el código actualmente en la función switch_cb en la función de devolución de llamada del botón de alternar. En este código uno puede cambiar entre los dos enlaces de imagen. Reemplazaría un receptor de imagen con fakesink para mantener la tubería en funcionamiento, en caso de que desee agregar un tee en el futuro con un archivo donde quiero grabar video y ofrecer al reproductor una opción para encender (selector en imágenes)/apagado (selector en fakesink) la pantalla. Esto permite agregar/eliminar contenedores en tiempo de ejecución usando el selector.

#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

que no alcanzó a crear archivos completos multiplexados legibles para Gstreamer 0,10 en la parte superior de multifilesink o salida selector.

Después de analizar muchas alternativas mi solución toma como base de código el ejemplo representado en: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html

El sondas API función se ha cambiado un poco 0,10-1.0 pero la solución a continuación trabaja para crear cada N segundos diferentes archivos 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; 
} 

Cuestiones relacionadas