[vlc-devel] [PATCH] Open Sound Control support using liblo

Rémi Denis-Courmont rem at videolan.org
Sun Apr 12 19:40:44 CEST 2009


	Hello,

Overall, the patch looks quite good, but I still a few minor coments:

diff --git a/configure.ac b/configure.ac
index 85f314c..4b7e800 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1013,6 +1013,21 @@ fi
 ;;
 esac
 
+dnl
+dnl liblo based Open Sound Control plugin
+dnl
+AC_ARG_ENABLE(osc,
+  [  --enable-osc            Open Sound Control plugin (default 
disabled)])
+if test "${enable_osc}" = "yes"; then
+  PKG_CHECK_MODULES(LIBLO, liblo >= 0.26,
+    [AC_DEFINE(HAVE_LIBLO, 1, [Define if you have the liblo library])

Why is this AC_DEFINE? You don't seem to use HAVE_LIBIO anywhere, so 
don't bother (with) <config.h>.

+     VLC_ADD_PLUGIN([osc])
+     VLC_ADD_LIBS([osc],[$LIBLO_LIBS])
+     VLC_ADD_CFLAGS([osc],[$LIBLO_CFLAGS])],
+    [AC_MSG_WARN(liblo library not found)])

If --enable-osc was explicitly specified but libio is not found, you 
should probably fail hard.

+fi
+
+
 dnl Build the gtk_main plugins?
 NEED_GTK_MAIN=no
 NEED_GNOME_MAIN=no
diff --git a/modules/control/Modules.am b/modules/control/Modules.am
index 61d2988..e88ff40 100644
--- a/modules/control/Modules.am
+++ b/modules/control/Modules.am
@@ -6,6 +6,7 @@ SOURCES_netsync = netsync.c
 SOURCES_ntservice = ntservice.c
 SOURCES_hotkeys = hotkeys.c
 SOURCES_lirc = lirc.c
+SOURCES_osc = osc.c
 SOURCES_rc = rc.c
 SOURCES_dbus = dbus.c dbus.h
 SOURCES_signals = signals.c
diff --git a/modules/control/osc.c b/modules/control/osc.c
new file mode 100644
index 0000000..524999d
--- /dev/null
+++ b/modules/control/osc.c
@@ -0,0 +1,281 @@
+/*****************************************************************************
+ * osc.c : Open Sound Control module for vlc
+ 
*****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team

I guess 2008-2009 ?

+ * $Id$
+ *
+ * Author: Nicholas J Humfrey <njh at aelius.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 
02110-1301, USA.
+ 
*****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ 
*****************************************************************************/
+
+#include <fcntl.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif

Generally, I recommend including fcntl.h _after_ config.h, so you get 
defines that may affect system headers (e.g. _GNU_SOURCE).

+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_interface.h>
+#include <vlc_playlist.h>
+
+#include <lo/lo.h>
+
+
+#define OSC_PORT_TEXT N_( "Port Number to listen for OSC messages." )
+#define OSC_PORT_LONGTEXT N_( \
+    "Set the port number that VLC should listen for OSC messages on. " 
\
+    "By default it chooses a random port number." )
+
+/* Thread-local variable to store object running the current thread */
+static vlc_threadvar_t thread_object_key;
+static int instance_count = 0;

unsigned.

