Make feed_data more robust
[gst-aseq-appsrc.git] / gst-aseq-appsrc.c
index 7d4a885..4988dc2 100644 (file)
@@ -38,8 +38,8 @@
 #define DEFAULT_TICK_PERIOD_MS 10
 #define DEFAULT_POLL_TIMEOUT_MS (DEFAULT_TICK_PERIOD_MS / 2)
 
 #define DEFAULT_TICK_PERIOD_MS 10
 #define DEFAULT_POLL_TIMEOUT_MS (DEFAULT_TICK_PERIOD_MS / 2)
 
-GST_DEBUG_CATEGORY (mysrc_debug);
-#define GST_CAT_DEFAULT mysrc_debug
+GST_DEBUG_CATEGORY (mysource_debug);
+#define GST_CAT_DEFAULT mysource_debug
 
 typedef struct _App App;
 
 
 typedef struct _App App;
 
@@ -52,7 +52,7 @@ struct _App
 
   snd_seq_t *seq;
   int port_count;
 
   snd_seq_t *seq;
   int port_count;
-  snd_seq_addr_t *ports;
+  snd_seq_addr_t *seq_ports;
   snd_midi_event_t *parser;
   unsigned char *buffer;
 
   snd_midi_event_t *parser;
   unsigned char *buffer;
 
@@ -62,7 +62,7 @@ struct _App
   guint64 tick;
 };
 
   guint64 tick;
 };
 
-App s_app;
+static App s_app;
 
 static int
 init_seq (App * app)
 
 static int
 init_seq (App * app)
@@ -89,55 +89,50 @@ error:
   return ret;
 }
 
   return ret;
 }
 
-/* parses one or more port addresses from the string */
+/* Parses one or more port addresses from the string */
 static int
 parse_ports (const char *arg, App * app)
 {
 static int
 parse_ports (const char *arg, App * app)
 {
-  char *buf, *s, *port_name;
-  int ret;
+  gchar **ports_list;
+  guint i;
+  int ret = 0;
 
   GST_DEBUG ("ports: %s", arg);
 
 
   GST_DEBUG ("ports: %s", arg);
 
-  /* make a copy of the string because we're going to modify it */
-  buf = strdup (arg);
-  if (!buf) {
+  /*
+   * Assume that ports are separated by commas.
+   *
+   * Commas are used instead of spaces because those are valid in client
+   * names.
+   */
+  ports_list = g_strsplit (arg, ",", 0);
+
+  app->port_count = g_strv_length (ports_list);
+  app->seq_ports = g_try_new (snd_seq_addr_t, app->port_count);
+  if (!app->seq_ports) {
     GST_ERROR ("Out of memory");
     ret = -ENOMEM;
     GST_ERROR ("Out of memory");
     ret = -ENOMEM;
-    goto out;
+    goto out_free_ports_list;
   }
 
   }
 
-  for (port_name = s = buf; s; port_name = s + 1) {
-    /* Assume that ports are separated by commas.  We don't use
-     * spaces because those are valid in client names. */
-    s = strchr (port_name, ',');
-    if (s)
-      *s = '\0';
-
-    app->port_count++;
-    app->ports =
-        realloc (app->ports, app->port_count * sizeof (snd_seq_addr_t));
-    if (!app->ports) {
-      GST_ERROR ("Out of memory");
-      ret = -ENOMEM;
-      goto out_free_buf;
-    }
+  for (i = 0; i < (guint)app->port_count; i++) {
+    gchar *port_name = ports_list[i];
 
 
-    ret =
-        snd_seq_parse_address (app->seq, &app->ports[app->port_count - 1],
+    ret = snd_seq_parse_address (app->seq, &app->seq_ports[i],
         port_name);
     if (ret < 0) {
         port_name);
     if (ret < 0) {
-      GST_ERROR ("Invalid port %s - %s", port_name, snd_strerror (ret));
-      goto error_free_ports;
+      GST_ERROR ("Invalid port %s - %s", port_name,
+          snd_strerror (ret));
+      goto error_free_seq_ports;
     }
   }
 
     }
   }
 
-  goto out_free_buf;
+  goto out_free_ports_list;
 
 
-error_free_ports:
-  free (app->ports);
-out_free_buf:
-  free (buf);
-out:
+error_free_seq_ports:
+  g_free (app->seq_ports);
+out_free_ports_list:
+  g_strfreev (ports_list);
   return ret;
 }
 
   return ret;
 }
 
