OpenMAX backend changed:
authorMikko Rasa <mikko.rasa@movial.fi>
Fri, 7 Nov 2008 14:09:30 +0000 (16:09 +0200)
committerMikko Rasa <mikko.rasa@movial.fi>
Fri, 7 Nov 2008 14:09:30 +0000 (16:09 +0200)
- Implement autoplugging for audio
- Free components when destroying route

data/Makefile.am
data/openmax.ocd
src/octopus-backend-omx.c
src/octopus-backend.c
src/octopus-backend.h
src/octopus-route.c

index fc92fab..4fa9a45 100644 (file)
@@ -8,7 +8,8 @@ pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = octopus.pc
 
 componentdir = $(datadir)/octopus
-component_DATA = gstreamer.ocd
+component_DATA = gstreamer.ocd \
+       openmax.ocd
 
 EXTRA_DIST = com.ixs.octopus.service.in octopus.pc.in gstreamer.ocd
 CLEANFILES = com.ixs.octopus.service octopus.pc
index fe05d70..1428ec5 100644 (file)
@@ -5,11 +5,12 @@ component src_filereader
                source local-file-audioreader
        dynamic
 
-component dec_mad
+component dec_mp3
        element OMX.st.audio_decoder.mp3.mad
 
-component dec_ogg
-       element OMX.st.audio_decoder.ogg.single
+# The vorbis decoder doesn't like filereader's buffers and calls exit(1)
+#component dec_vorbis
+#      element OMX.st.audio_decoder.ogg.single
 
 component sink_alsa
        element OMX.st.alsa.alsasink
index 7715b5f..c378bb0 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
  */
 #include <string.h>
+#include <stdlib.h>
 #include <OMX_Component.h>
 #include <OMX_Core.h>
 #include "octopus-backend-omx.h"
 
 typedef struct _RouteDataOmx  RouteDataOmx;
+typedef struct _OmxElement    OmxElement;
 typedef struct _OmxBuffer     OmxBuffer;
 typedef struct _OmxConnection OmxConnection;
 
 struct _RouteDataOmx
 {
-  GSList *omx_elems;
-  GSList *omx_conns;
+  OMX_STATETYPE    state;
+  OMX_CALLBACKTYPE callbacks;
+  GSList           *elements;
+  GSList           *connections;
+  GMutex           *mutex;
+  GCond            *cond;
+  unsigned         n_pending;
+};
+
+struct _OmxElement
+{
+  OMX_HANDLETYPE handle;
+  gchar          *name;
 };
 
 struct _OmxBuffer
 {
   OMX_BUFFERHEADERTYPE *out_hdr;
   OMX_BUFFERHEADERTYPE *in_hdr;
+  int                  state;
 };
 
 struct _OmxConnection