+static vlc_mutex_t instance_count_lock = VLC_STATIC_MUTEX;
+
+
+/*****************************************************************************
+ * Local prototypes
+ 
*****************************************************************************/
+static int  Open    ( vlc_object_t * );
+static void Close   ( vlc_object_t * );
+static void Run     ( intf_thread_t * );
+
+static void ErrorHandler( int num, const char *msg, const char 
*where );
+static int DeckHandler( const char *path, const char *types, lo_arg 
**argv,
+    int argc, lo_message msg, void *user_data );
+static int WildcardHandler( const char *path, const char *types, lo_arg 
**argv,
+    int argc, lo_message msg, void *user_data );
+
+/*****************************************************************************
+ * Module descriptor
+ 
*****************************************************************************/
+vlc_module_begin ()
+    set_shortname( N_("OSC") )
+    set_category( CAT_INTERFACE )
+    set_subcategory( SUBCAT_INTERFACE_CONTROL )
+    set_description( N_("Open Sound Control interface") )
+    set_capability( "interface", 0 )
+    set_callbacks( Open, Close )
+
+    add_string( "osc-port", NULL, NULL,
+                OSC_PORT_TEXT, OSC_PORT_LONGTEXT, false )
+vlc_module_end ()
+
+/*****************************************************************************
+ * intf_sys_t: description and status of interface
+ 
*****************************************************************************/
+struct intf_sys_t
+{
+    lo_server *p_server;
+};
+
+/*****************************************************************************
+ * ErrorHandler: callback for errors reported by liblo
+ 
*****************************************************************************/
+static void ErrorHandler( int num, const char *msg, const char *where )
+{
+    vlc_object_t *p_this = 
(vlc_object_t*)vlc_threadvar_get(thread_object_key);
+    msg_Err( p_this, "liblo error %d: '%s' at '%s'", num, msg, where );
+}
+
+/*****************************************************************************
+ * DeckHandler: callback for /deck/ * messages
+ 
*****************************************************************************/
+static int DeckHandler( const char *path, const char *types, lo_arg 
**argv,
+    int argc, lo_message msg, void *user_data )
+{
+    vlc_object_t *p_this = (vlc_object_t*)user_data;
+    char* psz_from = lo_address_get_url( lo_message_get_source(msg) );
+    playlist_t *p_playlist = NULL;

Assignment is not used.

+    int i_ret = 0;
+
+    msg_Info( p_this, "Recieved '%s' from '%s'.", path, psz_from );
+    free(psz_from);
+
+    p_playlist = pl_Hold( (vlc_object_t*) p_this );

Casting should not be needed here.

+    if ( !strcmp( path, "/deck/play" ) )
+    {
+        if (playlist_Status(p_playlist) == PLAYLIST_STOPPED) {
+            playlist_item_t *p_root = p_playlist->p_root_onelevel;
+            playlist_item_t *first_item = playlist_GetNextLeaf(
+                p_playlist, p_root, NULL, false, false );
+            if (first_item != NULL)
+            {
+                msg_Dbg( p_this, "Playing item id %d", 
first_item->i_id );
+                playlist_Control( p_playlist, PLAYLIST_VIEWPLAY,
+                                  pl_Unlocked, NULL, first_item );
+            } else {
+                msg_Err( p_this, "Failed to find item on playlist to 
play." );
+            }
+        } else {
+            msg_Dbg( p_this, "Resuming playback." );
+            playlist_Play( p_playlist );
+        }
+    }
+    else if ( !strcmp( path, "/deck/pause" ) )
+    {
+        playlist_Pause( p_playlist );
+    }
+    else if ( !strcmp( path, "/deck/stop" ) )
+    {
+        playlist_Stop( p_playlist );
+    }
+    else if ( !strcmp( path, "/deck/eject" ) )
+    {
+        playlist_Stop( p_playlist );
+        playlist_Clear( p_playlist, pl_Unlocked );
+    }
+    else if ( !strcmp( path, "/deck/load" ) )
+    {
+        playlist_Stop( p_playlist );
+        /* Add the item to the start of the playlist */
+        playlist_Add( p_playlist, &argv[0]->s, NULL, PLAYLIST_INSERT,
+                0, true, pl_Unlocked );
+    }
+    else
+    {
+        /* We shouldn't get here, should only get paths we added 
methods for */
+        msg_Err( p_this, "unhandled OSC message by DeckHandler: '%s'", 
path );
+        i_ret = 1;
+    }
+    pl_Release( (vlc_object_t*) p_this );

Again, you shouldn't need to cast here.

+
+    return i_ret;
+}
+
+/*****************************************************************************
+ * WildcardHandler: callback for any unmatched messages
+ 
*****************************************************************************/
+static int WildcardHandler( const char *path, const char *types, lo_arg 
**argv,
+    int argc, lo_message msg, void *user_data )
+{
+    vlc_object_t *p_this = (vlc_object_t*)user_data;
+    char* psz_from = lo_address_get_url( lo_message_get_source(msg) );
+
+    msg_Info( p_this, "unhandled OSC message: '%s' with args '%s' 
from '%s'.\n",
+        path, types, psz_from );
+    free(psz_from);
+    return 1;
+}
+
+/*****************************************************************************
+ * Open: initialize interface
+ 
*****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+    intf_thread_t *p_intf = (intf_thread_t *)p_this;
+    intf_sys_t *p_sys;
+    char *psz_port = NULL;
+    char *psz_url = NULL;
+
+    /* Allocate instance and initialize some members */
+    p_intf->p_sys = p_sys = malloc( sizeof( intf_sys_t ) );
+    if( p_sys == NULL )
+        return VLC_ENOMEM;
+
+    p_intf->pf_run = Run;
+
+    /* Get the port number parameter */
+    psz_port = var_CreateGetString( p_intf, "osc-port" );
+    if ( psz_port && *psz_port == '\0' )
+    {
+        psz_port = NULL;
+    }
+
+    /* Store pointer to self in a thread-local variable
+       (this is a work-around for the ErrorHandler callback) */
+    vlc_mutex_lock( &instance_count_lock );
+    if( instance_count++ == 0 )
+        vlc_threadvar_create( &thread_object_key, NULL );
+    vlc_mutex_unlock( &instance_count_lock );
+    vlc_threadvar_set( thread_object_key, p_this );
+
+    /* Create the liblo server structure */
+    msg_Dbg( p_this, "creating new lo_server(port=%s)", psz_port );
+    p_sys->p_server = lo_server_new( psz_port, ErrorHandler );
+    if( p_sys->p_server == NULL )
+    {
+        msg_Err( p_intf, "liblo initialisation failed" );
+        Close( p_this );
+        return VLC_EGENERIC;
+    }
+
+    /* Add callback handers to the server */
+    lo_server_add_method( p_sys->p_server, "/deck/play", NULL,
+        DeckHandler, p_intf );
+    lo_server_add_method( p_sys->p_server, "/deck/pause", NULL,
+        DeckHandler, p_intf );
+    lo_server_add_method( p_sys->p_server, "/deck/stop", NULL,
+        DeckHandler, p_intf );
+    lo_server_add_method( p_sys->p_server, "/deck/eject", NULL,
+        DeckHandler, p_intf );
+    lo_server_add_method( p_sys->p_server, "/deck/load", "s",
+        DeckHandler, p_intf );
+
+    /* add method that will match any path and args */
+    lo_server_add_method( p_sys->p_server, NULL, NULL,
+        WildcardHandler, p_intf );
+
+    /* Display our URL */
+    psz_url = lo_server_get_url( p_sys->p_server );
+    msg_Info( p_intf, "Listening for OSC messages on: %s", psz_url );
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close: destroy interface
+ 
*****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+    intf_thread_t *p_intf = (intf_thread_t *)p_this;
+    intf_sys_t *p_sys = p_intf->p_sys;
+
+    /* Destroy structure */
+    if (p_sys->p_server) lo_server_free( p_sys->p_server );
+    free( p_sys );
+
+    /* Delete thread-local variable, if we are the last instance */
+    vlc_mutex_lock( &instance_count_lock );
+    if( --instance_count == 0 )
+        vlc_threadvar_delete( &thread_object_key );
+    vlc_mutex_unlock( &instance_count_lock );
+}
+
+/*****************************************************************************
+ * Run: main loop
+ 
*****************************************************************************/
+static void Run( intf_thread_t *p_intf )
+{
+    intf_sys_t *p_sys = p_intf->p_sys;
+    int i_cancel, i_msg_available;

Is it so that ErrorHandler cannot be called from this thread?
I would have expected a vlc_threadvar_set() call here.

+
+    for( ;; )
+    {
+        /* Wait for a message / check if scheduled message needs 
processing */
+        i_msg_available = lo_server_wait( p_sys->p_server, 100 );
+        if (i_msg_available<=0) continue;

Is 100 some kind of timeout value? If so, why can you not use an 
infinite (or really long timeout)?

+
+        /* Recieve the message: hard work is perfomed in the callbacks 
*/
+        i_cancel = vlc_savecancel();
+        lo_server_recv( p_sys->p_server );
+        vlc_restorecancel( i_cancel );
+    }
+}


Le dimanche 12 avril 2009, Nicholas J Humfrey a écrit :
> Is there anything left stopping the patch being integrated into the
> master branch?

Convincing the release manager (not me!) that this can violate the 
feature freeze, or wait for the VLC 1.1 defrost.

Thanks and best regards,

-- 
Rémi Denis-Courmont
http://www.remlab.net/



More information about the vlc-devel mailing list