[vlc-commits] libvlc: add functions to register logging callbacks

Rémi Denis-Courmont git at videolan.org
Wed Apr 18 23:13:41 CEST 2012


vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Thu Apr 19 00:02:34 2012 +0300| [f7260e64ce8fd77a5f8a2be20322358c465de22e] | committer: Rémi Denis-Courmont

libvlc: add functions to register logging callbacks

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=f7260e64ce8fd77a5f8a2be20322358c465de22e
---

 include/vlc/libvlc.h  |   76 ++++++++++++++++++++++++++++++++++++++++++-
 lib/error.c           |    6 +++
 lib/libvlc.sym        |    2 +
 lib/libvlc_internal.h |    2 +
 lib/log.c             |   87 +++++++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 170 insertions(+), 3 deletions(-)

diff --git a/include/vlc/libvlc.h b/include/vlc/libvlc.h
index f7f4530..2a005ff 100644
--- a/include/vlc/libvlc.h
+++ b/include/vlc/libvlc.h
@@ -307,11 +307,85 @@ LIBVLC_API const char * libvlc_event_type_name( libvlc_event_type_t event_type )
 
 /** \defgroup libvlc_log LibVLC logging
  * libvlc_log_* functions provide access to the LibVLC messages log.
- * This is used for debugging or by advanced users.
+ * This is used for logging and debugging.
  * @{
  */
 
 /**
+ * Logging messages level.
+ * \note Future LibVLC versions may define new levels.
+ */
+enum libvlc_log_level
+{
+    LIBVLC_DEBUG=0 /**< Debug message */,
+    LIBVLC_NOTICE=2 /**< Important informational message */,
+    LIBVLC_WARNING=3 /**< Warning (potential error) message */,
+    LIBVLC_ERROR=4 /**< Error message */,
+};
+
+/**
+ * Callback prototype for LibVLC log message handler.
+ * \param data data pointer as given to libvlc_log_subscribe()
+ * \param level message level (@ref enum libvlc_log_level)
+ * \param fmt printf() format string (as defined by ISO C11)
+ * \param args variable argument list for the format
+ * \note Log message handlers <b>must</b> be thread-safe.
+ */
+typedef void (*libvlc_log_cb)(void *data, int level, const char *fmt,
+                              va_list args);
+
+/**
+ * Data structure for a LibVLC logging callbacks.
+ * \note This structure contains exactly 4 pointers and will never change.
+ * Nevertheless, it should not be accessed directly outside of LibVLC.
+ * (In fact, doing so would fail the thread memory model.)
+ */
+typedef struct libvlc_log_subscriber
+{
+    struct libvlc_log_subscriber *prev, *next;
+    libvlc_log_cb func;
+    void *opaque;
+} libvlc_log_subscriber_t;
+
+/**
+ * Registers a logging callback to LibVLC.
+ * This function is thread-safe.
+ *
+ * \param sub uninitialized subscriber structure
+ * \param cb callback function pointer
+ * \param data opaque data pointer for the callback function
+ *
+ * \note Some log messages (especially debug) are emitted by LibVLC while
+ * initializing, before any LibVLC instance even exists.
+ * Thus this function does not require a LibVLC instance parameter.
+ *
+ * \warning As a consequence of not depending on a LibVLC instance,
+ * all logging callbacks are shared by all LibVLC instances within the
+ * process / address space. This also enables log messages to be emitted
+ * by LibVLC components that are not specific to any given LibVLC instance.
+ *
+ * \warning Do not call this function from within a logging callback.
+ * It would trigger a dead lock.
+ */
+LIBVLC_API void libvlc_log_subscribe( libvlc_log_subscriber_t *sub,
+                                      libvlc_log_cb cb, void *data );
+
+/**
+ * Deregisters a logging callback from LibVLC.
+ * This function is thread-safe.
+ *
+ * \note After (and only after) libvlc_log_unsubscribe() has returned,
+ * LibVLC warrants that there are no more pending calls of the subscription
+ * callback function.
+ *
+ * \warning Do not call this function from within a logging callback.
+ * It would trigger a dead lock.
+ *
+ * \param sub initialized subscriber structure
+ */
+LIBVLC_API void libvlc_log_unsubscribe( libvlc_log_subscriber_t *sub );
+
+/**
  * Always returns minus one.
  * This function is only provided for backward compatibility.
  *
diff --git a/lib/error.c b/lib/error.c
index f942f4d..d053faa 100644
--- a/lib/error.c
+++ b/lib/error.c
@@ -37,7 +37,10 @@ void libvlc_threads_init (void)
 {
     vlc_mutex_lock (&lock);
     if (refs++ == 0)
+    {
         vlc_threadvar_create (&context, free);
+        libvlc_log_init ();
+    }
     vlc_mutex_unlock (&lock);
 }
 
@@ -46,7 +49,10 @@ void libvlc_threads_deinit (void)
     vlc_mutex_lock (&lock);
     assert (refs > 0);
     if (--refs == 0)
+    {
+        libvlc_log_deinit ();
         vlc_threadvar_delete (&context);
+    }
     vlc_mutex_unlock (&lock);
 }
 
diff --git a/lib/libvlc.sym b/lib/libvlc.sym
index 45327d5..974e04f 100644
--- a/lib/libvlc.sym
+++ b/lib/libvlc.sym
@@ -43,6 +43,8 @@ libvlc_get_fullscreen
 libvlc_get_input_thread
 libvlc_get_log_verbosity
 libvlc_get_version
+libvlc_log_subscribe
+libvlc_log_unsubscribe
 libvlc_log_clear
 libvlc_log_close
 libvlc_log_count
diff --git a/lib/libvlc_internal.h b/lib/libvlc_internal.h
index 27c1dc5..790ae1c 100644
--- a/lib/libvlc_internal.h
+++ b/lib/libvlc_internal.h
@@ -82,6 +82,8 @@ struct libvlc_instance_t
 /* Thread context */
 void libvlc_threads_init (void);
 void libvlc_threads_deinit (void);
