[vlc-commits] [Git][videolan/vlc][master] 23 commits: lua: extension: fix leak on exit

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sat Feb 11 21:31:24 UTC 2023



Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
c7d4e5f9 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: fix leak on exit

The extension were leaking the CMD_DEACTIVATE on close and not waiting
for executing it before closing. There were also issues when closing an
already closing extension, and then by extension opening a closing
extension.

This commit better tracks the state to handle those intertwined cases
while preventing deadlocks.

- - - - -
d82c134a by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
test: lua: extension: check activate/deactivate

Check that the activate and deactivate functions are correctly called
from the extension. Note that in this test, the code will wait for the
CMD_DEACTIVATE command to be executed before continuing towards
releasing the extensions_manager and all its extensions. It means that
it doesn't check the path where the extensions_manager is released
**before** the CMD_DEACTIVATE command is executed, which is more likely
to happen in the current UI scenario than the first.

- - - - -
13c3136b by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: fix memory leak on exit

The lua interrupt will get cleaned when reaching Close_Extension through

    if (sys->L)
    {
        lua_close(sys->L);
        vlclua_fd_cleanup(&sys->dtable);
    }

However, the call to lua_ExtensionDeactivate when reaching a
CMD_DEACTIVATE will also close the lua state and reset the sys->L to
NULL, preventing the previous snippet from cleaning the interrupt and
resulting in memory leaks.

    Direct leak of 120 byte(s) in 3 object(s) allocated from:
        #0 0x7f2a27cbfa89 in __interceptor_malloc /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:69
        #1 0x7f2a2725f870 in vlc_interrupt_create ../../src/misc/interrupt.c:61
        #2 0x7f29eea1d997 in vlclua_fd_init ../../modules/lua/libs/net.c:503
        #3 0x7f29ee9e9d6e in GetLuaState ../../modules/lua/extension.c:805
        #4 0x7f29ee9ec360 in lua_ExecuteFunctionVa ../../modules/lua/extension.c:896
        #5 0x7f29ee9eca25 in lua_ExecuteFunction ../../modules/lua/extension.c:874
        #6 0x7f29ee9f234d in Run ../../modules/lua/extension_thread.c:296
        #7 0x7f2a2609ebb4  (/usr/lib/libc.so.6+0x85bb4)

If we try to also clean the interrupt from there, we might also reach
use-after-free because of races between the extension thread and the
client thread.

    ==457157==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000069661 at pc 0x7f3894c5f648 bp 0x7ffc0f0e3bc0 sp 0x7ffc0f0e3bb0
    WRITE of size 1 at 0x604000069661 thread T0
        #0 0x7f3894c5f647 in vlc_interrupt_kill ../../src/misc/interrupt.c:181
        #1 0x7f3891329c40 in Close_Extension ../../modules/lua/extension.c:155
        #2 0x7f389497d22e in module_unneed ../../src/modules/modules.c:305
        #3 0x55b734884085 in OpenIntf ../../test/modules/lua/extension.c:95
        #4 0x7f389497b12e in generic_start ../../src/modules/modules.c:275
        #5 0x7f389497ccb5 in vlc_module_load ../../src/modules/modules.c:243
        #6 0x7f389497d073 in module_need ../../src/modules/modules.c:286
        #7 0x7f389499bd1d in intf_Create ../../src/interface/interface.c:172
        #8 0x7f389499c9aa in libvlc_InternalAddIntf ../../src/interface/interface.c:267
        #9 0x7f3895550b22 in libvlc_add_intf ../../lib/playlist.c:41
        #10 0x55b73488358c in main ../../test/modules/lua/extension.c:127
        #11 0x7f3893a3c78f  (/usr/lib/libc.so.6+0x2378f)
        #12 0x7f3893a3c849 in __libc_start_main (/usr/lib/libc.so.6+0x23849)
        #13 0x55b7348836e4 in _start (/home/janniaux/Projects/videolabs/vlc/build-asan/test/test_modules_lua_extension+0x36e4)

    0x604000069661 is located 17 bytes inside of 40-byte region [0x604000069650,0x604000069678)
    freed by thread T8 (vlc-lua-ext) here:
        #0 0x7f38956be672 in __interceptor_free /usr/src/debug/gcc/gcc/libsanitizer/asan/asan_malloc_linux.cpp:52
        #1 0x7f389132cd5d in lua_ExtensionDeactivate ../../modules/lua/extension.c:642
        #2 0x7f389132ec83 in Run ../../modules/lua/extension_thread.c:331
        #3 0x7f3893a9ebb4  (/usr/lib/libc.so.6+0x85bb4)

Removing the closing of sys->L in lua_ExtensionDeactivate and leaving it
after the vlc_join() in Close_Extension ensure it will get called at
some point and called only once.

- - - - -
24972a44 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: add ext parameter

Every controls are modifying an extension and require the extension to
be non-null as first parameter. Instead of forwarding the extension to
the va_list, extract it as first parameters unconditionnally.

This prepares some work to replace the pf_control callback by dedicated
operations and start reworking some code style along the way.

- - - - -
76ad32e9 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: use intermediate struct for sys

This prepares the change to a void* pointer for the sys of lua
extensions, and rework some code style along the way for the modified
lines.

- - - - -
e2c59b97 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
vlc_extensions: voidify the sys type

extension_sys_t is a type that actually depends on the implementation
and it shouldn't be named uniformly to avoid ODR in C++ code. It also
allows naming the internal structures with a type that can be retrieved
from a debugger instead of being potentially mistaken by another type.

Extends commit 79afb971ff3c1eada2d1217e2a1015cc56a321e4, ref #17078

- - - - -
cae35be1 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: rename sys type to lua_extension

The private sys type was extension_sys_t because that was the type
required by the structure. Now that it's a void pointer, we can rename
it to a more suitable name that can be found from a debugguer and
represent what the private implementation provides.

Refs #18033

- - - - -
fbe743a8 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: remove indentation level

There is a superfluous single nested if in the nesting else. The commit
also changes the code style to match new code for the lines that are
deindented.

- - - - -
ab52f3de by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
vlc_extensions: add vlc_logger

We'll try to use vlc_logger instead of object message whenever the
extension manager is required for that. Using a logger removes the
dependency to vlc_object_t and its hierarchy and is simpler to use.

- - - - -
d9d2f43b by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: provide logger through the manager

For now, we don't have any other reason to provide a different logger
than the extension one, especially since we mostly want to convert the
use cases.

- - - - -
3539ee15 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: use vlc_logger in Activate

Remove the dependency to the extensions_manager_t object for Activate
since it only uses it for logging.

- - - - -
19496999 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: use vlc_logger in KillExtension

Remove the dependency to the extensions_manager_t object for
KillExtension since it only uses it for logging.

- - - - -
dedcad3e by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: use vlc_logger in Run function

- - - - -
a7c1be79 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: check that command_lock is used

The function QueueDeactivateCommand requires it and even use
vlc_cond_signal, so it's essential that the modified state has been set
under the lock to ensure there's no race.

- - - - -
2668c399 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: rework thread start

We need the thread to be started to handle the callbacks to the player
listener, but we'll need to add the player_listener right after the
thread start, so rework early exit to handle that. In the mean time,
signal the thread stop state when joining.

- - - - -
8bb7a418 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
vlc_extensions: add player reference

The player will be used by the extensions to listen to events, like the
media changed for input-listener capabilities or meta changed for
meta-listener capabilities.

In the previous design for the vlc_extensions, the whole extension
system was setup by the UI, and since the UI were subscribing to
playlist events signalling that the input item changed, the UI was
forwarding those calls to the extension subsystem. However, there is now
the unified interface to control or listen to the playback, ie. the
vlc_player, which can better fit this goal.

It allows to split this need from the question of where the
vlc_extensions are initialized.

- - - - -
0016518b by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
qt: extensions: supply player to extensions

Previous commit added the requirement on the extensions manager creation
to supply the vlc_player_t instance that will be used by the extension
to listen to events.

