XMMS2
|
00001 /* XMMS2 - X Music Multiplexer System 00002 * Copyright (C) 2003-2009 XMMS2 Team 00003 * 00004 * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!! 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 */ 00016 00017 /** 00018 * @file 00019 * xforms 00020 */ 00021 00022 #include <string.h> 00023 00024 #include "xmmspriv/xmms_plugin.h" 00025 #include "xmmspriv/xmms_xform.h" 00026 #include "xmmspriv/xmms_streamtype.h" 00027 #include "xmmspriv/xmms_medialib.h" 00028 #include "xmmspriv/xmms_utils.h" 00029 #include "xmms/xmms_ipc.h" 00030 #include "xmms/xmms_log.h" 00031 #include "xmms/xmms_object.h" 00032 00033 struct xmms_xform_object_St { 00034 xmms_object_t obj; 00035 }; 00036 00037 struct xmms_xform_St { 00038 xmms_object_t obj; 00039 struct xmms_xform_St *prev; 00040 00041 const xmms_xform_plugin_t *plugin; 00042 xmms_medialib_entry_t entry; 00043 00044 gboolean inited; 00045 00046 void *priv; 00047 00048 xmms_stream_type_t *out_type; 00049 00050 GList *goal_hints; 00051 00052 gboolean eos; 00053 gboolean error; 00054 00055 char *buffer; 00056 gint buffered; 00057 gint buffersize; 00058 00059 gboolean metadata_collected; 00060 00061 gboolean metadata_changed; 00062 GHashTable *metadata; 00063 00064 GHashTable *privdata; 00065 GQueue *hotspots; 00066 00067 GList *browse_list; 00068 xmmsv_t *browse_dict; 00069 gint browse_index; 00070 00071 /** used for line reading */ 00072 struct { 00073 gchar buf[XMMS_XFORM_MAX_LINE_SIZE]; 00074 gchar *bufend; 00075 } lr; 00076 }; 00077 00078 typedef struct xmms_xform_hotspot_St { 00079 guint pos; 00080 gchar *key; 00081 xmmsv_t *obj; 00082 } xmms_xform_hotspot_t; 00083 00084 #define READ_CHUNK 4096 00085 00086 struct xmms_xform_plugin_St { 00087 xmms_plugin_t plugin; 00088 00089 xmms_xform_methods_t methods; 00090 00091 GList *in_types; 00092 }; 00093 00094 xmms_xform_t *xmms_xform_find (xmms_xform_t *prev, xmms_medialib_entry_t entry, 00095 GList *goal_hints); 00096 const char *xmms_xform_shortname (xmms_xform_t *xform); 00097 static xmms_xform_t *add_effects (xmms_xform_t *last, 00098 xmms_medialib_entry_t entry, 00099 GList *goal_formats); 00100 static xmms_xform_t *xmms_xform_new_effect (xmms_xform_t* last, 00101 xmms_medialib_entry_t entry, 00102 GList *goal_formats, 00103 const gchar *name); 00104 static void xmms_xform_destroy (xmms_object_t *object); 00105 static void effect_callbacks_init (void); 00106 00107 void 00108 xmms_xform_browse_add_entry_property_str (xmms_xform_t *xform, 00109 const gchar *key, 00110 const gchar *value) 00111 { 00112 xmmsv_t *val = xmmsv_new_string (value); 00113 xmms_xform_browse_add_entry_property (xform, key, val); 00114 xmmsv_unref (val); 00115 } 00116 00117 00118 void 00119 xmms_xform_browse_add_entry_property_int (xmms_xform_t *xform, 00120 const gchar *key, 00121 gint value) 00122 { 00123 xmmsv_t *val = xmmsv_new_int (value); 00124 xmms_xform_browse_add_entry_property (xform, key, val); 00125 xmmsv_unref (val); 00126 } 00127 00128 void 00129 xmms_xform_browse_add_symlink_args (xmms_xform_t *xform, const gchar *basename, 00130 const gchar *url, gint nargs, gchar **args) 00131 { 00132 GString *s; 00133 gchar *eurl; 00134 gchar bname[32]; 00135 gint i; 00136 00137 if (!basename) { 00138 g_snprintf (bname, sizeof (bname), "%d", xform->browse_index++); 00139 basename = bname; 00140 } 00141 00142 xmms_xform_browse_add_entry (xform, basename, 0); 00143 eurl = xmms_medialib_url_encode (url); 00144 s = g_string_new (eurl); 00145 00146 for (i = 0; i < nargs; i++) { 00147 g_string_append_c (s, i == 0 ? '?' : '&'); 00148 g_string_append (s, args[i]); 00149 } 00150 00151 xmms_xform_browse_add_entry_property_str (xform, "realpath", s->str); 00152 00153 g_free (eurl); 00154 g_string_free (s, TRUE); 00155 } 00156 00157 void 00158 xmms_xform_browse_add_symlink (xmms_xform_t *xform, const gchar *basename, 00159 const gchar *url) 00160 { 00161 xmms_xform_browse_add_symlink_args (xform, basename, url, 0, NULL); 00162 } 00163 00164 void 00165 xmms_xform_browse_add_entry_property (xmms_xform_t *xform, const gchar *key, 00166 xmmsv_t *val) 00167 { 00168 g_return_if_fail (xform); 00169 g_return_if_fail (xform->browse_dict); 00170 g_return_if_fail (key); 00171 g_return_if_fail (val); 00172 00173 xmmsv_dict_set (xform->browse_dict, key, val); 00174 } 00175 00176 void 00177 xmms_xform_browse_add_entry (xmms_xform_t *xform, const gchar *filename, 00178 guint32 flags) 00179 { 00180 xmmsv_t *val; 00181 const gchar *url; 00182 gchar *efile, *eurl, *t; 00183 gint l, isdir; 00184 00185 g_return_if_fail (filename); 00186 00187 t = strchr (filename, '/'); 00188 g_return_if_fail (!t); /* filenames can't contain '/', can they? */ 00189 00190 url = xmms_xform_get_url (xform); 00191 g_return_if_fail (url); 00192 00193 xform->browse_dict = xmmsv_new_dict (); 00194 00195 eurl = xmms_medialib_url_encode (url); 00196 efile = xmms_medialib_url_encode (filename); 00197 00198 /* can't use g_build_filename as we need to preserve 00199 slashes stuff like file:/// */ 00200 l = strlen (url); 00201 if (l && url[l - 1] == '/') { 00202 t = g_strdup_printf ("%s%s", eurl, efile); 00203 } else { 00204 t = g_strdup_printf ("%s/%s", eurl, efile); 00205 } 00206 00207 isdir = !!(flags & XMMS_XFORM_BROWSE_FLAG_DIR); 00208 xmms_xform_browse_add_entry_property_str (xform, "path", t); 00209 xmms_xform_browse_add_entry_property_int (xform, "isdir", isdir); 00210 00211 val = xform->browse_dict; 00212 xform->browse_list = g_list_prepend (xform->browse_list, val); 00213 00214 g_free (t); 00215 g_free (efile); 00216 g_free (eurl); 00217 } 00218 00219 static gint 00220 xmms_browse_list_sortfunc (gconstpointer a, gconstpointer b) 00221 { 00222 int r1, r2; 00223 xmmsv_t *val1, *val2, *tmp1, *tmp2; 00224 const gchar *s1, *s2; 00225 00226 val1 = (xmmsv_t *) a; 00227 val2 = (xmmsv_t *) b; 00228 00229 g_return_val_if_fail (xmmsv_get_type (val1) == XMMSV_TYPE_DICT, 0); 00230 g_return_val_if_fail (xmmsv_get_type (val2) == XMMSV_TYPE_DICT, 0); 00231 00232 r1 = xmmsv_dict_get (val1, "intsort", &tmp1); 00233 r2 = xmmsv_dict_get (val2, "intsort", &tmp2); 00234 00235 if (r1 && r2) { 00236 gint i1, i2; 00237 00238 if (!xmmsv_get_int (tmp1, &i1)) 00239 return 0; 00240 if (!xmmsv_get_int (tmp2, &i2)) 00241 return 0; 00242 return i1 > i2; 00243 } 00244 00245 if (!xmmsv_dict_get (val1, "path", &tmp1)) 00246 return 0; 00247 if (!xmmsv_dict_get (val2, "path", &tmp2)) 00248 return 0; 00249 00250 if (!xmmsv_get_string (tmp1, &s1)) 00251 return 0; 00252 if (!xmmsv_get_string (tmp2, &s2)) 00253 return 0; 00254 00255 return xmms_natcmp (s1, s2); 00256 } 00257 00258 GList * 00259 xmms_xform_browse_method (xmms_xform_t *xform, const gchar *url, 00260 xmms_error_t *error) 00261 { 00262 GList *list = NULL; 00263 00264 if (xform->plugin->methods.browse) { 00265 if (!xform->plugin->methods.browse (xform, url, error)) { 00266 return NULL; 00267 } 00268 list = xform->browse_list; 00269 xform->browse_list = NULL; 00270 list = g_list_sort (list, xmms_browse_list_sortfunc); 00271 } else { 00272 xmms_error_set (error, XMMS_ERROR_GENERIC, "Couldn't handle that URL"); 00273 } 00274 00275 return list; 00276 } 00277 00278 GList * 00279 xmms_xform_browse (const gchar *url, xmms_error_t *error) 00280 { 00281 GList *list = NULL; 00282 gchar *durl; 00283 xmms_xform_t *xform = NULL; 00284 xmms_xform_t *xform2 = NULL; 00285 00286 xform = xmms_xform_new (NULL, NULL, 0, NULL); 00287 00288 durl = g_strdup (url); 00289 xmms_medialib_decode_url (durl); 00290 XMMS_DBG ("url = %s", durl); 00291 00292 xmms_xform_outdata_type_add (xform, 00293 XMMS_STREAM_TYPE_MIMETYPE, 00294 "application/x-url", 00295 XMMS_STREAM_TYPE_URL, 00296 durl, 00297 XMMS_STREAM_TYPE_END); 00298 00299 xform2 = xmms_xform_find (xform, 0, NULL); 00300 if (xform2) { 00301 XMMS_DBG ("found xform %s", xmms_xform_shortname (xform2)); 00302 } else { 00303 xmms_error_set (error, XMMS_ERROR_GENERIC, "Couldn't handle that URL"); 00304 xmms_object_unref (xform); 00305 g_free (durl); 00306 return NULL; 00307 } 00308 00309 list = xmms_xform_browse_method (xform2, durl, error); 00310 00311 xmms_object_unref (xform); 00312 xmms_object_unref (xform2); 00313 00314 g_free (durl); 00315 00316 return list; 00317 } 00318 00319 static GList * 00320 xmms_xform_client_browse (xmms_xform_object_t *obj, const gchar *url, 00321 xmms_error_t *error) 00322 { 00323 return xmms_xform_browse (url, error); 00324 } 00325 XMMS_CMD_DEFINE (browse, xmms_xform_client_browse, xmms_xform_object_t *, 00326 LIST, STRING, NONE); 00327 00328 static void 00329 xmms_xform_object_destroy (xmms_object_t *obj) 00330 { 00331 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_XFORM); 00332 } 00333 00334 xmms_xform_object_t * 00335 xmms_xform_object_init (void) 00336 { 00337 xmms_xform_object_t *obj; 00338 00339 obj = xmms_object_new (xmms_xform_object_t, xmms_xform_object_destroy); 00340 00341 xmms_ipc_object_register (XMMS_IPC_OBJECT_XFORM, XMMS_OBJECT (obj)); 00342 00343 xmms_object_cmd_add (XMMS_OBJECT (obj), XMMS_IPC_CMD_BROWSE, 00344 XMMS_CMD_FUNC (browse)); 00345 00346 effect_callbacks_init (); 00347 00348 return obj; 00349 } 00350 00351 static void 00352 xmms_xform_destroy (xmms_object_t *object) 00353 { 00354 xmms_xform_t *xform = (xmms_xform_t *)object; 00355 00356 XMMS_DBG ("Freeing xform '%s'", xmms_xform_shortname (xform)); 00357 00358 /* The 'destroy' method is not mandatory */ 00359 if (xform->plugin && xform->plugin->methods.destroy && xform->inited) { 00360 xform->plugin->methods.destroy (xform); 00361 } 00362 00363 g_hash_table_destroy (xform->metadata); 00364 00365 g_hash_table_destroy (xform->privdata); 00366 g_queue_free (xform->hotspots); 00367 00368 g_free (xform->buffer); 00369 00370 xmms_object_unref (xform->out_type); 00371 xmms_object_unref (xform->plugin); 00372 00373 if (xform->prev) { 00374 xmms_object_unref (xform->prev); 00375 } 00376 00377 } 00378 00379 xmms_xform_t * 00380 xmms_xform_new (xmms_xform_plugin_t *plugin, xmms_xform_t *prev, 00381 xmms_medialib_entry_t entry, GList *goal_hints) 00382 { 00383 xmms_xform_t *xform; 00384 00385 xform = xmms_object_new (xmms_xform_t, xmms_xform_destroy); 00386 00387 xmms_object_ref (plugin); 00388 xform->plugin = plugin; 00389 xform->entry = entry; 00390 xform->goal_hints = goal_hints; 00391 xform->lr.bufend = &xform->lr.buf[0]; 00392 00393 if (prev) { 00394 xmms_object_ref (prev); 00395 xform->prev = prev; 00396 } 00397 00398 xform->metadata = g_hash_table_new_full (g_str_hash, g_str_equal, 00399 g_free, 00400 (GDestroyNotify) xmmsv_unref); 00401 00402 xform->privdata = g_hash_table_new_full (g_str_hash, g_str_equal, 00403 g_free, 00404 (GDestroyNotify) xmmsv_unref); 00405 xform->hotspots = g_queue_new (); 00406 00407 if (plugin && entry) { 00408 if (!plugin->methods.init (xform)) { 00409 xmms_object_unref (xform); 00410 return NULL; 00411 } 00412 xform->inited = TRUE; 00413 g_return_val_if_fail (xform->out_type, NULL); 00414 } 00415 00416 xform->buffer = g_malloc (READ_CHUNK); 00417 xform->buffersize = READ_CHUNK; 00418 00419 return xform; 00420 } 00421 00422 xmms_medialib_entry_t 00423 xmms_xform_entry_get (xmms_xform_t *xform) 00424 { 00425 return xform->entry; 00426 } 00427 00428 gpointer 00429 xmms_xform_private_data_get (xmms_xform_t *xform) 00430 { 00431 return xform->priv; 00432 } 00433 00434 void 00435 xmms_xform_private_data_set (xmms_xform_t *xform, gpointer data) 00436 { 00437 xform->priv = data; 00438 } 00439 00440 void 00441 xmms_xform_outdata_type_add (xmms_xform_t *xform, ...) 00442 { 00443 va_list ap; 00444 va_start (ap, xform); 00445 xform->out_type = xmms_stream_type_parse (ap); 00446 va_end (ap); 00447 } 00448 00449 void 00450 xmms_xform_outdata_type_set (xmms_xform_t *xform, xmms_stream_type_t *type) 00451 { 00452 xmms_object_ref (type); 00453 xform->out_type = type; 00454 } 00455 00456 void 00457 xmms_xform_outdata_type_copy (xmms_xform_t *xform) 00458 { 00459 xmms_object_ref (xform->prev->out_type); 00460 xform->out_type = xform->prev->out_type; 00461 } 00462 00463 const char * 00464 xmms_xform_indata_find_str (xmms_xform_t *xform, xmms_stream_type_key_t key) 00465 { 00466 const gchar *r; 00467 r = xmms_stream_type_get_str (xform->prev->out_type, key); 00468 if (r) { 00469 return r; 00470 } else if (xform->prev) { 00471 return xmms_xform_indata_find_str (xform->prev, key); 00472 } 00473 return NULL; 00474 } 00475 00476 const char * 00477 xmms_xform_indata_get_str (xmms_xform_t *xform, xmms_stream_type_key_t key) 00478 { 00479 return xmms_stream_type_get_str (xform->prev->out_type, key); 00480 } 00481 00482 gint 00483 xmms_xform_indata_get_int (xmms_xform_t *xform, xmms_stream_type_key_t key) 00484 { 00485 return xmms_stream_type_get_int (xform->prev->out_type, key); 00486 } 00487 00488 xmms_stream_type_t * 00489 xmms_xform_outtype_get (xmms_xform_t *xform) 00490 { 00491 return xform->out_type; 00492 } 00493 00494 xmms_stream_type_t * 00495 xmms_xform_intype_get (xmms_xform_t *xform) 00496 { 00497 return xmms_xform_outtype_get (xform->prev); 00498 } 00499 00500 00501 00502 const char * 00503 xmms_xform_outtype_get_str (xmms_xform_t *xform, xmms_stream_type_key_t key) 00504 { 00505 return xmms_stream_type_get_str (xform->out_type, key); 00506 } 00507 00508 gint 00509 xmms_xform_outtype_get_int (xmms_xform_t *xform, xmms_stream_type_key_t key) 00510 { 00511 return xmms_stream_type_get_int (xform->out_type, key); 00512 } 00513 00514 00515 void 00516 xmms_xform_metadata_set_int (xmms_xform_t *xform, const char *key, int val) 00517 { 00518 XMMS_DBG ("Setting '%s' to %d", key, val); 00519 g_hash_table_insert (xform->metadata, g_strdup (key), 00520 xmmsv_new_int (val)); 00521 xform->metadata_changed = TRUE; 00522 } 00523 00524 void 00525 xmms_xform_metadata_set_str (xmms_xform_t *xform, const char *key, 00526 const char *val) 00527 { 00528 const char *old; 00529 00530 if (!g_utf8_validate (val, -1, NULL)) { 00531 xmms_log_error ("xform '%s' tried to set property '%s' to a NON UTF-8 string!", xmms_xform_shortname (xform), key); 00532 return; 00533 } 00534 00535 if (xmms_xform_metadata_get_str (xform, key, &old)) { 00536 if (strcmp (old, val) == 0) { 00537 return; 00538 } 00539 } 00540 00541 g_hash_table_insert (xform->metadata, g_strdup (key), 00542 xmmsv_new_string (val)); 00543 00544 xform->metadata_changed = TRUE; 00545 } 00546 00547 static const xmmsv_t * 00548 xmms_xform_metadata_get_val (xmms_xform_t *xform, const char *key) 00549 { 00550 xmmsv_t *val = NULL; 00551 00552 for (; xform; xform = xform->prev) { 00553 val = g_hash_table_lookup (xform->metadata, key); 00554 if (val) { 00555 break; 00556 } 00557 } 00558 00559 return val; 00560 } 00561 00562 gboolean 00563 xmms_xform_metadata_has_val (xmms_xform_t *xform, const gchar *key) 00564 { 00565 return !!xmms_xform_metadata_get_val (xform, key); 00566 } 00567 00568 gboolean 00569 xmms_xform_metadata_get_int (xmms_xform_t *xform, const char *key, 00570 gint32 *val) 00571 { 00572 const xmmsv_t *obj; 00573 gboolean ret = FALSE; 00574 00575 obj = xmms_xform_metadata_get_val (xform, key); 00576 if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_INT32) { 00577 xmmsv_get_int (obj, val); 00578 ret = TRUE; 00579 } 00580 00581 return ret; 00582 } 00583 00584 gboolean 00585 xmms_xform_metadata_get_str (xmms_xform_t *xform, const char *key, 00586 const gchar **val) 00587 { 00588 const xmmsv_t *obj; 00589 gboolean ret = FALSE; 00590 00591 obj = xmms_xform_metadata_get_val (xform, key); 00592 if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_STRING) { 00593 xmmsv_get_string (obj, val); 00594 ret = TRUE; 00595 } 00596 00597 return ret; 00598 } 00599 00600 typedef struct { 00601 xmms_medialib_session_t *session; 00602 xmms_medialib_entry_t entry; 00603 guint32 source; 00604 } metadata_festate_t; 00605 00606 static void 00607 add_metadatum (gpointer _key, gpointer _value, gpointer user_data) 00608 { 00609 xmmsv_t *value = (xmmsv_t *) _value; 00610 gchar *key = (gchar *) _key; 00611 metadata_festate_t *st = (metadata_festate_t *) user_data; 00612 00613 if (xmmsv_get_type (value) == XMMSV_TYPE_STRING) { 00614 const gchar *s; 00615 xmmsv_get_string (value, &s); 00616 xmms_medialib_entry_property_set_str_source (st->session, 00617 st->entry, 00618 key, 00619 s, 00620 st->source); 00621 } else if (xmmsv_get_type (value) == XMMSV_TYPE_INT32) { 00622 gint i; 00623 xmmsv_get_int (value, &i); 00624 xmms_medialib_entry_property_set_int_source (st->session, 00625 st->entry, 00626 key, 00627 i, 00628 st->source); 00629 } else { 00630 XMMS_DBG ("Unknown type?!?"); 00631 } 00632 } 00633 00634 static void 00635 xmms_xform_metadata_collect_one (xmms_xform_t *xform, metadata_festate_t *info) 00636 { 00637 gchar src[XMMS_PLUGIN_SHORTNAME_MAX_LEN + 8]; 00638 00639 XMMS_DBG ("Collecting metadata from %s", xmms_xform_shortname (xform)); 00640 00641 g_snprintf (src, sizeof (src), "plugin/%s", 00642 xmms_xform_shortname (xform)); 00643 00644 info->source = xmms_medialib_source_to_id (info->session, src); 00645 g_hash_table_foreach (xform->metadata, add_metadatum, info); 00646 00647 xform->metadata_changed = FALSE; 00648 } 00649 00650 static void 00651 xmms_xform_metadata_collect_r (xmms_xform_t *xform, metadata_festate_t *info, 00652 GString *namestr) 00653 { 00654 if (xform->prev) { 00655 xmms_xform_metadata_collect_r (xform->prev, info, namestr); 00656 } 00657 00658 if (xform->plugin) { 00659 if (namestr->len) { 00660 g_string_append_c (namestr, ':'); 00661 } 00662 g_string_append (namestr, xmms_xform_shortname (xform)); 00663 } 00664 00665 if (xform->metadata_changed) { 00666 xmms_xform_metadata_collect_one (xform, info); 00667 } 00668 00669 xform->metadata_collected = TRUE; 00670 } 00671 00672 static void 00673 xmms_xform_metadata_collect (xmms_xform_t *start, GString *namestr, gboolean rehashing) 00674 { 00675 metadata_festate_t info; 00676 gint times_played; 00677 gint last_started; 00678 GTimeVal now; 00679 00680 info.entry = start->entry; 00681 info.session = xmms_medialib_begin_write (); 00682 00683 times_played = xmms_medialib_entry_property_get_int (info.session, 00684 info.entry, 00685 XMMS_MEDIALIB_ENTRY_PROPERTY_TIMESPLAYED); 00686 00687 /* times_played == -1 if we haven't played this entry yet. so after initial 00688 * metadata collection the mlib would have timesplayed = -1 if we didn't do 00689 * the following */ 00690 if (times_played < 0) { 00691 times_played = 0; 00692 } 00693 00694 last_started = xmms_medialib_entry_property_get_int (info.session, 00695 info.entry, 00696 XMMS_MEDIALIB_ENTRY_PROPERTY_LASTSTARTED); 00697 00698 xmms_medialib_entry_cleanup (info.session, info.entry); 00699 00700 xmms_xform_metadata_collect_r (start, &info, namestr); 00701 00702 xmms_medialib_entry_property_set_str (info.session, info.entry, 00703 XMMS_MEDIALIB_ENTRY_PROPERTY_CHAIN, 00704 namestr->str); 00705 00706 xmms_medialib_entry_property_set_int (info.session, info.entry, 00707 XMMS_MEDIALIB_ENTRY_PROPERTY_TIMESPLAYED, 00708 times_played + (rehashing ? 0 : 1)); 00709 00710 if (!rehashing || (rehashing && last_started)) { 00711 g_get_current_time (&now); 00712 00713 xmms_medialib_entry_property_set_int (info.session, info.entry, 00714 XMMS_MEDIALIB_ENTRY_PROPERTY_LASTSTARTED, 00715 (rehashing ? last_started : now.tv_sec)); 00716 } 00717 00718 xmms_medialib_entry_status_set (info.session, info.entry, 00719 XMMS_MEDIALIB_ENTRY_STATUS_OK); 00720 00721 xmms_medialib_end (info.session); 00722 xmms_medialib_entry_send_update (info.entry); 00723 } 00724 00725 static void 00726 xmms_xform_metadata_update (xmms_xform_t *xform) 00727 { 00728 metadata_festate_t info; 00729 00730 info.entry = xform->entry; 00731 info.session = xmms_medialib_begin_write (); 00732 00733 xmms_xform_metadata_collect_one (xform, &info); 00734 00735 xmms_medialib_end (info.session); 00736 xmms_medialib_entry_send_update (info.entry); 00737 } 00738 00739 static void 00740 xmms_xform_auxdata_set_val (xmms_xform_t *xform, char *key, xmmsv_t *val) 00741 { 00742 xmms_xform_hotspot_t *hs; 00743 00744 hs = g_new0 (xmms_xform_hotspot_t, 1); 00745 hs->pos = xform->buffered; 00746 hs->key = key; 00747 hs->obj = val; 00748 00749 g_queue_push_tail (xform->hotspots, hs); 00750 } 00751 00752 void 00753 xmms_xform_auxdata_barrier (xmms_xform_t *xform) 00754 { 00755 xmmsv_t *val = xmmsv_new_none (); 00756 xmms_xform_auxdata_set_val (xform, NULL, val); 00757 } 00758 00759 void 00760 xmms_xform_auxdata_set_int (xmms_xform_t *xform, const char *key, int intval) 00761 { 00762 xmmsv_t *val = xmmsv_new_int (intval); 00763 xmms_xform_auxdata_set_val (xform, g_strdup (key), val); 00764 } 00765 00766 void 00767 xmms_xform_auxdata_set_str (xmms_xform_t *xform, const gchar *key, 00768 const gchar *strval) 00769 { 00770 xmmsv_t *val; 00771 const char *old; 00772 00773 if (xmms_xform_auxdata_get_str (xform, key, &old)) { 00774 if (strcmp (old, strval) == 0) { 00775 return; 00776 } 00777 } 00778 00779 val = xmmsv_new_string (strval); 00780 xmms_xform_auxdata_set_val (xform, g_strdup (key), val); 00781 } 00782 00783 void 00784 xmms_xform_auxdata_set_bin (xmms_xform_t *xform, const gchar *key, 00785 gpointer data, gssize len) 00786 { 00787 xmmsv_t *val; 00788 00789 val = xmmsv_new_bin (data, len); 00790 xmms_xform_auxdata_set_val (xform, g_strdup (key), val); 00791 } 00792 00793 static const xmmsv_t * 00794 xmms_xform_auxdata_get_val (xmms_xform_t *xform, const gchar *key) 00795 { 00796 guint i; 00797 xmms_xform_hotspot_t *hs; 00798 xmmsv_t *val = NULL; 00799 00800 /* privdata is always got from the previous xform */ 00801 xform = xform->prev; 00802 00803 /* check if we have unhandled current (pos 0) hotspots for this key */ 00804 for (i=0; (hs = g_queue_peek_nth (xform->hotspots, i)) != NULL; i++) { 00805 if (hs->pos != 0) { 00806 break; 00807 } else if (hs->key && !strcmp (key, hs->key)) { 00808 val = hs->obj; 00809 } 00810 } 00811 00812 if (!val) { 00813 val = g_hash_table_lookup (xform->privdata, key); 00814 } 00815 00816 return val; 00817 } 00818 00819 gboolean 00820 xmms_xform_auxdata_has_val (xmms_xform_t *xform, const gchar *key) 00821 { 00822 return !!xmms_xform_auxdata_get_val (xform, key); 00823 } 00824 00825 gboolean 00826 xmms_xform_auxdata_get_int (xmms_xform_t *xform, const gchar *key, gint32 *val) 00827 { 00828 const xmmsv_t *obj; 00829 00830 obj = xmms_xform_auxdata_get_val (xform, key); 00831 if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_INT32) { 00832 xmmsv_get_int (obj, val); 00833 return TRUE; 00834 } 00835 00836 return FALSE; 00837 } 00838 00839 gboolean 00840 xmms_xform_auxdata_get_str (xmms_xform_t *xform, const gchar *key, 00841 const gchar **val) 00842 { 00843 const xmmsv_t *obj; 00844 00845 obj = xmms_xform_auxdata_get_val (xform, key); 00846 if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_STRING) { 00847 xmmsv_get_string (obj, val); 00848 return TRUE; 00849 } 00850 00851 return FALSE; 00852 } 00853 00854 gboolean 00855 xmms_xform_auxdata_get_bin (xmms_xform_t *xform, const gchar *key, 00856 const guchar **data, gsize *datalen) 00857 { 00858 const xmmsv_t *obj; 00859 00860 obj = xmms_xform_auxdata_get_val (xform, key); 00861 if (obj && xmmsv_get_type (obj) == XMMSV_TYPE_BIN) { 00862 xmmsv_get_bin (obj, data, datalen); 00863 return TRUE; 00864 } 00865 00866 return FALSE; 00867 } 00868 00869 const char * 00870 xmms_xform_shortname (xmms_xform_t *xform) 00871 { 00872 return (xform->plugin) 00873 ? xmms_plugin_shortname_get ((xmms_plugin_t *) xform->plugin) 00874 : "unknown"; 00875 } 00876 00877 static gint 00878 xmms_xform_this_peek (xmms_xform_t *xform, gpointer buf, gint siz, 00879 xmms_error_t *err) 00880 { 00881 while (xform->buffered < siz) { 00882 gint res; 00883 00884 if (xform->buffered + READ_CHUNK > xform->buffersize) { 00885 xform->buffersize *= 2; 00886 xform->buffer = g_realloc (xform->buffer, xform->buffersize); 00887 } 00888 00889 res = xform->plugin->methods.read (xform, 00890 &xform->buffer[xform->buffered], 00891 READ_CHUNK, err); 00892 00893 if (res < -1) { 00894 XMMS_DBG ("Read method of %s returned bad value (%d) - BUG IN PLUGIN", 00895 xmms_xform_shortname (xform), res); 00896 res = -1; 00897 } 00898 00899 if (res == 0) { 00900 xform->eos = TRUE; 00901 break; 00902 } else if (res == -1) { 00903 xform->error = TRUE; 00904 return -1; 00905 } else { 00906 xform->buffered += res; 00907 } 00908 } 00909 00910 /* might have eosed */ 00911 siz = MIN (siz, xform->buffered); 00912 memcpy (buf, xform->buffer, siz); 00913 return siz; 00914 } 00915 00916 static void 00917 xmms_xform_hotspot_callback (gpointer data, gpointer user_data) 00918 { 00919 xmms_xform_hotspot_t *hs = data; 00920 gint *read = user_data; 00921 00922 hs->pos -= *read; 00923 } 00924 00925 static gint 00926 xmms_xform_hotspots_update (xmms_xform_t *xform) 00927 { 00928 xmms_xform_hotspot_t *hs; 00929 gint ret = -1; 00930 00931 hs = g_queue_peek_head (xform->hotspots); 00932 while (hs != NULL && hs->pos == 0) { 00933 g_queue_pop_head (xform->hotspots); 00934 if (hs->key) { 00935 g_hash_table_insert (xform->privdata, hs->key, hs->obj); 00936 } 00937 hs = g_queue_peek_head (xform->hotspots); 00938 } 00939 00940 if (hs != NULL) { 00941 ret = hs->pos; 00942 } 00943 00944 return ret; 00945 } 00946 00947 gint 00948 xmms_xform_this_read (xmms_xform_t *xform, gpointer buf, gint siz, 00949 xmms_error_t *err) 00950 { 00951 gint read = 0; 00952 gint nexths; 00953 00954 if (xform->error) { 00955 xmms_error_set (err, XMMS_ERROR_GENERIC, "Read on errored xform"); 00956 return -1; 00957 } 00958 00959 /* update hotspots */ 00960 nexths = xmms_xform_hotspots_update (xform); 00961 if (nexths >= 0) { 00962 siz = MIN (siz, nexths); 00963 } 00964 00965 if (xform->buffered) { 00966 read = MIN (siz, xform->buffered); 00967 memcpy (buf, xform->buffer, read); 00968 xform->buffered -= read; 00969 00970 /* buffer edited, update hotspot positions */ 00971 g_queue_foreach (xform->hotspots, &xmms_xform_hotspot_callback, &read); 00972 00973 if (xform->buffered) { 00974 /* unless we are _peek:ing often 00975 this should be fine */ 00976 memmove (xform->buffer, &xform->buffer[read], xform->buffered); 00977 } 00978 } 00979 00980 if (xform->eos) { 00981 return read; 00982 } 00983 00984 while (read < siz) { 00985 gint res; 00986 00987 res = xform->plugin->methods.read (xform, buf + read, siz - read, err); 00988 if (xform->metadata_collected && xform->metadata_changed) 00989 xmms_xform_metadata_update (xform); 00990 00991 if (res < -1) { 00992 XMMS_DBG ("Read method of %s returned bad value (%d) - BUG IN PLUGIN", xmms_xform_shortname (xform), res); 00993 res = -1; 00994 } 00995 00996 if (res == 0) { 00997 xform->eos = TRUE; 00998 break; 00999 } else if (res == -1) { 01000 xform->error = TRUE; 01001 return -1; 01002 } else { 01003 if (read == 0) 01004 xmms_xform_hotspots_update (xform); 01005 01006 if (!g_queue_is_empty (xform->hotspots)) { 01007 if (xform->buffered + res > xform->buffersize) { 01008 xform->buffersize = MAX (xform->buffersize * 2, 01009 xform->buffersize + res); 01010 xform->buffer = g_realloc (xform->buffer, 01011 xform->buffersize); 01012 } 01013 01014 g_memmove (xform->buffer + xform->buffered, buf + read, res); 01015 xform->buffered += res; 01016 break; 01017 } 01018 read += res; 01019 } 01020 } 01021 01022 return read; 01023 } 01024 01025 gint64 01026 xmms_xform_this_seek (xmms_xform_t *xform, gint64 offset, 01027 xmms_xform_seek_mode_t whence, xmms_error_t *err) 01028 { 01029 gint64 res; 01030 01031 if (xform->error) { 01032 xmms_error_set (err, XMMS_ERROR_GENERIC, "Seek on errored xform"); 01033 return -1; 01034 } 01035 01036 if (!xform->plugin->methods.seek) { 01037 XMMS_DBG ("Seek not implemented in '%s'", xmms_xform_shortname (xform)); 01038 xmms_error_set (err, XMMS_ERROR_GENERIC, "Seek not implemented"); 01039 return -1; 01040 } 01041 01042 if (xform->buffered && whence == XMMS_XFORM_SEEK_CUR) { 01043 offset -= xform->buffered; 01044 } 01045 01046 res = xform->plugin->methods.seek (xform, offset, whence, err); 01047 if (res != -1) { 01048 xmms_xform_hotspot_t *hs; 01049 01050 xform->eos = FALSE; 01051 xform->buffered = 0; 01052 01053 /* flush the hotspot queue on seek */ 01054 while ((hs = g_queue_pop_head (xform->hotspots)) != NULL) { 01055 g_free (hs->key); 01056 xmmsv_unref (hs->obj); 01057 g_free (hs); 01058 } 01059 } 01060 01061 return res; 01062 } 01063 01064 gint 01065 xmms_xform_peek (xmms_xform_t *xform, gpointer buf, gint siz, 01066 xmms_error_t *err) 01067 { 01068 g_return_val_if_fail (xform->prev, -1); 01069 return xmms_xform_this_peek (xform->prev, buf, siz, err); 01070 } 01071 01072 gchar * 01073 xmms_xform_read_line (xmms_xform_t *xform, gchar *line, xmms_error_t *err) 01074 { 01075 gchar *p; 01076 01077 g_return_val_if_fail (xform, NULL); 01078 g_return_val_if_fail (line, NULL); 01079 01080 p = strchr (xform->lr.buf, '\n'); 01081 01082 if (!p) { 01083 gint l, r; 01084 01085 l = (XMMS_XFORM_MAX_LINE_SIZE - 1) - (xform->lr.bufend - xform->lr.buf); 01086 if (l) { 01087 r = xmms_xform_read (xform, xform->lr.bufend, l, err); 01088 if (r < 0) { 01089 return NULL; 01090 } 01091 xform->lr.bufend += r; 01092 } 01093 if (xform->lr.bufend <= xform->lr.buf) 01094 return NULL; 01095 01096 *(xform->lr.bufend) = '\0'; 01097 p = strchr (xform->lr.buf, '\n'); 01098 if (!p) { 01099 p = xform->lr.bufend; 01100 } 01101 } 01102 01103 if (p > xform->lr.buf && *(p-1) == '\r') { 01104 *(p-1) = '\0'; 01105 } else { 01106 *p = '\0'; 01107 } 01108 01109 strcpy (line, xform->lr.buf); 01110 memmove (xform->lr.buf, p + 1, xform->lr.bufend - p); 01111 xform->lr.bufend -= (p - xform->lr.buf) + 1; 01112 *xform->lr.bufend = '\0'; 01113 01114 return line; 01115 } 01116 01117 gint 01118 xmms_xform_read (xmms_xform_t *xform, gpointer buf, gint siz, xmms_error_t *err) 01119 { 01120 g_return_val_if_fail (xform->prev, -1); 01121 return xmms_xform_this_read (xform->prev, buf, siz, err); 01122 } 01123 01124 gint64 01125 xmms_xform_seek (xmms_xform_t *xform, gint64 offset, 01126 xmms_xform_seek_mode_t whence, xmms_error_t *err) 01127 { 01128 g_return_val_if_fail (xform->prev, -1); 01129 return xmms_xform_this_seek (xform->prev, offset, whence, err); 01130 } 01131 01132 const gchar * 01133 xmms_xform_get_url (xmms_xform_t *xform) 01134 { 01135 const gchar *url = NULL; 01136 xmms_xform_t *x; 01137 x = xform; 01138 01139 while (!url && x) { 01140 url = xmms_xform_indata_get_str (x, XMMS_STREAM_TYPE_URL); 01141 x = x->prev; 01142 } 01143 01144 return url; 01145 } 01146 01147 static void 01148 xmms_xform_plugin_destroy (xmms_object_t *obj) 01149 { 01150 xmms_xform_plugin_t *plugin = (xmms_xform_plugin_t *)obj; 01151 01152 while (plugin->in_types) { 01153 xmms_object_unref (plugin->in_types->data); 01154 01155 plugin->in_types = g_list_delete_link (plugin->in_types, 01156 plugin->in_types); 01157 } 01158 01159 xmms_plugin_destroy ((xmms_plugin_t *)obj); 01160 } 01161 01162 xmms_plugin_t * 01163 xmms_xform_plugin_new (void) 01164 { 01165 xmms_xform_plugin_t *res; 01166 01167 res = xmms_object_new (xmms_xform_plugin_t, xmms_xform_plugin_destroy); 01168 01169 return (xmms_plugin_t *)res; 01170 } 01171 01172 void 01173 xmms_xform_plugin_methods_set (xmms_xform_plugin_t *plugin, 01174 xmms_xform_methods_t *methods) 01175 { 01176 01177 g_return_if_fail (plugin); 01178 g_return_if_fail (plugin->plugin.type == XMMS_PLUGIN_TYPE_XFORM); 01179 01180 XMMS_DBG ("Registering xform '%s'", 01181 xmms_plugin_shortname_get ((xmms_plugin_t *) plugin)); 01182 01183 memcpy (&plugin->methods, methods, sizeof (xmms_xform_methods_t)); 01184 } 01185 01186 gboolean 01187 xmms_xform_plugin_verify (xmms_plugin_t *_plugin) 01188 { 01189 xmms_xform_plugin_t *plugin = (xmms_xform_plugin_t *) _plugin; 01190 01191 g_return_val_if_fail (plugin, FALSE); 01192 g_return_val_if_fail (plugin->plugin.type == XMMS_PLUGIN_TYPE_XFORM, FALSE); 01193 01194 /* more checks */ 01195 01196 return TRUE; 01197 } 01198 01199 void 01200 xmms_xform_plugin_indata_add (xmms_xform_plugin_t *plugin, ...) 01201 { 01202 xmms_stream_type_t *t; 01203 va_list ap; 01204 gchar *config_key, config_value[32]; 01205 gint priority; 01206 01207 va_start (ap, plugin); 01208 t = xmms_stream_type_parse (ap); 01209 va_end (ap); 01210 01211 config_key = g_strconcat ("priority.", 01212 xmms_stream_type_get_str (t, XMMS_STREAM_TYPE_NAME), 01213 NULL); 01214 priority = xmms_stream_type_get_int (t, XMMS_STREAM_TYPE_PRIORITY); 01215 g_snprintf (config_value, sizeof (config_value), "%d", priority); 01216 xmms_xform_plugin_config_property_register (plugin, config_key, 01217 config_value, NULL, NULL); 01218 g_free (config_key); 01219 01220 plugin->in_types = g_list_prepend (plugin->in_types, t); 01221 } 01222 01223 static gboolean 01224 xmms_xform_plugin_supports (xmms_xform_plugin_t *plugin, xmms_stream_type_t *st, 01225 gint *priority) 01226 { 01227 GList *t; 01228 01229 for (t = plugin->in_types; t; t = g_list_next (t)) { 01230 if (xmms_stream_type_match (t->data, st)) { 01231 if (priority) { 01232 gchar *config_key; 01233 xmms_config_property_t *conf_priority; 01234 01235 config_key = g_strconcat ("priority.", 01236 xmms_stream_type_get_str (t->data, XMMS_STREAM_TYPE_NAME), 01237 NULL); 01238 conf_priority = xmms_plugin_config_lookup ((xmms_plugin_t *)plugin, 01239 config_key); 01240 g_free (config_key); 01241 01242 if (conf_priority) { 01243 *priority = xmms_config_property_get_int (conf_priority); 01244 } else { 01245 *priority = XMMS_STREAM_TYPE_PRIORITY_DEFAULT; 01246 } 01247 } 01248 return TRUE; 01249 } 01250 } 01251 return FALSE; 01252 } 01253 01254 typedef struct match_state_St { 01255 xmms_xform_plugin_t *match; 01256 xmms_stream_type_t *out_type; 01257 gint priority; 01258 } match_state_t; 01259 01260 static gboolean 01261 xmms_xform_match (xmms_plugin_t *_plugin, gpointer user_data) 01262 { 01263 xmms_xform_plugin_t *plugin = (xmms_xform_plugin_t *)_plugin; 01264 match_state_t *state = (match_state_t *)user_data; 01265 gint priority; 01266 01267 g_assert (_plugin->type == XMMS_PLUGIN_TYPE_XFORM); 01268 01269 if (!plugin->in_types) { 01270 XMMS_DBG ("Skipping plugin '%s'", xmms_plugin_shortname_get (_plugin)); 01271 return TRUE; 01272 } 01273 01274 XMMS_DBG ("Trying plugin '%s'", xmms_plugin_shortname_get (_plugin)); 01275 if (xmms_xform_plugin_supports (plugin, state->out_type, &priority)) { 01276 XMMS_DBG ("Plugin '%s' matched (priority %d)", 01277 xmms_plugin_shortname_get (_plugin), priority); 01278 if (priority > state->priority) { 01279 if (state->match) { 01280 XMMS_DBG ("Using plugin '%s' (priority %d) instead of '%s' (priority %d)", 01281 xmms_plugin_shortname_get (_plugin), priority, 01282 xmms_plugin_shortname_get ((xmms_plugin_t *)state->match), 01283 state->priority); 01284 } 01285 01286 state->match = plugin; 01287 state->priority = priority; 01288 } 01289 } 01290 01291 return TRUE; 01292 } 01293 01294 xmms_xform_t * 01295 xmms_xform_find (xmms_xform_t *prev, xmms_medialib_entry_t entry, 01296 GList *goal_hints) 01297 { 01298 match_state_t state; 01299 xmms_xform_t *xform = NULL; 01300 01301 state.out_type = prev->out_type; 01302 state.match = NULL; 01303 state.priority = -1; 01304 01305 xmms_plugin_foreach (XMMS_PLUGIN_TYPE_XFORM, xmms_xform_match, &state); 01306 01307 if (state.match) { 01308 xform = xmms_xform_new (state.match, prev, entry, goal_hints); 01309 } else { 01310 XMMS_DBG ("Found no matching plugin..."); 01311 } 01312 01313 return xform; 01314 } 01315 01316 gboolean 01317 xmms_xform_iseos (xmms_xform_t *xform) 01318 { 01319 gboolean ret = TRUE; 01320 01321 if (xform->prev) { 01322 ret = xform->prev->eos; 01323 } 01324 01325 return ret; 01326 } 01327 01328 const xmms_stream_type_t * 01329 xmms_xform_get_out_stream_type (xmms_xform_t *xform) 01330 { 01331 return xform->out_type; 01332 } 01333 01334 const GList * 01335 xmms_xform_goal_hints_get (xmms_xform_t *xform) 01336 { 01337 return xform->goal_hints; 01338 } 01339 01340 01341 static gboolean 01342 has_goalformat (xmms_xform_t *xform, GList *goal_formats) 01343 { 01344 const xmms_stream_type_t *current; 01345 gboolean ret = FALSE; 01346 GList *n; 01347 01348 current = xmms_xform_get_out_stream_type (xform); 01349 01350 for (n = goal_formats; n; n = g_list_next (n)) { 01351 xmms_stream_type_t *goal_type = n->data; 01352 if (xmms_stream_type_match (goal_type, current)) { 01353 ret = TRUE; 01354 break; 01355 } 01356 01357 } 01358 01359 if (!ret) { 01360 XMMS_DBG ("Not in one of %d goal-types", g_list_length (goal_formats)); 01361 } 01362 01363 return ret; 01364 } 01365 01366 static void 01367 outdata_type_metadata_collect (xmms_xform_t *xform) 01368 { 01369 gint val; 01370 const char *mime; 01371 xmms_stream_type_t *type; 01372 01373 type = xform->out_type; 01374 mime = xmms_stream_type_get_str (type, XMMS_STREAM_TYPE_MIMETYPE); 01375 if (strcmp (mime, "audio/pcm") != 0) { 01376 return; 01377 } 01378 01379 val = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_FORMAT); 01380 if (val != -1) { 01381 const gchar *name = xmms_sample_name_get ((xmms_sample_format_t) val); 01382 xmms_xform_metadata_set_str (xform, 01383 XMMS_MEDIALIB_ENTRY_PROPERTY_SAMPLE_FMT, 01384 name); 01385 } 01386 01387 val = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_SAMPLERATE); 01388 if (val != -1) { 01389 xmms_xform_metadata_set_int (xform, 01390 XMMS_MEDIALIB_ENTRY_PROPERTY_SAMPLERATE, 01391 val); 01392 } 01393 01394 val = xmms_stream_type_get_int (type, XMMS_STREAM_TYPE_FMT_CHANNELS); 01395 if (val != -1) { 01396 xmms_xform_metadata_set_int (xform, 01397 XMMS_MEDIALIB_ENTRY_PROPERTY_CHANNELS, 01398 val); 01399 } 01400 } 01401 01402 static xmms_xform_t * 01403 chain_setup (xmms_medialib_entry_t entry, const gchar *url, GList *goal_formats) 01404 { 01405 xmms_xform_t *xform, *last; 01406 gchar *durl, *args; 01407 01408 if (!entry) { 01409 entry = 1; /* FIXME: this is soooo ugly, don't do this */ 01410 } 01411 01412 xform = xmms_xform_new (NULL, NULL, 0, goal_formats); 01413 01414 durl = g_strdup (url); 01415 01416 args = strchr (durl, '?'); 01417 if (args) { 01418 gchar **params; 01419 gint i; 01420 *args = 0; 01421 args++; 01422 xmms_medialib_decode_url (args); 01423 01424 params = g_strsplit (args, "&", 0); 01425 01426 for (i = 0; params && params[i]; i++) { 01427 gchar *v; 01428 v = strchr (params[i], '='); 01429 if (v) { 01430 *v = 0; 01431 v++; 01432 xmms_xform_metadata_set_str (xform, params[i], v); 01433 } else { 01434 xmms_xform_metadata_set_int (xform, params[i], 1); 01435 } 01436 } 01437 g_strfreev (params); 01438 } 01439 xmms_medialib_decode_url (durl); 01440 01441 xmms_xform_outdata_type_add (xform, XMMS_STREAM_TYPE_MIMETYPE, 01442 "application/x-url", XMMS_STREAM_TYPE_URL, 01443 durl, XMMS_STREAM_TYPE_END); 01444 01445 g_free (durl); 01446 01447 last = xform; 01448 01449 do { 01450 xform = xmms_xform_find (last, entry, goal_formats); 01451 if (!xform) { 01452 xmms_log_error ("Couldn't set up chain for '%s' (%d)", 01453 url, entry); 01454 xmms_object_unref (last); 01455 01456 return NULL; 01457 } 01458 xmms_object_unref (last); 01459 last = xform; 01460 } while (!has_goalformat (xform, goal_formats)); 01461 01462 outdata_type_metadata_collect (last); 01463 01464 return last; 01465 } 01466 01467 static void 01468 chain_finalize (xmms_xform_t *xform, xmms_medialib_entry_t entry, 01469 const gchar *url, gboolean rehashing) 01470 { 01471 GString *namestr; 01472 01473 namestr = g_string_new (""); 01474 xmms_xform_metadata_collect (xform, namestr, rehashing); 01475 xmms_log_info ("Successfully setup chain for '%s' (%d) containing %s", 01476 url, entry, namestr->str); 01477 01478 g_string_free (namestr, TRUE); 01479 } 01480 01481 static gchar * 01482 get_url_for_entry (xmms_medialib_entry_t entry) 01483 { 01484 xmms_medialib_session_t *session; 01485 gchar *url = NULL; 01486 01487 session = xmms_medialib_begin (); 01488 url = xmms_medialib_entry_property_get_str (session, entry, 01489 XMMS_MEDIALIB_ENTRY_PROPERTY_URL); 01490 xmms_medialib_end (session); 01491 01492 if (!url) { 01493 xmms_log_error ("Couldn't get url for entry (%d)", entry); 01494 } 01495 01496 return url; 01497 } 01498 01499 xmms_xform_t * 01500 xmms_xform_chain_setup (xmms_medialib_entry_t entry, GList *goal_formats, 01501 gboolean rehash) 01502 { 01503 gchar *url; 01504 xmms_xform_t *xform; 01505 01506 if (!(url = get_url_for_entry (entry))) { 01507 return NULL; 01508 } 01509 01510 xform = xmms_xform_chain_setup_url (entry, url, goal_formats, rehash); 01511 g_free (url); 01512 01513 return xform; 01514 } 01515 01516 xmms_xform_t * 01517 xmms_xform_chain_setup_url (xmms_medialib_entry_t entry, const gchar *url, 01518 GList *goal_formats, gboolean rehash) 01519 { 01520 xmms_xform_t *last; 01521 xmms_plugin_t *plugin; 01522 xmms_xform_plugin_t *xform_plugin; 01523 gboolean add_segment = FALSE; 01524 01525 last = chain_setup (entry, url, goal_formats); 01526 if (!last) { 01527 return NULL; 01528 } 01529 01530 /* first check that segment plugin is available in the system */ 01531 plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, "segment"); 01532 xform_plugin = (xmms_xform_plugin_t *) plugin; 01533 01534 /* if segment plugin input is the same as current output, include it 01535 * for collecting additional duration metadata on audio entries */ 01536 if (xform_plugin) { 01537 add_segment = xmms_xform_plugin_supports (xform_plugin, 01538 last->out_type, 01539 NULL); 01540 xmms_object_unref (plugin); 01541 } 01542 01543 /* add segment plugin to the chain if it can be added */ 01544 if (add_segment) { 01545 last = xmms_xform_new_effect (last, entry, goal_formats, "segment"); 01546 if (!last) { 01547 return NULL; 01548 } 01549 } 01550 01551 /* if not rehashing, also initialize all the effect plugins */ 01552 if (!rehash) { 01553 last = add_effects (last, entry, goal_formats); 01554 if (!last) { 01555 return NULL; 01556 } 01557 } 01558 01559 chain_finalize (last, entry, url, rehash); 01560 return last; 01561 } 01562 01563 xmms_config_property_t * 01564 xmms_xform_plugin_config_property_register (xmms_xform_plugin_t *xform_plugin, 01565 const gchar *name, 01566 const gchar *default_value, 01567 xmms_object_handler_t cb, 01568 gpointer userdata) 01569 { 01570 xmms_plugin_t *plugin = (xmms_plugin_t *) xform_plugin; 01571 01572 return xmms_plugin_config_property_register (plugin, name, 01573 default_value, 01574 cb, userdata); 01575 } 01576 01577 xmms_config_property_t * 01578 xmms_xform_config_lookup (xmms_xform_t *xform, const gchar *path) 01579 { 01580 g_return_val_if_fail (xform->plugin, NULL); 01581 01582 return xmms_plugin_config_lookup ((xmms_plugin_t *) xform->plugin, path); 01583 } 01584 01585 static xmms_xform_t * 01586 add_effects (xmms_xform_t *last, xmms_medialib_entry_t entry, 01587 GList *goal_formats) 01588 { 01589 gint effect_no; 01590 01591 for (effect_no = 0; TRUE; effect_no++) { 01592 xmms_config_property_t *cfg; 01593 gchar key[64]; 01594 const gchar *name; 01595 01596 g_snprintf (key, sizeof (key), "effect.order.%i", effect_no); 01597 01598 cfg = xmms_config_lookup (key); 01599 if (!cfg) { 01600 break; 01601 } 01602 01603 name = xmms_config_property_get_string (cfg); 01604 01605 if (!name[0]) { 01606 continue; 01607 } 01608 01609 last = xmms_xform_new_effect (last, entry, goal_formats, name); 01610 } 01611 01612 return last; 01613 } 01614 01615 static xmms_xform_t * 01616 xmms_xform_new_effect (xmms_xform_t *last, xmms_medialib_entry_t entry, 01617 GList *goal_formats, const gchar *name) 01618 { 01619 xmms_plugin_t *plugin; 01620 xmms_xform_plugin_t *xform_plugin; 01621 xmms_xform_t *xform; 01622 01623 plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, name); 01624 if (!plugin) { 01625 xmms_log_error ("Couldn't find any effect named '%s'", name); 01626 return last; 01627 } 01628 01629 xform_plugin = (xmms_xform_plugin_t *) plugin; 01630 if (!xmms_xform_plugin_supports (xform_plugin, last->out_type, NULL)) { 01631 xmms_log_info ("Effect '%s' doesn't support format, skipping", 01632 xmms_plugin_shortname_get (plugin)); 01633 xmms_object_unref (plugin); 01634 return last; 01635 } 01636 01637 xform = xmms_xform_new (xform_plugin, last, entry, goal_formats); 01638 01639 if (xform) { 01640 xmms_object_unref (last); 01641 last = xform; 01642 } else { 01643 xmms_log_info ("Effect '%s' failed to initialize, skipping", 01644 xmms_plugin_shortname_get (plugin)); 01645 } 01646 xmms_xform_plugin_config_property_register (xform_plugin, 01647 "enabled", "0", 01648 NULL, NULL); 01649 xmms_object_unref (plugin); 01650 return last; 01651 } 01652 01653 static void 01654 update_effect_properties (xmms_object_t *object, xmmsv_t *data, 01655 gpointer userdata) 01656 { 01657 gint effect_no = GPOINTER_TO_INT (userdata); 01658 const gchar *name; 01659 01660 xmms_config_property_t *cfg; 01661 xmms_xform_plugin_t *xform_plugin; 01662 xmms_plugin_t *plugin; 01663 gchar key[64]; 01664 01665 name = xmms_config_property_get_string ((xmms_config_property_t *) object); 01666 01667 if (name[0]) { 01668 plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, name); 01669 if (!plugin) { 01670 xmms_log_error ("Couldn't find any effect named '%s'", name); 01671 } else { 01672 xform_plugin = (xmms_xform_plugin_t *) plugin; 01673 xmms_xform_plugin_config_property_register (xform_plugin, "enabled", 01674 "1", NULL, NULL); 01675 xmms_object_unref (plugin); 01676 } 01677 01678 /* setup new effect.order.n */ 01679 g_snprintf (key, sizeof (key), "effect.order.%i", effect_no + 1); 01680 01681 cfg = xmms_config_lookup (key); 01682 if (!cfg) { 01683 xmms_config_property_register (key, "", update_effect_properties, 01684 GINT_TO_POINTER (effect_no + 1)); 01685 } 01686 } 01687 } 01688 01689 static void 01690 effect_callbacks_init (void) 01691 { 01692 gint effect_no; 01693 01694 xmms_config_property_t *cfg; 01695 xmms_xform_plugin_t *xform_plugin; 01696 xmms_plugin_t *plugin; 01697 gchar key[64]; 01698 const gchar *name; 01699 01700 for (effect_no = 0; ; effect_no++) { 01701 g_snprintf (key, sizeof (key), "effect.order.%i", effect_no); 01702 01703 cfg = xmms_config_lookup (key); 01704 if (!cfg) { 01705 break; 01706 } 01707 xmms_config_property_callback_set (cfg, update_effect_properties, 01708 GINT_TO_POINTER (effect_no)); 01709 01710 name = xmms_config_property_get_string (cfg); 01711 if (!name[0]) { 01712 continue; 01713 } 01714 01715 plugin = xmms_plugin_find (XMMS_PLUGIN_TYPE_XFORM, name); 01716 if (!plugin) { 01717 xmms_log_error ("Couldn't find any effect named '%s'", name); 01718 continue; 01719 } 01720 01721 xform_plugin = (xmms_xform_plugin_t *) plugin; 01722 xmms_xform_plugin_config_property_register (xform_plugin, "enabled", 01723 "1", NULL, NULL); 01724 01725 xmms_object_unref (plugin); 01726 } 01727 01728 /* the name stored in the last present property was not "" or there was no 01729 last present property */ 01730 if ((!effect_no) || name[0]) { 01731 xmms_config_property_register (key, "", update_effect_properties, 01732 GINT_TO_POINTER (effect_no)); 01733 } 01734 } 01735