@@ -164,24 +159,23 @@ connect_ports (App * app)
 
   for (i = 0; i < app->port_count; ++i) {
     ret =
 
   for (i = 0; i < app->port_count; ++i) {
     ret =
-        snd_seq_connect_from (app->seq, 0, app->ports[i].client,
-        app->ports[i].port);
+        snd_seq_connect_from (app->seq, 0, app->seq_ports[i].client,
+        app->seq_ports[i].port);
     if (ret < 0)
       /* warning */
       GST_WARNING ("Cannot connect from port %d:%d - %s",
     if (ret < 0)
       /* warning */
       GST_WARNING ("Cannot connect from port %d:%d - %s",
-          app->ports[i].client, app->ports[i].port, snd_strerror (ret));
+          app->seq_ports[i].client, app->seq_ports[i].port, snd_strerror (ret));
   }
 }
 
 static void
 push_buffer (App * app, gpointer data, guint size)
 {
   }
 }
 
 static void
 push_buffer (App * app, gpointer data, guint size)
 {
-  GstBuffer *buffer;
   GstClockTime time;
   GstClockTime time;
-  int ret;
   gpointer local_data;
   gpointer local_data;
+  GstBuffer *buffer;
+  int ret;
 
 
-  /* read the next chunk */
   buffer = gst_buffer_new ();
 
   time = app->tick * DEFAULT_TICK_PERIOD_MS * GST_MSECOND;
   buffer = gst_buffer_new ();
 
   time = app->tick * DEFAULT_TICK_PERIOD_MS * GST_MSECOND;
@@ -191,13 +185,14 @@ push_buffer (App * app, gpointer data, guint size)
   GST_BUFFER_OFFSET (buffer) = time;
   GST_BUFFER_DURATION (buffer) = DEFAULT_TICK_PERIOD_MS * GST_MSECOND;
 
   GST_BUFFER_OFFSET (buffer) = time;
   GST_BUFFER_DURATION (buffer) = DEFAULT_TICK_PERIOD_MS * GST_MSECOND;
 
-  local_data = g_malloc (size);
-  memcpy (local_data, data, size);
+  local_data = g_memdup (data, size);
 
   gst_buffer_append_memory (buffer,
       gst_memory_new_wrapped (0, local_data, size, 0, size, local_data,
           g_free));
 
 
   gst_buffer_append_memory (buffer,
       gst_memory_new_wrapped (0, local_data, size, 0, size, local_data,
           g_free));
 
+  GST_MEMDUMP ("MIDI data:", local_data, size);
+
   GST_DEBUG ("feed buffer %p, tick %" G_GUINT64_FORMAT " size: %u",
       (gpointer) buffer, app->tick, size);
   g_signal_emit_by_name (app->appsrc, "push-buffer", buffer, &ret);
   GST_DEBUG ("feed buffer %p, tick %" G_GUINT64_FORMAT " size: %u",
       (gpointer) buffer, app->tick, size);
   g_signal_emit_by_name (app->appsrc, "push-buffer", buffer, &ret);
@@ -227,14 +222,18 @@ feed_data (GstElement * appsrc, guint size, App * app)
   ret = poll (app->pfds, app->npfds, DEFAULT_POLL_TIMEOUT_MS);
   if (ret < 0) {
     GST_ERROR ("ERROR in poll: %s", strerror (errno));
   ret = poll (app->pfds, app->npfds, DEFAULT_POLL_TIMEOUT_MS);
   if (ret < 0) {
     GST_ERROR ("ERROR in poll: %s", strerror (errno));
+    gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
   } else if (ret == 0) {
     push_tick_buffer (app);
   } else {
     do {
       snd_seq_event_t *event;
       err = snd_seq_event_input (app->seq, &event);
   } else if (ret == 0) {
     push_tick_buffer (app);
   } else {
     do {
       snd_seq_event_t *event;
       err = snd_seq_event_input (app->seq, &event);
-      if (err < 0)
+      if (err < 0 && err != -EAGAIN) {
+        GST_ERROR ("Error in snd_seq_event_input: %s", snd_strerror (err));
+        gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
         break;
         break;
+      }
       if (event) {
         size_ev =
             snd_midi_event_decode (app->parser, app->buffer, DEFAULT_BUFSIZE,
       if (event) {
         size_ev =
             snd_midi_event_decode (app->parser, app->buffer, DEFAULT_BUFSIZE,
@@ -247,6 +246,7 @@ feed_data (GstElement * appsrc, guint size, App * app)
           } else {
             GST_ERROR ("Error decoding event from ALSA to output: %s",
                 strerror (-size_ev));
           } else {
             GST_ERROR ("Error decoding event from ALSA to output: %s",
                 strerror (-size_ev));
+            gst_app_src_end_of_stream (GST_APP_SRC (appsrc));
             break;
           }
         } else {
             break;
           }
         } else {
@@ -360,7 +360,7 @@ err_free_buffer:
 err_free_parser:
   snd_midi_event_free (app->parser);
 err_free_ports:
 err_free_parser:
   snd_midi_event_free (app->parser);
 err_free_ports:
-  free (app->ports);
+  g_free (app->seq_ports);
 err_seq_close:
   snd_seq_close (app->seq);
 err:
 err_seq_close:
   snd_seq_close (app->seq);
 err:
@@ -374,7 +374,7 @@ app_finalize (App * app)
   free (app->pfds);
   free (app->buffer);
   snd_midi_event_free (app->parser);
   free (app->pfds);
   free (app->buffer);
   snd_midi_event_free (app->parser);
-  free (app->ports);
+  g_free (app->seq_ports);
   snd_seq_close (app->seq);
 }
 
   snd_seq_close (app->seq);
 }
 
@@ -398,9 +398,12 @@ main (int argc, char *argv[])
   GOptionContext *ctx;
   GError *err = NULL;
   gchar *ports = NULL;
   GOptionContext *ctx;
   GError *err = NULL;
   gchar *ports = NULL;
+  gboolean verbose = FALSE;
   GOptionEntry options[] = {
     {"ports", 'p', 0, G_OPTION_ARG_STRING, &ports,
         "Comma separated list of sequencer ports", "client:port,..."},
   GOptionEntry options[] = {
     {"ports", 'p', 0, G_OPTION_ARG_STRING, &ports,
         "Comma separated list of sequencer ports", "client:port,..."},
+    {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
+         "Output status information and property notifications", NULL},
     {NULL}
   };
 
     {NULL}
   };
 
@@ -418,7 +421,7 @@ main (int argc, char *argv[])
 
   gst_init (&argc, &argv);
 
 
   gst_init (&argc, &argv);
 
-  GST_DEBUG_CATEGORY_INIT (mysrc_debug, "mysrc", 0,
+  GST_DEBUG_CATEGORY_INIT (mysource_debug, "mysource", 0,
       "ALSA MIDI sequencer appsrc pipeline");
 
   ret = app_init (app, ports);
       "ALSA MIDI sequencer appsrc pipeline");
 
   ret = app_init (app, ports);
@@ -439,6 +442,9 @@ main (int argc, char *argv[])
       ("appsrc name=mysource ! fluiddec ! audioconvert ! autoaudiosink", NULL);
   g_assert (app->pipeline);
 
       ("appsrc name=mysource ! fluiddec ! audioconvert ! autoaudiosink", NULL);
   g_assert (app->pipeline);
 
+  if (verbose)
+    g_signal_connect (app->pipeline, "deep-notify", G_CALLBACK (gst_object_default_deep_notify), NULL);
+
   bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
   g_assert (bus);
 
   bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
   g_assert (bus);