• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/xmms/xform.c

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

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1