1 /*****************************************************************************
2 * projectm: visualization module based on libprojectM
3 *****************************************************************************
4 * Copyright (C) 2009 the VideoLAN team
7 * Authors: RĂ©mi Duraffort <ivoire@videolan.org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 * You should have received a copy of the GNU General Public License along with
20 * this program; if not, write to the Free Software Foundation, Inc., 51
21 * Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
33 #include <libprojectM/projectM.hpp>
36 /*****************************************************************************
38 *****************************************************************************/
39 static int Open ( vlc_object_t * );
40 static void Close ( vlc_object_t * );
42 #define CONFIG_TEXT N_("projectM configuration file")
43 #define CONFIG_LONGTEXT N_("File that will be used to configure the projectM " \
46 #define WIDTH_TEXT N_("Video width")
47 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
49 #define HEIGHT_TEXT N_("Video height")
50 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
53 set_shortname( N_("projectM"))
54 set_description( N_("libprojectM effect") )
55 set_capability( "visualization", 0 )
56 set_category( CAT_AUDIO )
57 set_subcategory( SUBCAT_AUDIO_VISUAL )
58 add_file( "projectm-config", "/usr/share/projectM/config.inp", NULL,
59 CONFIG_TEXT, CONFIG_LONGTEXT, true )
60 add_integer( "projectm-width", 640, NULL, WIDTH_TEXT, WIDTH_LONGTEXT,
62 add_integer( "projectm-height", 480, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT,
64 add_shortcut( "projectm" )
65 set_callbacks( Open, Close )
69 /*****************************************************************************
71 *****************************************************************************/
76 /* video output module and opengl provider */
77 vout_thread_t *p_opengl;
80 /* libprojectM objects */
98 struct aout_filter_sys_t
100 projectm_thread_t *p_thread;
104 static void DoWork( aout_instance_t *, aout_filter_t *, aout_buffer_t *,
106 static void* Thread( vlc_object_t * );
110 * Init the openGL context
111 * p_thread: projectm thread object
112 * @return VLC_SUCCESS or vlc error codes
114 static int initOpenGL( projectm_thread_t *p_thread )
116 p_thread->p_opengl = (vout_thread_t *)vlc_object_create( p_thread,
117 sizeof( vout_thread_t ) );
118 if( !p_thread->p_opengl )
121 vlc_object_attach( p_thread->p_opengl, p_thread );
123 /* Initialize the opengl object */
124 video_format_Setup( &p_thread->p_opengl->fmt_in, VLC_CODEC_RGB32,
125 p_thread->i_width, p_thread->i_height, 1 );
126 p_thread->p_opengl->i_window_width = p_thread->i_width;
127 p_thread->p_opengl->i_window_height = p_thread->i_height;
128 p_thread->p_opengl->render.i_width = p_thread->i_width;
129 p_thread->p_opengl->render.i_height = p_thread->i_height;
130 p_thread->p_opengl->render.i_aspect = VOUT_ASPECT_FACTOR;
131 p_thread->p_opengl->b_fullscreen = false;
132 p_thread->p_opengl->i_alignment = 0;
133 p_thread->p_opengl->fmt_in.i_sar_num = 1;
134 p_thread->p_opengl->fmt_in.i_sar_den = 1;
135 p_thread->p_opengl->fmt_render = p_thread->p_opengl->fmt_in;
137 /* Ask for the opengl provider */
138 p_thread->p_module = module_need( p_thread->p_opengl, "opengl provider",
140 if( !p_thread->p_module )
142 msg_Err( p_thread, "unable to initialize OpenGL" );
143 vlc_object_detach( p_thread->p_opengl );
144 vlc_object_release( p_thread->p_opengl );
154 * @param p_this: the filter object
155 * @return VLC_SUCCESS or vlc error codes
157 static int Open( vlc_object_t * p_this )
159 aout_filter_t *p_filter = (aout_filter_t *)p_this;
160 aout_filter_sys_t *p_sys;
161 projectm_thread_t *p_thread;
163 /* Test the audio format */
164 if( p_filter->input.i_format != VLC_CODEC_FL32 ||
165 p_filter->output.i_format != VLC_CODEC_FL32 )
167 msg_Warn( p_filter, "bad input or output format" );
170 if( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
172 msg_Warn( p_filter, "input and outut are not similar" );
176 p_filter->pf_do_work = DoWork;
177 p_filter->b_in_place = true;
179 p_sys = p_filter->p_sys = (aout_filter_sys_t*)malloc( sizeof( *p_sys ) );
183 /* Create the object for the thread */
184 p_sys->p_thread = p_thread = (projectm_thread_t *)
185 vlc_object_create( p_filter, sizeof( projectm_thread_t ) );
186 vlc_object_attach( p_sys->p_thread, p_filter );
187 p_thread->i_width = var_CreateGetInteger( p_filter, "projectm-width" );
188 p_thread->i_height = var_CreateGetInteger( p_filter, "projectm-height" );
190 /* Create the openGL provider */
191 int i_ret = initOpenGL( p_sys->p_thread );
192 if( i_ret != VLC_SUCCESS )
194 vlc_object_detach( p_sys->p_thread );
195 vlc_object_release( p_sys->p_thread );
200 p_thread->i_channels = aout_FormatNbChannels( &p_filter->input );
201 p_thread->psz_config = var_CreateGetString( p_filter, "projectm-config" );
202 vlc_mutex_init( &p_thread->lock );
203 p_thread->p_buffer = NULL;
204 p_thread->i_buffer_size = 0;
205 p_thread->i_nb_samples = 0;
207 /* Create the thread */
208 if( vlc_thread_create( p_thread, "projectm update thread", Thread,
209 VLC_THREAD_PRIORITY_LOW ) )
211 msg_Err( p_filter, "cannot launch the projectm thread" );
212 vlc_object_detach( p_thread );
213 vlc_object_release( p_thread );
224 * @param p_this: the filter object
226 static void Close( vlc_object_t *p_this )
228 aout_filter_t *p_filter = (aout_filter_t *)p_this;
229 aout_filter_sys_t *p_sys = p_filter->p_sys;
230 projectm_thread_t *p_thread = p_sys->p_thread;
232 /* Stop the thread */
233 vlc_object_kill( p_thread );
234 vlc_thread_join( p_thread );
236 /* Free the ressources */
237 vlc_mutex_destroy( &p_thread->lock );
238 free( p_thread->p_buffer );
239 free( p_thread->psz_config );
241 vlc_object_detach( p_thread );
242 vlc_object_release( p_thread );
249 * Do the actual work with the new sample
250 * @param p_aout: audio output object
251 * @param p_filter: filter object
252 * @param p_in_buf: input buffer
253 * @param p_out_buf: output buffer
255 static void DoWork( aout_instance_t *p_aout, aout_filter_t *p_filter,
256 aout_buffer_t *p_in_buf, aout_buffer_t *p_out_buf )
258 projectm_thread_t *p_thread = p_filter->p_sys->p_thread;
260 p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
261 p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes;
263 vlc_mutex_lock( &p_thread->lock );
264 if( p_thread->i_buffer_size > 0 )
266 p_thread->p_buffer[0] = 0;
267 p_thread->i_nb_samples = __MIN( p_thread->i_buffer_size,
268 p_in_buf->i_nb_samples );
269 for( int i = 0; i < p_thread->i_nb_samples; i++ )
270 p_thread->p_buffer[i] = p_in_buf->p_buffer[i];
273 vlc_mutex_unlock( &p_thread->lock );
280 * ProjectM update thread which do the rendering
281 * @param p_this: the p_thread object
283 static void* Thread( vlc_object_t *p_this )
285 /* we don't want to be interupted in this thread */
286 int cancel = vlc_savecancel();
287 projectm_thread_t *p_thread = (projectm_thread_t *)p_this;
289 /* Initialize the opengl provider for this thread */
290 p_thread->p_opengl->pf_init( p_thread->p_opengl );
292 /* Create the projectM object */
293 p_thread->p_projectm = new projectM( p_thread->psz_config );
294 p_thread->i_buffer_size = p_thread->p_projectm->pcm()->maxsamples;
295 p_thread->p_buffer = (float*)malloc( p_thread->i_buffer_size *
298 /* TODO: Give to projectm the name of the input
299 p_thread->p_projectm->projectM_setTitle( "" ); */
301 /* Reset the dislay to get the right size */
302 p_thread->p_projectm->projectM_resetGL( p_thread->i_width,
303 p_thread->i_height );
305 while( vlc_object_alive( p_thread ) )
307 /* Render the image and swap the buffers */
308 vlc_mutex_lock( &p_thread->lock );
309 if( p_thread->i_nb_samples > 0 )
310 p_thread->p_projectm->pcm()->addPCMfloat( p_thread->p_buffer,
311 p_thread->i_nb_samples );
313 p_thread->p_projectm->renderFrame();
314 p_thread->p_opengl->pf_swap( p_thread->p_opengl );
315 vlc_mutex_unlock( &p_thread->lock );
317 /* TODO: use a fps limiter */
323 delete p_thread->p_projectm;
325 /* Free the openGL provider */
326 module_unneed( p_thread->p_opengl, p_thread->p_module );
327 vlc_object_detach( p_thread->p_opengl );
328 vlc_object_release( p_thread->p_opengl );
331 vlc_restorecancel( cancel );