@@ -70,6 +84,10 @@ stop_route(OctopusBackend *backend,
 static void
 route_mapping_changed(OctopusRoute *route,
                       gpointer     user_data);
+static void
+route_endpoint_changed(OctopusRoute    *route,
+                       OctopusEndpoint *endpoint,
+                       gpointer        user_data);
 static OMX_ERRORTYPE
 event_handler(OMX_HANDLETYPE comp,
               OMX_PTR        app_data,
@@ -117,12 +135,103 @@ octopus_backend_omx_init(OctopusBackendOmx *backend)
 
   base->name = "openmax";
 
+  if(!g_thread_supported())
+    g_thread_init(NULL);
+
   OMX_Init();
 
   g_debug("OpenMAX backend initialized");
 }
 
 /*
+ * Helper functions
+ */
+
+static int
+get_port_number(OMX_HANDLETYPE     handle,
+                OMX_PORTDOMAINTYPE domain,
+                OMX_DIRTYPE        direction,
+                unsigned           index)
+{
+  OMX_INDEXTYPE       pp_index;
+  OMX_PORT_PARAM_TYPE pparam;
+  unsigned            i;
+
+  if(domain == OMX_PortDomainAudio)
+    pp_index = OMX_IndexParamAudioInit;
+  else if(domain == OMX_PortDomainVideo)
+    pp_index = OMX_IndexParamVideoInit;
+  else
+    return -1;
+
+  pparam.nSize = sizeof(OMX_PORT_PARAM_TYPE);
+  pparam.nVersion.nVersion = 0x00000101;
+  OMX_GetParameter(handle, pp_index, &pparam);
+
+  if(index > pparam.nPorts)
+    return -1;
+
+  for(i = 0; i < pparam.nPorts; ++i) {
+    OMX_PARAM_PORTDEFINITIONTYPE pdef;
+
+    pdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+    pdef.nVersion.nVersion = 0x00000101;
+    pdef.nPortIndex = pparam.nStartPortNumber+i;
+    OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &pdef);
+
+    if(pdef.eDir == direction) {
+      if(index == 0)
+        return i;
+      --index;
+    }
+  }
+
+  return -1;
+}
+
+static OmxConnection *
+get_connection(OctopusRoute   *route,
+               OMX_HANDLETYPE handle,
+               OMX_DIRTYPE    dir)
+{
+  RouteDataOmx *data;
+  GSList       *iter;
+
+  data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+
+  for(iter = data->connections; iter; iter = iter->next) {
+    OmxConnection *conn = (OmxConnection *)iter->data;
+
+    if(dir == OMX_DirOutput && conn->source == handle)
+      return conn;
+    else if(dir == OMX_DirInput && conn->sink == handle)
+      return conn;
+  }
+
+  return NULL;
+}
+
+static void
+prod_connection(OmxConnection *conn)
+{
+  GSList *iter;
+
+  /* XXX There's no checking that buffer order is preserved */
+
+  for(iter = conn->buffers; iter; iter = iter->next) {
+    OmxBuffer *buf = (OmxBuffer *)iter->data;
+
+    if(buf->state == 0) {
+      OMX_FillThisBuffer(conn->source, buf->out_hdr);
+      buf->state = 1;
+    } else if(buf->state == 2) {
+      OMX_EmptyThisBuffer(conn->sink, buf->in_hdr);
+      buf->state = 3;
+    }
+  }
+}
+
+/*
  * OctopusBackend implementation
  */
 
@@ -156,7 +265,53 @@ can_link_components(OctopusBackend   *backend,
                     OctopusComponent *comp1,
                     OctopusComponent *comp2)
 {
-       return FALSE;
+  OctopusElement                 *elem;
+  OMX_ERRORTYPE                  err;
+  OMX_HANDLETYPE                 handle;
+  int                            port;
+  OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+  OMX_AUDIO_CODINGTYPE           coding;
+  OMX_CALLBACKTYPE               callbacks;
+
+  elem = (OctopusElement *)g_slist_last(comp1->elements)->data;
+  err = OMX_GetHandle(&handle, elem->name, NULL, &callbacks);
+  if(err != OMX_ErrorNone)
+    return FALSE;
+
+  port = get_port_number(handle, OMX_PortDomainAudio, OMX_DirOutput, 0);
+  if(port < 0) {
+    OMX_FreeHandle(handle);
+    return FALSE;
+  }
+
+  format.nSize = sizeof(format);
+  format.nVersion.nVersion = 0x00000101;
+  format.nPortIndex = port;
+  format.nIndex = 0;
+
+  OMX_GetParameter(handle, OMX_IndexParamAudioPortFormat, &format);
+  OMX_FreeHandle(handle);
+
+  coding = format.eEncoding;
+  if(coding == OMX_AUDIO_CodingUnused || coding == OMX_AUDIO_CodingAutoDetect)
+    return FALSE;
+
+  elem = (OctopusElement *)comp2->elements->data;
+  err = OMX_GetHandle(&handle, elem->name, NULL, &callbacks);
+  if(err != OMX_ErrorNone)
+    return FALSE;
+
+  port = get_port_number(handle, OMX_PortDomainAudio, OMX_DirInput, 0);
+  if(port < 0) {
+    OMX_FreeHandle(handle);
+    return FALSE;
+  }
+
+  format.nPortIndex = port;
+  OMX_GetParameter(handle, OMX_IndexParamAudioPortFormat, &format);
+  OMX_FreeHandle(handle);
+
+       return format.eEncoding == coding;
 }
 
 static gboolean
