12
votes

Des éléments de liaison de dynamisme (ONU) dans un pipeline en cours d'exécution (gstreamer)?

Il existe de nombreux exemples dans la documentation Gstreamer sur la construction et la gestion des pipelines statiques. Cependant, il n'ya pas grand chose à propos de la modification / de la réinelle des éléments dans un pipeline Live , tandis que les médias coulent réellement. C'est certainement possible, alors la question est la suivante:

  1. Quels concepts / mécaniques Gstreamer devrais-je comprendre avant de tenter cela?
  2. Y a-t-il des pièges à surveiller?
  3. Quelle est la procédure de base, ou un bon exemple?

    La réponse acceptée sera une cuillère alimentée, complète et avec code source


0 commentaires

5 Réponses :


2
votes

En fait, j'essaie de faire la même chose. Pas trop de chance encore: (

J'ai reçu le lien suivant en demandant sur la chaîne IRC #Gstreamer: http://cgit.freedesktop.org/gstreamer/ gstreamer / arbre / docs / design / parti-dynamic.txt

Peut-être une allusion à la bonne direction.

S'il vous plaît laissez-moi savoir quand vous trouverez une autre documentation ...



3
votes

Ce post montrait d'abord lorsque je cherchais à modifier dynamiquement tout pipeline Gstreamer. Trouvé des liens, mais il est maintenant bien documenté dans le manuel: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html


0 commentaires

6
votes

J'ai tendance à utiliser Sélecteur de sortie-sélecteur de sortie strong> ou Sélecteur d'entrée fort> Selon la situation plutôt que la complexité du blocage des pads (j'ai répondu au blocage du tampon dans un autre poste http: // gstreamer-devel.966125.n4.n.natable.com/dynamiquement-adding-and-removing-Branes-of-a-tee-td973635.html#a4656812 ). Et connectez le sélecteur aux bacs FakesRC ou FAKESINK lorsqu'il n'est pas utilisé. Dans l'exemple ci-dessous si on utilise GTK, on ​​peut remplacer la ligne g_timeout_add (switch_timeout, switch_cb, osel); code> avec gtk_toggle_button code> et mettez tout le code actuellement dans le SWITCH_CB CODE> Fonction dans la fonction de rappel de bouton basculante. Dans ce code, on peut basculer entre les deux imagesinks. Je substituerais un évier d'image avec FAKESINK pour garder le pipeline en cours d'exécution, au cas où je voudrais ajouter un tee-shirt à l'avenir avec un fichierInk où je souhaite enregistrer une vidéo encore, fournissez le lecteur une option pour activer (sélecteur sur Imagesink) / Off (sélecteur sur farsink) l'affichage. Cela permet d'ajouter / supprimer des bacs au moment de l'exécution à l'aide du sélecteur.

#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 commentaires

0
votes

Je n'ai pas réussi à créer des fichiers muxted complètes lisibles pour gstreamer 0,10 sur multifilesink ou < em> sélecteur de sortie .

Après avoir analysé beaucoup d'alternatives, ma solution prend comme base de code l'exemple décrit dans: http://gstreamer.freedesktop.org /data/doc/gstreamer/head/manual/html/section-dynamic-pipelines.html

Les manches la fonction API de la fonction ont été modifiées un peu de 0,10 à 1.0 Mais la solution ci-dessous fonctionne pour créer toutes les n secondes différentes fichiers MP4:

xxx


0 commentaires

11
votes
  1. Mon "concept" préféré pour la compréhension de la liaison (et la liaison dynamique), pense au pipeline comme un vrai tuyau avec de l'eau qui traverse l'eau. Une fois que vous faites cela, certaines choses deviendront très évidentes. Comme, "Définissez-vous la source de la lecture avant de relier l'élément?", Devient ", allumez-vous l'eau avant de connecter le tuyau?", Et c'est une sorte de réponse elle-même. Encore plus avec une liaison dynamique, comment veillerez-vous qu'aucune eau "fuites" (c'est mauvais, "fuites" dans gstreamer est l'équivalent d'obtenir un gst_flow_not_linked et arrêtera votre source et votre amusement provoquer la chute ou la congestion des paquets).

  2. oui. Beaucoup. Avec un peu de dénonciation que je travaille toujours sur 0,10 et que certaines d'entre elles auraient pu être corrigées avec 1,0, il est malheureusement très difficile de relier dynamique et de dissuader avec Gstreamer 0,10. Laisse-moi expliquer: Disons que vous utilisez un tee-shirt, et vous voulez franchir une branche. Vous commencerez en libérant le Tees Srcpad (sans parler de la non-manifestation, cela se produit dans le cadre de la libération du pad), et vous devez désormais pouvoir démolir les éléments en aval de ce tampon. (L'équivalent de l'eau est que vous fermez une vanne après le tee-shirt et devriez désormais pouvoir démanteler les tuyaux après la vanne, vous ne commencerez pas le démantèlement des tuyaux sans fermer la vanne en premier, à moins que vous ne souhaitiez de vous humilier ...) Cela fonctionnera la plupart du temps, mais il y a une course ici. Parce que, après avoir publié le tampon, il pourrait toujours y avoir une poussée ou un pad-alloc sur leur chemin sur ce tampon, et si vous vous trouvez maintenant dans votre code, commencez à déchirer les éléments en aval, cela pourrait maintenant planter à cause de la course qui existe Dans certains éléments s'ils obtiennent une poussée ou un pad-alloc lors de la déchirure, ou si vous obtenez un gst_flow_wrong_state ou gst_flow_not_linked et ils reviennent à la source arrêter le flux pour tout le monde ...

  3. J'ai fait beaucoup d'expériences avec cela, et j'ai constaté que si vous avez besoin de stabilité et que vous avez reculé / gel, n'est pas une option dont vous avez besoin d'un élément qui servira de filet de sécurité dynamique. Un élément qui garantira que l'absolument aucune activité ne se produira sur un coussinet après votre libération / dissimulation. Le seul moyen de faire est de casser un autre paradigme Gstreamer de ne pas pousser tout en maintenant une serrure: vous devez contenir une serrure tout en appuyant / envoi d'événements / pad-allocage. J'ai fait une telle chose un moment où retour ici . (Cas de test étant la chose la plus importante bien sûr, car il vous permet de tester vos propres éléments / autres éléments pour leur sécurité) Vous pouvez également imaginer un élément sans verrouillage qui avalerait tous les mauvais ruarrages et de peindre une jolie image pour son amont, mais vous devriez alors être absolument sûr que tous vos éléments en aval soient «Push ou Pad-Alloc reçut Lors de la fermeture "-Safe, puisque votre élément ne serait pas en mesure de garantir une fois" Arrêter le flux "(libérer / unlink) a été exécuté, une petite goutte ne serait pas passée.

    Bien sûr, vous devez mettre une partie de cela en perspective. La fenêtre de ces terribles conditions de race que je parle est en fait très, très petite et ne se produira que tous les 1000ème ou 10.000e fois que vous exécutez votre programme. Mais pour une application professionnelle, cela n'est bien sûr pas acceptable. J'ai fait une conversation où j'ai couvert une partie de ces choses ici


0 commentaires