]> git.sesse.net Git - vlc/blob - modules/stream_out/chromaprint.c
aout: add wait parameter to aout_DecFlush()
[vlc] / modules / stream_out / chromaprint.c
1 /*****************************************************************************
2  * chromaprint.c: Chromaprint Fingerprinter Module
3  *****************************************************************************
4  * Copyright (C) 2012 VLC authors and VideoLAN
5  *
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.
10  *
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.
15  *
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
21 /*****************************************************************************
22  * Preamble
23  *****************************************************************************/
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <vlc_common.h>
30 #include <vlc_plugin.h>
31 #include <vlc_input.h>
32 #include <vlc_block.h>
33 #include <vlc_sout.h>
34
35 #include <assert.h>
36
37 #ifdef _WIN32
38 # define CHROMAPRINT_NODLL
39 #endif
40
41 #include <chromaprint.h> /* chromaprint lib */
42 #include "chromaprint_data.h"
43
44 /*****************************************************************************
45  * Exported prototypes
46  *****************************************************************************/
47 static int      Open    ( vlc_object_t * );
48 static void     Close   ( vlc_object_t * );
49
50 static sout_stream_id_sys_t *Add( sout_stream_t *, const es_format_t * );
51 static void              Del ( sout_stream_t *, sout_stream_id_sys_t * );
52 static int               Send( sout_stream_t *, sout_stream_id_sys_t *, block_t* );
53
54 /*****************************************************************************
55  * Module descriptor
56  *****************************************************************************/
57 #define DURATION_TEXT N_("Duration of the fingerprinting" )
58 #define DURATION_LONGTEXT N_("Default: 90sec")
59
60 vlc_module_begin ()
61     set_description( N_("Chromaprint stream output") )
62     set_capability( "sout stream", 0 )
63     add_shortcut( "chromaprint" )
64     add_integer( "duration", 90, DURATION_TEXT, DURATION_LONGTEXT, true )
65     set_callbacks( Open, Close )
66 vlc_module_end ()
67
68 struct sout_stream_sys_t
69 {
70     unsigned int i_duration;
71     unsigned int i_total_samples;
72     int i_samples;
73     bool b_finished;
74     bool b_done;
75     ChromaprintContext *p_chromaprint_ctx;
76     sout_stream_id_sys_t *id;
77     chromaprint_fingerprint_t *p_data;
78 };
79
80 struct sout_stream_id_sys_t
81 {
82     int i_samples;
83     unsigned int i_channels;
84     unsigned int i_samplerate;
85 };
86
87 #define BYTESPERSAMPLE 2
88
89 /*****************************************************************************
90  * Open:
91  *****************************************************************************/
92 static int Open( vlc_object_t *p_this )
93 {
94     sout_stream_t *p_stream = (sout_stream_t*)p_this;
95     sout_stream_sys_t *p_sys;
96
97     p_stream->p_sys = p_sys = malloc(sizeof(sout_stream_sys_t));
98     if ( unlikely( ! p_sys ) ) return VLC_ENOMEM;
99     p_sys->id = NULL;
100     p_sys->b_finished = false;
101     p_sys->b_done = false;
102     p_sys->i_total_samples = 0;
103     p_sys->i_duration = var_InheritInteger( p_stream, "duration" );
104     p_sys->p_data = var_InheritAddress( p_stream, "fingerprint-data" );
105     if ( !p_sys->p_data )
106     {
107         msg_Err( p_stream, "Fingerprint data holder not set" );
108         free( p_sys );
109         return VLC_ENOVAR;
110     }
111     msg_Dbg( p_stream, "chromaprint version %s", chromaprint_get_version() );
112     p_sys->p_chromaprint_ctx = chromaprint_new( CHROMAPRINT_ALGORITHM_DEFAULT );
113     if ( ! p_sys->p_chromaprint_ctx )
114     {
115         msg_Err( p_stream, "Can't create chromaprint context" );
116         free( p_sys );
117         return VLC_EGENERIC;
118     }
119     p_stream->pf_add  = Add;
120     p_stream->pf_del  = Del;
121     p_stream->pf_send = Send;
122     return VLC_SUCCESS;
123 }
124
125 static void Finish( sout_stream_t *p_stream )
126 {
127     sout_stream_sys_t *p_sys = p_stream->p_sys;
128     char *psz_fingerprint = NULL;
129     if ( p_sys->b_finished && chromaprint_finish( p_sys->p_chromaprint_ctx ) )
130     {
131         chromaprint_get_fingerprint( p_sys->p_chromaprint_ctx,
132                                      &psz_fingerprint );
133         if ( psz_fingerprint )
134         {
135             p_sys->p_data->i_duration = p_sys->i_total_samples / p_sys->id->i_samplerate;
136             p_sys->p_data->psz_fingerprint = strdup( psz_fingerprint );
137             chromaprint_dealloc( psz_fingerprint );
138             msg_Dbg( p_stream, "DURATION=%u;FINGERPRINT=%s",
139                     p_sys->p_data->i_duration,
140                     p_sys->p_data->psz_fingerprint );
141         }
142     } else {
143         msg_Dbg( p_stream, "Cannot create %us fingerprint (not enough samples?)",
144                  p_sys->i_duration );
145     }
146     p_sys->b_done = true;
147     msg_Dbg( p_stream, "Fingerprinting finished" );
148 }
149
150 /*****************************************************************************
151  * Close:
152  *****************************************************************************/
153 static void Close( vlc_object_t * p_this )
154 {
155     sout_stream_t *p_stream = (sout_stream_t *)p_this;
156     sout_stream_sys_t *p_sys = p_stream->p_sys;
157
158     if ( !p_sys->b_done ) Finish( p_stream );
159     chromaprint_free( p_sys->p_chromaprint_ctx );
160     free( p_sys );
161 }
162
163 static sout_stream_id_sys_t *Add( sout_stream_t *p_stream, const es_format_t *p_fmt )
164 {
165     sout_stream_sys_t *p_sys = p_stream->p_sys;
166     sout_stream_id_sys_t *id = NULL;
167
168     if ( p_fmt->i_cat == AUDIO_ES && !p_sys->id )
169     {
170         if( p_fmt->i_codec != VLC_CODEC_S16N || p_fmt->audio.i_channels > 2 )
171         {
172             msg_Warn( p_stream, "bad input format: need s16l, 1 or 2 channels" );
173             goto error;
174         }
175
176         id = malloc( sizeof( sout_stream_id_sys_t ) );
177         if ( !id ) goto error;
178
179         id->i_channels = p_fmt->audio.i_channels;
180         id->i_samplerate = p_fmt->audio.i_rate;
181         id->i_samples = p_sys->i_duration * id->i_samplerate;
182
183         if ( !chromaprint_start( p_sys->p_chromaprint_ctx, p_fmt->audio.i_rate, id->i_channels ) )
184         {
185             msg_Err( p_stream, "Failed starting chromaprint on %uHz %uch samples",
186                      p_fmt->audio.i_rate, id->i_channels );
187             goto error;
188         }
189         else
190         {
191             p_sys->id = id;
192             msg_Dbg( p_stream, "Starting chromaprint on %uHz %uch samples",
193                      p_fmt->audio.i_rate, id->i_channels );
194         }
195         return id;
196     }
197
198 error:
199     free( id );
200     return NULL;
201 }
202
203 static void Del( sout_stream_t *p_stream, sout_stream_id_sys_t *id )
204 {
205     sout_stream_sys_t *p_sys = p_stream->p_sys;
206     Finish( p_stream );
207     if ( p_sys->id == id ) /* not assuming only 1 id is in use.. */
208         p_sys->id = NULL;
209     free( id );
210 }
211
212 static int Send( sout_stream_t *p_stream, sout_stream_id_sys_t *id,
213                  block_t *p_buf )
214 {
215     sout_stream_sys_t *p_sys = p_stream->p_sys;
216
217     if ( p_sys->id != id )
218     {
219         /* drop the whole buffer at once */
220         block_ChainRelease( p_buf );
221         return VLC_SUCCESS;
222     }
223
224     while( p_buf )
225     {
226         block_t *p_next;
227         int i_samples = p_buf->i_buffer / (BYTESPERSAMPLE * id->i_channels);
228         p_sys->i_total_samples += i_samples;
229         if ( !p_sys->b_finished && id->i_samples > 0 && p_buf->i_buffer )
230         {
231             if(! chromaprint_feed( p_sys->p_chromaprint_ctx,
232                                    p_buf->p_buffer,
233                                    p_buf->i_buffer / BYTESPERSAMPLE ) )
234                 msg_Warn( p_stream, "feed error" );
235             id->i_samples -= i_samples;
236             if ( id->i_samples < 1 && !p_sys->b_finished )
237             {
238                 p_sys->b_finished = true;
239                 msg_Dbg( p_stream, "Fingerprint collection finished" );
240             }
241         }
242         p_next = p_buf->p_next;
243         block_Release( p_buf );
244         p_buf = p_next;
245     }
246
247     return VLC_SUCCESS;
248 }