gstreamer/subprojects/gst-editing-services/ges/ges-discoverer-manager.c
Thibault Saunier d159cc624a ges: discoverer-manager: Fix race when freeing unused discoverer
When a new discoverer was created for a thread so discovery could
recurse we could end up removing the wrong discoverer info from the
cache leading to freeing it while it was still discovering URIS, which
lead to the following assertion:

``` bt
Thread 1 (Thread 0x7fcc2e1a5840 (LWP 1855496)):
 #0  __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
 #1  0x00007fcc2e9d98a3 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
 #2  0x00007fcc2e9878ee in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
 #3  0x00007fcc2e96f8ff in __GI_abort () at abort.c:79
 #4  0x00007fcc2ed80056 in g_assertion_message (domain=domain@entry=0x7fcc2f2c19f9 "GES", file=file@entry=0x7fcc2f2dfd68 "../subprojects/gst-editing-services/ges/ges-discoverer-manager.c", line=line@entry=20, func=func@entry=0x7fcc2f2e0030 <__func__.7> "ges_discoverer_data_free", message=message@entry=0x12dab70 "assertion failed: (data->n_uri == 0)") at ../glib/gtestutils.c:3497
 #5  0x00007fcc2ede1d87 in g_assertion_message_expr (domain=domain@entry=0x7fcc2f2c19f9 "GES", file=file@entry=0x7fcc2f2dfd68 "../subprojects/gst-editing-services/ges/ges-discoverer-manager.c", line=line@entry=20, func=func@entry=0x7fcc2f2e0030 <__func__.7> "ges_discoverer_data_free", expr=expr@entry=0x7fcc2f2dfcf1 "data->n_uri == 0") at ../glib/gtestutils.c:3523
 #6  0x00007fcc2f2bd5c5 in ges_discoverer_data_free (data=0x160e430) at ../subprojects/gst-editing-services/ges/ges-discoverer-manager.c:20
 #7  0x00007fcc2ed8509d in g_atomic_rc_box_release_full (clear_func=0x7fcc2f2bd4f0 <ges_discoverer_data_free>, mem_block=0x160e430) at ../glib/garcbox.c:355
 #8  g_atomic_rc_box_release_full (mem_block=0x160e430, clear_func=0x7fcc2f2bd4f0 <ges_discoverer_data_free>) at ../glib/garcbox.c:338
 #9  0x00007fcc2eda6809 in g_hash_table_remove_internal (notify=1, key=0x10448a0, hash_table=0x12e0be0) at ../glib/ghash.c:1776
 #10 g_hash_table_remove (hash_table=0x12e0be0, key=0x10448a0) at ../glib/ghash.c:1804
 #11 0x00007fcc2f2bd95f in cleanup_discoverer_cb (discoverer_data=discoverer_data@entry=0x13e7000) at ../subprojects/gst-editing-services/ges/ges-discoverer-manager.c:379
 #12 0x00007fcc2edbc759 in g_timeout_dispatch (source=0x15a6060, callback=0x7fcc2f2bd910 <cleanup_discoverer_cb>, user_data=0x13e7000) at ../glib/gmain.c:5121
 #13 0x00007fcc2edbbe1c in g_main_dispatch (context=0x1044700) at ../glib/gmain.c:3476
 #14 g_main_context_dispatch_unlocked (context=0x1044700) at ../glib/gmain.c:4284
 #15 0x00007fcc2ee16d78 in g_main_context_iterate_unlocked.isra.0 (context=0x1044700, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:4349
 #16 0x00007fcc2edbd407 in g_main_loop_run (loop=0x12ccbd0) at ../glib/gmain.c:4551
 #17 0x00007fcc2f285791 in ges_uri_clip_asset_request_sync (uri=uri@entry=0x12d7980 "file:///var/home/phil/gstreamer/build/subprojects/gst-integration-testsuites/logs/ges/scenarios/check_seek_on_very_deeply_nested_timeline/nested_timeline_depth6.xges", error=error@entry=0x7fff499015a8) at ../subprojects/gst-editing-services/ges/ges-uri-asset.c:688
 #18 0x00007fcc2f28949b in ges_project_create_asset_sync (project=0x12c1c70, id=id@entry=0x12d7980 "file:///var/home/phil/gstreamer/build/subprojects/gst-integration-testsuites/logs/ges/scenarios/check_seek_on_very_deeply_nested_timeline/nested_timeline_depth6.xges", extractable_type=extractable_type@entry=Python Exception <class 'gdb.error'>: value has been optimized out , error=error@entry=0x7fff499015a8) at ../subprojects/gst-editing-services/ges/ges-project.c:959
 #19 0x00007fcc2f2ba484 in _ges_get_asset_from_timeline (timeline=timeline@entry=0x12bdc80, type=type@entry=Python Exception <class 'gdb.error'>: value has been optimized out , id=id@entry=0x12d7980 "file:///var/home/phil/gstreamer/build/subprojects/gst-integration-testsuites/logs/ges/scenarios/check_seek_on_very_deeply_nested_timeline/nested_timeline_depth6.xges", error=error@entry=0x7fff49901728) at ../subprojects/gst-editing-services/ges/ges-structured-interface.c:540
 #20 0x00007fcc2f2ba9b2 in _ges_add_clip_from_struct (timeline=0x12bdc80, structure=0x157f690, error=0x7fff49901728) at ../subprojects/gst-editing-services/ges/ges-structured-interface.c:697
 #21 0x00007fcc2f2b6a9d in _validate_action_execute (scenario=0x15f7620, action=0x157f500) at ../subprojects/gst-editing-services/ges/ges-validate.c:922
 #22 0x00007fcc2eef5c9c in gst_validate_execute_action (action=0x157f500, action_type=0x13e0500) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2541
 #23 gst_validate_execute_action (action_type=0x13e0500, action=0x157f500) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2507
 #24 0x00007fcc2eef8ce3 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2782
 #25 0x00007fcc2eef9dee in _action_set_done (action=0x157efb0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #26 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157efb0, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #27 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157efb0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #28 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #29 0x00007fcc2eef9dee in _action_set_done (action=0x157ea60) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #30 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157ea60, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #31 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157ea60) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #32 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #33 0x00007fcc2eef9dee in _action_set_done (action=0x157e510) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #34 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157e510, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #35 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157e510) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #36 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #37 0x00007fcc2eef9dee in _action_set_done (action=0x157df10) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #38 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157df10, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #39 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157df10) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #40 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #41 0x00007fcc2eef9dee in _action_set_done (action=0x157d9e0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #42 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157d9e0, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #43 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157d9e0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #44 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #45 0x00007fcc2eef9dee in _action_set_done (action=0x157d3e0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #46 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157d3e0, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #47 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157d3e0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #48 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #49 0x00007fcc2eef9dee in _action_set_done (action=0x157cf70) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #50 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157cf70, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #51 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157cf70) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #52 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #53 0x00007fcc2eef9dee in _action_set_done (action=0x157cb00) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #54 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157cb00, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #55 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157cb00) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #56 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #57 0x00007fcc2eef9dee in _action_set_done (action=0x157c690) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #58 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157c690, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #59 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157c690) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #60 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #61 0x00007fcc2eef9dee in _action_set_done (action=0x157c220) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #62 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x157c220, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #63 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x157c220) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #64 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #65 0x00007fcc2eef9dee in _action_set_done (action=0x15233c0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #66 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x15233c0, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #67 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x15233c0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #68 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #69 0x00007fcc2eef9dee in _action_set_done (action=0x1522f80) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #70 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x1522f80, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #71 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x1522f80) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #72 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #73 0x00007fcc2eef9dee in _action_set_done (action=0x1522ae0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #74 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x1522ae0, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #75 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x1522ae0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #76 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #77 0x00007fcc2eef9dee in _action_set_done (action=0x1522190) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #78 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x1522190, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #79 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x1522190) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #80 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #81 0x00007fcc2eef9dee in _action_set_done (action=0x1520ea0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6368
 #82 0x00007fcc2edbd26d in g_main_context_invoke_full (context=0x1044700, priority=200, function=0x7fcc2eef9ab0 <_action_set_done>, data=0x1520ea0, notify=0x7fcc2eeea5d0 <gst_validate_action_unref>) at ../glib/gmain.c:6533
 #83 0x00007fcc2eef6cf2 in gst_validate_action_set_done (action=0x1520ea0) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:6411
 #84 0x00007fcc2eef9018 in execute_next_action_full (scenario=<optimized out>, message=<optimized out>) at ../subprojects/gst-devtools/validate/gst/validate/gst-validate-scenario.c:2803
 #85 0x00007fcc2edbc759 in g_timeout_dispatch (source=0x14b6340, callback=0x7fcc2eef99c0 <execute_next_action>, user_data=0x15f7620) at ../glib/gmain.c:5121
 #86 0x00007fcc2edbbe1c in g_main_dispatch (context=0x1044700) at ../glib/gmain.c:3476
 #87 g_main_context_dispatch_unlocked (context=0x1044700) at ../glib/gmain.c:4284
 #88 0x00007fcc2ee16d78 in g_main_context_iterate_unlocked.isra.0 (context=context@entry=0x1044700, block=block@entry=1, dispatch=dispatch@entry=1, self=<optimized out>) at ../glib/gmain.c:4349
 #89 0x00007fcc2edb9a93 in g_main_context_iteration (context=context@entry=0x1044700, may_block=may_block@entry=1) at ../glib/gmain.c:4414
 #90 0x00007fcc2ec14c3d in g_application_run (application=application@entry=0x1042ab0, argc=argc@entry=4, argv=argv@entry=0x7fff499031e8) at ../gio/gapplication.c:2577
 #91 0x0000000000405dfd in real_main (argv=0x7fff499031e8, argc=4) at ../subprojects/gst-editing-services/tools/ges-launch.c:38
 #92 main (argc=4, argv=0x7fff499031e8) at ../subprojects/gst-editing-services/tools/ges-launch.c:56

```

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5619>
2023-11-08 08:24:15 +00:00

