[vlc-commits] [Git][videolan/vlc][master] 6 commits: core: services_discovery: document input_item_node_t lifecycle

Jean-Baptiste Kempf (@jbk) gitlab at videolan.org
Sat Jul 11 17:24:08 UTC 2026



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


Commits:
f85cdb8b by Thomas Guillem at 2026-07-11T19:10:40+02:00
core: services_discovery: document input_item_node_t lifecycle

- - - - -
3aea1e5d by Thomas Guillem at 2026-07-11T19:10:40+02:00
core: es_out: document input_item_node_t lifecycle

- - - - -
1a4ccc4b by Thomas Guillem at 2026-07-11T19:10:40+02:00
input: add libvlc_owner_release

Called when the input item is destroyed, to release libvlc_owner.

This will allow the libvlc media to share the reference count of its
input item and to be destroyed with it.

- - - - -
fad210b8 by Thomas Guillem at 2026-07-11T19:10:40+02:00
lib: media: share the input item reference count

A media had its own reference count, linked to its item by the
unretained item->libvlc_owner pointer, never cleared on destruction.
The libvlc player callbacks could resolve medias via libvlc_owner after
its destruction.

The media is now destroyed with its input item, via the new
libvlc_owner_release callback.

There is now a one to one relation between `libvlc_media_t` and
`input_item_t` (not possible anymore to have several media referencing a
single item).

Fixes the following ASan report, triggered by the new
test_media_player_switch_media test without this fix:

