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 *****************************************************************************/
24 #include <vlc_common.h>
25 #include <vlc_plugin.h>
26 #include <vlc_stream.h>
27 #include <vlc_modules.h>
32 #include <vlc_input.h>
33 #include <vlc_fingerprinter.h>
34 #include <webservices/acoustid.h>
35 #include <../stream_out/chromaprint_data.h>
37 /*****************************************************************************
39 *****************************************************************************/
41 struct fingerprinter_sys_t
49 } incoming
, processing
, results
;
51 vlc_cond_t incoming_queue_filled
;
60 /* tracked in sys for cancelability */
62 input_thread_t
*p_input
;
63 chromaprint_fingerprint_t chroma_fingerprint
;
66 /* clobberable by cleanups */
71 static int Open (vlc_object_t
*);
72 static void Close (vlc_object_t
*);
73 static void Run (fingerprinter_thread_t
*);
75 /*****************************************************************************
77 ****************************************************************************/
79 set_category(CAT_ADVANCED
)
80 set_shortname(N_("acoustid"))
81 set_description(N_("Track fingerprinter (based on Acoustid)"))
82 set_capability("fingerprinter", 1)
83 set_callbacks(Open
, Close
)
86 /*****************************************************************************
88 *****************************************************************************/
90 static void EnqueueRequest( fingerprinter_thread_t
*f
, fingerprint_request_t
*r
)
92 fingerprinter_sys_t
*p_sys
= f
->p_sys
;
93 vlc_mutex_lock( &p_sys
->incoming
.lock
);
94 vlc_array_append( p_sys
->incoming
.queue
, r
);
95 vlc_mutex_unlock( &p_sys
->incoming
.lock
);
96 vlc_cond_signal( &p_sys
->incoming_queue_filled
);
99 static void QueueIncomingRequests( fingerprinter_sys_t
*p_sys
)
101 vlc_mutex_lock( &p_sys
->incoming
.lock
);
102 int i
= vlc_array_count( p_sys
->incoming
.queue
);
103 if ( i
== 0 ) goto end
;
104 vlc_mutex_lock( &p_sys
->processing
.lock
);
106 vlc_array_append( p_sys
->processing
.queue
,
107 vlc_array_item_at_index( p_sys
->incoming
.queue
, --i
) );
108 vlc_array_clear( p_sys
->incoming
.queue
);
109 vlc_mutex_unlock( &p_sys
->processing
.lock
);
111 vlc_mutex_unlock(&p_sys
->incoming
.lock
);
114 static fingerprint_request_t
* GetResult( fingerprinter_thread_t
*f
)
116 fingerprint_request_t
*r
= NULL
;
117 fingerprinter_sys_t
*p_sys
= f
->p_sys
;
118 vlc_mutex_lock( &p_sys
->results
.lock
);
119 if ( vlc_array_count( p_sys
->results
.queue
) )
121 r
= vlc_array_item_at_index( p_sys
->results
.queue
, 0 );
122 vlc_array_remove( p_sys
->results
.queue
, 0 );
124 vlc_mutex_unlock( &p_sys
->results
.lock
);
128 static void ApplyResult( fingerprint_request_t
*p_r
, int i_resultid
)
130 if ( i_resultid
>= vlc_array_count( & p_r
->results
.metas_array
) ) return;
132 vlc_meta_t
*p_meta
= (vlc_meta_t
*)
133 vlc_array_item_at_index( & p_r
->results
.metas_array
, i_resultid
);
134 input_item_t
*p_item
= p_r
->p_item
;
135 vlc_mutex_lock( &p_item
->lock
);
136 vlc_meta_Merge( p_item
->p_meta
, p_meta
);
137 vlc_mutex_unlock( &p_item
->lock
);
140 static void cancelDoFingerprint( void *p_arg
)
142 fingerprinter_sys_t
*p_sys
= ( fingerprinter_sys_t
* ) p_arg
;
143 if ( p_sys
->p_input
)
145 input_Stop( p_sys
->p_input
, true );
146 input_Close( p_sys
->p_input
);
148 /* cleanup temporary result */
149 if ( p_sys
->chroma_fingerprint
.psz_fingerprint
)
150 FREENULL( p_sys
->chroma_fingerprint
.psz_fingerprint
);
152 input_item_Release( p_sys
->p_item
);
155 static int inputStateCallback( vlc_object_t
*obj
, const char *var
,
156 vlc_value_t old
, vlc_value_t cur
, void *p_data
)
158 VLC_UNUSED(obj
);VLC_UNUSED(var
);VLC_UNUSED(old
);
159 fingerprinter_sys_t
*p_sys
= (fingerprinter_sys_t
*) p_data
;
160 if ( cur
.i_int
!= INPUT_EVENT_STATE
) return VLC_SUCCESS
;
161 p_sys
->condwait
.i_input_state
= var_GetInteger( p_sys
->p_input
, "state" );
162 vlc_cond_signal( & p_sys
->condwait
.wait
);
166 static void DoFingerprint( vlc_object_t
*p_this
, fingerprinter_sys_t
*p_sys
, acoustid_fingerprint_t
*fp
)
168 p_sys
->p_input
= NULL
;
169 p_sys
->p_item
= NULL
;
170 p_sys
->chroma_fingerprint
.psz_fingerprint
= NULL
;
171 vlc_cleanup_push( cancelDoFingerprint
, p_sys
);
173 p_sys
->p_item
= input_item_New( NULL
, NULL
);
174 if ( ! p_sys
->p_item
) goto end
;
176 char *psz_sout_option
;
177 /* Note: need at -max- 2 channels, but we can't guess it before playing */
178 /* the stereo upmix could make the mono tracks fingerprint to differ :/ */
179 if ( asprintf( &psz_sout_option
,
180 "sout=#transcode{acodec=%s,channels=2}:chromaprint",
181 ( VLC_CODEC_S16L
== VLC_CODEC_S16N
) ? "s16l" : "s16b" )
183 input_item_AddOption( p_sys
->p_item
, psz_sout_option
, VLC_INPUT_OPTION_TRUSTED
);
184 free( psz_sout_option
);
185 input_item_AddOption( p_sys
->p_item
, "vout=dummy", VLC_INPUT_OPTION_TRUSTED
);
186 input_item_AddOption( p_sys
->p_item
, "aout=dummy", VLC_INPUT_OPTION_TRUSTED
);
187 if ( fp
->i_duration
)
189 if ( asprintf( &psz_sout_option
, "stop-time=%u", fp
->i_duration
) == -1 ) goto end
;
190 input_item_AddOption( p_sys
->p_item
, psz_sout_option
, VLC_INPUT_OPTION_TRUSTED
);
191 free( psz_sout_option
);
193 input_item_SetURI( p_sys
->p_item
, p_sys
->psz_uri
) ;
195 p_sys
->p_input
= input_Create( p_this
, p_sys
->p_item
, "fingerprinter", NULL
);
196 if ( p_sys
->p_input
)
198 p_sys
->chroma_fingerprint
.i_duration
= fp
->i_duration
;
199 var_Create( p_sys
->p_input
, "fingerprint-data", VLC_VAR_ADDRESS
);
200 var_SetAddress( p_sys
->p_input
, "fingerprint-data", & p_sys
->chroma_fingerprint
);
202 input_Start( p_sys
->p_input
);
204 /* Wait for input to start && end */
205 p_sys
->condwait
.i_input_state
= var_GetInteger( p_sys
->p_input
, "state" );
207 if ( likely( var_AddCallback( p_sys
->p_input
, "intf-event",
208 inputStateCallback
, p_sys
) == VLC_SUCCESS
) )
210 while( p_sys
->condwait
.i_input_state
<= PAUSE_S
)
212 vlc_mutex_lock( &p_sys
->condwait
.lock
);
213 mutex_cleanup_push( &p_sys
->condwait
.lock
);
214 vlc_cond_wait( &p_sys
->condwait
.wait
, &p_sys
->condwait
.lock
);
217 var_DelCallback( p_sys
->p_input
, "intf-event", inputStateCallback
, p_sys
);
219 input_Stop( p_sys
->p_input
, true );
220 input_Close( p_sys
->p_input
);
221 p_sys
->p_input
= NULL
;
223 if ( p_sys
->chroma_fingerprint
.psz_fingerprint
)
225 fp
->psz_fingerprint
= strdup( p_sys
->chroma_fingerprint
.psz_fingerprint
);
226 if ( ! fp
->i_duration
) /* had not given hint */
227 fp
->i_duration
= p_sys
->chroma_fingerprint
.i_duration
;
234 /*****************************************************************************
236 *****************************************************************************/
237 static int Open(vlc_object_t
*p_this
)
239 fingerprinter_thread_t
*p_fingerprinter
= (fingerprinter_thread_t
*) p_this
;
240 fingerprinter_sys_t
*p_sys
= calloc(1, sizeof(fingerprinter_sys_t
));
245 p_fingerprinter
->p_sys
= p_sys
;
247 p_sys
->incoming
.queue
= vlc_array_new();
248 vlc_mutex_init( &p_sys
->incoming
.lock
);
249 vlc_cond_init( &p_sys
->incoming_queue_filled
);
251 p_sys
->processing
.queue
= vlc_array_new();
252 vlc_mutex_init( &p_sys
->processing
.lock
);
254 p_sys
->results
.queue
= vlc_array_new();
255 vlc_mutex_init( &p_sys
->results
.lock
);
257 vlc_mutex_init( &p_sys
->condwait
.lock
);
258 vlc_cond_init( &p_sys
->condwait
.wait
);
260 p_sys
->psz_uri
= NULL
;
262 p_fingerprinter
->pf_run
= Run
;
263 p_fingerprinter
->pf_enqueue
= EnqueueRequest
;
264 p_fingerprinter
->pf_getresults
= GetResult
;
265 p_fingerprinter
->pf_apply
= ApplyResult
;
267 var_Create( p_fingerprinter
, "results-available", VLC_VAR_BOOL
);
268 if( p_fingerprinter
->pf_run
269 && vlc_clone( &p_sys
->thread
,
270 (void *(*) (void *)) p_fingerprinter
->pf_run
,
271 p_fingerprinter
, VLC_THREAD_PRIORITY_LOW
) )
273 msg_Err( p_fingerprinter
, "cannot spawn fingerprinter thread" );
284 /*****************************************************************************
286 *****************************************************************************/
287 static void Close(vlc_object_t
*p_this
)
289 fingerprinter_thread_t
*p_fingerprinter
= (fingerprinter_thread_t
*) p_this
;
290 fingerprinter_sys_t
*p_sys
= p_fingerprinter
->p_sys
;
292 vlc_cancel( p_sys
->thread
);
293 vlc_join( p_sys
->thread
, NULL
);
295 vlc_mutex_destroy( &p_sys
->condwait
.lock
);
296 vlc_cond_destroy( &p_sys
->condwait
.wait
);
298 for ( int i
= 0; i
< vlc_array_count( p_sys
->incoming
.queue
); i
++ )
299 fingerprint_request_Delete( vlc_array_item_at_index( p_sys
->incoming
.queue
, i
) );
300 vlc_array_destroy( p_sys
->incoming
.queue
);
301 vlc_mutex_destroy( &p_sys
->incoming
.lock
);
302 vlc_cond_destroy( &p_sys
->incoming_queue_filled
);
304 for ( int i
= 0; i
< vlc_array_count( p_sys
->processing
.queue
); i
++ )
305 fingerprint_request_Delete( vlc_array_item_at_index( p_sys
->processing
.queue
, i
) );
306 vlc_array_destroy( p_sys
->processing
.queue
);
307 vlc_mutex_destroy( &p_sys
->processing
.lock
);
309 for ( int i
= 0; i
< vlc_array_count( p_sys
->results
.queue
); i
++ )
310 fingerprint_request_Delete( vlc_array_item_at_index( p_sys
->results
.queue
, i
) );
311 vlc_array_destroy( p_sys
->results
.queue
);
312 vlc_mutex_destroy( &p_sys
->results
.lock
);
317 static void fill_metas_with_results( fingerprint_request_t
*p_r
, acoustid_fingerprint_t
*p_f
)
319 for( unsigned int i
=0 ; i
< p_f
->results
.count
; i
++ )
321 acoustid_result_t
*p_result
= & p_f
->results
.p_results
[ i
];
322 for ( unsigned int j
=0 ; j
< p_result
->recordings
.count
; j
++ )
324 musicbrainz_recording_t
*p_record
= & p_result
->recordings
.p_recordings
[ j
];
325 vlc_meta_t
*p_meta
= vlc_meta_New();
328 vlc_meta_Set( p_meta
, vlc_meta_Title
, p_record
->psz_title
);
329 vlc_meta_Set( p_meta
, vlc_meta_Artist
, p_record
->psz_artist
);
330 vlc_meta_AddExtra( p_meta
, "musicbrainz-id", p_record
->sz_musicbrainz_id
);
331 vlc_array_append( & p_r
->results
.metas_array
, p_meta
);
337 /*****************************************************************************
339 *****************************************************************************/
340 static void cancelRun( void * p_arg
)
342 fingerprinter_sys_t
*p_sys
= ( fingerprinter_sys_t
* ) p_arg
;
343 if ( vlc_array_count( p_sys
->processing
.queue
) )
344 vlc_array_clear( p_sys
->processing
.queue
);
345 if ( p_sys
->psz_uri
)
346 free( p_sys
->psz_uri
);
349 static void clearPrint( void * p_arg
)
351 acoustid_fingerprint_t
*acoustid_print
= ( acoustid_fingerprint_t
* ) p_arg
;
352 for( unsigned int j
=0 ; j
< acoustid_print
->results
.count
; j
++ )
353 free_acoustid_result_t( &acoustid_print
->results
.p_results
[j
] );
354 if ( acoustid_print
->results
.count
)
355 free( acoustid_print
->results
.p_results
);
356 if ( acoustid_print
->psz_fingerprint
)
357 free( acoustid_print
->psz_fingerprint
);
360 static void Run( fingerprinter_thread_t
*p_fingerprinter
)
362 fingerprinter_sys_t
*p_sys
= p_fingerprinter
->p_sys
;
367 vlc_mutex_lock( &p_sys
->processing
.lock
);
368 mutex_cleanup_push( &p_sys
->processing
.lock
);
369 vlc_cond_timedwait( &p_sys
->incoming_queue_filled
, &p_sys
->processing
.lock
, mdate() + 1000000 );
372 QueueIncomingRequests( p_sys
);
374 vlc_mutex_lock( &p_sys
->processing
.lock
); // L0
375 mutex_cleanup_push( &p_sys
->processing
.lock
);
376 vlc_cleanup_push( cancelRun
, p_sys
); // C1
378 for ( p_sys
->i
= 0 ; p_sys
->i
< vlc_array_count( p_sys
->processing
.queue
); p_sys
->i
++ )
380 fingerprint_request_t
*p_data
= vlc_array_item_at_index( p_sys
->processing
.queue
, p_sys
->i
);
381 acoustid_fingerprint_t acoustid_print
;
382 memset( &acoustid_print
, 0, sizeof(acoustid_fingerprint_t
) );
383 vlc_cleanup_push( clearPrint
, &acoustid_print
); // C2
384 p_sys
->psz_uri
= input_item_GetURI( p_data
->p_item
);
385 if ( p_sys
->psz_uri
)
387 /* overwrite with hint, as in this case, fingerprint's session will be truncated */
388 if ( p_data
->i_duration
) acoustid_print
.i_duration
= p_data
->i_duration
;
390 DoFingerprint( VLC_OBJECT(p_fingerprinter
), p_sys
, &acoustid_print
);
392 DoAcoustIdWebRequest( VLC_OBJECT(p_fingerprinter
), &acoustid_print
);
393 fill_metas_with_results( p_data
, &acoustid_print
);
394 FREENULL( p_sys
->psz_uri
);
396 vlc_cleanup_run( ); // C2
399 vlc_mutex_lock( &p_sys
->results
.lock
);
400 vlc_array_append( p_sys
->results
.queue
, p_data
);
401 vlc_mutex_unlock( &p_sys
->results
.lock
);
406 if ( vlc_array_count( p_sys
->processing
.queue
) )
408 var_TriggerCallback( p_fingerprinter
, "results-available" );
409 vlc_array_clear( p_sys
->processing
.queue
);
411 vlc_cleanup_pop( ); // C1
413 vlc_cleanup_run(); // L0