This commit fullfill this requirement for the Qt interface.

- - - - -
d40d6c5b by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
macosx: VLCExtensionsManager: supply player to extensions


Previous commit added the requirement on the extensions manager creation
to supply the vlc_player_t instance that will be used by the extension
to listen to events.

This commit fullfill this requirement for the MacOS interface.

- - - - -
56e8c83b by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
test: lua: supply vlc_player to the extension manager

The vlc_player instance will become mandatory in following commits since
it will be used to bind the input / meta events for the different
extensions requiring it. Since it's not used yet, we can provide it in
the test right now, and really make use of it later.

- - - - -
970c9676 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
modules: lua: move extensions flags to header

The extension flags will also be used from extension_thread.

- - - - -
4b525798 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: listen to player

Use a player listener per extension to signal input changes instead of
relying on the UI to supply any new input change. This is using one
listener per extension so that we can remove it when deleting the
extension and be signalled of the first input when attaching the
listener the first time.

Fixes #22585

- - - - -
43196929 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
lua: extension: remove previous input code

The previous input code was using the legacy input events, whereas we
can now use the vlc_player listener to notify each extension directly.

- - - - -
3958d2c9 by Alexandre Janniaux at 2023-02-11T20:32:36+00:00
test: lua: extension: check input capabilities

Check that the input-capability is correctly handled and will trigger
the required function from the lua extension engine.

- - - - -


8 changed files:

- include/vlc_extensions.h
- modules/gui/macosx/windows/extensions/VLCExtensionsManager.m
- modules/gui/qt/dialogs/extensions/extensions_manager.cpp
- modules/lua/extension.c
- modules/lua/extension.h
- modules/lua/extension_thread.c
- test/modules/lua/extension.c
- test/modules/lua/extensions/extensions.lua


Changes:

=====================================
include/vlc_extensions.h
=====================================
@@ -29,10 +29,18 @@
 /* Structures */
 typedef struct extensions_manager_sys_t extensions_manager_sys_t;
 typedef struct extensions_manager_t extensions_manager_t;
-typedef struct extension_sys_t extension_sys_t;
+struct vlc_player_t;
+struct vlc_logger;
 
 /** Extension descriptor: name, title, author, ... */
 typedef struct extension_t {
+    void *p_sys;              /**< Reserved for the manager module */
+
+    /**
+     * The LibVLC logger to use for the extension.
+     */
+    struct vlc_logger *logger;
+
     /* Below, (ro) means read-only for the GUI */
     char *psz_name;           /**< Real name of the extension (ro) */
 
@@ -44,8 +52,6 @@ typedef struct extension_t {
     char *psz_shortdescription; /**< Short description (eg. 1 line)  (ro) */
     char *p_icondata;         /**< Embedded data for the icon (ro) */
     int   i_icondata_size;    /**< Size of that data */
-
-    extension_sys_t *p_sys;   /**< Reserved for the manager module */
 } extension_t;
 
 /** Extensions manager object */
@@ -55,12 +61,13 @@ struct extensions_manager_t
 
     module_t *p_module;                /**< Extensions manager module */
     void *p_sys;              /**< Reserved for the module */
+    struct vlc_player_t *player;
 
     DECL_ARRAY(extension_t*) extensions; /**< Array of extension descriptors */
     vlc_mutex_t lock;                  /**< A lock for the extensions array */
 
     /** Control, see extension_Control */
-    int ( *pf_control ) ( extensions_manager_t*, int, va_list );
+    int (*pf_control)(extensions_manager_t*, int, extension_t *, va_list);
 };
 
 /* Control commands */
@@ -85,11 +92,12 @@ enum
  * Every GUI -> extension command will go through this function.
  **/
 static inline int extension_Control( extensions_manager_t *p_mgr,
-                                     int i_control, ... )
+                                     int i_control,
+                                     extension_t *ext, ... )
 {
     va_list args;
-    va_start( args, i_control );
-    int i_ret = p_mgr->pf_control( p_mgr, i_control, args );
+    va_start(args, ext);
+    int i_ret = p_mgr->pf_control(p_mgr, i_control, ext, args);
     va_end( args );
     return i_ret;
 }


=====================================
modules/gui/macosx/windows/extensions/VLCExtensionsManager.m
=====================================
@@ -27,6 +27,7 @@
 
 #import <vlc_modules.h>
 #import <vlc_extensions.h>
+#import <vlc_playlist.h>
 
 #import "main/VLCMain.h"
 #import "extensions/NSString+Helpers.h"
@@ -162,6 +163,8 @@
             return false;
         }
 
+        vlc_playlist_t *playlist = vlc_intf_GetMainPlaylist(p_intf);
+        p_extensions_manager->player = vlc_playlist_GetPlayer(playlist);
         p_extensions_manager->p_module = module_need(p_extensions_manager, "extension", NULL, false);
 
         if (!p_extensions_manager->p_module) {


=====================================
modules/gui/qt/dialogs/extensions/extensions_manager.cpp
=====================================
@@ -25,6 +25,8 @@
 #include "extensions.hpp"
 
 #include <vlc_modules.h>
+#include <vlc_interface.h>
+#include <vlc_playlist.h>
 #include "assert.h"
 
 #include <QMenu>
@@ -72,6 +74,8 @@ bool ExtensionsManager::loadExtensions()
             return false;
         }
 
+        vlc_playlist_t *playlist = vlc_intf_GetMainPlaylist(p_intf->intf);
+        p_extensions_manager->player = vlc_playlist_GetPlayer(playlist);
         p_extensions_manager->p_module =
                 module_need( p_extensions_manager, "extension", NULL, false );
 


=====================================
modules/lua/extension.c
=====================================
@@ -37,6 +37,7 @@
 #include <vlc_interface.h>
 #include <vlc_events.h>
 #include <vlc_dialog.h>
+#include <vlc_player.h>
 
 /* Functions to register */
 static const luaL_Reg p_reg[] =
@@ -49,22 +50,17 @@ static const luaL_Reg p_reg[] =
  * Note: #define and ppsz_capabilities must be in sync
  */
 static const char caps[][20] = {
-#define EXT_HAS_MENU          (1 << 0)   ///< Hook: menu
     "menu",
-#define EXT_TRIGGER_ONLY      (1 << 1)   ///< Hook: trigger. Not activable
     "trigger",
-#define EXT_INPUT_LISTENER    (1 << 2)   ///< Hook: input_changed
     "input-listener",
-#define EXT_META_LISTENER     (1 << 3)   ///< Hook: meta_changed
     "meta-listener",
-#define EXT_PLAYING_LISTENER  (1 << 4)   ///< Hook: status_changed
     "playing-listener",
 };
 
 static int ScanExtensions( extensions_manager_t *p_this );
 static int ScanLuaCallback( vlc_object_t *p_this, const char *psz_script,
                             const struct luabatch_context_t * );
-static int Control( extensions_manager_t *, int, va_list );
+static int Control( extensions_manager_t *, int, extension_t *, va_list );
 static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext,
                     char ***pppsz_titles, uint16_t **ppi_ids );
 static lua_State* GetLuaState( extensions_manager_t *p_mgr,
@@ -84,11 +80,6 @@ static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
                                              vlc_value_t newval,
                                              void *p_data );
 
-/* Input item callback: vlc_InputItemMetaChanged */
-static void inputItemMetaChanged( const vlc_event_t *p_event,
-                                  void *data );
-
-
 /**
  * Module entry-point
  **/
