XMMS2
src/xmms/streamtype.c
Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2011 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 <glib.h>
00023 
00024 #include "xmmspriv/xmms_xform.h"
00025 #include "xmms/xmms_log.h"
00026 #include "xmms/xmms_object.h"
00027 
00028 
00029 struct xmms_stream_type_St {
00030     xmms_object_t obj;
00031     gint priority;
00032     gchar *name;
00033     GList *list;
00034 };
00035 
00036 typedef enum xmms_stream_type_val_type_E {
00037     STRING,
00038     INT,
00039 } xmms_stream_type_val_type_t;
00040 
00041 typedef struct xmms_stream_type_val_St {
00042     xmms_stream_type_key_t key;
00043     xmms_stream_type_val_type_t type;
00044     union {
00045         char *string;
00046         int num;
00047     } d;
00048 } xmms_stream_type_val_t;
00049 
00050 
00051 static void
00052 xmms_stream_type_destroy (xmms_object_t *obj)
00053 {
00054     xmms_stream_type_t *st = (xmms_stream_type_t *)obj;
00055     GList *n;
00056 
00057     g_free (st->name);
00058 
00059     for (n = st->list; n; n = g_list_next (n)) {
00060         xmms_stream_type_val_t *val = n->data;
00061         if (val->type == STRING) {
00062             g_free (val->d.string);
00063         }
00064         g_free (val);
00065     }
00066 
00067     g_list_free (st->list);
00068 }
00069 
00070 xmms_stream_type_t *
00071 xmms_stream_type_parse (va_list ap)
00072 {
00073     xmms_stream_type_t *res;
00074 
00075     res = xmms_object_new (xmms_stream_type_t, xmms_stream_type_destroy);
00076     if (!res) {
00077         return NULL;
00078     }
00079 
00080     res->priority = -1;
00081     res->name = NULL;
00082 
00083     for (;;) {
00084         xmms_stream_type_val_t *val;
00085         xmms_stream_type_key_t key;
00086 
00087         key = va_arg (ap, int);
00088         if (key == XMMS_STREAM_TYPE_END)
00089             break;
00090 
00091         if (key == XMMS_STREAM_TYPE_NAME) {
00092             res->name = g_strdup (va_arg (ap, char *));
00093             continue;
00094         }
00095 
00096         if (key == XMMS_STREAM_TYPE_PRIORITY) {
00097             res->priority = va_arg (ap, int);
00098             continue;
00099         }
00100 
00101         val = g_new0 (xmms_stream_type_val_t, 1);
00102         val->key = key;
00103 
00104         switch (val->key) {
00105         case XMMS_STREAM_TYPE_MIMETYPE:
00106         case XMMS_STREAM_TYPE_URL:
00107             val->type = STRING;
00108             val->d.string = g_strdup (va_arg (ap, char *));
00109             break;
00110         case XMMS_STREAM_TYPE_FMT_FORMAT:
00111         case XMMS_STREAM_TYPE_FMT_CHANNELS:
00112         case XMMS_STREAM_TYPE_FMT_SAMPLERATE:
00113             val->type = INT;
00114             val->d.num = va_arg (ap, int);
00115             break;
00116         default:
00117             XMMS_DBG ("UNKNOWN TYPE!!");
00118             g_free (val);
00119             xmms_object_unref (res);
00120             return NULL;
00121         }
00122         res->list = g_list_append (res->list, val);
00123     }
00124 
00125     if (!res->name) {
00126         const gchar *mime = xmms_stream_type_get_str (res, XMMS_STREAM_TYPE_MIMETYPE);
00127         const gchar *url = xmms_stream_type_get_str (res, XMMS_STREAM_TYPE_URL);
00128 
00129         if (mime && url) {
00130             res->name = g_strconcat (mime, ":", url, NULL);
00131         } else if (mime) {
00132             res->name = g_strdup (mime);
00133         } else {
00134             g_assert_not_reached ();
00135         }
00136 
00137         g_strdelimit (res->name, ".", '_');
00138     }
00139 
00140     if (res->priority < 0) {
00141         res->priority = XMMS_STREAM_TYPE_PRIORITY_DEFAULT;
00142     }
00143 
00144     return res;
00145 }
00146 
00147 const char *
00148 xmms_stream_type_get_str (const xmms_stream_type_t *st, xmms_stream_type_key_t key)
00149 {
00150     GList *n;
00151 
00152     if (key == XMMS_STREAM_TYPE_NAME) {
00153         return st->name;
00154     }
00155 
00156     for (n = st->list; n; n = g_list_next (n)) {
00157         xmms_stream_type_val_t *val = n->data;
00158         if (val->key == key) {
00159             if (val->type != STRING) {
00160                 XMMS_DBG ("Key passed to get_str is not string");
00161                 return NULL;
00162             }
00163             return val->d.string;
00164         }
00165     }
00166     return NULL;
00167 }
00168 
00169 
00170 gint
00171 xmms_stream_type_get_int (const xmms_stream_type_t *st, xmms_stream_type_key_t key)
00172 {
00173     GList *n;
00174 
00175     if (key == XMMS_STREAM_TYPE_PRIORITY) {
00176         return st->priority;
00177     }
00178 
00179     for (n = st->list; n; n = g_list_next (n)) {
00180         xmms_stream_type_val_t *val = n->data;
00181         if (val->key == key) {
00182             if (val->type != INT) {
00183                 XMMS_DBG ("Key passed to get_int is not int");
00184                 return -1;
00185             }
00186             return val->d.num;
00187         }
00188     }
00189     return -1;
00190 }
00191 
00192 
00193 
00194 
00195 static gboolean
00196 match_val (xmms_stream_type_val_t *vin, xmms_stream_type_val_t *vout)
00197 {
00198     if (vin->type != vout->type)
00199         return FALSE;
00200     switch (vin->type) {
00201     case STRING:
00202         return g_pattern_match_simple (vin->d.string, vout->d.string);
00203     case INT:
00204         return vin->d.num == vout->d.num;
00205     }
00206     return FALSE;
00207 }
00208 
00209 gboolean
00210 xmms_stream_type_match (const xmms_stream_type_t *in_type, const xmms_stream_type_t *out_type)
00211 {
00212     GList *in;
00213 
00214     for (in = in_type->list; in; in = g_list_next (in)) {
00215         xmms_stream_type_val_t *inval = in->data;
00216         GList *n;
00217 
00218         for (n = out_type->list; n; n = g_list_next (n)) {
00219             xmms_stream_type_val_t *outval = n->data;
00220             if (inval->key == outval->key) {
00221                 if (!match_val (inval, outval))
00222                     return FALSE;
00223                 break;
00224             }
00225 
00226         }
00227         if (!n) {
00228             /* didn't exist in out */
00229             return FALSE;
00230         }
00231     }
00232 
00233     return TRUE;
00234 }
00235 
00236 /**
00237  * Find the best pair of formats
00238  */
00239 xmms_stream_type_t *
00240 xmms_stream_type_coerce (const xmms_stream_type_t *in, const GList *goal_types)
00241 {
00242     xmms_stream_type_t *best = NULL;
00243     const GList *on;
00244 /*  gint bestscore = GINT_MAX;*/
00245     gint bestscore = 100000;
00246     gint format, samplerate, channels;
00247     gint gformat, gsamplerate, gchannels;
00248     const gchar *gmime;
00249 
00250     format = xmms_stream_type_get_int (in, XMMS_STREAM_TYPE_FMT_FORMAT);
00251     samplerate = xmms_stream_type_get_int (in, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00252     channels = xmms_stream_type_get_int (in, XMMS_STREAM_TYPE_FMT_CHANNELS);
00253 
00254     if (format == -1 || samplerate == -1 || channels == -1) {
00255         xmms_log_info ("In-type lacks format, samplerate or channels");
00256         return NULL;
00257     }
00258 
00259     for (on = goal_types ; on; on = g_list_next (on)) {
00260         xmms_stream_type_t *goal = on->data;
00261         const gchar *mime;
00262         gint score = 0;
00263 
00264         mime = xmms_stream_type_get_str (goal, XMMS_STREAM_TYPE_MIMETYPE);
00265         if (strcmp (mime, "audio/pcm") != 0) {
00266             continue;
00267         }
00268 
00269         gformat = xmms_stream_type_get_int (goal, XMMS_STREAM_TYPE_FMT_FORMAT);
00270         gsamplerate = xmms_stream_type_get_int (goal, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00271         gchannels = xmms_stream_type_get_int (goal, XMMS_STREAM_TYPE_FMT_CHANNELS);
00272         if (gsamplerate == -1) {
00273             gsamplerate = samplerate;
00274         }
00275         if (gformat == -1 || gchannels == -1) {
00276             continue;
00277         }
00278 
00279 
00280         if (gchannels > channels) {
00281             /* we loose no quality, just cputime */
00282             score += gchannels - channels;
00283         } else if (gchannels < channels) {
00284             /* quality loss! */
00285             score += 10 * (channels - gchannels);
00286         }
00287 
00288         /* the format enum should be ordered in
00289            quality order */
00290         if (gformat > format) {
00291             /* we loose no quality, just cputime */
00292             score += gformat - format;
00293         } else if (gformat < format) {
00294             /* quality loss! */
00295             score += 10 * (format - gformat);
00296         }
00297 
00298 
00299         if (gsamplerate > samplerate) {
00300             /* we loose no quality, just cputime */
00301             score += 2 * gsamplerate / samplerate;
00302         } else if (gsamplerate < samplerate) {
00303             /* quality loss! */
00304             score += 20 * samplerate / gsamplerate;
00305         }
00306 
00307         if (score < bestscore) {
00308             best = goal;
00309             bestscore = score;
00310         }
00311 
00312     }
00313 
00314     if (!best) {
00315         xmms_log_error ("Couldn't convert sample format to any of the %d goal formats",
00316                         g_list_length ((GList *)goal_types));
00317         return NULL;
00318     }
00319 
00320     gmime = xmms_stream_type_get_str (best, XMMS_STREAM_TYPE_MIMETYPE);
00321     gformat = xmms_stream_type_get_int (best, XMMS_STREAM_TYPE_FMT_FORMAT);
00322     gchannels = xmms_stream_type_get_int (best, XMMS_STREAM_TYPE_FMT_CHANNELS);
00323     gsamplerate = xmms_stream_type_get_int (best, XMMS_STREAM_TYPE_FMT_SAMPLERATE);
00324 
00325     /* Use the requested samplerate if target accepts any. */
00326     if (gsamplerate == -1) {
00327         gsamplerate = samplerate;
00328     }
00329 
00330     best = _xmms_stream_type_new (XMMS_STREAM_TYPE_BEGIN,
00331                                   XMMS_STREAM_TYPE_MIMETYPE, gmime,
00332                                   XMMS_STREAM_TYPE_FMT_FORMAT, gformat,
00333                                   XMMS_STREAM_TYPE_FMT_CHANNELS, gchannels,
00334                                   XMMS_STREAM_TYPE_FMT_SAMPLERATE, gsamplerate,
00335                                   XMMS_STREAM_TYPE_END);
00336 
00337     return best;
00338 }
00339 
00340 
00341 
00342 /*
00343     XMMS_DBG ("Looking for xform with intypes matching:");
00344     for (n = prev->out_types; n; n = g_list_next (n)) {
00345         xmms_stream_type_val_t *val = n->data;
00346         switch (val->type) {
00347         case INT:
00348             XMMS_DBG (" - %d = %d", val->key, val->d.num);
00349             break;
00350         case STRING:
00351             XMMS_DBG (" - %d = '%s'", val->key, val->d.string);
00352             break;
00353         default:
00354             XMMS_DBG (" - ????");
00355             break;
00356         }
00357     }
00358 
00359 */
00360 
00361 xmms_stream_type_t *
00362 _xmms_stream_type_new (const gchar *begin, ...)
00363 {
00364     xmms_stream_type_t *res;
00365     va_list ap;
00366 
00367     va_start (ap, begin);
00368     res = xmms_stream_type_parse (ap);
00369     va_end (ap);
00370 
00371     return res;
00372 }