Audacious  $Id:Doxyfile42802007-03-2104:39:00Znenolod$
art.c
Go to the documentation of this file.
1 /*
2  * art.c
3  * Copyright 2011-2012 John Lindgren
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions, and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions, and the following disclaimer in the documentation
13  * provided with the distribution.
14  *
15  * This software is provided "as is" and without any warranty, express or
16  * implied. In no event shall the authors be liable for any damages arising from
17  * the use of this software.
18  */
19 
20 #include <assert.h>
21 #include <errno.h>
22 #include <glib.h>
23 #include <pthread.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include <libaudcore/audstrings.h>
30 #include <libaudcore/hook.h>
31 
32 #include "main.h"
33 #include "misc.h"
34 #include "playlist.h"
35 #include "scanner.h"
36 #include "util.h"
37 
38 #define FLAG_DONE 1
39 #define FLAG_SENT 2
40 
41 typedef struct {
42  int refcount;
43  int flag;
44 
45  /* album art as JPEG or PNG data */
46  void * data;
47  int64_t len;
48 
49  /* album art as (possibly a temporary) file */
50  char * art_file;
52 } ArtItem;
53 
54 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
55 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
56 
57 static GHashTable * art_items; /* of ArtItem */
58 static char * current_ref; /* pooled */
59 static int send_source;
60 
61 static void art_item_free (ArtItem * item)
62 {
63  /* delete temporary file */
64  if (item->art_file && item->is_temp)
65  {
66  char * unixname = uri_to_filename (item->art_file);
67  if (unixname)
68  {
69  unlink (unixname);
70  free (unixname);
71  }
72  }
73 
74  free (item->data);
75  free (item->art_file);
76  g_slice_free (ArtItem, item);
77 }
78 
79 static bool_t send_requests (void * unused)
80 {
81  pthread_mutex_lock (& mutex);
82 
83  GQueue queue = G_QUEUE_INIT;
84 
85  GHashTableIter iter;
86  char * file;
87  ArtItem * item;
88 
89  for (g_hash_table_iter_init (& iter, art_items);
90  g_hash_table_iter_next (& iter, (void * *) & file, (void * *) & item);)
91  {
92  if (item->flag == FLAG_DONE)
93  {
94  g_queue_push_tail (& queue, str_ref (file));
95  item->flag = FLAG_SENT;
96  }
97  }
98 
99  if (send_source)
100  {
101  g_source_remove (send_source);
102  send_source = 0;
103  }
104 
105  pthread_mutex_unlock (& mutex);
106 
107  char * current = NULL;
108  if (! current_ref)
109  current = playback_entry_get_filename ();
110 
111  while ((file = g_queue_pop_head (& queue)))
112  {
113  hook_call ("art ready", file);
114 
115  if (current && ! strcmp (file, current))
116  {
117  hook_call ("current art ready", file);
118  current_ref = file;
119  }
120  else
121  {
122  art_unref (file); /* release temporary reference */
123  str_unref (file);
124  }
125  }
126 
127  str_unref (current);
128  return FALSE;
129 }
130 
131 static void request_callback (ScanRequest * request)
132 {
133  pthread_mutex_lock (& mutex);
134 
135  const char * file = scan_request_get_filename (request);
136  ArtItem * item = g_hash_table_lookup (art_items, file);
137  assert (item != NULL && ! item->flag);
138 
139  scan_request_get_image_data (request, & item->data, & item->len);
140  item->art_file = scan_request_get_image_file (request);
141  item->flag = FLAG_DONE;
142 
143  if (! send_source)
144  send_source = g_idle_add (send_requests, NULL);
145 
146  pthread_cond_broadcast (& cond);
147  pthread_mutex_unlock (& mutex);
148 }
149 
150 static ArtItem * art_item_get (const char * file, bool_t blocking)
151 {
152  ArtItem * item = g_hash_table_lookup (art_items, file);
153 
154  if (item && item->flag)
155  {
156  item->refcount ++;
157  return item;
158  }
159 
160  if (! item)
161  {
162  item = g_slice_new0 (ArtItem);
163  g_hash_table_insert (art_items, str_get (file), item);
164  item->refcount = 1; /* temporary reference */
165 
167  }
168 
169  if (! blocking)
170  return NULL;
171 
172  item->refcount ++;
173 
174  while (! item->flag)
175  pthread_cond_wait (& cond, & mutex);
176 
177  return item;
178 }
179 
180 static void art_item_unref (const char * file, ArtItem * item)
181 {
182  if (! -- item->refcount)
183  g_hash_table_remove (art_items, file);
184 }
185 
186 static void release_current (void)
187 {
188  if (current_ref)
189  {
192  current_ref = NULL;
193  }
194 }
195 
196 void art_init (void)
197 {
198  art_items = g_hash_table_new_full (g_str_hash, g_str_equal,
199  (GDestroyNotify) str_unref, (GDestroyNotify) art_item_free);
200 
201  hook_associate ("playlist position", (HookFunction) release_current, NULL);
202  hook_associate ("playlist set playing", (HookFunction) release_current, NULL);
203 }
204 
205 void art_cleanup (void)
206 {
207  hook_dissociate ("playlist position", (HookFunction) release_current);
208  hook_dissociate ("playlist set playing", (HookFunction) release_current);
209 
210  if (send_source)
211  {
212  g_source_remove (send_source);
213  send_source = 0;
214  }
215 
216  release_current ();
217 
218  g_hash_table_destroy (art_items);
219  art_items = NULL;
220 }
221 
222 void art_get_data_real (const char * file, const void * * data, int64_t * len,
223  bool_t blocking)
224 {
225  * data = NULL;
226  * len = 0;
227 
228  pthread_mutex_lock (& mutex);
229 
230  ArtItem * item = art_item_get (file, blocking);
231  if (! item)
232  goto UNLOCK;
233 
234  /* load data from external image file */
235  if (! item->data && item->art_file)
236  vfs_file_get_contents (item->art_file, & item->data, & item->len);
237 
238  if (item->data)
239  {
240  * data = item->data;
241  * len = item->len;
242  }
243  else
244  art_item_unref (file, item);
245 
246 UNLOCK:
247  pthread_mutex_unlock (& mutex);
248 }
249 
250 const char * art_get_file_real (const char * file, bool_t blocking)
251 {
252  const char * art_file = NULL;
253  pthread_mutex_lock (& mutex);
254 
255  ArtItem * item = art_item_get (file, blocking);
256  if (! item)
257  goto UNLOCK;
258 
259  /* save data to temporary file */
260  if (item->data && ! item->art_file)
261  {
262  char * unixname = write_temp_file (item->data, item->len);
263  if (unixname)
264  {
265  item->art_file = filename_to_uri (unixname);
266  item->is_temp = TRUE;
267  free (unixname);
268  }
269  }
270 
271  if (item->art_file)
272  art_file = item->art_file;
273  else
274  art_item_unref (file, item);
275 
276 UNLOCK:
277  pthread_mutex_unlock (& mutex);
278  return art_file;
279 }
280 
281 void art_request_data (const char * file, const void * * data, int64_t * len)
282 {
283  return art_get_data_real (file, data, len, FALSE);
284 }
285 
286 const char * art_request_file (const char * file)
287 {
288  return art_get_file_real (file, FALSE);
289 }
290 
291 void art_get_data (const char * file, const void * * data, int64_t * len)
292 {
293  fprintf (stderr, "aud_art_get_data() is deprecated. Use "
294  "aud_art_request_data() instead.\n");
295  return art_get_data_real (file, data, len, TRUE);
296 }
297 
298 const char * art_get_file (const char * file)
299 {
300  fprintf (stderr, "aud_art_get_file() is deprecated. Use "
301  "aud_art_request_file() instead.\n");
302  return art_get_file_real (file, TRUE);
303 }
304 
305 void art_unref (const char * file)
306 {
307  pthread_mutex_lock (& mutex);
308 
309  ArtItem * item = g_hash_table_lookup (art_items, file);
310  assert (item != NULL);
311 
312  art_item_unref (file, item);
313 
314  pthread_mutex_unlock (& mutex);
315 }