@@ -139,32 +130,34 @@ void Close_Extension( vlc_object_t *p_this )
     {
         if( !p_ext )
             break;
+        struct lua_extension *sys = p_ext->p_sys;
 
-        vlc_mutex_lock( &p_ext->p_sys->command_lock );
-        if( p_ext->p_sys->b_activated == true && p_ext->p_sys->p_progress_id == NULL )
+        vlc_mutex_lock(&sys->command_lock);
+        if (sys->b_activated && sys->p_progress_id == NULL &&
+            !sys->b_deactivating)
         {
-            p_ext->p_sys->b_exiting = true;
             // QueueDeactivateCommand will signal the wait condition.
+            sys->b_exiting = true;
             QueueDeactivateCommand( p_ext );
         }
         else
         {
-            if ( p_ext->p_sys->L != NULL )
-                vlclua_fd_interrupt( &p_ext->p_sys->dtable );
+            if (sys->L != NULL)
+                vlclua_fd_interrupt(&sys->dtable);
             // however here we need to manually signal the wait cond, since no command is queued.
-            p_ext->p_sys->b_exiting = true;
-            vlc_cond_signal( &p_ext->p_sys->wait );
+            sys->b_exiting = true;
+            vlc_cond_signal(&sys->wait);
         }
-        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        vlc_mutex_unlock(&sys->command_lock);
 
-        if( p_ext->p_sys->b_thread_running == true )
-            vlc_join( p_ext->p_sys->thread, NULL );
+        if (sys->b_thread_running)
+            vlc_join(sys->thread, NULL);
 
         /* Clear Lua State */
-        if( p_ext->p_sys->L )
+        if (sys->L)
         {
-            lua_close( p_ext->p_sys->L );
-            vlclua_fd_cleanup( &p_ext->p_sys->dtable );
+            lua_close(sys->L);
+            vlclua_fd_cleanup(&sys->dtable);
         }
 
         free( p_ext->psz_name );
@@ -176,7 +169,7 @@ void Close_Extension( vlc_object_t *p_this )
         free( p_ext->psz_version );
         free( p_ext->p_icondata );
 
-        vlc_timer_destroy( p_ext->p_sys->timer );
+        vlc_timer_destroy(sys->timer);
 
         free( p_ext->p_sys );
         free( p_ext );
@@ -308,30 +301,33 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_filename,
         return 0;
     }
 
+    p_ext->logger = vlc_object_logger(p_mgr);
     p_ext->psz_name = psz_script;
-    p_ext->p_sys = calloc( 1, sizeof( extension_sys_t ) );
-    if( !p_ext->p_sys || !p_ext->psz_name )
+    struct lua_extension *sys
+        = p_ext->p_sys
+        = calloc(1, sizeof(*sys));
+    if (sys == NULL || !p_ext->psz_name)
     {
         free( p_ext->psz_name );
-        free( p_ext->p_sys );
+        free(sys);
         free( p_ext );
         return 0;
     }
-    p_ext->p_sys->p_mgr = p_mgr;
+    sys->p_mgr = p_mgr;
 
     /* Watch timer */
-    if( vlc_timer_create( &p_ext->p_sys->timer, WatchTimerCallback, p_ext ) )
+    if( vlc_timer_create( &sys->timer, WatchTimerCallback, p_ext ) )
     {
         free( p_ext->psz_name );
-        free( p_ext->p_sys );
+        free(sys);
         free( p_ext );
         return 0;
     }
 
     /* Mutexes and conditions */
-    vlc_mutex_init( &p_ext->p_sys->command_lock );
-    vlc_mutex_init( &p_ext->p_sys->running_lock );
-    vlc_cond_init( &p_ext->p_sys->wait );
+    vlc_mutex_init(&sys->command_lock);
+    vlc_mutex_init(&sys->running_lock);
+    vlc_cond_init(&sys->wait);
 
     /* Prepare Lua state */
     lua_State *L = luaL_newstate();
@@ -384,7 +380,7 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_filename,
                         if( !strcmp( caps[i], psz_cap ) )
                         {
                             /* Flag it! */
-                            p_ext->p_sys->i_capabilities |= 1 << i;
+                            sys->i_capabilities |= 1 << i;
                             found = true;
                             break;
                         }
@@ -454,8 +450,8 @@ int ScanLuaCallback( vlc_object_t *p_this, const char *psz_filename,
         goto exit;
     }
 
-    msg_Dbg( p_mgr, "Script %s has the following capability flags: 0x%x",
-             psz_script, p_ext->p_sys->i_capabilities );
+    msg_Dbg(p_mgr, "Script %s has the following capability flags: 0x%x",
+            psz_script, sys->i_capabilities);
 
     b_ok = true;
 exit:
@@ -469,7 +465,7 @@ exit:
         free( p_ext->psz_description );
         free( p_ext->psz_shortdescription );
         free( p_ext->psz_version );
-        free( p_ext->p_sys );
+        free(sys);
         free( p_ext );
     }
     else
@@ -482,9 +478,11 @@ exit:
     return VLC_EGENERIC;
 }
 