+void libvlc_log_init (void);
+void libvlc_log_deinit (void);
 
 /* Events */
 libvlc_event_manager_t * libvlc_event_manager_new(
diff --git a/lib/log.c b/lib/log.c
index 77162c0..83f66d2 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -26,9 +26,92 @@
 # include "config.h"
 #endif
 
-#include "libvlc_internal.h"
-#include <vlc/libvlc.h>
 #include <assert.h>
+#include <vlc/libvlc.h>
+#include "libvlc_internal.h"
+#include <vlc_common.h>
+#include <vlc_interface.h>
+
+/*** Logging core dispatcher ***/
+
+static vlc_rwlock_t log_lock = VLC_STATIC_RWLOCK;
+static libvlc_log_subscriber_t *log_first = NULL;
+static msg_subscription_t sub;
+
+VLC_FORMAT(2,3)
+static void libvlc_log (int level, const char *fmt, ...)
+{
+    libvlc_log_subscriber_t *sub;
+    va_list ap;
+
+    switch (level)
+    {
+        case VLC_MSG_INFO: level = LIBVLC_NOTICE;  break;
+        case VLC_MSG_ERR:  level = LIBVLC_ERROR;   break;
+        case VLC_MSG_WARN: level = LIBVLC_WARNING; break;
+        case VLC_MSG_DBG:  level = LIBVLC_DEBUG;   break;
+    }
+
+    va_start (ap, fmt);
+    vlc_rwlock_rdlock (&log_lock);
+    for (sub = log_first; sub != NULL; sub = sub->next)
+        sub->func (sub->opaque, level, fmt, ap);
+    vlc_rwlock_unlock (&log_lock);
+    va_end (ap);
+}
+
+static void libvlc_logf (void *dummy, int level, const msg_item_t *item,
+                         const char *fmt, va_list ap)
+{
+    char *msg;
+
+    if (unlikely(vasprintf (&msg, fmt, ap) == -1))
+        msg = NULL;
+    if (item->psz_header != NULL)
+        libvlc_log (level, "[%p] [%s]: %s %s %s", (void *)item->i_object_id,
+                    item->psz_header, item->psz_module, item->psz_object_type,
+                    msg ? msg : "Not enough memory");
+    else
+        libvlc_log (level, "[%p]: %s %s %s", (void *)item->i_object_id,
+                    item->psz_module, item->psz_object_type,
+                    msg ? msg : "Not enough memory");
+    free (msg);
+    (void) dummy;
+}
+
+void libvlc_log_init (void)
+{
+    vlc_Subscribe (&sub, libvlc_logf, NULL);
+}
+
+void libvlc_log_deinit (void)
+{
+    vlc_Unsubscribe (&sub);
+}
+
+void libvlc_log_subscribe (libvlc_log_subscriber_t *sub,
+                           libvlc_log_cb cb, void *data)
+{
+    sub->prev = NULL;
+    sub->func = cb;
+    sub->opaque = data;
+    vlc_rwlock_wrlock (&log_lock);
+    sub->next = log_first;
+    log_first = sub;
+    vlc_rwlock_unlock (&log_lock);
+}
+
+void libvlc_log_unsubscribe( libvlc_log_subscriber_t *sub )
+{
+    vlc_rwlock_wrlock (&log_lock);
+    if (sub->next != NULL)
+        sub->next->prev = sub->prev;
+    if (sub->prev != NULL)
+        sub->prev->next = sub->next;
+    else
+        log_first = sub->next;
+    vlc_rwlock_unlock (&log_lock);
+}
 
 unsigned libvlc_get_log_verbosity( const libvlc_instance_t *p_instance )
 {



More information about the vlc-commits mailing list