XMMS2
|
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 }