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 *****************************************************************************/
26 #include <vlc_common.h>
27 #include <vlc_plugin.h>
28 #include <vlc_stream.h>
29 #include <vlc_modules.h>
34 #include <vlc_input.h>
35 #include <vlc_fingerprinter.h>
36 #include "webservices/acoustid.h"
37 #include "../stream_out/chromaprint_data.h"
39 /*****************************************************************************
41 *****************************************************************************/
43 struct fingerprinter_sys_t
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 /*****************************************************************************
69 ****************************************************************************/
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
)
79 /*****************************************************************************
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
);
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
);
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
,
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
);
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
) )
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" )
169 input_item_Release( p_item
);
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
);
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
)
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
);
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 /*****************************************************************************
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
));
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" );
271 /*****************************************************************************
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
);
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();
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 /*****************************************************************************
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
);
340 msleep( CLOCK_FREQ
);
342 QueueIncomingRequests( p_sys
);
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
);
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
);
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
);
384 if ( vlc_array_count( &p_sys
->processing
.queue
) )
386 var_TriggerCallback( p_fingerprinter
, "results-available" );
387 vlc_array_clear( &p_sys
->processing
.queue
);
392 vlc_assert_unreachable();