Update translations from 2.2.x branch
[vlc.git] / modules / misc / fingerprinter.c
blobbede1cd8095c9903836469e31957169b9c91cb17
1 /*****************************************************************************
2 * fingerprinter.c: Audio fingerprinter module
3 *****************************************************************************
4 * Copyright (C) 2012 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
24 #include <assert.h>
26 #include <vlc_common.h>
27 #include <vlc_plugin.h>
28 #include <vlc_stream.h>
29 #include <vlc_modules.h>
30 #include <vlc_meta.h>
31 #include <vlc_url.h>
33 #include <vlc/vlc.h>
34 #include <vlc_input.h>
35 #include <vlc_fingerprinter.h>
36 #include "webservices/acoustid.h"
37 #include "../stream_out/chromaprint_data.h"
39 /*****************************************************************************
40 * Local prototypes
41 *****************************************************************************/
43 struct fingerprinter_sys_t
45 vlc_thread_t thread;
47 struct
49 vlc_array_t queue;
50 vlc_mutex_t lock;
51 } incoming, results;
53 struct
55 vlc_array_t queue;
56 vlc_mutex_t lock;
57 vlc_cond_t cond;
58 bool b_working;
59 } processing;
62 static int Open (vlc_object_t *);
63 static void Close (vlc_object_t *);
64 static void CleanSys (fingerprinter_sys_t *);
65 static void *Run(void *);
67 /*****************************************************************************
68 * Module descriptor
69 ****************************************************************************/
70 vlc_module_begin ()
71 set_category(CAT_ADVANCED)
72 set_subcategory(SUBCAT_ADVANCED_MISC)
73 set_shortname(N_("acoustid"))
74 set_description(N_("Track fingerprinter (based on Acoustid)"))
75 set_capability("fingerprinter", 10)
76 set_callbacks(Open, Close)
77 vlc_module_end ()
79 /*****************************************************************************
80 * Requests lifecycle
81 *****************************************************************************/
83 static int EnqueueRequest( fingerprinter_thread_t *f, fingerprint_request_t *r )
85 fingerprinter_sys_t *p_sys = f->p_sys;
86 vlc_mutex_lock( &p_sys->incoming.lock );
87 int i_ret = vlc_array_append( &p_sys->incoming.queue, r );
88 vlc_mutex_unlock( &p_sys->incoming.lock );
89 return i_ret;
92 static void QueueIncomingRequests( fingerprinter_sys_t *p_sys )
94 vlc_mutex_lock( &p_sys->incoming.lock );
96 for( size_t i = vlc_array_count( &p_sys->incoming.queue ); i > 0 ; i-- )
98 fingerprint_request_t *r = vlc_array_item_at_index( &p_sys->incoming.queue, i - 1 );
99 if( vlc_array_append( &p_sys->processing.queue, r ) )
100 fingerprint_request_Delete( r );
102 vlc_array_clear( &p_sys->incoming.queue );
103 vlc_mutex_unlock(&p_sys->incoming.lock);
106 static fingerprint_request_t * GetResult( fingerprinter_thread_t *f )
108 fingerprint_request_t *r = NULL;
109 fingerprinter_sys_t *p_sys = f->p_sys;
110 vlc_mutex_lock( &p_sys->results.lock );
111 if ( vlc_array_count( &p_sys->results.queue ) )
113 r = vlc_array_item_at_index( &p_sys->results.queue, 0 );
114 vlc_array_remove( &p_sys->results.queue, 0 );
116 vlc_mutex_unlock( &p_sys->results.lock );
117 return r;
120 static void ApplyResult( fingerprint_request_t *p_r, size_t i_resultid )
122 if ( i_resultid >= vlc_array_count( & p_r->results.metas_array ) ) return;
124 vlc_meta_t *p_meta = (vlc_meta_t *)
125 vlc_array_item_at_index( & p_r->results.metas_array, i_resultid );
126 input_item_t *p_item = p_r->p_item;
127 vlc_mutex_lock( &p_item->lock );
128 vlc_meta_Merge( p_item->p_meta, p_meta );
129 vlc_mutex_unlock( &p_item->lock );
132 static int InputEventHandler( vlc_object_t *p_this, char const *psz_cmd,
133 vlc_value_t oldval, vlc_value_t newval,
134 void *p_data )
136 VLC_UNUSED( psz_cmd );
137 VLC_UNUSED( oldval );
138 input_thread_t *p_input = (input_thread_t *) p_this;
139 fingerprinter_sys_t *p_sys = (fingerprinter_sys_t *) p_data;
140 if( newval.i_int == INPUT_EVENT_STATE )
142 if( var_GetInteger( p_input, "state" ) >= PAUSE_S )
144 vlc_mutex_lock( &p_sys->processing.lock );
145 p_sys->processing.b_working = false;
146 vlc_cond_signal( &p_sys->processing.cond );
147 vlc_mutex_unlock( &p_sys->processing.lock );
150 return VLC_SUCCESS;
153 static void DoFingerprint( fingerprinter_thread_t *p_fingerprinter,
154 acoustid_fingerprint_t *fp,
155 const char *psz_uri )
157 input_item_t *p_item = input_item_New( NULL, NULL );
158 if ( unlikely(p_item == NULL) )
159 return;
161 char *psz_sout_option;
162 /* Note: need at -max- 2 channels, but we can't guess it before playing */
163 /* the stereo upmix could make the mono tracks fingerprint to differ :/ */
164 if ( asprintf( &psz_sout_option,
165 "sout=#transcode{acodec=%s,channels=2}:chromaprint",
166 ( VLC_CODEC_S16L == VLC_CODEC_S16N ) ? "s16l" : "s16b" )
167 == -1 )
169 input_item_Release( p_item );
170 return;
173 input_item_AddOption( p_item, psz_sout_option, VLC_INPUT_OPTION_TRUSTED );
174 free( psz_sout_option );
175 input_item_AddOption( p_item, "vout=dummy", VLC_INPUT_OPTION_TRUSTED );
176 input_item_AddOption( p_item, "aout=dummy", VLC_INPUT_OPTION_TRUSTED );
177 if ( fp->i_duration )
179 if ( asprintf( &psz_sout_option, "stop-time=%u", fp->i_duration ) == -1 )
181 input_item_Release( p_item );
182 return;
184 input_item_AddOption( p_item, psz_sout_option, VLC_INPUT_OPTION_TRUSTED );
185 free( psz_sout_option );
187 input_item_SetURI( p_item, psz_uri ) ;
189 input_thread_t *p_input = input_Create( p_fingerprinter, p_item, "fingerprinter", NULL, NULL );
190 input_item_Release( p_item );
192 if( p_input == NULL )
193 return;
195 chromaprint_fingerprint_t chroma_fingerprint;
197 chroma_fingerprint.psz_fingerprint = NULL;
198 chroma_fingerprint.i_duration = fp->i_duration;
200 var_Create( p_input, "fingerprint-data", VLC_VAR_ADDRESS );
201 var_SetAddress( p_input, "fingerprint-data", &chroma_fingerprint );
203 var_AddCallback( p_input, "intf-event", InputEventHandler, p_fingerprinter->p_sys );
205 if( input_Start( p_input ) != VLC_SUCCESS )
207 var_DelCallback( p_input, "intf-event", InputEventHandler, p_fingerprinter->p_sys );
208 input_Close( p_input );
210 else
212 p_fingerprinter->p_sys->processing.b_working = true;
213 while( p_fingerprinter->p_sys->processing.b_working )
215 vlc_cond_wait( &p_fingerprinter->p_sys->processing.cond,
216 &p_fingerprinter->p_sys->processing.lock );
218 var_DelCallback( p_input, "intf-event", InputEventHandler, p_fingerprinter->p_sys );
219 input_Stop( p_input );
220 input_Close( p_input );
222 fp->psz_fingerprint = chroma_fingerprint.psz_fingerprint;
223 if( !fp->i_duration ) /* had not given hint */
224 fp->i_duration = chroma_fingerprint.i_duration;
228 /*****************************************************************************
229 * Open:
230 *****************************************************************************/
231 static int Open(vlc_object_t *p_this)
233 fingerprinter_thread_t *p_fingerprinter = (fingerprinter_thread_t*) p_this;
234 fingerprinter_sys_t *p_sys = calloc(1, sizeof(fingerprinter_sys_t));
236 if ( !p_sys )
237 return VLC_ENOMEM;
239 p_fingerprinter->p_sys = p_sys;
241 vlc_array_init( &p_sys->incoming.queue );
242 vlc_mutex_init( &p_sys->incoming.lock );
244 vlc_array_init( &p_sys->processing.queue );
245 vlc_mutex_init( &p_sys->processing.lock );
246 vlc_cond_init( &p_sys->processing.cond );
248 vlc_array_init( &p_sys->results.queue );
249 vlc_mutex_init( &p_sys->results.lock );
251 p_fingerprinter->pf_enqueue = EnqueueRequest;
252 p_fingerprinter->pf_getresults = GetResult;
253 p_fingerprinter->pf_apply = ApplyResult;
255 var_Create( p_fingerprinter, "results-available", VLC_VAR_BOOL );
256 if( vlc_clone( &p_sys->thread, Run, p_fingerprinter,
257 VLC_THREAD_PRIORITY_LOW ) )
259 msg_Err( p_fingerprinter, "cannot spawn fingerprinter thread" );
260 goto error;
263 return VLC_SUCCESS;
265 error:
266 CleanSys( p_sys );
267 free( p_sys );
268 return VLC_EGENERIC;
271 /*****************************************************************************
272 * Close:
273 *****************************************************************************/
274 static void Close(vlc_object_t *p_this)
276 fingerprinter_thread_t *p_fingerprinter = (fingerprinter_thread_t*) p_this;
277 fingerprinter_sys_t *p_sys = p_fingerprinter->p_sys;
279 vlc_cancel( p_sys->thread );
280 vlc_join( p_sys->thread, NULL );
282 CleanSys( p_sys );
283 free( p_sys );
286 static void CleanSys( fingerprinter_sys_t *p_sys )
288 for ( size_t i = 0; i < vlc_array_count( &p_sys->incoming.queue ); i++ )
289 fingerprint_request_Delete( vlc_array_item_at_index( &p_sys->incoming.queue, i ) );
290 vlc_array_clear( &p_sys->incoming.queue );
291 vlc_mutex_destroy( &p_sys->incoming.lock );
293 for ( size_t i = 0; i < vlc_array_count( &p_sys->processing.queue ); i++ )
294 fingerprint_request_Delete( vlc_array_item_at_index( &p_sys->processing.queue, i ) );
295 vlc_array_clear( &p_sys->processing.queue );
296 vlc_mutex_destroy( &p_sys->processing.lock );
297 vlc_cond_destroy( &p_sys->processing.cond );
299 for ( size_t i = 0; i < vlc_array_count( &p_sys->results.queue ); i++ )
300 fingerprint_request_Delete( vlc_array_item_at_index( &p_sys->results.queue, i ) );
301 vlc_array_clear( &p_sys->results.queue );
302 vlc_mutex_destroy( &p_sys->results.lock );
305 static void fill_metas_with_results( fingerprint_request_t *p_r, acoustid_fingerprint_t *p_f )
307 for( unsigned int i=0 ; i < p_f->results.count; i++ )
309 acoustid_result_t *p_result = & p_f->results.p_results[ i ];
310 for ( unsigned int j=0 ; j < p_result->recordings.count; j++ )
312 musicbrainz_recording_t *p_record = & p_result->recordings.p_recordings[ j ];
313 vlc_meta_t *p_meta = vlc_meta_New();
314 if ( p_meta )
316 vlc_meta_Set( p_meta, vlc_meta_Title, p_record->psz_title );
317 vlc_meta_Set( p_meta, vlc_meta_Artist, p_record->psz_artist );
318 vlc_meta_AddExtra( p_meta, "musicbrainz-id", p_record->s_musicbrainz_id );
319 if( vlc_array_append( & p_r->results.metas_array, p_meta ) )
320 vlc_meta_Delete( p_meta );
326 /*****************************************************************************
327 * Run :
328 *****************************************************************************/
329 static void *Run( void *opaque )
331 fingerprinter_thread_t *p_fingerprinter = opaque;
332 fingerprinter_sys_t *p_sys = p_fingerprinter->p_sys;
334 vlc_mutex_lock( &p_sys->processing.lock );
335 mutex_cleanup_push( &p_sys->processing.lock );
337 /* main loop */
338 for (;;)
340 msleep( CLOCK_FREQ );
342 QueueIncomingRequests( p_sys );
344 vlc_testcancel();
346 for ( size_t i = 0 ; i < vlc_array_count( &p_sys->processing.queue ); i++ )
348 int canc = vlc_savecancel();
349 fingerprint_request_t *p_data = vlc_array_item_at_index( &p_sys->processing.queue, i );
351 char *psz_uri = input_item_GetURI( p_data->p_item );
352 if ( psz_uri != NULL )
354 acoustid_fingerprint_t acoustid_print;
356 memset( &acoustid_print , 0, sizeof (acoustid_print) );
357 /* overwrite with hint, as in this case, fingerprint's session will be truncated */
358 if ( p_data->i_duration )
359 acoustid_print.i_duration = p_data->i_duration;
361 DoFingerprint( p_fingerprinter, &acoustid_print, psz_uri );
362 free( psz_uri );
364 DoAcoustIdWebRequest( VLC_OBJECT(p_fingerprinter), &acoustid_print );
365 fill_metas_with_results( p_data, &acoustid_print );
367 for( unsigned j = 0; j < acoustid_print.results.count; j++ )
368 free_acoustid_result_t( &acoustid_print.results.p_results[j] );
369 if( acoustid_print.results.count )
370 free( acoustid_print.results.p_results );
371 free( acoustid_print.psz_fingerprint );
373 vlc_restorecancel(canc);
375 /* copy results */
376 vlc_mutex_lock( &p_sys->results.lock );
377 if( vlc_array_append( &p_sys->results.queue, p_data ) )
378 fingerprint_request_Delete( p_data );
379 vlc_mutex_unlock( &p_sys->results.lock );
381 vlc_testcancel();
384 if ( vlc_array_count( &p_sys->processing.queue ) )
386 var_TriggerCallback( p_fingerprinter, "results-available" );
387 vlc_array_clear( &p_sys->processing.queue );
391 vlc_cleanup_pop();
392 vlc_assert_unreachable();