1 /*
2 This file is part of Deadbeef Player source code
3 http://deadbeef.sourceforge.net
4
5 plugin management
6
7 Copyright (C) 2009-2013 Alexey Yakovenko
8
9 This software is provided 'as-is', without any express or implied
10 warranty. In no event will the authors be held liable for any damages
11 arising from the use of this software.
12
13 Permission is granted to anyone to use this software for any purpose,
14 including commercial applications, and to alter it and redistribute it
15 freely, subject to the following restrictions:
16
17 1. The origin of this software must not be misrepresented; you must not
18 claim that you wrote the original software. If you use this software
19 in a product, an acknowledgment in the product documentation would be
20 appreciated but is not required.
21 2. Altered source versions must be plainly marked as such, and must not be
22 misrepresented as being the original software.
23 3. This notice may not be removed or altered from any source distribution.
24
25 Alexey Yakovenko waker@users.sourceforge.net
26 */
27 #include <dirent.h>
28 #include <dlfcn.h>
29 #include <assert.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #ifndef __linux__
36 #define _POSIX_C_SOURCE 1
37 #endif
38 #include <limits.h>
39 #ifdef HAVE_CONFIG_H
40 # include "config.h"
41 #endif
42 #include "gettext.h"
43 #include "plugins.h"
44 #include "md5/md5.h"
45 #include "messagepump.h"
46 #include "threading.h"
47 #include "playlist.h"
48 #include "volume.h"
49 #include "streamer.h"
50 #include "common.h"
51 #include "conf.h"
52 #include "junklib.h"
53 #include "vfs.h"
54 #include "premix.h"
55 #include "dsppreset.h"
56 #include "pltmeta.h"
57 #include "metacache.h"
58 #include "tf.h"
59 #include "playqueue.h"
60 #include "sort.h"
61
62 #define trace(...) { fprintf(stderr, __VA_ARGS__); }
63 //#define trace(fmt,...)
64
65 //#define DISABLE_VERSIONCHECK 1
66
67 #if defined(HAVE_COCOAUI) || defined(OSX_APPBUNDLE)
68 #define PLUGINEXT ".dylib"
69 #else
70 #define PLUGINEXT ".so"
71 #endif
72
73 const char *lowprio_plugin_ids[] = {
74 "ffmpeg",
75 NULL
76 };
77
78 // internal plugin list
79 typedef struct plugin_s {
80 void *handle;
81 DB_plugin_t *plugin;
82 struct plugin_s *next;
83 } plugin_t;
84
85 static plugin_t *plugins;
86 static plugin_t *plugins_tail;
87
88 // this list only gets used during plugin loading,
89 // then it gets appended to the above "plugins" list,
90 // and set to NULL
91 static plugin_t *plugins_lowprio;
92 static plugin_t *plugins_lowprio_tail;
93
94 #define MAX_PLUGINS 100
95 static DB_plugin_t *g_plugins[MAX_PLUGINS+1];
96
97 #define MAX_GUI_PLUGINS 10
98 static char *g_gui_names[MAX_GUI_PLUGINS+1];
99 static int g_num_gui_names;
100
101 #define MAX_DECODER_PLUGINS 50
102 static DB_decoder_t *g_decoder_plugins[MAX_DECODER_PLUGINS+1];
103
104 #define MAX_VFS_PLUGINS 10
105 static DB_vfs_t *g_vfs_plugins[MAX_VFS_PLUGINS+1];
106
107 #define MAX_DSP_PLUGINS 10
108 static DB_dsp_t *g_dsp_plugins[MAX_DSP_PLUGINS+1];
109
110 #define MAX_OUTPUT_PLUGINS 10
111 static DB_output_t *g_output_plugins[MAX_OUTPUT_PLUGINS+1];
112 static DB_output_t *output_plugin = NULL;
113
114 #define MAX_PLAYLIST_PLUGINS 10
115 static DB_playlist_t *g_playlist_plugins[MAX_PLAYLIST_PLUGINS+1];
116
117 static uintptr_t background_jobs_mutex;
118 static int num_background_jobs;
119
120 // deadbeef api
121 static DB_functions_t deadbeef_api = {
122 .vmajor = DB_API_VERSION_MAJOR,
123 .vminor = DB_API_VERSION_MINOR,
124 .md5 = plug_md5,
125 .md5_to_str = plug_md5_to_str,
126 .md5_init = (void (*)(DB_md5_t *s))md5_init,
127 .md5_append = (void (*)(DB_md5_t *s, const uint8_t *daya, int nbytes))md5_append,
128 .md5_finish = (void (*)(DB_md5_t *s, uint8_t digest[16]))md5_finish,
129 .get_output = plug_get_output,
130 .playback_get_pos = plug_playback_get_pos,
131 .playback_set_pos = plug_playback_set_pos,
132 // streamer access
133 .streamer_get_playing_track = (DB_playItem_t *(*) (void))streamer_get_playing_track,
134 .streamer_get_streaming_track = (DB_playItem_t *(*) (void))streamer_get_streaming_track,
135 .streamer_get_playpos = streamer_get_playpos,
136 .streamer_ok_to_read = streamer_ok_to_read,
137 .streamer_reset = streamer_reset,
138 .streamer_read = streamer_read,
139 .streamer_set_bitrate = streamer_set_bitrate,
140 .streamer_get_apx_bitrate = streamer_get_apx_bitrate,
141 .streamer_get_current_fileinfo = streamer_get_current_fileinfo,
142 .streamer_get_current_playlist = streamer_get_current_playlist,
143 .streamer_get_dsp_chain = streamer_get_dsp_chain,
144 .streamer_set_dsp_chain = streamer_set_dsp_chain,
145 .streamer_dsp_refresh = streamer_dsp_refresh,
146 // folders
147 .get_config_dir = plug_get_config_dir,
148 .get_prefix = plug_get_prefix,
149 .get_doc_dir = plug_get_doc_dir,
150 .get_plugin_dir = plug_get_plugin_dir,
151 .get_pixmap_dir = plug_get_pixmap_dir,
152 // threading
153 .thread_start = thread_start,
154 .thread_start_low_priority = thread_start_low_priority,
155 .thread_join = thread_join,
156 .thread_detach = thread_detach,
157 .thread_exit = thread_exit,
158 .mutex_create = mutex_create,
159 .mutex_create_nonrecursive = mutex_create_nonrecursive,
160 .mutex_free = mutex_free,
161 .mutex_lock = mutex_lock,
162 .mutex_unlock = mutex_unlock,
163 .cond_create = cond_create,
164 .cond_free = cond_free,
165 .cond_wait = cond_wait,
166 .cond_signal = cond_signal,
167 .cond_broadcast = cond_broadcast,
168 // playlist management
169 .plt_ref = (void (*) (ddb_playlist_t *plt))plt_ref,
170 .plt_unref = (void (*) (ddb_playlist_t *plt))plt_unref,
171 .plt_get_count = plt_get_count,
172 .plt_get_head = (DB_playItem_t * (*) (int plt))plt_get_head,
173 .plt_get_sel_count = plt_get_sel_count,
174 .plt_add = plt_add,
175 .plt_remove = plt_remove,
176 .plt_clear = (void (*)(ddb_playlist_t *))plt_clear,
177 .pl_clear = pl_clear,
178 .plt_set_curr = (void (*) (ddb_playlist_t *plt))plt_set_curr,
179 .plt_get_curr = (ddb_playlist_t *(*) (void))plt_get_curr,
180 .plt_set_curr_idx = (void (*) (int plt))plt_set_curr_idx,
181 .plt_get_curr_idx = (int (*) (void))plt_get_curr_idx,
182 .plt_move = plt_move,
183 // playlist saving and loading
184 .plt_load = (DB_playItem_t * (*) (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_load,
185 .plt_save = (int (*)(ddb_playlist_t *plt, DB_playItem_t *first, DB_playItem_t *last, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_save,
186 // getting and working with a handle must be guarded using plt_lock/unlock
187 .plt_get_for_idx = (ddb_playlist_t *(*)(int idx))plt_get_for_idx,
188 .plt_get_title = (int (*)(ddb_playlist_t *handle, char *buffer, int sz))plt_get_title,
189 .plt_set_title = (int (*)(ddb_playlist_t *handle, const char *buffer))plt_set_title,
190 .plt_modified = (void (*) (ddb_playlist_t *handle))plt_modified,
191 .plt_get_modification_idx = (int (*) (ddb_playlist_t *handle))plt_get_modification_idx,
192 .plt_get_item_idx = (int (*) (ddb_playlist_t *plt, DB_playItem_t *it, int iter))plt_get_item_idx,
193
194 // playlist metadata
195 .plt_add_meta = (void (*) (ddb_playlist_t *handle, const char *key, const char *value))plt_add_meta,
196 .plt_replace_meta = (void (*) (ddb_playlist_t *handle, const char *key, const char *value))plt_replace_meta,
197 .plt_append_meta = (void (*) (ddb_playlist_t *handle, const char *key, const char *value))plt_append_meta,
198 .plt_set_meta_int = (void (*) (ddb_playlist_t *handle, const char *key, int value))plt_set_meta_int,
199 .plt_set_meta_float = (void (*) (ddb_playlist_t *handle, const char *key, float value))plt_set_meta_float,
200 .plt_find_meta = (const char *(*) (ddb_playlist_t *handle, const char *key))plt_find_meta,
201 .plt_get_metadata_head = (DB_metaInfo_t * (*) (ddb_playlist_t *handle))plt_get_metadata_head,
202 .plt_delete_metadata = (void (*) (ddb_playlist_t *handle, DB_metaInfo_t *meta))plt_delete_metadata,
203 .plt_find_meta_int = (int (*) (ddb_playlist_t *handle, const char *key, int def))plt_find_meta_int,
204 .plt_find_meta_float = (float (*) (ddb_playlist_t *handle, const char *key, float def))plt_find_meta_float,
205 .plt_delete_all_meta = (void (*) (ddb_playlist_t *handle))plt_delete_all_meta,
206
207 // operating on playlist items
208 .plt_insert_item = (DB_playItem_t *(*) (ddb_playlist_t *playlist, DB_playItem_t *after, DB_playItem_t *it))plt_insert_item,
209 .plt_insert_file = (DB_playItem_t *(*) (ddb_playlist_t *playlist, DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_insert_file,
210 .plt_insert_dir = (DB_playItem_t *(*) (ddb_playlist_t *plt, DB_playItem_t *after, const char *dirname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_insert_dir,
211 .plt_set_item_duration = (void (*) (ddb_playlist_t *plt, DB_playItem_t *it, float duration))plt_set_item_duration,
212 .plt_remove_item = (int (*) (ddb_playlist_t *playlist, DB_playItem_t *it))plt_remove_item,
213 .plt_getselcount = (int (*) (ddb_playlist_t *playlist))plt_getselcount,
214
215 // playlist access
216 .pl_lock = pl_lock,
217 .pl_unlock = pl_unlock,
218 //.plt_lock = plt_lock,
219 //.plt_unlock = plt_unlock,
220 .pl_item_alloc = (DB_playItem_t* (*)(void))pl_item_alloc,
221 .pl_item_alloc_init = (DB_playItem_t* (*)(const char *fname, const char *decoder_id))pl_item_alloc_init,
222 .pl_item_ref = (void (*)(DB_playItem_t *))pl_item_ref,
223 .pl_item_unref = (void (*)(DB_playItem_t *))pl_item_unref,
224 .pl_item_copy = (void (*)(DB_playItem_t *, DB_playItem_t *))pl_item_copy,
225 .plt_add_file = (int (*) (ddb_playlist_t *plt, const char *, int (*cb)(DB_playItem_t *it, void *data), void *))plt_add_file,
226 .plt_add_dir = (int (*) (ddb_playlist_t *plt, const char *dirname, int (*cb)(DB_playItem_t *it, void *data), void *user_data))plt_add_dir,
227 .pl_add_files_begin = (int (*) (ddb_playlist_t *plt))pl_add_files_begin,
228 .pl_add_files_end = pl_add_files_end,
229 .pl_get_idx_of = (int (*) (DB_playItem_t *it))pl_get_idx_of,
230 .pl_get_idx_of_iter = (int (*) (DB_playItem_t *it, int iter))pl_get_idx_of_iter,
231 .pl_get_for_idx = (DB_playItem_t * (*)(int))pl_get_for_idx,
232 .pl_get_for_idx_and_iter = (DB_playItem_t * (*) (int idx, int iter))pl_get_for_idx_and_iter,
233 .pl_get_item_duration = (float (*) (DB_playItem_t *it))pl_get_item_duration,
234 .pl_get_item_flags = (uint32_t (*) (DB_playItem_t *it))pl_get_item_flags,
235 .pl_set_item_flags = (void (*) (DB_playItem_t *it, uint32_t flags))pl_set_item_flags,
236 .plt_sort = (void (*) (ddb_playlist_t *plt, int iter, int id, const char *format, int order))plt_sort,
237 .pl_items_copy_junk = (void (*)(DB_playItem_t *from, DB_playItem_t *first, DB_playItem_t *last))pl_items_copy_junk,
238 .pl_set_item_replaygain = (void (*)(DB_playItem_t *it, int idx, float value))pl_set_item_replaygain,
239 .pl_get_item_replaygain = (float (*)(DB_playItem_t *it, int idx))pl_get_item_replaygain,
240 .plt_get_totaltime = (float (*) (ddb_playlist_t *plt))plt_get_totaltime,
241 .plt_get_item_for_idx = (DB_playItem_t * (*) (ddb_playlist_t *playlist, int idx, int iter))plt_get_item_for_idx,
242 .pl_get_totaltime = pl_get_totaltime,
243 .pl_getcount = pl_getcount,
244 .plt_get_item_count = (int (*)(ddb_playlist_t *plt, int iter))plt_get_item_count,
245 .plt_delete_selected = (int (*) (ddb_playlist_t *plt))plt_delete_selected,
246 .pl_delete_selected = pl_delete_selected,
247 .plt_set_cursor = (void (*)(ddb_playlist_t *plt, int iter, int cursor))plt_set_cursor,
248 .pl_set_cursor = pl_set_cursor,
249 .plt_get_cursor = (int (*)(ddb_playlist_t *plt, int iter))plt_get_cursor,
250 .pl_get_cursor = pl_get_cursor,
251 .pl_set_selected = (void (*) (DB_playItem_t *, int))pl_set_selected,
252 .pl_is_selected = (int (*) (DB_playItem_t *))pl_is_selected,
253 .pl_save_current = pl_save_current,
254 .pl_save_all = pl_save_all,
255 .plt_select_all = (void (*) (ddb_playlist_t *plt))plt_select_all,
256 .pl_select_all = pl_select_all,
257 .plt_crop_selected = (void (*) (ddb_playlist_t *plt))plt_crop_selected,
258 .pl_crop_selected = pl_crop_selected,
259 .pl_getselcount = pl_getselcount,
260 .plt_get_first = (DB_playItem_t *(*) (ddb_playlist_t *plt, int))plt_get_first,
261 .pl_get_first = (DB_playItem_t *(*) (int))pl_get_first,
262 .plt_get_last = (DB_playItem_t *(*) (ddb_playlist_t *plt, int))plt_get_last,
263 .pl_get_last = (DB_playItem_t *(*) (int))pl_get_last,
264 .pl_get_next = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_next,
265 .pl_get_prev = (DB_playItem_t *(*) (DB_playItem_t *, int))pl_get_prev,
266 .pl_format_title = (int (*) (DB_playItem_t *it, int idx, char *s, int size, int id, const char *fmt))pl_format_title,
267 .pl_format_title_escaped = (int (*) (DB_playItem_t *it, int idx, char *s, int size, int id, const char *fmt))pl_format_title_escaped,
268 .pl_format_time = pl_format_time,
269 .plt_move_items = (void (*) (ddb_playlist_t *to, int iter, ddb_playlist_t *from, DB_playItem_t *drop_before, uint32_t *indexes, int count))plt_move_items,
270 .plt_copy_items = (void (*) (ddb_playlist_t *to, int iter, ddb_playlist_t *from, DB_playItem_t *before, uint32_t *indices, int cnt))plt_copy_items,
271 .plt_search_reset = (void (*)(ddb_playlist_t *plt))plt_search_reset,
272 .plt_search_process = (void (*)(ddb_playlist_t *plt, const char *t))plt_search_process,
273 .pl_get_playlist = (ddb_playlist_t * (*) (DB_playItem_t *it))pl_get_playlist,
274 // metainfo
275 .pl_add_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_add_meta,
276 .pl_append_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_append_meta,
277 .pl_set_meta_int = (void (*) (DB_playItem_t *it, const char *key, int value))pl_set_meta_int,
278 .pl_set_meta_float = (void (*) (DB_playItem_t *it, const char *key, float value))pl_set_meta_float,
279 .pl_delete_meta = (void (*)(DB_playItem_t *, const char *key))pl_delete_meta,
280 .pl_find_meta = (const char *(*) (DB_playItem_t *, const char *))pl_find_meta,
281 .pl_find_meta_int = (int (*) (DB_playItem_t *it, const char *key, int def))pl_find_meta_int,
282 .pl_find_meta_float = (float (*) (DB_playItem_t *it, const char *key, float def))pl_find_meta_float,
283 .pl_replace_meta = (void (*) (DB_playItem_t *, const char *, const char *))pl_replace_meta,
284 .pl_delete_all_meta = (void (*) (DB_playItem_t *it))pl_delete_all_meta,
285 .pl_get_metadata_head = (DB_metaInfo_t *(*)(DB_playItem_t *it))pl_get_metadata_head,
286 .pl_delete_metadata = (void (*)(DB_playItem_t *, DB_metaInfo_t *))pl_delete_metadata,
287 // cuesheet support
288 .plt_insert_cue_from_buffer = (DB_playItem_t *(*) (ddb_playlist_t *plt, DB_playItem_t *after, DB_playItem_t *origin, const uint8_t *buffer, int buffersize, int numsamples, int samplerate))plt_insert_cue_from_buffer,
289 .plt_insert_cue = (DB_playItem_t *(*)(ddb_playlist_t *plt, DB_playItem_t *after, DB_playItem_t *origin, int numsamples, int samplerate))plt_insert_cue,
290 // playqueue support
291 .pl_playqueue_push = (int (*) (DB_playItem_t *))playqueue_push,
292 .pl_playqueue_clear = playqueue_clear,
293 .pl_playqueue_pop = playqueue_pop,
294 .pl_playqueue_remove = (void (*) (DB_playItem_t *))playqueue_remove,
295 .pl_playqueue_test = (int (*) (DB_playItem_t *))playqueue_test,
296 // volume control
297 .volume_set_db = plug_volume_set_db,
298 .volume_get_db = volume_get_db,
299 .volume_set_amp = plug_volume_set_amp,
300 .volume_get_amp = volume_get_amp,
301 .volume_get_min_db = volume_get_min_db,
302 // junk reading
303 .junk_id3v1_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_id3v1_read,
304 .junk_id3v1_find = junk_id3v1_find,
305 .junk_id3v1_write = (int (*) (FILE *, DB_playItem_t *, const char *))junk_id3v1_write,
306 .junk_id3v2_find = junk_id3v2_find,
307 .junk_id3v2_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_id3v2_read,
308 .junk_id3v2_read_full = (int (*)(DB_playItem_t *, DB_id3v2_tag_t *tag, DB_FILE *fp))junk_id3v2_read_full,
309 .junk_id3v2_convert_24_to_23 = junk_id3v2_convert_24_to_23,
310 .junk_id3v2_convert_23_to_24 = junk_id3v2_convert_23_to_24,
311 .junk_id3v2_convert_22_to_24 = junk_id3v2_convert_22_to_24,
312 .junk_id3v2_free = junk_id3v2_free,
313 .junk_id3v2_write = junk_id3v2_write,
314 .junk_id3v2_remove_frames = junk_id3v2_remove_frames,
315 .junk_id3v2_add_text_frame = junk_id3v2_add_text_frame,
316 .junk_apev2_read = (int (*)(DB_playItem_t *it, DB_FILE *fp))junk_apev2_read,
317 .junk_apev2_read_mem = (int (*) (DB_playItem_t *it, char *mem, int size))junk_apev2_read_mem,
318 .junk_apev2_read_full = (int (*) (DB_playItem_t *it, DB_apev2_tag_t *tag_store, DB_FILE *fp))junk_apev2_read_full,
319 .junk_apev2_read_full_mem = (int (*) (DB_playItem_t *it, DB_apev2_tag_t *tag_store, char *mem, int memsize))junk_apev2_read_full_mem,
320 .junk_apev2_find = junk_apev2_find,
321 .junk_apev2_remove_frames = junk_apev2_remove_frames,
322 .junk_apev2_add_text_frame = junk_apev2_add_text_frame,
323 .junk_apev2_free = junk_apev2_free,
324 .junk_apev2_write = junk_apev2_write,
325 .junk_get_leading_size = junk_get_leading_size,
326 .junk_get_leading_size_stdio = junk_get_leading_size_stdio,
327 .junk_detect_charset = junk_detect_charset,
328 .junk_recode = junk_recode,
329 .junk_iconv = junk_iconv,
330 .junk_rewrite_tags = (int (*) (DB_playItem_t *it, uint32_t flags, int id3v2_version, const char *id3v1_encoding))junk_rewrite_tags,
331 // vfs
332 .fopen = vfs_fopen,
333 .fclose = vfs_fclose,
334 .fread = vfs_fread,
335 .fseek = vfs_fseek,
336 .ftell = vfs_ftell,
337 .rewind = vfs_rewind,
338 .fgetlength = vfs_fgetlength,
339 .fget_content_type = vfs_get_content_type,
340 .fset_track = (void (*) (DB_FILE *stream, DB_playItem_t *it))vfs_set_track,
341 .fabort = vfs_fabort,
342 // message passing
343 .sendmessage = messagepump_push,
344 .event_alloc = messagepump_event_alloc,
345 .event_free = messagepump_event_free,
346 .event_send = messagepump_push_event,
347 // configuration access
348 .conf_lock = conf_lock,
349 .conf_unlock = conf_unlock,
350 .conf_get_str_fast = conf_get_str_fast,
351 .conf_get_str = conf_get_str,
352 .conf_get_float = conf_get_float,
353 .conf_get_int = conf_get_int,
354 .conf_get_int64 = conf_get_int64,
355 .conf_set_str = conf_set_str,
356 .conf_set_int = conf_set_int,
357 .conf_set_int64 = conf_set_int64,
358 .conf_set_float = conf_set_float,
359 .conf_find = conf_find,
360 .conf_remove_items = conf_remove_items,
361 .conf_save = conf_save,
362 // plugin communication
363 .plug_get_decoder_list = plug_get_decoder_list,
364 .plug_get_vfs_list = plug_get_vfs_list,
365 .plug_get_output_list = plug_get_output_list,
366 .plug_get_dsp_list = plug_get_dsp_list,
367 .plug_get_playlist_list = plug_get_playlist_list,
368 .plug_get_list = plug_get_list,
369 .plug_get_gui_names = plug_get_gui_names,
370 .plug_get_decoder_id = plug_get_decoder_id,
371 .plug_remove_decoder_id = plug_remove_decoder_id,
372 .plug_get_for_id = plug_get_for_id,
373 // misc utilities
374 .is_local_file = plug_is_local_file,
375 // pcm utilities
376 .pcm_convert = pcm_convert,
377 // dsp preset management
378 .dsp_preset_load = dsp_preset_load,
379 .dsp_preset_save = dsp_preset_save,
380 .dsp_preset_free = dsp_preset_free,
381 // new 1.2 APIs
382 .plt_alloc = (ddb_playlist_t *(*)(const char *title))plt_alloc,
383 .plt_free = (void (*)(ddb_playlist_t *plt))plt_free,
384 //.plt_insert = plt_insert,
385 .plt_set_fast_mode = (void (*)(ddb_playlist_t *plt, int fast))plt_set_fast_mode,
386 .plt_is_fast_mode = (int (*)(ddb_playlist_t *plt))plt_is_fast_mode,
387 .metacache_add_string = metacache_add_string,
388 .metacache_remove_string = metacache_remove_string,
389 .metacache_ref = metacache_ref,
390 .metacache_unref = metacache_unref,
391 .pl_find_meta_raw = (const char *(*) (DB_playItem_t *it, const char *key))pl_find_meta_raw,
392 // ******* new 1.3 APIs ********
393 .streamer_dsp_chain_save = streamer_dsp_chain_save,
394 // ******* new 1.4 APIs ********
395 .pl_get_meta = (int (*) (DB_playItem_t *it, const char *key, char *val, int size))pl_get_meta,
396 .pl_get_meta_raw = (int (*) (DB_playItem_t *it, const char *key, char *val, int size))pl_get_meta_raw,
397 .plt_get_meta = (int (*) (ddb_playlist_t *handle, const char *key, char *val, int size))plt_get_meta,
398 .pl_meta_exists = (int (*) (DB_playItem_t *it, const char *key))pl_meta_exists,
399 // ******* new 1.5 APIs ********
400 .vis_waveform_listen = vis_waveform_listen,
401 .vis_waveform_unlisten = vis_waveform_unlisten,
402 .vis_spectrum_listen = vis_spectrum_listen,
403 .vis_spectrum_unlisten = vis_spectrum_unlisten,
404 .audio_set_mute = audio_set_mute,
405 .audio_is_mute = audio_is_mute,
406 .background_job_increment = background_job_increment,
407 .background_job_decrement = background_job_decrement,
408 .have_background_jobs = have_background_jobs,
409 .plt_get_idx = (int (*)(ddb_playlist_t *))plt_get_idx,
410 .plt_save_n = plt_save_n,
411 .plt_save_config = (int (*)(ddb_playlist_t *))plt_save_config,
412 .listen_file_added = listen_file_added,
413 .unlisten_file_added = unlisten_file_added,
414 .listen_file_add_beginend = listen_file_add_beginend,
415 .unlisten_file_add_beginend = unlisten_file_add_beginend,
416 .plt_load2 = (DB_playItem_t * (*) (int visibility, ddb_playlist_t *plt, ddb_playItem_t *after, const char *fname, int *pabort, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_load2,
417 .plt_add_file2 = (int (*) (int visibility, ddb_playlist_t *plt, const char *fname, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_add_file2,
418 .plt_add_dir2 = (int (*) (int visibility, ddb_playlist_t *plt, const char *dirname, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_add_dir2,
419 .plt_insert_file2 = (ddb_playItem_t * (*) (int visibility, ddb_playlist_t *playlist, ddb_playItem_t *after, const char *fname, int *pabort, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_insert_file2,
420 .plt_insert_dir2 = (ddb_playItem_t *(*) (int visibility, ddb_playlist_t *plt, ddb_playItem_t *after, const char *dirname, int *pabort, int (*callback)(DB_playItem_t *it, void *user_data), void *user_data))plt_insert_dir2,
421 .plt_add_files_begin = (int (*) (ddb_playlist_t *plt, int visibility))plt_add_files_begin,
422 .plt_add_files_end = (void (*) (ddb_playlist_t *plt, int visibility))plt_add_files_end,
423 .plt_deselect_all = (void (*) (ddb_playlist_t *plt))plt_deselect_all,
424 // ******* new 1.6 APIs ********
425 .plt_set_scroll = (void (*) (ddb_playlist_t *plt, int scroll))plt_set_scroll,
426 .plt_get_scroll = (int (*) (ddb_playlist_t *plt))plt_get_scroll,
427 .tf_compile = tf_compile,
428 .tf_free = tf_free,
429 .tf_eval= tf_eval,
430
431 .plt_sort_v2 = (void (*) (ddb_playlist_t *plt, int iter, int id, const char *format, int order))plt_sort_v2,
432
433 .playqueue_push = (int (*) (DB_playItem_t *))playqueue_push,
434 .playqueue_clear = playqueue_clear,
435 .playqueue_pop = playqueue_pop,
436 .playqueue_remove = (void (*) (DB_playItem_t *))playqueue_remove,
437 .playqueue_test = (int (*) (DB_playItem_t *))playqueue_test,
438 .playqueue_get_count = (int (*) (void))playqueue_getcount,
439 .playqueue_get_item = (DB_playItem_t *(*) (int n))playqueue_get_item,
440 .playqueue_remove_nth = (int (*) (int n))playqueue_remove_nth,
441 .playqueue_insert_at = (void (*) (int n, DB_playItem_t *it))playqueue_insert_at,
442
443 .get_system_dir = plug_get_system_dir,
444
445 .action_set_playlist = action_set_playlist,
446 .action_get_playlist = action_get_playlist,
447
448 .tf_import_legacy = tf_import_legacy,
449 };
450
451 DB_functions_t *deadbeef = &deadbeef_api;
452
453 const char *
plug_get_config_dir(void)454 plug_get_config_dir (void) {
455 return dbconfdir;
456 }
457
458 const char *
plug_get_prefix(void)459 plug_get_prefix (void) {
460 return dbinstalldir;
461 }
462
463 const char *
plug_get_doc_dir(void)464 plug_get_doc_dir (void) {
465 return dbdocdir;
466 }
467
468 const char *
plug_get_plugin_dir(void)469 plug_get_plugin_dir (void) {
470 return dbplugindir;
471 }
472
473 const char *
plug_get_pixmap_dir(void)474 plug_get_pixmap_dir (void) {
475 return dbpixmapdir;
476 }
477
478 const char *
plug_get_system_dir(int dir_id)479 plug_get_system_dir (int dir_id) {
480 switch (dir_id) {
481 case DDB_SYS_DIR_CONFIG:
482 return dbconfdir;
483 case DDB_SYS_DIR_PREFIX:
484 return dbinstalldir;
485 case DDB_SYS_DIR_DOC:
486 return dbdocdir;
487 case DDB_SYS_DIR_PLUGIN:
488 return dbplugindir;
489 case DDB_SYS_DIR_PIXMAP:
490 return dbpixmapdir;
491 case DDB_SYS_DIR_CACHE:
492 return dbcachedir;
493 }
494 return NULL;
495 }
496
497 void
plug_volume_set_db(float db)498 plug_volume_set_db (float db) {
499 volume_set_db (db);
500 messagepump_push (DB_EV_VOLUMECHANGED, 0, 0, 0);
501 }
502
503 void
plug_volume_set_amp(float amp)504 plug_volume_set_amp (float amp) {
505 volume_set_amp (amp);
506 messagepump_push (DB_EV_VOLUMECHANGED, 0, 0, 0);
507 }
508
509 void
plug_md5(uint8_t sig[16],const char * in,int len)510 plug_md5 (uint8_t sig[16], const char *in, int len) {
511 md5_state_t st;
512 md5_init (&st);
513 md5_append (&st, in, len);
514 md5_finish (&st, sig);
515 }
516
517 void
plug_md5_to_str(char * str,const uint8_t sig[16])518 plug_md5_to_str (char *str, const uint8_t sig[16]) {
519 int i = 0;
520 static const char hex[] = "0123456789abcdef";
521 for (i = 0; i < 16; i++) {
522 *str++ = hex[(sig[i]&0xf0)>>4];
523 *str++ = hex[sig[i]&0xf];
524 }
525 *str = 0;
526 }
527
528 float
plug_playback_get_pos(void)529 plug_playback_get_pos (void) {
530 playItem_t *trk = streamer_get_playing_track ();
531 float dur = trk ? pl_get_item_duration (trk) : -1;
532 if (!trk || dur <= 0) {
533 if (trk) {
534 pl_item_unref (trk);
535 }
536 return 0;
537 }
538 if (trk) {
539 pl_item_unref (trk);
540 }
541 return streamer_get_playpos () * 100 / dur;
542 }
543
544 void
plug_playback_set_pos(float pos)545 plug_playback_set_pos (float pos) {
546 playItem_t *trk = streamer_get_playing_track ();
547 float dur = trk ? pl_get_item_duration (trk) : -1;
548 if (!trk || dur <= 0) {
549 if (trk) {
550 pl_item_unref (trk);
551 }
552 return;
553 }
554 float t = pos * dur / 100.f;
555 if (trk) {
556 pl_item_unref (trk);
557 }
558 streamer_set_seek (t);
559 }
560
561 int
plug_init_plugin(DB_plugin_t * (* loadfunc)(DB_functions_t *),void * handle)562 plug_init_plugin (DB_plugin_t* (*loadfunc)(DB_functions_t *), void *handle) {
563 DB_plugin_t *plugin_api = loadfunc (&deadbeef_api);
564 if (!plugin_api) {
565 return -1;
566 }
567
568 // check if same plugin with the same or bigger version is loaded already
569 plugin_t *prev = NULL;
570 for (plugin_t *p = plugins; p; prev = p, p = p->next) {
571 int same_id = p->plugin->id && plugin_api->id && !strcmp (p->plugin->id, plugin_api->id);
572 int same_name = (!p->plugin->id || !plugin_api->id) && p->plugin->name && plugin_api->name && !strcmp (p->plugin->name, plugin_api->name);
573 if (same_id || same_name) {
574 if (plugin_api->version_major > p->plugin->version_major || (plugin_api->version_major == p->plugin->version_major && plugin_api->version_minor > p->plugin->version_minor)) {
575 trace ("found newer version of plugin \"%s\" (%s), replacing\n", plugin_api->id, plugin_api->name);
576 // unload older plugin before replacing
577 dlclose (p->handle);
578 if (prev) {
579 prev->next = p->next;
580 }
581 else {
582 plugins = p->next;
583 }
584 if (p->handle) {
585 dlclose (p->handle);
586 }
587 free (p);
588 }
589 else {
590 trace ("found copy of plugin \"%s\" (%s), but newer version is already loaded\n", plugin_api->id, plugin_api->name)
591 return -1;
592 }
593 }
594 }
595
596 #if !DISABLE_VERSIONCHECK
597 if (plugin_api->api_vmajor != 0 || plugin_api->api_vminor != 0) {
598 // version check enabled
599 if (DB_API_VERSION_MAJOR != 9 && DB_API_VERSION_MINOR != 9) {
600 if (plugin_api->api_vmajor != DB_API_VERSION_MAJOR || plugin_api->api_vminor > DB_API_VERSION_MINOR) {
601 trace ("\033[0;31mWARNING: plugin \"%s\" wants API v%d.%d (got %d.%d), will not be loaded\033[0;m\n", plugin_api->name, plugin_api->api_vmajor, plugin_api->api_vminor, DB_API_VERSION_MAJOR, DB_API_VERSION_MINOR);
602 return -1;
603 }
604 }
605 }
606 else {
607 trace ("\033[0;31mWARNING: plugin \"%s\" has disabled version check. please don't distribute it!\033[0;m\n", plugin_api->name);
608 }
609 #endif
610
611 plugin_t *plug = malloc (sizeof (plugin_t));
612 memset (plug, 0, sizeof (plugin_t));
613 plug->plugin = plugin_api;
614 plug->handle = handle;
615
616 int lowprio = 0;
617 for (int n = 0; lowprio_plugin_ids[n]; n++) {
618 if (plugin_api->id && !strcmp (lowprio_plugin_ids[n], plugin_api->id)) {
619 lowprio = 1;
620 break;
621 }
622 }
623
624 if (lowprio) {
625 if (plugins_lowprio_tail) {
626 plugins_lowprio_tail->next = plug;
627 plugins_lowprio_tail = plug;
628 }
629 else {
630 plugins_lowprio = plugins_lowprio_tail = plug;
631 }
632 }
633 else {
634 if (plugins_tail) {
635 plugins_tail->next = plug;
636 plugins_tail = plug;
637 }
638 else {
639 plugins = plugins_tail = plug;
640 }
641 }
642
643 return 0;
644 }
645
dirent_alphasort(const struct dirent ** a,const struct dirent ** b)646 static int dirent_alphasort (const struct dirent **a, const struct dirent **b) {
647 return strcmp ((*a)->d_name, (*b)->d_name);
648 }
649
650 void
plug_remove_plugin(void * p)651 plug_remove_plugin (void *p) {
652 int i;
653 for (i = 0; g_plugins[i]; i++) {
654 if (g_plugins[i] == p) {
655 memmove (&g_plugins[i], &g_plugins[i+1], (MAX_PLUGINS+1-i-1) * sizeof (void*));
656 }
657 }
658 for (i = 0; g_decoder_plugins[i]; i++) {
659 if (g_decoder_plugins[i] == p) {
660 memmove (&g_decoder_plugins[i], &g_decoder_plugins[i+1], (MAX_DECODER_PLUGINS+1-i-1) * sizeof (void*));
661 break;
662 }
663 }
664 for (i = 0; g_vfs_plugins[i]; i++) {
665 if (g_vfs_plugins[i] == p) {
666 memmove (&g_vfs_plugins[i], &g_vfs_plugins[i+1], (MAX_VFS_PLUGINS+1-i-1) * sizeof (void*));
667 break;
668 }
669 }
670 for (i = 0; g_dsp_plugins[i]; i++) {
671 if (g_dsp_plugins[i] == p) {
672 memmove (&g_dsp_plugins[i], &g_dsp_plugins[i+1], (MAX_DSP_PLUGINS+1-i-1) * sizeof (void*));
673 break;
674 }
675 }
676 for (i = 0; g_output_plugins[i]; i++) {
677 if (g_output_plugins[i] == p) {
678 memmove (&g_output_plugins[i], &g_output_plugins[i+1], (MAX_OUTPUT_PLUGINS+1-i-1) * sizeof (void*));
679 break;
680 }
681 }
682 for (i = 0; g_playlist_plugins[i]; i++) {
683 if (g_playlist_plugins[i] == p) {
684 memmove (&g_playlist_plugins[i], &g_playlist_plugins[i+1], (MAX_PLAYLIST_PLUGINS+1-i-1) * sizeof (void*));
685 break;
686 }
687 }
688 }
689
690 // d_name must be writable w/o sideeffects; contain valid .so name
691 // l must be strlen(d_name)
692 static int
load_plugin(const char * plugdir,char * d_name,int l)693 load_plugin (const char *plugdir, char *d_name, int l) {
694 // hack for osx to skip *.0.so files
695 if (strstr (d_name, ".0.so")) {
696 return -1;
697 }
698 char fullname[PATH_MAX];
699 snprintf (fullname, PATH_MAX, "%s/%s", plugdir, d_name);
700
701 // check if the file exists, to avoid printing bogus errors
702 struct stat s;
703 if (0 != stat (fullname, &s)) {
704 return -1;
705 }
706
707 trace ("loading plugin %s/%s\n", plugdir, d_name);
708 void *handle = dlopen (fullname, RTLD_NOW);
709 if (!handle) {
710 trace ("dlopen error: %s\n", dlerror ());
711 #if defined(ANDROID) || defined(HAVE_COCOAUI)
712 return -1;
713 #else
714 strcpy (fullname + strlen(fullname) - sizeof (PLUGINEXT)+1, ".fallback.so");
715 trace ("trying %s...\n", fullname);
716 handle = dlopen (fullname, RTLD_NOW);
717 if (!handle) {
718 //trace ("dlopen error: %s\n", dlerror ());
719 return -1;
720 }
721 else {
722 fprintf (stderr, "successfully started fallback plugin %s\n", fullname);
723 }
724 #endif
725 }
726 d_name[l-sizeof (PLUGINEXT)+1] = 0;
727 strcat (d_name, "_load");
728 #ifndef ANDROID
729 DB_plugin_t *(*plug_load)(DB_functions_t *api) = dlsym (handle, d_name);
730 #else
731 DB_plugin_t *(*plug_load)(DB_functions_t *api) = dlsym (handle, d_name+3);
732 #endif
733 if (!plug_load) {
734 trace ("dlsym error: %s (%s)\n", dlerror (), d_name + 3);
735 dlclose (handle);
736 return -1;
737 }
738 if (plug_init_plugin (plug_load, handle) < 0) {
739 d_name[l-sizeof (PLUGINEXT)+1] = 0;
740 dlclose (handle);
741 return -1;
742 }
743 return 0;
744 }
745
746 static int
load_gui_plugin(const char ** plugdirs)747 load_gui_plugin (const char **plugdirs) {
748 #if defined HAVE_COCOAUI
749 return 0;
750 #endif
751
752 char conf_gui_plug[100];
753 conf_get_str ("gui_plugin", "GTK2", conf_gui_plug, sizeof (conf_gui_plug));
754 char name[100];
755
756 // try to load selected plugin
757 for (int i = 0; g_gui_names[i]; i++) {
758 trace ("checking GUI plugin: %s\n", g_gui_names[i]);
759 if (!strcmp (g_gui_names[i], conf_gui_plug)) {
760 trace ("found selected GUI plugin: %s\n", g_gui_names[i]);
761 for (int n = 0; plugdirs[n]; n++) {
762 snprintf (name, sizeof (name), "ddb_gui_%s" PLUGINEXT, conf_gui_plug);
763 if (!load_plugin (plugdirs[n], name, strlen (name))) {
764 return 0;
765 }
766 }
767 break;
768 }
769 }
770 trace ("selected GUI plugin not found or failed to load, trying to find another GUI plugin\n");
771
772 // try any plugin
773 for (int i = 0; g_gui_names[i]; i++) {
774 for (int n = 0; plugdirs[n]; n++) {
775 snprintf (name, sizeof (name), "ddb_gui_%s" PLUGINEXT, g_gui_names[i]);
776 if (!load_plugin (plugdirs[n], name, strlen (name))) {
777 return 0;
778 }
779 else {
780 trace ("the plugin not found or failed to load\n");
781 }
782 }
783 }
784 return -1;
785 }
786
787 static int
load_plugin_dir(const char * plugdir,int gui_scan)788 load_plugin_dir (const char *plugdir, int gui_scan) {
789 int n = 0;
790 char conf_blacklist_plugins[1000];
791 conf_get_str ("blacklist_plugins", "", conf_blacklist_plugins, sizeof (conf_blacklist_plugins));
792 if (gui_scan) {
793 trace ("searching for GUI plugins in %s\n", plugdir);
794 }
795 else {
796 trace ("loading plugins from %s\n", plugdir);
797 }
798 struct dirent **namelist = NULL;
799 n = scandir (plugdir, &namelist, NULL, dirent_alphasort);
800 if (n < 0)
801 {
802 if (namelist) {
803 free (namelist);
804 }
805 return 0;
806 }
807 else
808 {
809 trace ("load_plugin_dir %s: scandir found %d files\n", plugdir, n);
810 int i;
811 for (i = 0; i < n; i++)
812 {
813 // skip hidden files and fallback plugins
814 while (namelist[i]->d_name[0] != '.'
815 #if !defined(ANDROID) && !defined(HAVE_COCOAUI)
816 && !strstr (namelist[i]->d_name, ".fallback.")
817 #elif !defined(ANDROID)
818 && !strstr (namelist[i]->d_name, "libdeadbeef")
819 #endif
820 )
821 {
822 size_t l = strlen (namelist[i]->d_name);
823 if (l < (sizeof(PLUGINEXT)-1)) {
824 break;
825 }
826 if (strcasecmp (namelist[i]->d_name + l - sizeof(PLUGINEXT) + 1, PLUGINEXT)) {
827 break;
828 }
829 char d_name[256];
830 memcpy (d_name, namelist[i]->d_name, l+1);
831 #ifndef ANDROID
832 // no blacklisted
833 const uint8_t *p = conf_blacklist_plugins;
834 while (*p) {
835 const uint8_t *e = p;
836 while (*e && *e > 0x20) {
837 e++;
838 }
839 if (l-sizeof (PLUGINEXT)+1 == e-p) {
840 if (!strncmp (p, d_name, e-p)) {
841 p = NULL;
842 break;
843 }
844 }
845 p = e;
846 while (*p && *p <= 0x20) {
847 p++;
848 }
849 }
850 if (!p) {
851 trace ("plugin %s is blacklisted in config file\n", d_name);
852 break;
853 }
854 #endif
855
856 // add gui plugin names
857 if (!strncmp (d_name, "ddb_gui_", 8)) {
858 if (gui_scan) {
859 trace ("found gui plugin %s\n", d_name);
860 if (g_num_gui_names >= MAX_GUI_PLUGINS) {
861 fprintf (stderr, "too many gui plugins\n");
862 break; // no more gui plugins allowed
863 }
864 char *nm = d_name + 8;
865 char *e = strrchr (nm, '.');
866 if (!e) {
867 break;
868 }
869 if (strcmp (e, PLUGINEXT)) {
870 break;
871 }
872 *e = 0;
873 // ignore fallbacks
874 e = strrchr (nm, '.');
875 if (e && !strcasecmp (e, ".fallback")) {
876 break;
877 }
878 // add to list of unique names
879 size_t i;
880 for (i = 0; g_gui_names[i] && strcmp(g_gui_names[i], nm); i++);
881 if (!g_gui_names[i]) {
882 g_gui_names[i] = strdup (nm);
883 g_gui_names[++g_num_gui_names] = NULL;
884 trace ("added %s gui plugin\n", nm);
885 }
886 }
887 break;
888 }
889
890 if (!gui_scan) {
891 if (0 != load_plugin (plugdir, d_name, (int)l)) {
892 trace ("plugin not found or failed to load\n");
893 }
894 }
895 break;
896 }
897 free (namelist[i]);
898 }
899 free (namelist);
900 }
901 return 0;
902 }
903
904 int
plug_load_all(void)905 plug_load_all (void) {
906 #if DISABLE_VERSIONCHECK
907 trace ("\033[0;31mDISABLE_VERSIONCHECK=1! do not distribute!\033[0;m\n");
908 #endif
909
910 background_jobs_mutex = mutex_create ();
911
912 const char *dirname = deadbeef->get_plugin_dir ();
913
914 #ifdef HAVE_COCOAUI
915 const char *plugins_dirs[] = { dirname, NULL };
916 #else
917 #ifndef ANDROID
918 char *xdg_local_home = getenv ("XDG_LOCAL_HOME");
919 char xdg_plugin_dir[1024];
920 char xdg_plugin_dir_explicit_arch[1024];
921
922 if (xdg_local_home) {
923 strncpy (xdg_plugin_dir, xdg_local_home, sizeof (xdg_plugin_dir));
924 xdg_plugin_dir[sizeof(xdg_plugin_dir)-1] = 0;
925 } else {
926 char *homedir = getenv ("HOME");
927
928 if (!homedir) {
929 trace ("plug_load_all: warning: unable to find home directory\n");
930 xdg_plugin_dir[0] = 0;
931 }
932 else {
933 // multilib support:
934 // 1. load from lib$ARCH if present
935 // 2. load from lib if present
936 int written = snprintf (xdg_plugin_dir, sizeof (xdg_plugin_dir), "%s/.local/lib/deadbeef", homedir);
937 if (written > sizeof (xdg_plugin_dir)) {
938 trace ("warning: XDG_LOCAL_HOME value is too long: %s. Ignoring.", xdg_local_home);
939 xdg_plugin_dir[0] = 0;
940 }
941 written = snprintf (xdg_plugin_dir_explicit_arch, sizeof (xdg_plugin_dir_explicit_arch), "%s/.local/lib%d/deadbeef", homedir, (int)(sizeof (long) * 8));
942 if (written > sizeof (xdg_plugin_dir_explicit_arch)) {
943 trace ("warning: XDG_LOCAL_HOME value is too long: %s. Ignoring.", xdg_local_home);
944 xdg_plugin_dir_explicit_arch[0] = 0;
945 }
946 }
947 }
948
949 // load from HOME 1st, than replace from installdir if needed
950 const char *plugins_dirs[] = { xdg_plugin_dir_explicit_arch, xdg_plugin_dir, dirname, NULL };
951
952 // If xdg_plugin_dir and dirname is the same, we should avoid each plugin
953 // to be load twice.
954 // XXX: Here absolute path is assumed, however if dirname is a relative
955 // path it won't work.
956 if (strcmp(xdg_plugin_dir, dirname) == 0) {
957 plugins_dirs[1] = NULL;
958 }
959 #else
960 const char *plugins_dirs[] = { dirname, NULL };
961 #endif
962 #endif
963
964 int k = 0;
965 #ifndef ANDROID
966 // load gui plugin before others
967 while (plugins_dirs[k]) {
968 const char *plugdir = plugins_dirs[k++];
969 if (!(*plugdir)) {
970 continue;
971 }
972 load_plugin_dir (plugdir, 1);
973 }
974 trace ("load gui plugin\n");
975 load_gui_plugin (plugins_dirs);
976 #endif
977
978 k = 0;
979 while (plugins_dirs[k]) {
980 const char *plugdir = plugins_dirs[k++];
981 if (!(*plugdir)) {
982 continue;
983 }
984 load_plugin_dir (plugdir, 0);
985 }
986
987 #ifdef ANDROID
988 char plugin_path[1000];
989 strncpy (plugin_path, conf_get_str_fast ("android.plugin_path", ""), sizeof (plugin_path)-1);
990 plugin_path[sizeof(plugin_path)-1] = 0;
991 char *p = plugin_path;
992 while (*p) {
993 while (*p == ':') {
994 p++;
995 }
996 if (!(*p)) {
997 break;
998 }
999 char *e = strchr (p, ':');
1000 if (e) {
1001 *e = 0;
1002 }
1003
1004 char path[PATH_MAX];
1005 snprintf (path, sizeof (path), "/data/data/%s/lib", p);
1006 load_plugin_dir (path);
1007 if (!e) {
1008 break;
1009 }
1010 p = e+1;
1011 }
1012 #endif
1013
1014 // load all compiled-in modules
1015 #define PLUG(n) extern DB_plugin_t * n##_load (DB_functions_t *api);
1016 #include "moduleconf.h"
1017 #undef PLUG
1018 #define PLUG(n) plug_init_plugin (n##_load, NULL);
1019 #include "moduleconf.h"
1020 #undef PLUG
1021 #ifdef ANDROID
1022 #define PLUG(n) extern DB_plugin_t * n##_load (DB_functions_t *api);
1023 #include "moduleconf-android.h"
1024 #undef PLUG
1025 #define PLUG(n) plug_init_plugin (n##_load, NULL);
1026 #include "moduleconf-android.h"
1027 #undef PLUG
1028 #endif
1029
1030 if (plugins_lowprio) {
1031 if (plugins_tail) {
1032 plugins_tail->next = plugins_lowprio;
1033 }
1034 else {
1035 plugins = plugins_tail = plugins_lowprio;
1036 }
1037 while (plugins_tail->next) {
1038 plugins_tail = plugins_tail->next;
1039 }
1040 plugins_lowprio = plugins_lowprio_tail = NULL;
1041 }
1042
1043 plugin_t *plug;
1044 // categorize plugins
1045 int numplugins = 0;
1046 int numdecoders = 0;
1047 int numvfs = 0;
1048 int numoutput = 0;
1049 int numdsp = 0;
1050 int numplaylist = 0;
1051 for (plug = plugins; plug; plug = plug->next) {
1052 g_plugins[numplugins++] = plug->plugin;
1053 if (plug->plugin->type == DB_PLUGIN_DECODER) {
1054 // trace ("found decoder plugin %s\n", plug->plugin->name);
1055 if (numdecoders >= MAX_DECODER_PLUGINS) {
1056 break;
1057 }
1058 g_decoder_plugins[numdecoders++] = (DB_decoder_t *)plug->plugin;
1059 }
1060 else if (plug->plugin->type == DB_PLUGIN_VFS) {
1061 // trace ("found vfs plugin %s\n", plug->plugin->name);
1062 if (numvfs >= MAX_VFS_PLUGINS) {
1063 break;
1064 }
1065 g_vfs_plugins[numvfs++] = (DB_vfs_t *)plug->plugin;
1066 }
1067 else if (plug->plugin->type == DB_PLUGIN_OUTPUT) {
1068 // trace ("found output plugin %s\n", plug->plugin->name);
1069 if (numoutput >= MAX_OUTPUT_PLUGINS) {
1070 break;
1071 }
1072 g_output_plugins[numoutput++] = (DB_output_t *)plug->plugin;
1073 }
1074 else if (plug->plugin->type == DB_PLUGIN_DSP) {
1075 // trace ("found dsp plugin %s\n", plug->plugin->name);
1076 if (numdsp >= MAX_DSP_PLUGINS) {
1077 break;
1078 }
1079 g_dsp_plugins[numdsp++] = (DB_dsp_t *)plug->plugin;
1080 }
1081 else if (plug->plugin->type == DB_PLUGIN_PLAYLIST) {
1082 if (numplaylist >= MAX_PLAYLIST_PLUGINS) {
1083 break;
1084 }
1085 g_playlist_plugins[numplaylist++] = (DB_playlist_t *)plug->plugin;
1086 }
1087 }
1088 // start plugins
1089 plugin_t *prev = NULL;
1090 for (plug = plugins; plug;) {
1091 if (plug->plugin->type != DB_PLUGIN_GUI && plug->plugin->start) {
1092 if (plug->plugin->start () < 0) {
1093 fprintf (stderr, "plugin %s failed to start, deactivated.\n", plug->plugin->name);
1094 if (plug->plugin->stop) {
1095 plug->plugin->stop ();
1096 }
1097 if (plug->handle) {
1098 dlclose (plug->handle);
1099 }
1100 plug_remove_plugin (plug->plugin);
1101 if (prev) {
1102 prev->next = plug->next;
1103 }
1104 else {
1105 plugins = plug->next;
1106 }
1107 plugin_t *next = plug->next;
1108 free (plug);
1109 plug = next;
1110 continue;
1111 }
1112 }
1113 prev = plug;
1114 plug = plug->next;
1115 }
1116 // trace ("numplugins: %d, numdecoders: %d, numvfs: %d\n", numplugins, numdecoders, numvfs);
1117 g_plugins[numplugins] = NULL;
1118 g_decoder_plugins[numdecoders] = NULL;
1119 g_vfs_plugins[numvfs] = NULL;
1120 g_output_plugins[numoutput] = NULL;
1121 g_dsp_plugins[numdsp] = NULL;
1122 g_playlist_plugins[numplaylist] = NULL;
1123
1124 // select output plugin
1125 if (plug_select_output () < 0) {
1126 trace ("failed to find output plugin!\n");
1127 return -1;
1128 }
1129 return 0;
1130 }
1131
1132 void
plug_connect_all(void)1133 plug_connect_all (void) {
1134 plugin_t *plug;
1135 plugin_t *prev = NULL;
1136 for (plug = plugins; plug;) {
1137 if (plug->plugin->connect) {
1138 if (plug->plugin->connect () < 0) {
1139 fprintf (stderr, "plugin %s failed to connect to dependencies, deactivated.\n", plug->plugin->name);
1140
1141 if (plug->plugin->disconnect) {
1142 plug->plugin->disconnect ();
1143 }
1144 if (plug->plugin->type != DB_PLUGIN_GUI && plug->plugin->stop) {
1145 plug->plugin->stop ();
1146 }
1147 if (plug->handle) {
1148 dlclose (plug->handle);
1149 }
1150 plug_remove_plugin (plug->plugin);
1151
1152 if (prev) {
1153 prev->next = plug->next;
1154 }
1155 else {
1156 plugins = plug->next;
1157 }
1158 plugin_t *next = plug->next;
1159 free (plug);
1160 plug = next;
1161 continue;
1162 }
1163 }
1164 prev = plug;
1165 plug = plug->next;
1166 }
1167
1168 }
1169
1170 void
plug_disconnect_all(void)1171 plug_disconnect_all (void) {
1172 trace ("plug_disconnect_all\n");
1173 plugin_t *plug;
1174 plugin_t *prev = NULL;
1175 for (plug = plugins; plug;) {
1176 if (plug->plugin->disconnect) {
1177 if (plug->plugin->disconnect () < 0) {
1178 trace ("plugin %s failed to disconnect\n", plug->plugin->name);
1179 }
1180 }
1181 prev = plug;
1182 plug = plug->next;
1183 }
1184 }
1185
1186 void
plug_unload_all(void)1187 plug_unload_all (void) {
1188 action_set_playlist (NULL);
1189 trace ("plug_unload_all\n");
1190 plugin_t *p;
1191 for (p = plugins; p; p = p->next) {
1192 if (p->plugin->stop) {
1193 trace ("stopping %s...\n", p->plugin->name);
1194 fflush (stderr);
1195 #if HAVE_COCOAUI
1196 if (p->plugin->type == DB_PLUGIN_GUI) {
1197 continue;
1198 }
1199 #endif
1200 p->plugin->stop ();
1201 }
1202 }
1203 trace ("stopped all plugins\n");
1204 while (plugins) {
1205 plugin_t *next = plugins->next;
1206 if (plugins->handle) {
1207 dlclose (plugins->handle);
1208 }
1209 free (plugins);
1210 plugins = next;
1211 }
1212 for (int i = 0; g_gui_names[i]; i++) {
1213 free (g_gui_names[i]);
1214 g_gui_names[i] = NULL;
1215 }
1216 plugins_tail = NULL;
1217
1218 memset (g_plugins, 0, sizeof (g_plugins));
1219 memset (g_gui_names, 0, sizeof (g_gui_names));
1220 g_num_gui_names = 0;
1221 memset (g_decoder_plugins, 0, sizeof (g_decoder_plugins));
1222 memset (g_vfs_plugins, 0, sizeof (g_vfs_plugins));
1223 memset (g_dsp_plugins, 0, sizeof (g_dsp_plugins));
1224 memset (g_output_plugins, 0, sizeof (g_output_plugins));
1225 output_plugin = NULL;
1226 memset (g_playlist_plugins, 0, sizeof (g_playlist_plugins));
1227
1228 trace ("all plugins had been unloaded\n");
1229 if (background_jobs_mutex) {
1230 mutex_free (background_jobs_mutex);
1231 background_jobs_mutex = 0;
1232 }
1233 }
1234
1235 void
plug_set_output(DB_output_t * out)1236 plug_set_output (DB_output_t *out) {
1237 output_plugin = out;
1238 }
1239
1240 void
plug_cleanup(void)1241 plug_cleanup (void) {
1242 plug_free_decoder_ids ();
1243 }
1244
1245 struct DB_decoder_s **
plug_get_decoder_list(void)1246 plug_get_decoder_list (void) {
1247 return g_decoder_plugins;
1248 }
1249
1250 struct DB_vfs_s **
plug_get_vfs_list(void)1251 plug_get_vfs_list (void) {
1252 return g_vfs_plugins;
1253 }
1254
1255 struct DB_output_s **
plug_get_output_list(void)1256 plug_get_output_list (void) {
1257 return g_output_plugins;
1258 }
1259
1260 struct DB_dsp_s **
plug_get_dsp_list(void)1261 plug_get_dsp_list (void) {
1262 return g_dsp_plugins;
1263 }
1264
1265 struct DB_playlist_s **
plug_get_playlist_list(void)1266 plug_get_playlist_list (void) {
1267 return g_playlist_plugins;
1268 }
1269
1270 struct DB_plugin_s **
plug_get_list(void)1271 plug_get_list (void) {
1272 return g_plugins;
1273 }
1274
1275 const char **
plug_get_gui_names(void)1276 plug_get_gui_names (void) {
1277 return (const char **)g_gui_names;
1278 }
1279
1280 DB_output_t *
plug_get_output(void)1281 plug_get_output (void) {
1282 return output_plugin;
1283 }
1284
1285 int
plug_select_output(void)1286 plug_select_output (void) {
1287 #ifdef ANDROID
1288 return 0;
1289 #else
1290 char outplugname[100];
1291 #ifdef HAVE_COCOAUI
1292 conf_get_str ("output_plugin", "CoreAudio", outplugname, sizeof (outplugname));
1293 #else
1294 conf_get_str ("output_plugin", "ALSA output plugin", outplugname, sizeof (outplugname));
1295 #endif
1296 for (int i = 0; g_output_plugins[i]; i++) {
1297 DB_output_t *p = g_output_plugins[i];
1298 if (!strcmp (p->plugin.name, outplugname)) {
1299 trace ("selected output plugin: %s\n", outplugname);
1300 output_plugin = p;
1301 break;
1302 }
1303 }
1304 if (!output_plugin) {
1305 output_plugin = g_output_plugins[0];
1306 if (output_plugin) {
1307 trace ("selected output plugin: %s\n", output_plugin->plugin.name);
1308 conf_set_str ("output_plugin", output_plugin->plugin.name);
1309 }
1310 }
1311 if (!output_plugin) {
1312 return -1;
1313 }
1314 messagepump_push (DB_EV_OUTPUTCHANGED, 0, 0, 0);
1315 return 0;
1316 #endif
1317 }
1318
1319 void
plug_reinit_sound(void)1320 plug_reinit_sound (void) {
1321 DB_output_t *prev = plug_get_output ();
1322 int state = OUTPUT_STATE_STOPPED;
1323
1324 ddb_waveformat_t fmt = {0};
1325
1326 streamer_get_output_format (&fmt);
1327 if (prev) {
1328 state = prev->state ();
1329 if (!fmt.channels) {
1330 memcpy (&fmt, &prev->fmt, sizeof (fmt));
1331 }
1332 prev->free ();
1333 }
1334
1335 if (plug_select_output () < 0) {
1336 char outplugname[100];
1337 #ifdef HAVE_COCOAUI
1338 conf_get_str ("output_plugin", "core audio output plugin", outplugname, sizeof (outplugname));
1339 #else
1340 conf_get_str ("output_plugin", "ALSA output plugin", outplugname, sizeof (outplugname));
1341 #endif
1342 trace ("failed to select output plugin %s\nreverted to %s\n", outplugname, prev->plugin.name);
1343 output_plugin = prev;
1344 }
1345 DB_output_t *output = plug_get_output ();
1346 if (fmt.channels) {
1347 output->setformat (&fmt);
1348 }
1349 if (output->init () < 0) {
1350 streamer_reset (1);
1351 streamer_set_nextsong (-2, 0);
1352 return;
1353 }
1354
1355 if (state != OUTPUT_STATE_PAUSED && state != OUTPUT_STATE_STOPPED) {
1356 if (output->play () < 0) {
1357 trace ("failed to reinit sound output\n");
1358 streamer_set_nextsong (-2, 0);
1359 }
1360 }
1361 }
1362
1363 // list of all unique decoder ids used in current session
1364 static char *decoder_ids[MAX_DECODER_PLUGINS];
1365
1366 const char *
plug_get_decoder_id(const char * id)1367 plug_get_decoder_id (const char *id) {
1368 int i;
1369 char **lastnull = NULL;
1370 for (i = 0; i < MAX_DECODER_PLUGINS; i++) {
1371 if (decoder_ids[i] && !strcmp (id, decoder_ids[i])) {
1372 return decoder_ids[i];
1373 }
1374 else if (!lastnull && !decoder_ids[i]) {
1375 lastnull = &decoder_ids[i];
1376 }
1377 }
1378 if (!lastnull) {
1379 return NULL;
1380 }
1381 char *newid = strdup (id);
1382 *lastnull = newid;
1383 return newid;
1384 }
1385
1386 void
plug_remove_decoder_id(const char * id)1387 plug_remove_decoder_id (const char *id) {
1388 int i;
1389 for (i = 0; i < MAX_DECODER_PLUGINS; i++) {
1390 if (decoder_ids[i] && !strcmp (decoder_ids[i], id)) {
1391 free (decoder_ids[i]);
1392 decoder_ids[i] = NULL;
1393 }
1394 }
1395 }
1396
1397 void
plug_free_decoder_ids(void)1398 plug_free_decoder_ids (void) {
1399 int i;
1400 for (i = 0; i < MAX_DECODER_PLUGINS; i++) {
1401 if (decoder_ids[i]) {
1402 free (decoder_ids[i]);
1403 decoder_ids[i] = NULL;
1404 }
1405 }
1406 }
1407
1408 DB_decoder_t *
plug_get_decoder_for_id(const char * id)1409 plug_get_decoder_for_id (const char *id) {
1410 DB_decoder_t **plugins = plug_get_decoder_list ();
1411 for (int c = 0; plugins[c]; c++) {
1412 if (!strcmp (id, plugins[c]->plugin.id)) {
1413 return plugins[c];
1414 }
1415 }
1416 return NULL;
1417 }
1418
1419 DB_plugin_t *
plug_get_for_id(const char * id)1420 plug_get_for_id (const char *id) {
1421 DB_plugin_t **plugins = plug_get_list ();
1422 for (int c = 0; plugins[c]; c++) {
1423 if (plugins[c]->id && !strcmp (id, plugins[c]->id)) {
1424 return plugins[c];
1425 }
1426 }
1427 return NULL;
1428 }
1429
1430 int
plug_is_local_file(const char * fname)1431 plug_is_local_file (const char *fname) {
1432 if (!strncasecmp (fname, "file://", 7)) {
1433 return 1;
1434 }
1435
1436 const char *f = fname;
1437 for (; *f; f++) {
1438 if (!strncmp (f, "://", 3)) {
1439 DB_vfs_t **plug = plug_get_vfs_list ();
1440 for (int i = 0; plug[i]; i++) {
1441 if (plug[i]->get_schemes && plug[i]->is_streaming && plug[i]->is_streaming()) {
1442 const char **sch = plug[i]->get_schemes ();
1443 for (int k = 0; sch[k]; k++) {
1444 if (!strncmp (sch[k], fname, strlen (sch[k]))) {
1445 return 0;
1446 }
1447 }
1448 }
1449 }
1450 break;
1451 }
1452 }
1453
1454 return 1;
1455 }
1456
1457 void
background_job_increment(void)1458 background_job_increment (void) {
1459 mutex_lock (background_jobs_mutex);
1460 num_background_jobs++;
1461 mutex_unlock (background_jobs_mutex);
1462 }
1463
1464 void
background_job_decrement(void)1465 background_job_decrement (void) {
1466 mutex_lock (background_jobs_mutex);
1467 num_background_jobs--;
1468 mutex_unlock (background_jobs_mutex);
1469 }
1470
1471 int
have_background_jobs(void)1472 have_background_jobs (void) {
1473 return num_background_jobs;
1474 }
1475
1476 static ddb_playlist_t *action_playlist;
1477
1478 void
action_set_playlist(ddb_playlist_t * plt)1479 action_set_playlist (ddb_playlist_t *plt) {
1480 if (action_playlist) {
1481 plt_unref ((playlist_t *)action_playlist);
1482 }
1483 action_playlist = plt;
1484 if (action_playlist) {
1485 plt_ref ((playlist_t *)action_playlist);
1486 }
1487 }
1488
1489 ddb_playlist_t *
action_get_playlist(void)1490 action_get_playlist (void) {
1491 if (!action_playlist) {
1492 return (ddb_playlist_t *)plt_get_curr ();
1493 }
1494
1495 plt_ref ((playlist_t *)action_playlist);
1496 return action_playlist;
1497 }
1498