[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