@@ -168,8 +323,15 @@ init_route(OctopusBackend *backend,
   data = g_new0(RouteDataOmx, 1);
   g_object_set_data(G_OBJECT(route), "data", data);
 
+  data->callbacks.EventHandler = event_handler;
+  data->callbacks.EmptyBufferDone = empty_buffer_done;
+  data->callbacks.FillBufferDone = fill_buffer_done;
+
+  data->mutex = g_mutex_new();
+  data->cond = g_cond_new();
+
   g_signal_connect(route, "mapping-changed", G_CALLBACK(route_mapping_changed), 0);
-  //g_signal_connect(route, "endpoint-changed", G_CALLBACK(route_endpoint_changed), 0);
+  g_signal_connect(route, "endpoint-changed", G_CALLBACK(route_endpoint_changed), 0);
 
   return TRUE;
 }
@@ -179,8 +341,44 @@ destroy_route(OctopusBackend *backend,
               OctopusRoute   *route)
 {
   RouteDataOmx *data;
+  GSList       *iter;
+
+  stop_route(backend, route);
 
   data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+
+  g_mutex_lock(data->mutex);
+  data->n_pending = 0;
+  for(iter = data->elements; iter; iter = iter->next) {
+    OmxElement    *elem = (OmxElement *)iter->data;
+    OMX_ERRORTYPE err;
+
+    err = OMX_SendCommand(elem->handle, OMX_CommandStateSet, OMX_StateLoaded, NULL);
+    if(err != OMX_ErrorNone)
+      g_debug("Could not set component to loaded state");
+    else
+      ++data->n_pending;
+  }
+
+  for(iter = data->connections; iter; iter = iter->next) {
+    OmxConnection *conn = (OmxConnection *)iter->data;
+    GSList        *iter2;
+
+    for(iter2 = conn->buffers; iter2; iter2 = iter2->next) {
+      OmxBuffer *buf = (OmxBuffer *)iter2->data;
+
+      if(conn->source)
+        OMX_FreeBuffer(conn->source, conn->source_port, buf->out_hdr);
+      if(conn->sink)
+        OMX_FreeBuffer(conn->sink, conn->sink_port, buf->in_hdr);
+    }
+  }
+  g_cond_wait(data->cond, data->mutex);
+  g_mutex_unlock(data->mutex);
+
+  for(iter = data->elements; iter; iter = iter->next)
+    OMX_FreeHandle(((OmxElement *)iter->data)->handle);
+
   g_free(data);
 }
 