```
xeon ~/work/git/vlc/build-asan $ ./test/test_libvlc_media_player
testapi: Testing set_media
testapi: Waiting for playing
testapi: Testing media switch while playing
testapi: Waiting for playing
testapi: Waiting for the media change
=================================================================
==953845==ERROR: AddressSanitizer: heap-use-after-free on address 0x504000054c70 at pc 0x7f83b5f47330 bp 0x7f83a88fcfb0 sp 0x7f83a88fcfa8
WRITE of size 4 at 0x504000054c70 thread T4 (vlc-input)
    #0 0x7f83b5f4732f in on_media_meta_changed ../../lib/media_player.c:417
    #1 0x7f83b5588a29 in input_thread_Events ../../src/player/input.c:1130
    #2 0x7f83b552e47d in input_SendEvent ../../src/input/event.h:36
    #3 0x7f83b552ed2e in input_SendEventMetaInfo ../../src/input/event.h:167
    #4 0x7f83b553dc57 in EsOutDeleteInfoEs ../../src/input/es_out.c:4911
    #5 0x7f83b5544a73 in EsOutDelLocked ../../src/input/es_out.c:3303
    #6 0x7f83b5545c1f in EsOutDel ../../src/input/es_out.c:3354
    #7 0x7f83b555475c in es_out_Del ../../include/vlc_es_out.h:153
    #8 0x7f83b555475c in CmdExecuteDel ../../src/input/es_out_timeshift.c:1600
    #9 0x7f83b5554afb in Del ../../src/input/es_out_timeshift.c:447
    #10 0x7f83aa338e7a in es_out_Del ../../include/vlc_es_out.h:151
    #11 0x7f83aa338e7a in DeleteTrack ../../modules/demux/mock.c:902
    #12 0x7f83aa338f4d in Close ../../modules/demux/mock.c:1544
    #13 0x7f83b54dba60 in module_unneed ../../src/modules/modules.c:291
    #14 0x7f83b550d06b in vlc_access_Destroy ../../src/input/access.c:53
    #15 0x7f83b559cb2e in vlc_stream_Delete ../../src/input/stream.c:150
    #16 0x7f83b555f47c in demux_Delete ../../include/vlc_demux.h:298
    #17 0x7f83b555f47c in InputSourceDestroy ../../src/input/input.c:3037
    #18 0x7f83b5562cf5 in End ../../src/input/input.c:1442
    #19 0x7f83b556d443 in Run ../../src/input/input.c:421
    #20 0x7f83b585b1d5 in asan_thread_start ../../../../src/libsanitizer/asan/asan_interceptors.cpp:234
    #21 0x7f83b529eb7a in start_thread nptl/pthread_create.c:448
    #22 0x7f83b531c7f7 in __clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:78

0x504000054c70 is located 32 bytes inside of 48-byte region [0x504000054c50,0x504000054c80)
freed by thread T0 here:
    #0 0x7f83b58f38f8 in free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:52
    #1 0x7f83b5f421fe in libvlc_media_release ../../lib/media.c:379
    #2 0x7f83b5f49578 in libvlc_media_player_set_media ../../lib/media_player.c:983
    #3 0x55dce6183996 in test_media_player_switch_media ../../test/libvlc/media_player.c:200
    #4 0x55dce6187017 in main ../../test/libvlc/media_player.c:759
    #5 0x7f83b5235ca7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

previously allocated by thread T0 here:
    #0 0x7f83b58f4610 in calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
    #1 0x7f83b5f41c21 in libvlc_media_new_from_input_item ../../lib/media.c:236
    #2 0x7f83b5f41e2e in libvlc_media_new_location ../../lib/media.c:278
    #3 0x55dce6183902 in test_media_player_switch_media ../../test/libvlc/media_player.c:184
    #4 0x55dce6187017 in main ../../test/libvlc/media_player.c:759
    #5 0x7f83b5235ca7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

Thread T4 (vlc-input) created by T0 here:
    #0 0x7f83b58ecae1 in pthread_create ../../../../src/libsanitizer/asan/asan_interceptors.cpp:245
    #1 0x7f83b5645891 in vlc_clone_attr ../../src/posix/thread.c:179
    #2 0x7f83b5645d75 in vlc_clone ../../src/posix/thread.c:190
    #3 0x7f83b5563f04 in input_Start ../../src/input/input.c:134
    #4 0x7f83b5584138 in vlc_player_input_Start ../../src/player/input.c:101
    #5 0x7f83b55772e4 in vlc_player_Start ../../src/player/player.c:1255
    #6 0x7f83b5f498a5 in libvlc_media_player_play ../../lib/media_player.c:1051
    #7 0x55dce6183932 in test_media_player_switch_media ../../test/libvlc/media_player.c:192
    #8 0x55dce6187017 in main ../../test/libvlc/media_player.c:759
    #9 0x7f83b5235ca7 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58

SUMMARY: AddressSanitizer: heap-use-after-free ../../lib/media_player.c:417 in on_media_meta_changed
Shadow bytes around the buggy address:
  0x504000054980: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 fa
  0x504000054a00: fa fa 00 00 00 00 00 00 fa fa fd fd fd fd fd fa
  0x504000054a80: fa fa fd fd fd fd fd fa fa fa 00 00 00 00 00 00
  0x504000054b00: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 04
  0x504000054b80: fa fa fd fd fd fd fd fd fa fa fd fd fd fd fd fa
=>0x504000054c00: fa fa 00 00 00 00 00 04 fa fa fd fd fd fd[fd]fd
  0x504000054c80: fa fa 00 00 00 00 00 00 fa fa fd fd fd fd fd fd
  0x504000054d00: fa fa 00 00 00 00 00 01 fa fa 00 00 00 00 00 00
  0x504000054d80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000054e00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x504000054e80: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==953845==ABORTING
```

Refs https://code.videolan.org/videolan/VLCKit/-/merge_requests/391

- - - - -
8b2e77c8 by Thomas Guillem at 2026-07-11T19:10:40+02:00
lib: media_player: don't hold media and next_media

Not needed since the last commit (libvlc_media_t and input_item_t share
the same ref count). This allows to fix a discrepancy between lib and
core for libvlc_media_player_get_media (cf. updated comment).

- - - - -
637b37d7 by Thomas Guillem at 2026-07-11T19:10:40+02:00
core: input_item: hide libvlc_owner in input_item_owner

- - - - -


16 changed files:

