Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
probe.c
Go to the documentation of this file.
00001 /*
00002  * probe.c
00003  * Copyright 2009-2010 John Lindgren
00004  *
00005  * This file is part of Audacious.
00006  *
00007  * Audacious is free software: you can redistribute it and/or modify it under
00008  * the terms of the GNU General Public License as published by the Free Software
00009  * Foundation, version 2 or version 3 of the License.
00010  *
00011  * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY
00012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
00013  * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License along with
00016  * Audacious. If not, see <http://www.gnu.org/licenses/>.
00017  *
00018  * The Audacious team does not consider modular code linking to Audacious or
00019  * using our public API to be a derived work.
00020  */
00021 
00022 #include <glib.h>
00023 #include <stdio.h>
00024 #include <string.h>
00025 
00026 #include <libaudcore/audstrings.h>
00027 
00028 #include "debug.h"
00029 #include "misc.h"
00030 #include "playlist.h"
00031 #include "plugin.h"
00032 #include "plugins.h"
00033 #include "probe-buffer.h"
00034 
00035 typedef struct
00036 {
00037     const char * filename;
00038     VFSFile * handle;
00039     bool_t buffered, failed;
00040     PluginHandle * plugin;
00041 }
00042 ProbeState;
00043 
00044 static bool_t check_opened (ProbeState * state)
00045 {
00046     if (state->handle != NULL)
00047         return TRUE;
00048     if (state->failed)
00049         return FALSE;
00050 
00051     AUDDBG ("Opening %s.\n", state->filename);
00052     if ((state->buffered = vfs_is_remote (state->filename)))
00053         state->handle = probe_buffer_new (state->filename);
00054     else
00055         state->handle = vfs_fopen (state->filename, "r");
00056 
00057     if (state->handle != NULL)
00058         return TRUE;
00059 
00060     AUDDBG ("FAILED.\n");
00061     state->failed = TRUE;
00062     return FALSE;
00063 }
00064 
00065 static bool_t probe_func (PluginHandle * plugin, ProbeState * state)
00066 {
00067     AUDDBG ("Trying %s.\n", plugin_get_name (plugin));
00068     InputPlugin * decoder = plugin_get_header (plugin);
00069     if (decoder == NULL)
00070         return TRUE;
00071 
00072     if (decoder->is_our_file_from_vfs != NULL)
00073     {
00074         if (! check_opened (state))
00075             return FALSE;
00076 
00077         if (decoder->is_our_file_from_vfs (state->filename, state->handle))
00078         {
00079             state->plugin = plugin;
00080             return FALSE;
00081         }
00082 
00083         if (vfs_fseek (state->handle, 0, SEEK_SET) < 0)
00084             return FALSE;
00085     }
00086 
00087     return TRUE;
00088 }
00089 
00090 /* Optimization: If we have found plugins with a key match, assume that at least
00091  * one of them will succeed.  This means that we need not check the very last
00092  * plugin.  (If there is only one, we do not need to check it at all.)  This is
00093  * implemented as follows:
00094  *
00095  * 1. On the first call, assume until further notice the plugin passed is the
00096  *    last one and will therefore succeed.
00097  * 2. On a subsequent call, think twice and probe the plugin we assumed would
00098  *    succeed.  If it does in fact succeed, then we are done.  If not, assume
00099  *    similarly that the plugin passed in this call is the last one.
00100  */
00101 
00102 static bool_t probe_func_fast (PluginHandle * plugin, ProbeState * state)
00103 {
00104     if (state->plugin != NULL)
00105     {
00106         PluginHandle * prev = state->plugin;
00107         state->plugin = NULL;
00108 
00109         if (prev != NULL && ! probe_func (prev, state))
00110             return FALSE;
00111     }
00112 
00113     AUDDBG ("Guessing %s.\n", plugin_get_name (plugin));
00114     state->plugin = plugin;
00115     return TRUE;
00116 }
00117 
00118 static void probe_by_scheme (ProbeState * state)
00119 {
00120     const char * s = strstr (state->filename, "://");
00121 
00122     if (s == NULL)
00123         return;
00124 
00125     AUDDBG ("Probing by scheme.\n");
00126     char buf[s - state->filename + 1];
00127     memcpy (buf, state->filename, s - state->filename);
00128     buf[s - state->filename] = 0;
00129 
00130     input_plugin_for_key (INPUT_KEY_SCHEME, buf, (PluginForEachFunc) probe_func_fast, state);
00131 }
00132 
00133 static void probe_by_extension (ProbeState * state)
00134 {
00135     const char * ext, * sub;
00136     uri_parse (state->filename, NULL, & ext, & sub, NULL);
00137 
00138     if (ext == sub)
00139         return;
00140 
00141     AUDDBG ("Probing by extension.\n");
00142     char buf[sub - ext];
00143     memcpy (buf, ext + 1, sub - ext - 1);
00144     buf[sub - ext - 1] = 0;
00145 
00146     input_plugin_for_key (INPUT_KEY_EXTENSION, buf, (PluginForEachFunc) probe_func_fast, state);
00147 }
00148 
00149 static void probe_by_mime (ProbeState * state)
00150 {
00151     char * mime;
00152 
00153     if (! check_opened (state))
00154         return;
00155 
00156     if ((mime = vfs_get_metadata (state->handle, "content-type")) == NULL)
00157         return;
00158 
00159     AUDDBG ("Probing by MIME type.\n");
00160     input_plugin_for_key (INPUT_KEY_MIME, mime, (PluginForEachFunc)
00161      probe_func_fast, state);
00162     g_free (mime);
00163 }
00164 
00165 static void probe_by_content (ProbeState * state)
00166 {
00167     AUDDBG ("Probing by content.\n");
00168     plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc) probe_func, state);
00169 }
00170 
00171 PluginHandle * file_find_decoder (const char * filename, bool_t fast)
00172 {
00173     ProbeState state;
00174 
00175     AUDDBG ("Probing %s.\n", filename);
00176     state.plugin = NULL;
00177     state.filename = filename;
00178     state.handle = NULL;
00179     state.failed = FALSE;
00180 
00181     probe_by_scheme (& state);
00182 
00183     if (state.plugin != NULL)
00184         goto DONE;
00185 
00186     probe_by_extension (& state);
00187 
00188     if (state.plugin != NULL || fast)
00189         goto DONE;
00190 
00191     probe_by_mime (& state);
00192 
00193     if (state.plugin != NULL)
00194         goto DONE;
00195 
00196     probe_by_content (& state);
00197 
00198 DONE:
00199     if (state.handle != NULL)
00200         vfs_fclose (state.handle);
00201 
00202     return state.plugin;
00203 }
00204 
00205 Tuple * file_read_tuple (const char * filename, PluginHandle * decoder)
00206 {
00207     InputPlugin * ip = plugin_get_header (decoder);
00208     g_return_val_if_fail (ip, NULL);
00209     g_return_val_if_fail (ip->probe_for_tuple, NULL);
00210 
00211     VFSFile * handle = vfs_fopen (filename, "r");
00212     Tuple * tuple = ip->probe_for_tuple (filename, handle);
00213 
00214     if (handle)
00215         vfs_fclose (handle);
00216 
00217     return tuple;
00218 }
00219 
00220 bool_t file_read_image (const char * filename, PluginHandle * decoder,
00221  void * * data, int64_t * size)
00222 {
00223     if (! input_plugin_has_images (decoder))
00224         return FALSE;
00225 
00226     InputPlugin * ip = plugin_get_header (decoder);
00227     g_return_val_if_fail (ip, FALSE);
00228     g_return_val_if_fail (ip->get_song_image, FALSE);
00229 
00230     VFSFile * handle = vfs_fopen (filename, "r");
00231     bool_t success = ip->get_song_image (filename, handle, data, size);
00232 
00233     if (handle)
00234         vfs_fclose (handle);
00235 
00236     if (! success)
00237     {
00238         * data = NULL;
00239         * size = 0;
00240     }
00241 
00242     return success;
00243 }
00244 
00245 bool_t file_can_write_tuple (const char * filename, PluginHandle * decoder)
00246 {
00247     return input_plugin_can_write_tuple (decoder);
00248 }
00249 
00250 bool_t file_write_tuple (const char * filename, PluginHandle * decoder,
00251  const Tuple * tuple)
00252 {
00253     InputPlugin * ip = plugin_get_header (decoder);
00254     g_return_val_if_fail (ip, FALSE);
00255     g_return_val_if_fail (ip->update_song_tuple, FALSE);
00256 
00257     VFSFile * handle = vfs_fopen (filename, "r+");
00258 
00259     if (! handle)
00260         return FALSE;
00261 
00262     bool_t success = ip->update_song_tuple (tuple, handle);
00263 
00264     if (handle)
00265         vfs_fclose (handle);
00266 
00267     if (success)
00268         playlist_rescan_file (filename);
00269 
00270     return success;
00271 }
00272 
00273 bool_t custom_infowin (const char * filename, PluginHandle * decoder)
00274 {
00275     if (! input_plugin_has_infowin (decoder))
00276         return FALSE;
00277 
00278     InputPlugin * ip = plugin_get_header (decoder);
00279     g_return_val_if_fail (ip, FALSE);
00280     g_return_val_if_fail (ip->file_info_box, FALSE);
00281 
00282     ip->file_info_box (filename);
00283     return TRUE;
00284 }