@@ -192,25 +390,22 @@ play_route(OctopusBackend *backend,
   GSList       *iter;
 
   data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+  data->state = OMX_StateExecuting;
 
-  for(iter = data->omx_elems; iter; iter = iter->next) {
+  g_mutex_lock(data->mutex);
+  data->n_pending = 0;
+  for(iter = data->elements; iter; iter = iter->next) {
+    OmxElement    *elem = (OmxElement *)iter->data;
     OMX_ERRORTYPE err;
 
-    err = OMX_SendCommand((OMX_HANDLETYPE)iter->data, OMX_CommandStateSet, OMX_StateExecuting, NULL);
+    err = OMX_SendCommand(elem->handle, OMX_CommandStateSet, OMX_StateExecuting, NULL);
     if(err != OMX_ErrorNone)
       g_debug("Could not set component to executing state");
+    else
+      ++data->n_pending;
   }
-
-  for(iter = data->omx_conns; iter; iter = iter->next) {
-    OmxConnection *conn = (OmxConnection *)iter->data;
-    GSList        *iter2;
-
-    for(iter2 = conn->buffers; iter2; iter2 = iter2->next) {
-      OmxBuffer *buf = (OmxBuffer *)iter2->data;
-
-      OMX_FillThisBuffer(conn->source, buf->out_hdr);
-    }
-  }
+  g_cond_wait(data->cond, data->mutex);
+  g_mutex_unlock(data->mutex);
 
   return TRUE;
 }
@@ -223,14 +418,25 @@ stop_route(OctopusBackend *backend,
   GSList       *iter;
 
   data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+  if(data->state == OMX_StateIdle)
+    return TRUE;
 
-  for(iter = data->omx_elems; iter; iter = iter->next) {
+  data->state = OMX_StateIdle;
+
+  g_mutex_lock(data->mutex);
+  data->n_pending = 0;
+  for(iter = data->elements; iter; iter = iter->next) {
+    OmxElement    *elem = (OmxElement *)iter->data;
     OMX_ERRORTYPE err;
 
-    err = OMX_SendCommand((OMX_HANDLETYPE)iter->data, OMX_CommandStateSet, OMX_StateIdle, NULL);
+    err = OMX_SendCommand(elem->handle, OMX_CommandStateSet, OMX_StateIdle, NULL);
     if(err != OMX_ErrorNone)
       g_debug("Could not set component to idle state");
+    else
+      ++data->n_pending;
   }
+  g_cond_wait(data->cond, data->mutex);
+  g_mutex_unlock(data->mutex);
 
   return TRUE;
 }
@@ -239,77 +445,211 @@ stop_route(OctopusBackend *backend,
  * Private functions
  */
 
-static OMX_HANDLETYPE
+static void
+configure_endpoint(OMX_HANDLETYPE        handle,
+                   const OctopusEndpoint *endpoint)
+{
+  if(endpoint->uri && g_str_has_prefix(endpoint->uri, "file://")) {
+    OMX_ERRORTYPE err;
+    OMX_INDEXTYPE input_fn_index;
+
+    err = OMX_GetExtensionIndex(handle, "OMX.ST.index.param.filereader.inputfilename", &input_fn_index);
+    if(err == OMX_ErrorNone) {
+      gchar    *filename;
+      unsigned i, j;
+
+      /* g_uri_unescape_string appeared in GLib 2.16 */
+      filename = g_strdup(endpoint->uri+7);
+      for(i = 0, j = 0; filename[i];) {
+        if(filename[i] == '%') {
+          filename[i] = filename[i+1];
+          filename[i+1] = filename[i+2];
+          filename[i+2] = 0;
+          filename[j++] = strtoul(filename+i, NULL, 16);
+          i += 3;
+        } else {
+          filename[j++] = filename[i++];
+        }
+      }
+      filename[j]=0;
+
+      g_debug("Setting filename '%s'", filename);
+      OMX_SetParameter(handle, input_fn_index, filename);
+      g_free(filename);
+    }
+  }
+}
+
+static OmxElement *
+create_element(OctopusRoute *route,
+               const gchar  *type,
+               const gchar  *name)
+{
+  RouteDataOmx   *data;
+  OMX_ERRORTYPE  err;
+  OMX_HANDLETYPE handle;
+  OmxElement     *elem;
+
+  data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+
+  err = OMX_GetHandle(&handle, (char *)type, route, &data->callbacks);
+  if(err != OMX_ErrorNone) {
+    g_debug("Could not get handle for '%s'", name);
+    return NULL;
+  }
+
+  elem = g_new0(OmxElement, 1);
+  elem->handle = handle;
+  if(name)
+    elem->name = g_strdup(name);
+
+  return elem;
+}
+
+static OmxElement *
 realize_element(OctopusRoute   *route,
-                OctopusElement *elem)
+                OctopusElement *oct_elem)
 {
-  g_debug("Realizing element '%s'", elem->name);
-  g_debug("Not implemented yet");
+  RouteDataOmx *data;
+  OmxElement   *omx_elem;
 
-  return NULL;
+  g_debug("Realizing element '%s'", oct_elem->name);
+
+  data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+
+  if(!(omx_elem = create_element(route, oct_elem->name, oct_elem->endpoint)))
+    return NULL;
+
+  if(oct_elem->endpoint) {
+    const OctopusEndpoint *endpoint;
+    if((endpoint = octopus_route_get_endpoint(route, oct_elem->endpoint)))
+      configure_endpoint(omx_elem->handle, endpoint);
+  }
+
+  return omx_elem;
+}
+
+static OmxConnection *
+create_connection(OMX_HANDLETYPE handle)
+{
+  int                          out_port;
+  OmxConnection                *conn;
+  OMX_PARAM_PORTDEFINITIONTYPE pdef;
+  unsigned                     i;
+
+  out_port = get_port_number(handle, OMX_PortDomainAudio, OMX_DirOutput, 0);
+  if(out_port < 0)
+    return NULL;
+
+  conn = g_new0(OmxConnection, 1);
+  conn->source = handle;
+  conn->source_port = out_port;
+
+  pdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
+  pdef.nVersion.nVersion = 0x00000101;
+  pdef.nPortIndex = out_port;
+  OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &pdef);
+
+  g_debug("Allocating %lu buffers of %lu bytes", pdef.nBufferCountMin, pdef.nBufferSize);
+  for(i = 0; i < pdef.nBufferCountMin; ++i) {
+    OmxBuffer *buf;
+
+    buf = g_new0(OmxBuffer, 1);
+
+    OMX_AllocateBuffer(handle, &buf->out_hdr, out_port, conn, pdef.nBufferSize);
+    conn->buffers = g_slist_prepend(conn->buffers, buf);
+  }
+
+  return conn;
 }
 
 static void
+finish_connection(OmxConnection *conn,
+                  OMX_HANDLETYPE handle)
+{
+  int    in_port;
+  GSList *iter;
+
+  g_assert(!conn->sink);
+
+  in_port = get_port_number(handle, OMX_PortDomainAudio, OMX_DirInput, 0);
+  if(in_port < 0)
+    return;
+
+  conn->sink = handle;
+  conn->sink_port = in_port;
+
+  for(iter = conn->buffers; iter; iter = iter->next) {
+    OmxBuffer *buf = (OmxBuffer *)iter->data;
+    OMX_UseBuffer(handle, &buf->in_hdr, in_port, conn, buf->out_hdr->nAllocLen, buf->out_hdr->pBuffer);
+  }
+}
+
+static OmxConnection *
 realize_component(OctopusBackend   *backend,
                   OctopusRoute     *route,
-                  OctopusComponent *component)
+                  OctopusComponent *component,
+                  OmxConnection    *conn)
 {
-  RouteDataOmx *data;
-  GSList       *iter;
+  RouteDataOmx  *data;
+  GSList        *iter;
 
   g_debug("Realizing component '%s'", component->name);
 
   data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
 
   for(iter = component->elements; iter; iter = iter->next) {
-    OctopusElement *elem = (OctopusElement *)iter->data;
-    OMX_HANDLETYPE handle;
+    OctopusElement *oct_elem = (OctopusElement *)iter->data;
+    OmxElement     *omx_elem;
+
+    omx_elem = realize_element(route, oct_elem);
+    data->elements = g_slist_prepend(data->elements, omx_elem);
+
+    OMX_SendCommand(omx_elem->handle, OMX_CommandStateSet, OMX_StateIdle, NULL);
+
+    /* XXX Possible race condition, should wait before the state transition completes */
+
+    if(conn) {
+      finish_connection(conn, omx_elem->handle);
+      if(data->state != OMX_StateIdle)
+        OMX_SendCommand(omx_elem->handle, OMX_CommandStateSet, data->state, NULL);
+    }
 
-    handle = realize_element(route, elem);
-    data->omx_elems = g_slist_prepend(data->omx_elems, handle);
+    conn = create_connection(omx_elem->handle);
+    if(conn)
+      data->connections = g_slist_prepend(data->connections, conn);
   }
+
+  return conn;
 }
 
 static void
 realize_component_chain(OctopusBackend *backend,
                         OctopusRoute   *route,
-                        GSList         *chain)
+                        GSList         *chain,
+                        OmxConnection  *conn)
 {
   GSList *iter;
 
   for(iter = chain; iter; iter = iter->next)
-    realize_component(backend, route, (OctopusComponent *)iter->data);
+    conn = realize_component(backend, route, (OctopusComponent *)iter->data, conn);
 }
 
 static void
 realize_fixed_pipeline(OctopusRoute *route,
                        gchar        **parts)
 {
-  RouteDataOmx     *data;
-  OMX_CALLBACKTYPE *callbacks;
-  gchar            **ptr;
-  OmxConnection    *conn = 0;
-
-  g_debug("route %p", route);
+  RouteDataOmx  *data;
+  gchar         **ptr;
+  OmxConnection *conn = 0;
 
   data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
 
-  callbacks = g_new0(OMX_CALLBACKTYPE, 1);
-  callbacks->EventHandler = event_handler;
-  callbacks->EmptyBufferDone = empty_buffer_done;
-  callbacks->FillBufferDone = fill_buffer_done;
-
   for(ptr = parts; *ptr; ++ptr) {
-    gchar               *colon;
-    gchar               *cname;
-    OMX_HANDLETYPE      handle;
-    OMX_ERRORTYPE       err;
-    OMX_PORT_PARAM_TYPE pparam;
-    unsigned            i;
-    int                 in_port = -1;
-    int                 out_port = -1;
-    unsigned            nbufs = 0;
-    unsigned            bufsize = 32768;
+    gchar         *colon;
+    gchar         *cname;
+    OmxElement    *elem;
+    OMX_ERRORTYPE err;
 
     colon = strchr(*ptr, ':');
     if(colon)
@@ -319,83 +659,33 @@ realize_fixed_pipeline(OctopusRoute *route,
 
     g_debug(cname);
 
-    err = OMX_GetHandle(&handle, cname, route, callbacks);
-    if(err != OMX_ErrorNone) {
-      g_debug("Could not get handle for %s", *ptr);
+    if(!(elem = create_element(route, cname, NULL)))
       return;
-    }
-
-    g_debug("%p", handle);
 
     if(cname != *ptr)
       g_free(cname);
 
-    data->omx_elems = g_slist_prepend(data->omx_elems, handle);
+    data->elements = g_slist_prepend(data->elements, elem);
 
     if(colon) {
       OMX_INDEXTYPE input_fn_index;
 
-      OMX_GetExtensionIndex(handle, "OMX.ST.index.param.filereader.inputfilename", &input_fn_index);
-      OMX_SetParameter(handle, input_fn_index, colon+1);
-    }
-
-    pparam.nSize = sizeof(OMX_PORT_PARAM_TYPE);
-    pparam.nVersion.nVersion = 0x00000101;
-    OMX_GetParameter(handle, OMX_IndexParamAudioInit, &pparam);
-    for(i = 0; i < pparam.nPorts; ++i) {
-      OMX_PARAM_PORTDEFINITIONTYPE pdef;
-
-      pdef.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
-      pdef.nVersion.nVersion = 0x00000101;
-      pdef.nPortIndex = pparam.nStartPortNumber+i;
-      OMX_GetParameter(handle, OMX_IndexParamPortDefinition, &pdef);
-
-      if(pdef.eDir == OMX_DirInput) {
-        in_port = pparam.nStartPortNumber+i;
-      } else if(pdef.eDir == OMX_DirOutput) {
-        nbufs = pdef.nBufferCountMin;
-        out_port = pparam.nStartPortNumber+i;
-      }
+      OMX_GetExtensionIndex(elem->handle, "OMX.ST.index.param.filereader.inputfilename", &input_fn_index);
+      OMX_SetParameter(elem->handle, input_fn_index, colon+1);
     }
 
-    err = OMX_SendCommand(handle, OMX_CommandStateSet, OMX_StateIdle, NULL);
+    err = OMX_SendCommand(elem->handle, OMX_CommandStateSet, OMX_StateIdle, NULL);
     if(err != OMX_ErrorNone) {
       g_debug("Could not set component to idle state");
       return;
     }
 
-    if(conn) {
-      GSList *iter;
+    if(conn)
+      finish_connection(conn, elem->handle);
 
-      conn->sink = handle;
-      conn->sink_port = in_port;
-
-      for(iter = conn->buffers; iter; iter = iter->next) {
-        OmxBuffer *buf = (OmxBuffer *)iter->data;
-        OMX_UseBuffer(handle, &buf->in_hdr, in_port, conn, buf->out_hdr->nAllocLen, buf->out_hdr->pBuffer);
-      }
-      conn = 0;
-    }
-
-    if(out_port >= 0) {
-      unsigned j;
-
-      conn = g_new0(OmxConnection, 1);
-      conn->source = handle;
-      conn->source_port = out_port;
-
-      data->omx_conns = g_slist_prepend(data->omx_conns, conn);
-
-      g_debug("Creating %d buffers", nbufs);
-      for(j = 0; j < nbufs; ++j) {
-        OmxBuffer *buf;
-
-        buf = g_new0(OmxBuffer, 1);
-
-        OMX_AllocateBuffer(handle, &buf->out_hdr, out_port, conn, bufsize);
-        conn->buffers = g_slist_prepend(conn->buffers, buf);
-      }
-    }
+    conn = create_connection(elem->handle);
+    if(conn)
+      data->connections = g_slist_prepend(data->connections, conn);
   }
 }
 
@@ -440,7 +730,7 @@ route_mapping_changed(OctopusRoute *route,
 
       for(j = 0; sinks[j]; ++j) {
         if((chain = octopus_backend_build_component_chain(backend, component, sinks[j]))) {
-          realize_component_chain(backend, route, chain);
+          realize_component_chain(backend, route, chain, NULL);
           g_slist_free(chain);
           break;
         }
@@ -449,6 +739,26 @@ route_mapping_changed(OctopusRoute *route,
   }
 }
 
+static void
+route_endpoint_changed(OctopusRoute    *route,
+                       OctopusEndpoint *endpoint,
+                       gpointer        user_data)
+{
+  RouteDataOmx *data;
+  GSList       *iter;
+
+  data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+
+  for(iter = data->elements; iter; iter = iter->next) {
+    OmxElement *elem = (OmxElement *)iter->data;
+
+    if(g_str_has_prefix(elem->name, endpoint->name)) {
+      configure_endpoint(elem->handle, endpoint);
+      break;
+    }
+  }
+}
+
 static OMX_ERRORTYPE
 event_handler(OMX_HANDLETYPE comp,
               OMX_PTR        app_data,
@@ -457,16 +767,104 @@ event_handler(OMX_HANDLETYPE comp,
               OMX_U32        data2,
               OMX_PTR        event_data)
 {
-  OctopusRoute *route = (OctopusRoute *)app_data;
+  OctopusRoute   *route = (OctopusRoute *)app_data;
+  RouteDataOmx   *data;
+  OctopusBackend *backend;
+
+  data = (RouteDataOmx *)g_object_get_data(G_OBJECT(route), "data");
+  g_object_get(G_OBJECT(route), "backend", &backend, NULL);
 
   switch(event) {
+  case OMX_EventCmdComplete:
+    if(data1 == OMX_CommandStateSet) {
+      g_debug("State transition complete: %lx", data2);
+
+      if(data2 == OMX_StateExecuting) {
+        OmxConnection *conn;
+
+        if((conn = get_connection(route, comp, OMX_DirInput)))
+          prod_connection(conn);
+        if((conn = get_connection(route, comp, OMX_DirOutput)))
+          prod_connection(conn);
+      }
+
+      g_mutex_lock(data->mutex);
+      if(data->n_pending > 0) {
+        --data->n_pending;
+        if(data->n_pending == 0)
+          g_cond_broadcast(data->cond);
+      }
+      g_mutex_unlock(data->mutex);
+    } else {
+      g_debug("Command complete: %lx %lx", data1, data2);
+    }
+    break;
+
   case OMX_EventBufferFlag:
     g_debug("End of stream");
-    stop_route(NULL, route);
+    stop_route(backend, route);
+    break;
+
+  case OMX_EventPortFormatDetected:
+    if(data1 == OMX_IndexParamAudioPortFormat) {
+      OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+
+      format.nSize = sizeof(format);
+      format.nVersion.nVersion = 0x00000101;
+      format.nPortIndex = data2;
+      format.nIndex = 0;
+
+      OMX_GetParameter(comp, data1, &format);
+
+      const gchar *coding = NULL;
+      if(format.eEncoding == OMX_AUDIO_CodingMP3)
+        coding = "mp3";
+      else if(format.eEncoding == OMX_AUDIO_CodingVORBIS)
+        coding = "vorbis";
+      else
+        g_debug("Port encoding: unknown (%d)", format.eEncoding);
+
+      if(coding) {
+        gchar            *dec_name;
+        OctopusComponent *decoder;
+
+        g_debug("Port encoding: %s", coding);
+
+        dec_name = g_strconcat("dec_", coding, NULL);
+        decoder = octopus_backend_get_component(backend, dec_name);
+        g_free(dec_name);
+
+        if(decoder) {
+          OmxConnection *conn;
+          GSList        *chain;
+          gchar         **sinks;
+          unsigned      i;
+
+          if(!(conn = get_connection(route, comp, OMX_DirOutput))) {
+            g_debug("No connection?");
+            break;
+          } else if(conn->sink) {
+            break;
+          }
+
+          g_object_get(G_OBJECT(route), "sinks", &sinks, NULL);
+
+          for(i = 0; sinks[i]; ++i) {
+            chain = octopus_backend_build_component_chain(backend, decoder, sinks[i]);
+            if(chain) {
+              realize_component_chain(backend, route, chain, conn);
+              break;
+            }
+          }
+        }
+      }
+    } else {
+      g_debug("Port format detected: %lx %lx", data1, data2);
+    }
     break;
 
   default:
-    g_debug("event_handler %p %d %lu %lu", comp, event, data1, data2);
+    g_debug("event_handler %p %d %lx %lx", comp, event, data1, data2);
   }
 
   return OMX_ErrorNone;
@@ -484,8 +882,18 @@ empty_buffer_done(OMX_HANDLETYPE       comp,
   for(iter = conn->buffers; iter; iter = iter->next) {
     buf = (OmxBuffer *)iter->data;
     if(buf->in_hdr == buffer) {
+      OMX_STATETYPE state;
+
       buf->out_hdr->nFilledLen = 0;
-      OMX_FillThisBuffer(conn->source, buf->out_hdr);
+
+      OMX_GetState(conn->source, &state);
+      if(state == OMX_StateExecuting) {
+        OMX_FillThisBuffer(conn->source, buf->out_hdr);
+        buf->state = 1;
+      } else {
+        buf->state = 0;
+      }
+
       break;
     }
   }
@@ -505,8 +913,18 @@ fill_buffer_done(OMX_HANDLETYPE       comp,
   for(iter = conn->buffers; iter; iter = iter->next) {
     buf = (OmxBuffer *)iter->data;
     if(buf->out_hdr == buffer) {
+      OMX_STATETYPE state;
+
       buf->in_hdr->nFilledLen = buf->out_hdr->nFilledLen;
-      OMX_EmptyThisBuffer(conn->sink, buf->in_hdr);
+
+      OMX_GetState(conn->sink, &state);
+      if(state == OMX_StateExecuting) {
+        OMX_EmptyThisBuffer(conn->sink, buf->in_hdr);
+        buf->state = 3;
+      } else {
+        buf->state = 2;
+      }
+
       break;
     }
   }
index 9c626c2..20e5790 100644 (file)
@@ -318,6 +318,22 @@ octopus_backend_load_components_from_directory(OctopusBackend *backend,
 }
 
 OctopusComponent *
+octopus_backend_get_component(OctopusBackend      *backend,
+                              gchar               *name)
+{
+  GSList *iter;
+
+  for(iter = backend->components; iter; iter = iter->next) {
+    OctopusComponent *comp = (OctopusComponent *)iter->data;
+
+    if(!strcmp(comp->name, name))
+      return comp;
+  }
+
+  return NULL;
+}
+
+OctopusComponent *
 octopus_backend_get_component_by_endpoint(OctopusBackend      *backend,
                                           gchar               *ep_name,
                                           OctopusEndpointType ep_type)
index 87f6c4f..f55958c 100644 (file)
@@ -121,6 +121,9 @@ void
 octopus_backend_load_components_from_directory(OctopusBackend *backend,
                                           gchar          *dirname);
 OctopusComponent *
+octopus_backend_get_component            (OctopusBackend      *backend,
+                                          gchar               *name);
+OctopusComponent *
 octopus_backend_get_component_by_endpoint(OctopusBackend      *backend,
                                           gchar               *ep_name,
                                           OctopusEndpointType ep_type);
index 896fde5..922fff3 100644 (file)
@@ -51,7 +51,9 @@ struct _OctopusRoutePrivate
 };
 
 static void
-octopus_route_finalize(GObject *route);
+octopus_route_dispose(GObject *object);
+static void
+octopus_route_finalize(GObject *object);
 static void
 set_property(GObject      *object,
              guint        prop_id,
@@ -80,6 +82,7 @@ octopus_route_class_init(OctopusRouteClass *klass)
 
   gobject_class = G_OBJECT_CLASS(klass);
 
+  gobject_class->dispose = octopus_route_dispose;
   gobject_class->finalize = octopus_route_finalize;
   gobject_class->set_property = set_property;
   gobject_class->get_property = get_property;
@@ -164,13 +167,22 @@ octopus_route_init(OctopusRoute *route)
 }
 
 static void
-octopus_route_finalize(GObject *object)
+octopus_route_dispose(GObject *object)
 {
   OctopusRoute *route;
 
   route = OCTOPUS_ROUTE(object);
 
   octopus_backend_destroy_route(route->priv->backend, route);
+}
+
+static void
+octopus_route_finalize(GObject *object)
+{
+  OctopusRoute *route;
+
+  route = OCTOPUS_ROUTE(object);
+
   g_hash_table_destroy(route->priv->endpoints);
 }