-static int Control( extensions_manager_t *p_mgr, int i_control, va_list args )
+static int Control(extensions_manager_t *p_mgr, int i_control,
+                   extension_t *ext, va_list args)
 {
-    extension_t *p_ext = NULL;
+    struct lua_extension *sys = ext->p_sys;
+
     bool *pb = NULL;
     uint16_t **ppus = NULL;
     char ***pppsz = NULL;
@@ -493,118 +491,84 @@ static int Control( extensions_manager_t *p_mgr, int i_control, va_list args )
     switch( i_control )
     {
         case EXTENSION_ACTIVATE:
-            p_ext = va_arg( args, extension_t* );
-            return Activate( p_mgr, p_ext );
+            return Activate(ext);
 
         case EXTENSION_DEACTIVATE:
-            p_ext = va_arg( args, extension_t* );
-            return Deactivate( p_mgr, p_ext );
+            return Deactivate(p_mgr, ext);
 
         case EXTENSION_IS_ACTIVATED:
-            p_ext = va_arg( args, extension_t* );
             pb = va_arg( args, bool* );
-            vlc_mutex_lock( &p_ext->p_sys->command_lock );
-            *pb = p_ext->p_sys->b_activated;
-            vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+            vlc_mutex_lock(&sys->command_lock);
+            *pb = sys->b_activated;
+            vlc_mutex_unlock(&sys->command_lock);
             break;
 
         case EXTENSION_HAS_MENU:
-            p_ext = va_arg( args, extension_t* );
             pb = va_arg( args, bool* );
-            *pb = ( p_ext->p_sys->i_capabilities & EXT_HAS_MENU ) ? 1 : 0;
+            *pb = (sys->i_capabilities & EXT_HAS_MENU) ? 1 : 0;
             break;
 
         case EXTENSION_GET_MENU:
-            p_ext = va_arg( args, extension_t* );
             pppsz = va_arg( args, char*** );
             ppus = va_arg( args, uint16_t** );
-            if( p_ext == NULL )
-                return VLC_EGENERIC;
-            return GetMenuEntries( p_mgr, p_ext, pppsz, ppus );
+            return GetMenuEntries(p_mgr, ext, pppsz, ppus);
 
         case EXTENSION_TRIGGER_ONLY:
-            p_ext = va_arg( args, extension_t* );
             pb = va_arg( args, bool* );
-            *pb = ( p_ext->p_sys->i_capabilities & EXT_TRIGGER_ONLY ) ? 1 : 0;
+            *pb = (sys->i_capabilities & EXT_TRIGGER_ONLY) ? 1 : 0;
             break;
 
         case EXTENSION_TRIGGER:
-            p_ext = va_arg( args, extension_t* );
-            return TriggerExtension( p_mgr, p_ext );
+            return TriggerExtension(p_mgr, ext);
 
         case EXTENSION_TRIGGER_MENU:
-            p_ext = va_arg( args, extension_t* );
             i = va_arg( args, int );
-            return TriggerMenu( p_ext, i );
+            return TriggerMenu(ext, i);
 
         case EXTENSION_SET_INPUT:
         {
-            p_ext = va_arg( args, extension_t* );
             input_item_t *p_item = va_arg( args, struct input_item_t * );
 
-            if( p_ext == NULL )
-                return VLC_EGENERIC;
-            vlc_mutex_lock( &p_ext->p_sys->command_lock );
-            if ( p_ext->p_sys->b_exiting == true )
+            vlc_mutex_lock(&sys->command_lock);
+            if (sys->b_exiting)
             {
-                vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+                vlc_mutex_unlock(&sys->command_lock);
                 return VLC_EGENERIC;
             }
-            vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+            vlc_mutex_unlock(&sys->command_lock);
 
-            vlc_mutex_lock( &p_ext->p_sys->running_lock );
+            vlc_mutex_lock(&sys->running_lock);
 
             // Change input
-            input_item_t *old = p_ext->p_sys->p_item;
+            input_item_t *old = sys->p_item;
             if( old )
-            {
-                // Untrack meta fetched events
-                if( p_ext->p_sys->i_capabilities & EXT_META_LISTENER )
-                {
-                    vlc_event_detach( &old->event_manager,
-                                      vlc_InputItemMetaChanged,
-                                      inputItemMetaChanged,
-                                      p_ext );
-                }
                 input_item_Release( old );
-            }
 
-            p_ext->p_sys->p_item = p_item ? input_item_Hold(p_item) : NULL;
+            sys->p_item = p_item ? input_item_Hold(p_item) : NULL;
 
             // Tell the script the input changed
-            if( p_ext->p_sys->i_capabilities & EXT_INPUT_LISTENER )
+            if (sys->i_capabilities & EXT_INPUT_LISTENER)
             {
-                PushCommandUnique( p_ext, CMD_SET_INPUT );
+                PushCommandUnique(ext, CMD_SET_INPUT);
             }
 
-            // Track meta fetched events
-            if( p_ext->p_sys->p_item &&
-                p_ext->p_sys->i_capabilities & EXT_META_LISTENER )
-            {
-                vlc_event_attach( &p_item->event_manager,
-                                  vlc_InputItemMetaChanged,
-                                  inputItemMetaChanged,
-                                  p_ext );
-            }
-
-            vlc_mutex_unlock( &p_ext->p_sys->running_lock );
+            vlc_mutex_unlock(&sys->running_lock);
             break;
         }
         case EXTENSION_PLAYING_CHANGED:
         {
-            p_ext = va_arg( args, extension_t* );
-            assert( p_ext->psz_name != NULL );
+            assert(ext->psz_name != NULL);
             i = va_arg( args, int );
-            if( p_ext->p_sys->i_capabilities & EXT_PLAYING_LISTENER )
+            if (sys->i_capabilities & EXT_PLAYING_LISTENER)
             {
-                PushCommand( p_ext, CMD_PLAYING_CHANGED, i );
+                PushCommand(ext, CMD_PLAYING_CHANGED, i);
             }
             break;
         }
         case EXTENSION_META_CHANGED:
         {
-            p_ext = va_arg( args, extension_t* );
-            PushCommand( p_ext, CMD_UPDATE_META );
+            ext = va_arg( args, extension_t* );
+            PushCommand(ext, CMD_UPDATE_META);
             break;
         }
         default:
@@ -625,39 +589,26 @@ int lua_ExtensionActivate( extensions_manager_t *p_mgr, extension_t *p_ext )
 int lua_ExtensionDeactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
 {
     assert( p_mgr != NULL && p_ext != NULL );
+    struct lua_extension *sys = p_ext->p_sys;
 
-    if( p_ext->p_sys->b_activated == false )
-        return VLC_SUCCESS;
-
-    vlclua_fd_interrupt( &p_ext->p_sys->dtable );
+    vlclua_fd_interrupt(&sys->dtable);
 
     // Unset and release input objects
-    if( p_ext->p_sys->p_item )
+    if (sys->p_item)
     {
-        if( p_ext->p_sys->i_capabilities & EXT_META_LISTENER )
-            vlc_event_detach( &p_ext->p_sys->p_item->event_manager,
-                              vlc_InputItemMetaChanged,
-                              inputItemMetaChanged,
-                              p_ext );
-        input_item_Release(p_ext->p_sys->p_item);
-        p_ext->p_sys->p_item = NULL;
+        input_item_Release(sys->p_item);
+        sys->p_item = NULL;
     }
 
-    int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "deactivate", LUA_END );
-
-    if ( p_ext->p_sys->L == NULL )
-        return VLC_EGENERIC;
-    lua_close( p_ext->p_sys->L );
-    p_ext->p_sys->L = NULL;
-
-    return i_ret;
+    return lua_ExecuteFunction( p_mgr, p_ext, "deactivate", LUA_END );
 }
 
 int lua_ExtensionWidgetClick( extensions_manager_t *p_mgr,
                               extension_t *p_ext,
                               extension_widget_t *p_widget )
 {
-    if( !p_ext->p_sys->L )
+    struct lua_extension *sys = p_ext->p_sys;
+    if (sys->L == NULL)
         return VLC_SUCCESS;
 
     lua_State *L = GetLuaState( p_mgr, p_ext );
@@ -682,22 +633,23 @@ static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext,
 {
     assert( *pppsz_titles == NULL );
     assert( *ppi_ids == NULL );
+    struct lua_extension *sys = p_ext->p_sys;
 
-    vlc_mutex_lock( &p_ext->p_sys->command_lock );
-    if( p_ext->p_sys->b_activated == false || p_ext->p_sys->b_exiting == true )
+    vlc_mutex_lock(&sys->command_lock);
+    if (!sys->b_activated || sys->b_exiting)
     {
-        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        vlc_mutex_unlock(&sys->command_lock);
         msg_Dbg( p_mgr, "Can't get menu of an unactivated/dying extension!" );
         return VLC_EGENERIC;
     }
-    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+    vlc_mutex_unlock(&sys->command_lock);
 
-    vlc_mutex_lock( &p_ext->p_sys->running_lock );
+    vlc_mutex_lock(&sys->running_lock);
 
     int i_ret = VLC_EGENERIC;
     lua_State *L = GetLuaState( p_mgr, p_ext );
 
-    if( ( p_ext->p_sys->i_capabilities & EXT_HAS_MENU ) == 0 )
+    if ((sys->i_capabilities & EXT_HAS_MENU) == 0)
     {
         msg_Dbg( p_mgr, "can't get a menu from an extension without menu!" );
         goto exit;
@@ -763,7 +715,7 @@ static int GetMenuEntries( extensions_manager_t *p_mgr, extension_t *p_ext,
     i_ret = VLC_SUCCESS;
 
 exit:
-    vlc_mutex_unlock( &p_ext->p_sys->running_lock );
+    vlc_mutex_unlock(&sys->running_lock);
     if( i_ret != VLC_SUCCESS )
     {
         msg_Dbg( p_mgr, "Something went wrong in %s (%s:%d)",
@@ -777,7 +729,8 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr,
                                extension_t *p_ext )
 {
     assert( p_ext != NULL );
-    lua_State *L = p_ext->p_sys->L;
+    struct lua_extension *sys = p_ext->p_sys;
+    lua_State *L = sys->L;
 
     if( !L )
     {
@@ -802,7 +755,7 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr,
         luaopen_dialog( L, p_ext );
         luaopen_input( L );
         luaopen_msg( L );
-        if( vlclua_fd_init( L, &p_ext->p_sys->dtable ) )
+        if (vlclua_fd_init(L, &sys->dtable))
         {
             lua_close( L );
             return NULL;
@@ -839,28 +792,26 @@ static lua_State* GetLuaState( extensions_manager_t *p_mgr,
             /* Load all required modules manually */
             lua_register( L, "require", &vlclua_extension_require );
         }
-        else
+        else if (vlclua_add_modules_path(L, p_ext->psz_name))
         {
-            if( vlclua_add_modules_path( L, p_ext->psz_name ) )
-            {
-                msg_Warn( p_mgr, "Error while setting the module "
-                          "search path for %s", p_ext->psz_name );
-                vlclua_fd_cleanup( &p_ext->p_sys->dtable );
-                lua_close( L );
-                return NULL;
-            }
+            msg_Warn(p_mgr, "Error while setting the module "
+                     "search path for %s", p_ext->psz_name);
+            vlclua_fd_cleanup(&sys->dtable);
+            lua_close(L);
+            return NULL;
         }
+
         /* Load and run the script(s) */
         if( vlclua_dofile( VLC_OBJECT( p_mgr ), L, p_ext->psz_name ) )
         {
             msg_Warn( p_mgr, "Error loading script %s: %s", p_ext->psz_name,
                       lua_tostring( L, lua_gettop( L ) ) );
-            vlclua_fd_cleanup( &p_ext->p_sys->dtable );
+            vlclua_fd_cleanup(&sys->dtable);
             lua_close( L );
             return NULL;
         }
 
-        p_ext->p_sys->L = L;
+        sys->L = L;
     }
 
     return L;
@@ -1000,15 +951,17 @@ int lua_ExtensionTriggerMenu( extensions_manager_t *p_mgr,
 static int TriggerExtension( extensions_manager_t *p_mgr,
                              extension_t *p_ext )
 {
+    struct lua_extension *sys = p_ext->p_sys;
+
     int i_ret = lua_ExecuteFunction( p_mgr, p_ext, "trigger", LUA_END );
 
     /* Close lua state for trigger-only extensions */
-    if( p_ext->p_sys->L )
+    if (sys->L)
     {
-        vlclua_fd_cleanup( &p_ext->p_sys->dtable );
-        lua_close( p_ext->p_sys->L );
+        vlclua_fd_cleanup(&sys->dtable);
+        lua_close(sys->L);
     }
-    p_ext->p_sys->L = NULL;
+    sys->L = NULL;
 
     return i_ret;
 }
@@ -1045,9 +998,11 @@ extension_t *vlclua_extension_get( lua_State *L )
 int vlclua_extension_deactivate( lua_State *L )
 {
     extension_t *p_ext = vlclua_extension_get( L );
-    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    struct lua_extension *sys = p_ext->p_sys;
+
+    vlc_mutex_lock(&sys->command_lock);
     bool b_ret = QueueDeactivateCommand( p_ext );
-    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+    vlc_mutex_unlock(&sys->command_lock);
     return ( b_ret == true ) ? 1 : 0;
 }
 
@@ -1058,16 +1013,17 @@ int vlclua_extension_deactivate( lua_State *L )
 int vlclua_extension_keep_alive( lua_State *L )
 {
     extension_t *p_ext = vlclua_extension_get( L );
+    struct lua_extension *sys = p_ext->p_sys;
 
-    vlc_mutex_lock( &p_ext->p_sys->command_lock );
-    if( p_ext->p_sys->p_progress_id != NULL )
+    vlc_mutex_lock(&sys->command_lock);
+    if (sys->p_progress_id != NULL)
     {
-        vlc_dialog_release( p_ext->p_sys->p_mgr, p_ext->p_sys->p_progress_id );
-        p_ext->p_sys->p_progress_id = NULL;
+        vlc_dialog_release(sys->p_mgr, sys->p_progress_id);
+        sys->p_progress_id = NULL;
     }
-    vlc_timer_schedule( p_ext->p_sys->timer, false, WATCH_TIMER_PERIOD,
-                        VLC_TIMER_FIRE_ONCE );
-    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+    vlc_timer_schedule(sys->timer, false, WATCH_TIMER_PERIOD,
+                       VLC_TIMER_FIRE_ONCE);
+    vlc_mutex_unlock(&sys->command_lock);
 
     return 1;
 }
@@ -1117,74 +1073,62 @@ static int vlclua_extension_dialog_callback( vlc_object_t *p_this,
     return VLC_SUCCESS;
 }
 
-/** Callback on vlc_InputItemMetaChanged event
- **/
-static void inputItemMetaChanged( const vlc_event_t *p_event,
-                                  void *data )
-{
-    assert( p_event && p_event->type == vlc_InputItemMetaChanged );
-
-    extension_t *p_ext = ( extension_t* ) data;
-    assert( p_ext != NULL );
-
-    PushCommandUnique( p_ext, CMD_UPDATE_META );
-}
-
 /** Watch timer callback
  * The timer expired, Lua may be stuck, ask the user what to do now
  **/
 static void WatchTimerCallback( void *data )
 {
     extension_t *p_ext = data;
-    extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
+    struct lua_extension *sys = p_ext->p_sys;
+    extensions_manager_t *p_mgr = sys->p_mgr;
 
-    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    vlc_mutex_lock(&sys->command_lock);
 
-    for( struct command_t *cmd = p_ext->p_sys->command;
+    for( struct command_t *cmd = sys->command;
          cmd != NULL;
          cmd = cmd->next )
         if( cmd->i_command == CMD_DEACTIVATE )
         {   /* We have a pending Deactivate command... */
-            if( p_ext->p_sys->p_progress_id != NULL )
+            if (sys->p_progress_id != NULL)
             {
-                vlc_dialog_release( p_mgr, p_ext->p_sys->p_progress_id );
-                p_ext->p_sys->p_progress_id = NULL;
+                vlc_dialog_release(p_mgr, sys->p_progress_id);
+                sys->p_progress_id = NULL;
             }
-            KillExtension( p_mgr, p_ext );
-            vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+            KillExtension(p_ext);
+            vlc_mutex_unlock(&sys->command_lock);
             return;
         }
 
-    if( p_ext->p_sys->p_progress_id == NULL )
+    if (sys->p_progress_id == NULL)
     {
-        p_ext->p_sys->p_progress_id =
+        sys->p_progress_id =
             vlc_dialog_display_progress( p_mgr, true, 0.0,
                                          _( "Yes" ),
                                          _( "Extension not responding!" ),
                                          _( "Extension '%s' does not respond.\n"
                                          "Do you want to kill it now? " ),
                                          p_ext->psz_title );
-        if( p_ext->p_sys->p_progress_id == NULL )
+        if (sys->p_progress_id == NULL)
         {
-            KillExtension( p_mgr, p_ext );
-            vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+            KillExtension(p_ext);
+            vlc_mutex_unlock(&sys->command_lock);
             return;
         }
-        vlc_timer_schedule( p_ext->p_sys->timer, false, VLC_TICK_FROM_MS(100),
-                            VLC_TIMER_FIRE_ONCE );
+        vlc_timer_schedule(sys->timer, false, VLC_TICK_FROM_MS(100),
+                           VLC_TIMER_FIRE_ONCE);
     }
     else
     {
-        if( vlc_dialog_is_cancelled( p_mgr, p_ext->p_sys->p_progress_id ) )
+        if (vlc_dialog_is_cancelled(p_mgr, sys->p_progress_id))
         {
-            vlc_dialog_release( p_mgr, p_ext->p_sys->p_progress_id );
-            p_ext->p_sys->p_progress_id = NULL;
-            KillExtension( p_mgr, p_ext );
-            vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+            vlc_dialog_release(p_mgr, sys->p_progress_id);
+            sys->p_progress_id = NULL;
+            KillExtension(p_ext);
+            vlc_mutex_unlock(&sys->command_lock);
             return;
         }
-        vlc_timer_schedule( p_ext->p_sys->timer, false, VLC_TICK_FROM_MS(100),
-                            VLC_TIMER_FIRE_ONCE );
+        vlc_timer_schedule(sys->timer, false, VLC_TICK_FROM_MS(100),
+                           VLC_TIMER_FIRE_ONCE);
     }
-    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+    vlc_mutex_unlock(&sys->command_lock);
 }


=====================================
modules/lua/extension.h
=====================================
@@ -29,6 +29,13 @@
 
 #define WATCH_TIMER_PERIOD    VLC_TICK_FROM_SEC(10) ///< 10s period for the timer
 
+/* Extension capabilities */
+#define EXT_HAS_MENU          (1 << 0)   ///< Hook: menu
+#define EXT_TRIGGER_ONLY      (1 << 1)   ///< Hook: trigger. Not activable
+#define EXT_INPUT_LISTENER    (1 << 2)   ///< Hook: input_changed
+#define EXT_META_LISTENER     (1 << 3)   ///< Hook: meta_changed
+#define EXT_PLAYING_LISTENER  (1 << 4)   ///< Hook: status_changed
+
 /* List of available commands */
 typedef enum
 {
@@ -50,7 +57,7 @@ typedef enum
     LUA_TEXT
 } lua_datatype_e;
 
-struct extension_sys_t
+struct lua_extension
 {
     /* Extension general */
     int i_capabilities;
@@ -69,6 +76,7 @@ struct extension_sys_t
     /* The item this extension should use for vlc.input
      * or NULL if it should use playlist's current input */
     struct input_item_t *p_item;
+    struct vlc_player_listener_id *player_listener;
 
     extensions_manager_t *p_mgr;     ///< Parent
     /* Queue of commands to execute */
@@ -87,13 +95,15 @@ struct extension_sys_t
 
     bool b_thread_running; //< Only accessed out of the extension thread.
     bool b_activated; ///< Protected by the command lock
+    bool b_activating; ///< Protected by the command lock
+    bool b_deactivating; ///< Protected by the command lock
 };
 
 /* Extensions: manager functions */
-int Activate( extensions_manager_t *p_mgr, extension_t * );
+int Activate(extension_t *);
 int Deactivate( extensions_manager_t *p_mgr, extension_t * );
 bool QueueDeactivateCommand( extension_t *p_ext );
-void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext );
+void KillExtension(extension_t *p_ext);
 int PushCommand__( extension_t *ext, bool unique, command_type_e cmd, va_list options );
 static inline int PushCommand( extension_t *ext, int cmd, ... )
 {


=====================================
modules/lua/extension_thread.c
=====================================
@@ -28,6 +28,10 @@
 #include "extension.h"
 #include "assert.h"
 
+#include <vlc_common.h>
+#include <vlc_messages.h>
+#include <vlc_player.h>
+
 struct thread_sys_t
 {
     extensions_manager_t *p_mgr;
@@ -38,56 +42,108 @@ struct thread_sys_t
 static void* Run( void *data );
 static void FreeCommands( struct command_t *command );
 
+/**
+ * Player listeners
+ */
+
+static void
+OnCurrentMediaChanged(vlc_player_t *player,
+                      input_item_t *new_media,
+                      void *opaque)
+{
+    (void)player;
+    extension_t *ext = opaque;
+    struct lua_extension *sys = ext->p_sys;
+
+    vlc_mutex_lock(&sys->running_lock);
+    // Change input
+    input_item_t *old = sys->p_item;
+    if (old)
+        input_item_Release(old);
+
+    if (new_media)
+        input_item_Hold(new_media);
+    sys->p_item = new_media;
+
+    // Tell the script the input changed
+    if (sys->i_capabilities & EXT_INPUT_LISTENER)
+        PushCommandUnique(ext, CMD_SET_INPUT);
+    vlc_mutex_unlock(&sys->running_lock);
+}
+
 /**
  * Activate an extension
- * @param p_mgr This manager
  * @param p_ext Extension to activate
  * @return The usual VLC return codes
  **/
-int Activate( extensions_manager_t *p_mgr, extension_t *p_ext )
+int Activate(extension_t *p_ext)
 {
     assert( p_ext != NULL );
 
-    struct extension_sys_t *p_sys = p_ext->p_sys;
-    assert( p_sys != NULL );
+    struct lua_extension *sys = p_ext->p_sys;
+    assert(sys != NULL);
+    extensions_manager_t *mgr = sys->p_mgr;
 
-    vlc_mutex_lock( &p_sys->command_lock );
-    if ( p_sys->b_activated == false )
+    vlc_mutex_lock(&sys->command_lock);
+    if (sys->b_activating)
+    {
+        vlc_mutex_unlock(&sys->command_lock);
+        return VLC_SUCCESS;
+    }
+    if (!sys->b_activated)
     {
         /* Prepare first command */
-        assert(p_sys->command == NULL);
-        p_sys->command = calloc( 1, sizeof( struct command_t ) );
-        if( !p_sys->command )
+        assert(sys->command == NULL);
+        sys->command = calloc(1, sizeof(*sys->command));
+        if (!sys->command)
         {
-            vlc_mutex_unlock( &p_sys->command_lock );
+            vlc_mutex_unlock(&sys->command_lock);
             return VLC_ENOMEM;
         }
-        p_sys->command->i_command = CMD_ACTIVATE; /* No params */
-        if (p_sys->b_thread_running == true)
+        sys->command->i_command = CMD_ACTIVATE; /* No params */
+        if (sys->b_thread_running)
         {
-            msg_Dbg( p_mgr, "Reactivating extension %s", p_ext->psz_title);
-            vlc_cond_signal( &p_sys->wait );
+            vlc_debug(p_ext->logger, "Reactivating extension %s", p_ext->psz_title);
+            vlc_cond_signal(&sys->wait);
         }
+        sys->b_activating = true;
     }
-    vlc_mutex_unlock( &p_sys->command_lock );
-
-    if (p_sys->b_thread_running == true)
-        return VLC_SUCCESS;
-
-    msg_Dbg( p_mgr, "Activating extension '%s'", p_ext->psz_title );
-    /* Start thread */
-    p_sys->b_exiting = false;
-    p_sys->b_thread_running = true;
+    vlc_mutex_unlock(&sys->command_lock);
 
-    if( vlc_clone( &p_sys->thread, Run, p_ext )
-        != VLC_SUCCESS )
+    if (!sys->b_thread_running)
     {
-        p_sys->b_exiting = true;
-        p_sys->b_thread_running = false;
-        return VLC_ENOMEM;
+        vlc_debug(p_ext->logger, "Activating extension '%s'", p_ext->psz_title);
+        sys->b_exiting = false;
+        if (vlc_clone(&sys->thread, Run, p_ext) != VLC_SUCCESS)
+        {
+            sys->b_exiting = true;
+            return VLC_ENOMEM;
+        }
+        sys->b_thread_running = true;
     }
 
+    /* Setup the listeners on the player object. */
+    static const struct vlc_player_cbs player_cbs =
+    {
+        .on_current_media_changed = OnCurrentMediaChanged,
+    };
+
+    vlc_player_Lock(mgr->player);
+    sys->player_listener =
+        vlc_player_AddListener(mgr->player, &player_cbs, p_ext);
+    vlc_player_Unlock(mgr->player);
+
+    if (sys->player_listener == NULL)
+        goto error_listener;
+
     return VLC_SUCCESS;
+error_listener:
+    vlc_mutex_lock(&sys->command_lock);
+    sys->b_exiting = true;
+    vlc_cond_signal(&sys->wait);
+    vlc_mutex_unlock(&sys->command_lock);
+    vlc_join(sys->thread, NULL);
+    return VLC_ENOMEM;
 }
 
 /** Recursively drop and free commands starting from "command" */
@@ -116,66 +172,85 @@ static void FreeCommands( struct command_t *command )
 
 bool QueueDeactivateCommand( extension_t *p_ext )
 {
+    struct lua_extension *sys = p_ext->p_sys;
+    vlc_mutex_assert(&sys->command_lock);
+
     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
     if( unlikely( cmd == NULL ) )
         return false;
     /* Free the list of commands */
-    if( p_ext->p_sys->command )
-        FreeCommands( p_ext->p_sys->command->next );
+    if (sys->command != NULL)
+        FreeCommands(sys->command->next);
 
     /* Push command */
 
     cmd->i_command = CMD_DEACTIVATE;
-    if( p_ext->p_sys->command )
-        p_ext->p_sys->command->next = cmd;
+    if (sys->command)
+        sys->command->next = cmd;
     else
-        p_ext->p_sys->command = cmd;
+        sys->command = cmd;
+
+    sys->b_deactivating = true;
+    sys->b_activating = false;
+    vlc_cond_signal(&sys->wait);
+
+    vlc_player_Lock(sys->p_mgr->player);
+    vlc_player_RemoveListener(sys->p_mgr->player, sys->player_listener);
+    vlc_player_Unlock(sys->p_mgr->player);
 
-    vlc_cond_signal( &p_ext->p_sys->wait );
     return true;
 }
 
 /** Deactivate this extension: pushes immediate command and drops queued */
 int Deactivate( extensions_manager_t *p_mgr, extension_t *p_ext )
 {
-    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    struct lua_extension *sys = p_ext->p_sys;
 
-    if( p_ext->p_sys->b_exiting )
+    vlc_mutex_lock(&sys->command_lock);
+    if (!sys->b_activated && !sys->b_activating)
     {
-        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        vlc_mutex_unlock(&sys->command_lock);
+        return VLC_SUCCESS;
+    }
+
+    if (sys->b_exiting)
+    {
+        vlc_mutex_unlock(&sys->command_lock);
         return VLC_EGENERIC;
     }
 
-    if( p_ext->p_sys->p_progress_id != NULL )
+    if (sys->p_progress_id != NULL)
     {
         // Extension is stuck, kill it now
-        vlc_dialog_release( p_mgr, p_ext->p_sys->p_progress_id );
-        p_ext->p_sys->p_progress_id = NULL;
-        KillExtension( p_mgr, p_ext );
-        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        vlc_dialog_release(p_mgr, sys->p_progress_id);
+        sys->p_progress_id = NULL;
+        KillExtension(p_ext);
+        vlc_mutex_unlock(&sys->command_lock);
         return VLC_SUCCESS;
     }
 
     bool b_success = QueueDeactivateCommand( p_ext );
-    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+    vlc_mutex_unlock(&sys->command_lock);
 
     return b_success ? VLC_SUCCESS : VLC_ENOMEM;
 }
 
 /* MUST be called with command_lock held */
-void KillExtension( extensions_manager_t *p_mgr, extension_t *p_ext )
+void KillExtension(extension_t *p_ext)
 {
-    msg_Dbg( p_mgr, "Killing extension now" );
-    vlclua_fd_interrupt( &p_ext->p_sys->dtable );
-    p_ext->p_sys->b_activated = false;
-    p_ext->p_sys->b_exiting = true;
-    vlc_cond_signal( &p_ext->p_sys->wait );
+    struct lua_extension *sys = p_ext->p_sys;
+    vlc_debug(p_ext->logger, "Killing extension now");
+    vlclua_fd_interrupt(&sys->dtable);
+    sys->b_activated = false;
+    sys->b_exiting = true;
+    vlc_cond_signal(&sys->wait);
 }
 
 /** Push a UI command */
 int PushCommand__( extension_t *p_ext,  bool b_unique, command_type_e i_command,
                    va_list args )
 {
+    struct lua_extension *sys = p_ext->p_sys;
     /* Create command */
     struct command_t *cmd = calloc( 1, sizeof( struct command_t ) );
     if( unlikely( cmd == NULL ) )
@@ -216,18 +291,18 @@ int PushCommand__( extension_t *p_ext,  bool b_unique, command_type_e i_command,
             // Nothing to do here
             break;
         default:
-            msg_Dbg( p_ext->p_sys->p_mgr,
-                     "Unknown command send to extension: %d", i_command );
+            msg_Dbg(sys->p_mgr, "Unknown command send to extension: %d",
+                    i_command);
             break;
     }
 
-    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    vlc_mutex_lock(&sys->command_lock);
 
     /* Push command to the end of the queue */
-    struct command_t *last = p_ext->p_sys->command;
+    struct command_t *last = sys->command;
     if( !last )
     {
-        p_ext->p_sys->command = cmd;
+        sys->command = cmd;
     }
     else
     {
@@ -255,8 +330,8 @@ int PushCommand__( extension_t *p_ext,  bool b_unique, command_type_e i_command,
         }
     }
 
-    vlc_cond_signal( &p_ext->p_sys->wait );
-    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+    vlc_cond_signal(&sys->wait);
+    vlc_mutex_unlock(&sys->command_lock);
     return VLC_SUCCESS;
 }
 
@@ -264,31 +339,32 @@ int PushCommand__( extension_t *p_ext,  bool b_unique, command_type_e i_command,
 static void* Run( void *data )
 {
     extension_t *p_ext = data;
-    extensions_manager_t *p_mgr = p_ext->p_sys->p_mgr;
+    struct lua_extension *sys = p_ext->p_sys;
+    extensions_manager_t *p_mgr = sys->p_mgr;
 
     vlc_thread_set_name("vlc-lua-ext");
 
-    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+    vlc_mutex_lock(&sys->command_lock);
 
-    while( !p_ext->p_sys->b_exiting )
+    while (!sys->b_exiting || (sys->command && sys->command->i_command == CMD_DEACTIVATE))
     {
-        struct command_t *cmd = p_ext->p_sys->command;
+        struct command_t *cmd = sys->command;
 
         /* Pop command in front */
         if( cmd == NULL )
         {
-            vlc_cond_wait( &p_ext->p_sys->wait, &p_ext->p_sys->command_lock );
+            vlc_cond_wait(&sys->wait, &sys->command_lock);
             continue;
         }
-        p_ext->p_sys->command = cmd->next;
+        sys->command = cmd->next;
         cmd->next = NULL; /* unlink command (for FreeCommands()) */
         // Create watch timer
-        vlc_timer_schedule( p_ext->p_sys->timer, false, WATCH_TIMER_PERIOD,
-                            VLC_TIMER_FIRE_ONCE );
-        vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+        vlc_timer_schedule(sys->timer, false, WATCH_TIMER_PERIOD,
+                           VLC_TIMER_FIRE_ONCE);
+        vlc_mutex_unlock(&sys->command_lock);
 
         /* Run command */
-        vlc_mutex_lock( &p_ext->p_sys->running_lock );
+        vlc_mutex_lock(&sys->running_lock);
         switch( cmd->i_command )
         {
             case CMD_ACTIVATE:
@@ -296,28 +372,30 @@ static void* Run( void *data )
                 if( lua_ExecuteFunction( p_mgr, p_ext, "activate", LUA_END ) < 0 )
                 {
                     msg_Err( p_mgr, "Could not activate extension!" );
-                    vlc_mutex_lock( &p_ext->p_sys->command_lock );
+                    vlc_mutex_lock(&sys->command_lock);
                     QueueDeactivateCommand( p_ext );
-                    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+                    vlc_mutex_unlock(&sys->command_lock);
                     break;
                 }
-                vlc_mutex_lock( &p_ext->p_sys->command_lock );
-                p_ext->p_sys->b_activated = true;
-                vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+                vlc_mutex_lock(&sys->command_lock);
+                sys->b_activated = true;
+                sys->b_activating = false;
+                vlc_mutex_unlock(&sys->command_lock);
                 break;
             }
 
             case CMD_DEACTIVATE:
             {
-                msg_Dbg( p_mgr, "Deactivating '%s'", p_ext->psz_title );
+                vlc_debug(p_ext->logger, "Deactivating '%s'", p_ext->psz_title);
                 if( lua_ExtensionDeactivate( p_mgr, p_ext ) < 0 )
                 {
-                    msg_Warn( p_mgr, "Extension '%s' did not deactivate properly",
-                              p_ext->psz_title );
+                    vlc_warning(p_ext->logger, "Extension '%s' did not deactivate properly",
+                                p_ext->psz_title);
                 }
-                vlc_mutex_lock( &p_ext->p_sys->command_lock );
-                p_ext->p_sys->b_activated = false;
-                vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+                vlc_mutex_lock(&sys->command_lock);
+                sys->b_activated = false;
+                sys->b_deactivating = false;
+                vlc_mutex_unlock(&sys->command_lock);
                 break;
             }
 
@@ -331,12 +409,10 @@ static void* Run( void *data )
             {
                 extension_widget_t *p_widget = cmd->data[0];
                 assert( p_widget );
-                msg_Dbg( p_mgr, "Clicking '%s': '%s'",
-                         p_ext->psz_name, p_widget->psz_text );
+                vlc_debug(p_ext->logger, "Clicking '%s': '%s'",
+                         p_ext->psz_name, p_widget->psz_text);
                 if( lua_ExtensionWidgetClick( p_mgr, p_ext, p_widget ) < 0 )
-                {
-                    msg_Warn( p_mgr, "Could not translate click" );
-                }
+                    vlc_debug(p_ext->logger, "Could not translate click");
                 break;
             }
 
@@ -344,8 +420,8 @@ static void* Run( void *data )
             {
                 int *pi_id = cmd->data[0];
                 assert( pi_id );
-                msg_Dbg( p_mgr, "Trigger menu %d of '%s'",
-                         *pi_id, p_ext->psz_name );
+                vlc_debug(p_ext->logger, "Trigger menu %d of '%s'",
+                          *pi_id, p_ext->psz_name);
                 lua_ExtensionTriggerMenu( p_mgr, p_ext, *pi_id );
                 break;
             }
@@ -376,21 +452,21 @@ static void* Run( void *data )
                 break;
             }
         }
-        vlc_mutex_unlock( &p_ext->p_sys->running_lock );
+        vlc_mutex_unlock(&sys->running_lock);
 
         FreeCommands( cmd );
 
-        vlc_mutex_lock( &p_ext->p_sys->command_lock );
+        vlc_mutex_lock(&sys->command_lock);
         // Reset watch timer and timestamp
-        if( p_ext->p_sys->p_progress_id != NULL )
+        if (sys->p_progress_id != NULL)
         {
-            vlc_dialog_release( p_mgr, p_ext->p_sys->p_progress_id );
-            p_ext->p_sys->p_progress_id = NULL;
+            vlc_dialog_release(p_mgr, sys->p_progress_id);
+            sys->p_progress_id = NULL;
         }
-        vlc_timer_disarm( p_ext->p_sys->timer );
+        vlc_timer_disarm(sys->timer);
     }
 
-    vlc_mutex_unlock( &p_ext->p_sys->command_lock );
+    vlc_mutex_unlock(&sys->command_lock);
     msg_Dbg( p_mgr, "Extension thread end: '%s'", p_ext->psz_title );
 
     // Note: At this point, the extension should be deactivated


=====================================
test/modules/lua/extension.c
=====================================
@@ -37,14 +37,33 @@ const char vlc_module_name[] = MODULE_STRING;
 #include <vlc_common.h>
 #include <vlc_plugin.h>
 #include <vlc_modules.h>
+#include <vlc_interface.h>
+#include <vlc_playlist.h>
+#include <vlc_player.h>
 #include <vlc_extensions.h>
+#include <vlc_input_item.h>
 
 #include <limits.h>
 
 static int exitcode = 0;
 
+static int OnLuaEventTriggered(vlc_object_t *obj, const char *name,
+        vlc_value_t oldv, vlc_value_t newv, void *opaque)
+{
+    (void)obj; (void)name; (void)oldv; (void)newv;
+    vlc_sem_t *sem = opaque;
+    vlc_sem_post(sem);
+    return VLC_SUCCESS;
+}
+
 static int OpenIntf(vlc_object_t *root)
 {
+    vlc_object_t *libvlc = (vlc_object_t*)vlc_object_instance(root);
+    var_Create(libvlc, "test-lua-activate", VLC_VAR_STRING | VLC_VAR_ISCOMMAND);
+    var_Create(libvlc, "test-lua-deactivate", VLC_VAR_STRING | VLC_VAR_ISCOMMAND);
+    var_Create(libvlc, "test-lua-input-changed", VLC_VAR_STRING | VLC_VAR_ISCOMMAND);
+
+    intf_thread_t *intf = (intf_thread_t*)root;
     extensions_manager_t *mgr =
         vlc_object_create(root, sizeof *mgr);
     assert(mgr);
@@ -53,6 +72,9 @@ static int OpenIntf(vlc_object_t *root)
     setenv("VLC_DATA_PATH", LUA_EXTENSION_DIR, 1);
     setenv("VLC_LIB_PATH", LUA_EXTENSION_DIR, 1);
 
+    vlc_playlist_t *playlist = vlc_intf_GetMainPlaylist(intf);
+    vlc_player_t *player = vlc_playlist_GetPlayer(playlist);
+    mgr->player = player;
     mgr->p_module = module_need(mgr, "extension", "lua", true);
 
     if (mgr->p_module == NULL)
@@ -61,12 +83,39 @@ static int OpenIntf(vlc_object_t *root)
         goto end;
     }
 
+    vlc_sem_t sem_input, sem_activate, sem_deactivate;
+    vlc_sem_init(&sem_input, 0);
+    vlc_sem_init(&sem_activate, 0);
+    vlc_sem_init(&sem_deactivate, 0);
+
+    var_AddCallback(libvlc, "test-lua-activate", OnLuaEventTriggered, &sem_activate);
+    var_AddCallback(libvlc, "test-lua-deactivate", OnLuaEventTriggered, &sem_deactivate);
+    var_AddCallback(libvlc, "test-lua-input-changed", OnLuaEventTriggered, &sem_input);
+
     /* Check that the extension from the test is correctly probed. */
     assert(mgr->extensions.i_size == 1);
     extension_Activate(mgr, mgr->extensions.p_elems[0]);
+    vlc_sem_wait(&sem_activate);
+
+    vlc_player_Lock(player);
+    input_item_t *item = input_item_New(
+            "mock://length=100000000000000000", // TODO: make it infinite
+            "lua_test_sample");
+    vlc_player_SetCurrentMedia(player, item);
+    vlc_player_Start(player);
+    vlc_player_Unlock(player);
+
+    vlc_sem_wait(&sem_input);
+
     extension_Deactivate(mgr, mgr->extensions.p_elems[0]);
+    vlc_sem_wait(&sem_deactivate);
+
+    var_DelCallback(libvlc, "test-lua-activate", OnLuaEventTriggered, &sem_activate);
+    var_DelCallback(libvlc, "test-lua-deactivate", OnLuaEventTriggered, &sem_deactivate);
+    var_DelCallback(libvlc, "test-lua-input-changed", OnLuaEventTriggered, &sem_input);
 
     module_unneed(mgr, mgr->p_module);
+    input_item_Release(item);
 end:
     vlc_object_delete(mgr);
     return VLC_SUCCESS;


=====================================
test/modules/lua/extensions/extensions.lua
=====================================
@@ -13,26 +13,32 @@ function descriptor()
   }
 end
 
+function signal_test(event_name)
+  vlc.msg.dbg("lua test event: " .. event_name)
+  libvlc = vlc.object.libvlc()
+  vlc.var.trigger_callback(libvlc, "test-lua-" .. event_name)
+end
+
 function activate()
-  vlc.msg.dbg("Activate")
+  signal_test("activate")
 end
 
 function close()
-  vlc.msg.dbg("Close")
+  signal_test("close")
 end
 
 function deactivate()
-  vlc.msg.dbg("Deactivate")
+  signal_test("deactivate")
 end
 
 function input_changed()
-  vlc.msg.dbg("Input changed")
+  signal_test("input-changed")
 end
 
 function playing_changed()
-  vlc.msg.dbg("Playing changed")
+  signal_test("playing-changed")
 end
 
 function meta_changed()
-  vlc.msg.dbg("Meta changed")
+  signal_test("meta-changed")
 end



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/4acab97b2110dbb71267f9109411e7ede81e2277...3958d2c9e3d98c335ea4bf19c38570f4d4451489

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/4acab97b2110dbb71267f9109411e7ede81e2277...3958d2c9e3d98c335ea4bf19c38570f4d4451489
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list