- include/vlc/libvlc_media_player.h
- include/vlc_es_out.h
- include/vlc_input_item.h
- include/vlc_services_discovery.h
- lib/libvlc_internal.h
- lib/media.c
- lib/media_discoverer.c
- lib/media_internal.h
- lib/media_list_player.c
- lib/media_player.c
- lib/media_player_internal.h
- lib/video.c
- src/input/item.c
- src/input/item.h
- src/libvlccore.sym
- test/libvlc/media_player.c


Changes:

=====================================
include/vlc/libvlc_media_player.h
=====================================
@@ -701,10 +701,10 @@ LIBVLC_API void libvlc_media_player_set_media( libvlc_media_player_t *p_mi,
 /**
  * Get the media used by the media_player.
  *
- * \warning Calling this function just after libvlc_media_player_set_media()
- * will return the media that was just set, but this media might not be
- * currently used internally by the player. To detect such case, the user
- * should listen to the libvlc_media_player_cbs.on_media_changed callback.
+ * \note The returned media is the one currently used by the player. After
+ * calling libvlc_media_player_set_media() while a media is playing, the
+ * previous media is still returned until the switch is notified by the
+ * libvlc_media_player_cbs.on_media_changed callback.
  *
  * \param p_mi the Media Player
  * \return the media associated with p_mi, or NULL if no
@@ -3616,7 +3616,7 @@ typedef struct libvlc_media_player_time_point_t
  * struct defining callbacks for libvlc_media_player_watch_time()
  */
 struct libvlc_media_player_watch_time_cbs {
-    /** 
+    /**
      * Version of struct libvlc_media_player_watch_time_cbs
      */
     uint32_t version;


=====================================
include/vlc_es_out.h
=====================================
@@ -98,6 +98,8 @@ enum es_out_query_e
     /* Set global meta data (The vlc_meta_t is not modified nor released) */
     ES_OUT_SET_META, /* arg1=const vlc_meta_t * */
 
+    /* Post a node of sub items. Every input item in the node must be newly
+     * created */
     ES_OUT_POST_SUBNODE, /* arg1=input_item_node_t *, res=can fail */
 
     ES_OUT_VOUT_SET_MOUSE_EVENT, /* arg1= es_out_id_t* (video es),


=====================================
include/vlc_input_item.h
=====================================
@@ -129,9 +129,6 @@ struct input_item_t
     enum input_item_type_e i_type;   /**< Type (file, disc, ... see input_item_type_e) */
     bool        b_net;               /**< Net: always true for TYPE_STREAM, it
                                           depends for others types */
-
-    void        *libvlc_owner;       /**< LibVLC private data, can only be set
-                                          before events are registered. */
 };
 
 #define INPUT_ITEM_URI_NOP "vlc://nop" /* dummy URI for node/directory items */


=====================================
include/vlc_services_discovery.h
=====================================
@@ -175,6 +175,9 @@ static inline void services_discovery_AddItem(services_discovery_t *sd,
  * @note This callback does not take ownership of the input item; it might
  * however (and most probably will) add one of more references to the item.
  *
+ * @note Each input item must be reported at most once: do not pass the same
+ * item to this function again, even under a different parent.
+ *
  * The caller is responsible for releasing its own reference(s) eventually.
  * Keeping a reference is necessary to call services_discovery_RemoveItem() or
  * to alter the item later. However, if the caller will never remove nor alter


=====================================
lib/libvlc_internal.h
=====================================
@@ -115,6 +115,11 @@ struct libvlc_instance_t
  * Other internal functions
  ***************************************************************************/
 
+/* Not synchronized: set the LibVLC owner before publishing the item. */
+VLC_API void *input_item_GetLibvlcOwner(input_item_t *);
+VLC_API void input_item_SetLibvlcOwner(input_item_t *, void *,
+                                       void (*release)(void *));
+
 /* Thread context */
 void libvlc_threads_init (void);
 void libvlc_threads_deinit (void);


=====================================
lib/media.c
=====================================
@@ -217,6 +217,16 @@ void libvlc_media_add_subtree(libvlc_media_t *p_md, const input_item_node_t *nod
     input_item_add_subnode( p_md, node );
 }
 
+static void media_destroy( void *libvlc_owner )
+{
+    libvlc_media_t *p_md = libvlc_owner;
+
+    if( p_md->p_subitems )
+        libvlc_media_list_release( p_md->p_subitems );
+
+    free( p_md );
+}
+
 /**
  * \internal
  * Create a new media descriptor object from an input_item (Private)
@@ -233,6 +243,9 @@ libvlc_media_t * libvlc_media_new_from_input_item(input_item_t *p_input_item )
         return NULL;
     }
 
+    /* The item is wrapped by a single media for its whole lifetime */
+    assert( input_item_GetLibvlcOwner( p_input_item ) == NULL );
+
     p_md = calloc( 1, sizeof(libvlc_media_t) );
     if( !p_md )
     {
@@ -250,9 +263,8 @@ libvlc_media_t * libvlc_media_new_from_input_item(input_item_t *p_input_item )
     p_md->p_subitems->p_internal_md = p_md;
 
     p_md->p_input_item      = p_input_item;
-    vlc_atomic_rc_init(&p_md->rc);
 
-    p_md->p_input_item->libvlc_owner = p_md;
+    input_item_SetLibvlcOwner( p_md->p_input_item, p_md, media_destroy );
     atomic_init(&p_md->parsed_status, libvlc_media_parsed_status_none);
     p_md->req = NULL;
 
@@ -368,22 +380,16 @@ void libvlc_media_release( libvlc_media_t *p_md )
     if (!p_md)
         return;
 
-    if( !vlc_atomic_rc_dec(&p_md->rc) )
-        return;
-
-    if( p_md->p_subitems )
-        libvlc_media_list_release( p_md->p_subitems );
-
+    /* The media shares the reference count of its input item and is
+     * destroyed with it, see media_destroy() */
     input_item_Release( p_md->p_input_item );
-
-    free( p_md );
 }
 
 // Retain a media descriptor object
 libvlc_media_t *libvlc_media_retain( libvlc_media_t *p_md )
 {
     assert (p_md);
-    vlc_atomic_rc_inc( &p_md->rc );
+    input_item_Hold( p_md->p_input_item );
     return p_md;
 }
 


=====================================
lib/media_discoverer.c
=====================================
@@ -67,7 +67,8 @@ static void services_discovery_item_added( services_discovery_t *sd,
     if (p_md == NULL)
         return;
 
-    libvlc_media_t *parent_media = parent != NULL ? parent->libvlc_owner : NULL;
+    libvlc_media_t *parent_media = parent != NULL
+                                 ? input_item_GetLibvlcOwner( parent ) : NULL;
 
     libvlc_media_list_lock( p_mlist );
     libvlc_media_list_internal_add_media( p_mlist, p_md );
@@ -95,7 +96,7 @@ static void services_discovery_item_removed( services_discovery_t *sd,
     {
         p_md = libvlc_media_list_item_at_index( p_mdis->p_mlist, i );
         assert(p_md != NULL);
-        if( p_md == p_item->libvlc_owner )
+        if( p_md == input_item_GetLibvlcOwner( p_item ) )
         {
             libvlc_media_list_internal_remove_index( p_mdis->p_mlist, i );
             libvlc_media_list_unlock( p_mdis->p_mlist );


=====================================
lib/media_internal.h
=====================================
@@ -40,10 +40,12 @@ typedef enum libvlc_media_parsed_status_t
     libvlc_media_parsed_status_done,
 } libvlc_media_parsed_status_t;
 
+/* A media is a wrapper of its input item and shares its reference count:
+ * libvlc_media_retain()/libvlc_media_release() forward to the input item and
+ * the media is destroyed with it through input_item_SetLibvlcOwner(). */
 struct libvlc_media_t
 {
     input_item_t      *p_input_item;
-    vlc_atomic_rc_t    rc;
 
     VLC_FORWARD_DECLARE_OBJECT(libvlc_media_list_t*) p_subitems; /* A media descriptor can have Sub items. This is the only dependency we really have on media_list */
     void *p_user_data;


=====================================
lib/media_list_player.c
=====================================
@@ -326,7 +326,8 @@ internal_player_media_changed(vlc_player_t *player, input_item_t *new_media,
 
     /* Update the internal current path from the new media */
     libvlc_media_list_path_t path =
-        libvlc_media_list_path_of_item(p_mlp->p_mlist, new_media->libvlc_owner);
+        libvlc_media_list_path_of_item(p_mlp->p_mlist,
+                                      input_item_GetLibvlcOwner(new_media));
     if (p_mlp->current_playing_item_path != path)
     {
         free(p_mlp->current_playing_item_path);


=====================================
lib/media_player.c
=====================================
@@ -85,7 +85,7 @@ on_current_media_changed(vlc_player_t *player, input_item_t *new_media,
     libvlc_media_t *libmedia;
     if (new_media != NULL)
     {
-        libmedia = new_media->libvlc_owner;
+        libmedia = input_item_GetLibvlcOwner(new_media);
         assert(libmedia != NULL);
     }
     else
@@ -111,7 +111,7 @@ on_stopping_current_media(vlc_player_t *player, input_item_t *item,
     if (mp->cbs == NULL || mp->cbs->on_media_stopping == NULL)
         return;
 
-    libvlc_media_t *media = item->libvlc_owner;
+    libvlc_media_t *media = input_item_GetLibvlcOwner(item);
     assert(media != NULL);
 
     static_assert((int) VLC_PLAYER_MEDIA_STOPPING_ERROR == libvlc_stopping_reason_error &&
@@ -410,7 +410,7 @@ static void on_media_meta_changed(vlc_player_t *player,
     libvlc_media_player_t *mp = data;
 
     assert(media != NULL);
-    libvlc_media_t *libmedia = media->libvlc_owner;
+    libvlc_media_t *libmedia = input_item_GetLibvlcOwner(media);
     assert(libmedia != NULL);
 
     libvlc_media_parsed_status_t expected = libvlc_media_parsed_status_none;
@@ -432,7 +432,7 @@ on_media_subitems_changed(vlc_player_t *player, input_item_t *media,
     libvlc_media_player_t *mp = data;
 
     assert(media != NULL);
-    libvlc_media_t *libmedia = media->libvlc_owner;
+    libvlc_media_t *libmedia = input_item_GetLibvlcOwner(media);
     assert(libmedia != NULL);
 
     libvlc_media_add_subtree(libmedia, new_subitems);
@@ -441,18 +441,19 @@ on_media_subitems_changed(vlc_player_t *player, input_item_t *media,
 }
 
 static void
-on_media_attachments_added(vlc_player_t *player, input_item_t *media,
+on_media_attachments_added(vlc_player_t *player, input_item_t *item,
                            input_attachment_t *const *array, size_t count,
                            void *data)
 {
     (void) player;
 
     libvlc_media_player_t *mp = data;
+    assert(item == NULL || input_item_GetLibvlcOwner(item) != NULL);
 
-    input_item_t *current = mp->p_md ? mp->p_md->p_input_item : NULL;
-    if (media == current && mp->cbs != NULL
-     && mp->cbs->on_media_attachments_added != NULL)
+    if (mp->cbs != NULL && mp->cbs->on_media_attachments_added != NULL)
     {
+        libvlc_media_t *p_md = item != NULL
+                             ? input_item_GetLibvlcOwner(item) : NULL;
         libvlc_picture_list_t *list =
             libvlc_picture_list_from_attachments(array, count);
         if (list == NULL)
@@ -462,7 +463,7 @@ on_media_attachments_added(vlc_player_t *player, input_item_t *media,
             libvlc_picture_list_destroy(list);
             return;
         }
-        mp->cbs->on_media_attachments_added(mp->cbs_opaque, mp->p_md, list);
+        mp->cbs->on_media_attachments_added(mp->cbs_opaque, p_md, list);
         libvlc_picture_list_destroy(list);
     }
 }
@@ -808,7 +809,6 @@ libvlc_media_player_new( libvlc_instance_t *instance,
     var_Create(mp, "record-file", VLC_VAR_STRING);
 
     mp->timer.id = NULL;
-    mp->p_md = mp->p_next_md = NULL;
     mp->p_libvlc_instance = instance;
     /* use a reentrant lock to allow calling libvlc functions from callbacks */
     mp->player = vlc_player_New(VLC_OBJECT(mp), VLC_PLAYER_LOCK_REENTRANT);
@@ -871,17 +871,12 @@ libvlc_media_player_new_from_media( libvlc_instance_t *inst,
     if( !p_mi )
         return NULL;
 
-    libvlc_media_retain( p_md );
-    p_mi->p_md = p_md;
-
     vlc_player_Lock(p_mi->player);
     int ret = vlc_player_SetCurrentMedia(p_mi->player, p_md->p_input_item);
     vlc_player_Unlock(p_mi->player);
 
     if (ret != VLC_SUCCESS)
     {
-        libvlc_media_release(p_md);
-        p_mi->p_md = NULL;
         libvlc_media_player_release(p_mi);
         return NULL;
     }
@@ -910,9 +905,6 @@ static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
 
     vlc_player_Delete(p_mi->player);
 
-    libvlc_media_release( p_mi->p_md );
-    libvlc_media_release( p_mi->p_next_md );
-
     free(p_mi->vout.default_dec_dev);
     free(p_mi->vout.default_vout);
     free(p_mi->vout.default_gl);
@@ -980,14 +972,6 @@ void libvlc_media_player_set_media(
 {
     vlc_player_Lock(p_mi->player);
 
-    libvlc_media_release( p_mi->p_md );
-    libvlc_media_release( p_mi->p_next_md );
-
-    if( p_md )
-        libvlc_media_retain( p_md );
-    p_mi->p_md = p_md;
-    p_mi->p_next_md = NULL;
-
     vlc_player_SetCurrentMedia(p_mi->player, p_md ? p_md->p_input_item : NULL);
 
     vlc_player_Unlock(p_mi->player);
@@ -999,12 +983,15 @@ void libvlc_media_player_set_media(
 libvlc_media_t *
 libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
 {
+    vlc_player_Lock(p_mi->player);
+
+    input_item_t *item = vlc_player_GetCurrentMedia(p_mi->player);
     libvlc_media_t *p_m;
+    if (item != NULL)
+        p_m = input_item_GetLibvlcOwner(input_item_Hold(item));
+    else
+        p_m = NULL;
 
-    vlc_player_Lock(p_mi->player);
-    p_m = p_mi->p_md;
-    if( p_m )
-        libvlc_media_retain( p_m );
     vlc_player_Unlock(p_mi->player);
 
     return p_m;
@@ -1015,12 +1002,6 @@ void libvlc_media_player_set_next_media( libvlc_media_player_t *p_mi,
 {
     vlc_player_Lock(p_mi->player);
 
-    libvlc_media_release( p_mi->p_next_md );
-
-    if( p_md )
-        libvlc_media_retain( p_md );
-    p_mi->p_next_md = p_md;
-
     vlc_player_SetNextMedia(p_mi->player, p_md ? p_md->p_input_item : NULL);
 
     vlc_player_Unlock(p_mi->player);
@@ -1029,12 +1010,15 @@ void libvlc_media_player_set_next_media( libvlc_media_player_t *p_mi,
 libvlc_media_t *
 libvlc_media_player_get_next_media( libvlc_media_player_t *p_mi )
 {
+    vlc_player_Lock(p_mi->player);
+
+    input_item_t *item = vlc_player_GetNextMedia(p_mi->player);
     libvlc_media_t *p_m;
+    if (item != NULL)
+        p_m = input_item_GetLibvlcOwner(input_item_Hold(item));
+    else
+        p_m = NULL;
 
-    vlc_player_Lock(p_mi->player);
-    p_m = p_mi->p_next_md;
-    if( p_m )
-        libvlc_media_retain( p_m );
     vlc_player_Unlock(p_mi->player);
 
     return p_m;


=====================================
lib/media_player_internal.h
=====================================
@@ -48,8 +48,6 @@ struct libvlc_media_player_t
     vlc_cond_t wait;
 
     struct libvlc_instance_t * p_libvlc_instance; /* Parent instance */
-    libvlc_media_t * p_md; /* current media descriptor */
-    libvlc_media_t * p_next_md;
 
     struct {
         vlc_player_timer_id *id;


=====================================
lib/video.c
=====================================
@@ -160,8 +160,6 @@ int libvlc_video_get_size( libvlc_media_player_t *p_mi, unsigned ignored,
                            unsigned *restrict px, unsigned *restrict py )
 {
     (void) ignored;
-    if (p_mi->p_md == NULL)
-        return -1;
 
     int ret = -1;
     libvlc_media_track_t *track =


=====================================
src/input/item.c
=====================================
@@ -406,6 +406,24 @@ input_item_t *input_item_Hold( input_item_t *p_item )
     return p_item;
 }
 
+void *
+input_item_GetLibvlcOwner(input_item_t *item)
+{
+    input_item_owner_t *owner = item_owner(item);
+
+    return owner->libvlc_owner;
+}
+
+void
+input_item_SetLibvlcOwner(input_item_t *item, void *libvlc_owner,
+                          void (*release)(void *))
+{
+    input_item_owner_t *owner = item_owner(item);
+
+    owner->libvlc_owner = libvlc_owner;
+    owner->libvlc_owner_release = release;
+}
+
 void input_item_Release( input_item_t *p_item )
 {
     input_item_owner_t *owner = item_owner(p_item);
@@ -413,6 +431,9 @@ void input_item_Release( input_item_t *p_item )
     if( !vlc_atomic_rc_dec( &owner->rc ) )
         return;
 
+    if( owner->libvlc_owner_release != NULL )
+        owner->libvlc_owner_release( owner->libvlc_owner );
+
     free( p_item->psz_name );
     free( p_item->psz_uri );
     free( p_item->p_stats );
@@ -979,6 +1000,8 @@ input_item_NewExt( const char *psz_uri, const char *psz_name,
         return NULL;
 
     vlc_atomic_rc_init( &owner->rc );
+    owner->libvlc_owner = NULL;
+    owner->libvlc_owner_release = NULL;
 
     input_item_t *p_input = &owner->item;
 


=====================================
src/input/item.h
=====================================
@@ -28,10 +28,23 @@
 
 void input_item_UpdateTracksInfo( input_item_t *item, const es_format_t *fmt,
                                   const char *es_id, bool stable );
+/**
+ * Set the libvlc owner (libvlc_media_t *) with a release callback
+ *
+ * Can only be set after the item is just created.
+ */
+VLC_API void input_item_SetLibvlcOwner(input_item_t *, void *,
+                                       void (*release)(void *));
+/**
+ * Get the libvlc owner (libvlc_media_t *)
+ */
+VLC_API void *input_item_GetLibvlcOwner(input_item_t *);
 
 typedef struct input_item_owner
 {
     input_item_t item;
+    void *libvlc_owner;
+    void (*libvlc_owner_release)(void *libvlc_owner);
     vlc_atomic_rc_t rc;
 } input_item_owner_t;
 


=====================================
src/libvlccore.sym
=====================================
@@ -203,6 +203,7 @@ input_item_DelInfo
 input_item_GetDuration
 input_item_GetInfo
 input_item_GetInfoLocked
+input_item_GetLibvlcOwner
 input_item_GetMeta
 input_item_GetMetaLocked
 input_item_GetName
@@ -225,6 +226,7 @@ input_item_node_Create
 input_item_node_Delete
 input_item_ReplaceInfos
 input_item_SetDuration
+input_item_SetLibvlcOwner
 input_item_SetMeta
 input_item_SetName
 input_item_SetURI


=====================================
test/libvlc/media_player.c
=====================================
@@ -134,6 +134,82 @@ static void test_media_player_set_media(const char** argv, int argc)
     mp_event_ctx_destroy(&ctx);
 }
 
+struct switch_media_ctx
+{
+    vlc_sem_t playing_sem;
+    vlc_sem_t media_changed_sem;
+};
+
+static void switch_media_on_state_changed(void *opaque, libvlc_state_t state)
+{
+    struct switch_media_ctx *ctx = opaque;
+    if (state == libvlc_Playing)
+        vlc_sem_post(&ctx->playing_sem);
+}
+
+static void switch_media_on_media_changed(void *opaque, libvlc_media_t *media)
+{
+    struct switch_media_ctx *ctx = opaque;
+    assert(media != NULL);
+    vlc_sem_post(&ctx->media_changed_sem);
+}
+
+static const struct libvlc_media_player_cbs switch_media_cbs = {
+    .version = 0,
+    .on_state_changed = switch_media_on_state_changed,
+    .on_media_changed = switch_media_on_media_changed,
+};
+
+static void test_media_player_switch_media(const char** argv, int argc)
+{
+    /* The track gives the es_out an info category, deleted during the input
+     * teardown: this sends media meta events referencing the draining media
+     * after the player switched to the next one. */
+    const char *file1 = "mock://audio_track_count=1;length=100000000";
+    const char *file2 = test_default_sample;
+
+    test_log ("Testing media switch while playing\n");
+
+    libvlc_instance_t *vlc = libvlc_new (argc, argv);
+    assert (vlc != NULL);
+
+    struct switch_media_ctx ctx;
+    vlc_sem_init(&ctx.playing_sem, 0);
+    vlc_sem_init(&ctx.media_changed_sem, 0);
+
+    libvlc_media_player_t *mp =
+        libvlc_media_player_new (vlc, &switch_media_cbs, &ctx);
+    assert (mp != NULL);
+
+    libvlc_media_t *md = libvlc_media_new_location(file1);
+    assert (md != NULL);
+    libvlc_media_player_set_media (mp, md);
+    vlc_sem_wait(&ctx.media_changed_sem);
+    /* The application drops its last reference: the media must stay valid as
+     * long as the player (and its draining input) can reference it */
+    libvlc_media_release (md);
+
+    libvlc_media_player_play (mp);
+    test_log ("Waiting for playing\n");
+    vlc_sem_wait(&ctx.playing_sem);
+
+    /* Switch to another media while playing: the first media input drains
+     * asynchronously and still sends events referencing the first media */
+    md = libvlc_media_new_location(file2);
+    assert (md != NULL);
+    libvlc_media_player_set_media (mp, md);
+    libvlc_media_release (md);
+
+    /* The media change is notified once the first input is destroyed: all the
+     * teardown events referencing the first media have been sent by then */
+    test_log ("Waiting for the media change\n");
+    vlc_sem_wait(&ctx.media_changed_sem);
+
+    libvlc_media_player_stop_async (mp);
+    libvlc_media_player_release (mp);
+    libvlc_release (vlc);
+}
+
 static void test_media_player_play_stop(const char** argv, int argc)
 {
     libvlc_instance_t *vlc;
@@ -680,6 +756,7 @@ int main (void)
     test_init();
 
     test_media_player_set_media (test_defaults_args, test_defaults_nargs);
+    test_media_player_switch_media (test_defaults_args, test_defaults_nargs);
     test_media_player_play_stop (test_defaults_args, test_defaults_nargs);
     test_media_player_pause_stop (test_defaults_args, test_defaults_nargs);
     test_media_player_tracks (test_defaults_args, test_defaults_nargs);



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/21bc9c4720a22e5a24b1cf96175b6f8f2592b63c...637b37d7d365ac9ba05fa56b80aa7b59b95a5066

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/21bc9c4720a22e5a24b1cf96175b6f8f2592b63c...637b37d7d365ac9ba05fa56b80aa7b59b95a5066
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list