488 lines
13 KiB
C

#include "ges-internal.h"
#include "ges-discoverer-manager.h"
typedef struct
{
GWeakRef /*<GESDiscovererManager> */ manager;
GstDiscoverer *discoverer;
GThread *thread;
gint n_uri;
gulong load_serialized_info_id;
gulong source_setup_id;
gulong discovered_id;
} GESDiscovererData;
static void
ges_discoverer_data_free (GESDiscovererData * data)
{
GST_LOG ("Freeing discoverer %" GST_PTR_FORMAT, data->discoverer);
g_assert (data->n_uri == 0);
gst_discoverer_stop (data->discoverer);
g_signal_handler_disconnect (data->discoverer, data->load_serialized_info_id);
g_signal_handler_disconnect (data->discoverer, data->source_setup_id);
g_signal_handler_disconnect (data->discoverer, data->discovered_id);
g_weak_ref_clear (&data->manager);
g_object_unref (data->discoverer);
}
static void
ges_discoverer_data_unref (GESDiscovererData * data)
{
g_atomic_rc_box_release_full (data,
(GDestroyNotify) ges_discoverer_data_free);
}
/**
* GESDiscovererManager:
*
* Since: 1.24
*/
struct _GESDiscovererManager
{
GObject parent;
GHashTable *discoverers;
GMutex lock;
GstClockTime timeout;
gboolean use_cache;
};
G_DEFINE_TYPE (GESDiscovererManager, ges_discoverer_manager, G_TYPE_OBJECT);
enum
{
PROP_0,
PROP_TIMEOUT,
PROP_USE_CACHE,
N_PROPERTIES
};
enum
{
LOAD_SERIALIZED_INFO_SIGNAL,
DISCOVERER_SOURCE_SETUP,
DISCOVERER_SIGNAL,
N_SIGNALS
};
#define DEFAULT_USE_CACHE FALSE
#define DEFAULT_TIMEOUT (60 * GST_SECOND)
static GParamSpec *properties[N_PROPERTIES] = { NULL, };
static guint signals[N_SIGNALS] = { 0, };
G_LOCK_DEFINE_STATIC (singleton_lock);
static GESDiscovererManager *self = NULL;
static void
ges_discoverer_manager_get_property (GObject * object,
guint property_id, GValue * value, GParamSpec * pspec)
{
GESDiscovererManager *self = GES_DISCOVERER_MANAGER (object);
switch (property_id) {
case PROP_TIMEOUT:
g_value_set_uint64 (value, ges_discoverer_manager_get_timeout (self));
break;
case PROP_USE_CACHE:
g_value_set_boolean (value, ges_discoverer_manager_get_use_cache (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
ges_discoverer_manager_set_property (GObject * object,
guint property_id, const GValue * value, GParamSpec * pspec)
{
GESDiscovererManager *self = GES_DISCOVERER_MANAGER (object);
switch (property_id) {
case PROP_TIMEOUT:
ges_discoverer_manager_set_timeout (self, g_value_get_uint64 (value));
break;
case PROP_USE_CACHE:
ges_discoverer_manager_set_use_cache (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
ges_discoverer_manager_finalize (GObject * object)
{
GESDiscovererManager *self = GES_DISCOVERER_MANAGER (object);
GHashTableIter iter;
GESDiscovererData *discoverer_data;
GMainContext *context = g_main_context_get_thread_default ();
if (!context)
context = g_main_context_default ();
g_mutex_lock (&self->lock);
g_hash_table_iter_init (&iter, self->discoverers);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) & discoverer_data)) {
GSource *source;
while ((source =
g_main_context_find_source_by_user_data (context,
discoverer_data))) {
g_source_destroy (source);
}
}
g_hash_table_unref (self->discoverers);
g_mutex_unlock (&self->lock);
G_OBJECT_CLASS (ges_discoverer_manager_parent_class)->finalize (object);
}
static void
ges_discoverer_manager_class_init (GESDiscovererManagerClass * klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = ges_discoverer_manager_finalize;
object_class->set_property = ges_discoverer_manager_set_property;
object_class->get_property = ges_discoverer_manager_get_property;
/**
* GESDiscovererManager:timeout:
*
* The timeout (in milliseconds) for the #GstDiscoverer operations
*
* Since: 1.24
*/
properties[PROP_TIMEOUT] =
g_param_spec_uint64 ("timeout", "Timeout",
"The timeout for the discoverer", 0, GST_CLOCK_TIME_NONE, DEFAULT_TIMEOUT,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
/**
* GESDiscovererManager::use-cache:
*
* Whether to use a serialized version of the discoverer info from our own
* cache if accessible. This allows the discovery to be much faster as when
* using this option, we do not need to create a #GstPipeline and run it, but
* instead, just reload the #GstDiscovererInfo in its serialized form.
*
* The cache files are saved in `$XDG_CACHE_DIR/gstreamer-1.0/discoverer/`.
*
* For more granularity or to use your own cache, using the
* #GESDiscovererManager::load-serialized-info signal is recommended.
*
* Since: 1.24
*/
properties[PROP_USE_CACHE] =
g_param_spec_boolean ("use-cache", "Use cache",
"Whether to use a serialized version of the discoverer info from our own cache if accessible",
DEFAULT_USE_CACHE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class, N_PROPERTIES, properties);
/**
* GESDiscovererManager::load-serialized-info:
* @manager: the #GESDiscovererManager
* @uri: The URI to load the serialized info for
*
* Retrieves information about a URI from and external source of information,
* like a cache file. This is used by the discoverer to speed up the
* discovery.
*
* Returns: (nullable) (transfer full): The #GstDiscovererInfo representing
* @uri, or %NULL if no information
*
* Since: 1.24
*/
signals[LOAD_SERIALIZED_INFO_SIGNAL] =
g_signal_new ("load-serialized-info", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, GST_TYPE_DISCOVERER_INFO, 1, G_TYPE_STRING);
/**
* GESDiscovererManager::source-setup:
* @manager: the #GESDiscovererManager
* @source: The source #GstElement to setup
*
* Allows to setup the source element before the discoverer runs.
*
* Since: 1.24
*/
signals[DISCOVERER_SOURCE_SETUP] =
g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
/**
* GESDiscovererManager::discovered: (attributes doc.skip=true)
* @manager: the #GESDiscovererManager
* @info: The #GstDiscovererInfo representing the discovered URI
* @error: (nullable): The #GError that occurred, or %NULL
*
* Since: 1.24
*/
signals[DISCOVERER_SIGNAL] =
g_signal_new ("discovered", G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL, G_TYPE_NONE, 2, GST_TYPE_DISCOVERER_INFO,
G_TYPE_ERROR);
}
void
ges_discoverer_manager_init (GESDiscovererManager * self)
{
self->discoverers = g_hash_table_new_full (g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify) ges_discoverer_data_unref);
}
/**
* ges_discoverer_manager_get_default:
*
* Returns: (transfer full): The default #GESDiscovererManager
*
* Since: 1.24
*/
GESDiscovererManager *
ges_discoverer_manager_get_default (void)
{
G_LOCK (singleton_lock);
if (G_UNLIKELY (self == NULL)) {
self = g_object_new (GES_TYPE_DISCOVERER_MANAGER, NULL);
}
G_UNLOCK (singleton_lock);
return g_object_ref (self);
}
/**
* ges_discoverer_manager_get_use_cache:
* @self: The #GESDiscovererManager
*
* Returns: Whether to use the cache or not
*
* Since: 1.24
*/
gboolean
ges_discoverer_manager_get_use_cache (GESDiscovererManager * self)
{
g_return_val_if_fail (GES_IS_DISCOVERER_MANAGER (self), FALSE);
return self->use_cache;
}
/**
* ges_discoverer_manager_set_use_cache:
* @self: The #GESDiscovererManager
* @use_cache: Whether to use the cache
*
* Sets whether to use the cache or not
*
* Since: 1.24
*/
void
ges_discoverer_manager_set_use_cache (GESDiscovererManager * self,
gboolean use_cache)
{
g_return_if_fail (GES_IS_DISCOVERER_MANAGER (self));
self->use_cache = use_cache;
}
/**
* ges_discoverer_manager_get_timeout:
* @self: The #GESDiscovererManager
*
* Returns: The timeout to use for the discoverer
*
* Since: 1.24
*/
GstClockTime
ges_discoverer_manager_get_timeout (GESDiscovererManager * self)
{
g_return_val_if_fail (GES_IS_DISCOVERER_MANAGER (self), 0);
return self->timeout;
}
/**
* ges_discoverer_manager_set_timeout:
* @self: The #GESDiscovererManager
* @timeout: The timeout to set
*
* Sets the timeout to use for the discoverer
*
* Since: 1.24
*/
void
ges_discoverer_manager_set_timeout (GESDiscovererManager * self,
GstClockTime timeout)
{
GHashTableIter iter;
GESDiscovererData *discoverer_data;
g_return_if_fail (GES_IS_DISCOVERER_MANAGER (self));
self->timeout = timeout;
g_mutex_lock (&self->lock);
g_hash_table_iter_init (&iter, self->discoverers);
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) & discoverer_data))
g_object_set (discoverer_data->discoverer, "timeout", timeout, NULL);
g_mutex_unlock (&self->lock);
}
static GstDiscovererInfo *
proxy_load_serialized_info_cb (GESDiscovererManager * self, const gchar * uri)
{
GstDiscovererInfo *info;
g_signal_emit (self, signals[LOAD_SERIALIZED_INFO_SIGNAL], 0, uri, &info);
return info;
}
static void
source_setup_cb (GESDiscovererManager * self, GstElement * source)
{
g_signal_emit (self, signals[DISCOVERER_SOURCE_SETUP], 0, source);
}
static gboolean
cleanup_discoverer_cb (GESDiscovererData * discoverer_data)
{
GESDiscovererManager *self = g_weak_ref_get (&discoverer_data->manager);
gint res = G_SOURCE_CONTINUE;
if (!self) {
return G_SOURCE_REMOVE;
}
g_mutex_lock (&self->lock);
if (discoverer_data->n_uri > 0) {
GST_DEBUG_OBJECT (self, "Discoverer still has %d uris to discover",
discoverer_data->n_uri);
goto done;
}
GST_DEBUG_OBJECT (self, "Removing unused discoverer");
// Remove the discoverer if the one is use for that thread is still the
// one we have been asked to free, otherwise this one will be destroyed anyway
// once this source is removed
res = G_SOURCE_REMOVE;
if (g_hash_table_lookup (self->discoverers,
discoverer_data->thread) == discoverer_data) {
g_hash_table_remove (self->discoverers, discoverer_data->thread);
}
done:
g_mutex_unlock (&self->lock);
g_object_unref (self);
return res;
}
static void
proxy_discovered_cb (GESDiscovererManager * self,
GstDiscovererInfo * info, GError * err, GstDiscoverer * discoverer)
{
g_signal_emit (self, signals[DISCOVERER_SIGNAL], 0, info, err);
g_mutex_lock (&self->lock);
GESDiscovererData *data =
g_hash_table_lookup (self->discoverers, g_thread_self ());
if (data) {
data->n_uri--;
data = g_atomic_rc_box_acquire (data);
}
g_mutex_unlock (&self->lock);
if (data) {
ges_timeout_add (1000, (GSourceFunc) cleanup_discoverer_cb, data,
(GDestroyNotify) ges_discoverer_data_unref);
}
}
static GESDiscovererData *
create_discoverer (GESDiscovererManager * self)
{
GstDiscoverer *discoverer;
GESDiscovererData *data = g_atomic_rc_box_new0 (GESDiscovererData);
discoverer = gst_discoverer_new (self->timeout, NULL);
data->thread = g_thread_self ();
g_weak_ref_set (&data->manager, self);
data->load_serialized_info_id =
g_signal_connect_swapped (discoverer, "load-serialized-info",
G_CALLBACK (proxy_load_serialized_info_cb), self);
data->source_setup_id =
g_signal_connect_swapped (discoverer, "source-setup",
G_CALLBACK (source_setup_cb), self);
data->discovered_id =
g_signal_connect_swapped (discoverer, "discovered",
G_CALLBACK (proxy_discovered_cb), self);
g_object_set (discoverer, "use-cache", self->use_cache, NULL);
gst_discoverer_start (discoverer);
data->discoverer = discoverer;
return data;
}
static GESDiscovererData *
ges_discoverer_manager_get_discoverer (GESDiscovererManager * self)
{
GESDiscovererData *ret;
g_return_val_if_fail (GES_IS_DISCOVERER_MANAGER (self), NULL);
g_mutex_lock (&self->lock);
ret = g_hash_table_lookup (self->discoverers, g_thread_self ());
if (!ret) {
ret = create_discoverer (self);
} else {
g_hash_table_steal (self->discoverers, g_thread_self ());
}
g_mutex_unlock (&self->lock);
return ret;
}
gboolean
ges_discoverer_manager_start_discovery (GESDiscovererManager * self,
const gchar * uri)
{
GESDiscovererData *disco_data;
g_return_val_if_fail (uri != NULL, FALSE);
disco_data = ges_discoverer_manager_get_discoverer (self);
g_mutex_lock (&self->lock);
gboolean res =
gst_discoverer_discover_uri_async (disco_data->discoverer, uri);
disco_data->n_uri++;
g_hash_table_insert (self->discoverers, g_thread_self (), disco_data);
g_mutex_unlock (&self->lock);
return res;
}
void
ges_discoverer_manager_cleanup (void)
{
G_LOCK (singleton_lock);
gst_clear_object (&self);
G_UNLOCK (singleton_lock);
}