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", 800, NULL, WIDTH_TEXT, WIDTH_LONGTEXT,
62 add_integer( "projectm-height", 640, 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->b_autoscale = true;
133 p_thread->p_opengl->i_alignment = 0;
134 p_thread->p_opengl->fmt_in.i_sar_num = 1;
135 p_thread->p_opengl->fmt_in.i_sar_den = 1;
136 p_thread->p_opengl->fmt_render = p_thread->p_opengl->fmt_in;
138 /* Ask for the opengl provider */
139 p_thread->p_module = module_need( p_thread->p_opengl, "opengl provider",
141 if( !p_thread->p_module )
143 msg_Err( p_thread, "unable to initialize OpenGL" );
144 vlc_object_detach( p_thread->p_opengl );
145 vlc_object_release( p_thread->p_opengl );
155 * @param p_this: the filter object
156 * @return VLC_SUCCESS or vlc error codes
158 static int Open( vlc_object_t * p_this )
160 aout_filter_t *p_filter = (aout_filter_t *)p_this;
161 aout_filter_sys_t *p_sys;
162 projectm_thread_t *p_thread;
164 /* Test the audio format */
165 if( p_filter->input.i_format != VLC_CODEC_FL32 ||
166 p_filter->output.i_format != VLC_CODEC_FL32 )
168 msg_Warn( p_filter, "bad input or output format" );
171 if( !AOUT_FMTS_SIMILAR( &p_filter->input, &p_filter->output ) )
173 msg_Warn( p_filter, "input and outut are not similar" );
177 p_filter->pf_do_work = DoWork;
178 p_filter->b_in_place = true;
180 p_sys = p_filter->p_sys = (aout_filter_sys_t*)malloc( sizeof( *p_sys ) );
184 /* Create the object for the thread */
185 p_sys->p_thread = p_thread = (projectm_thread_t *)
186 vlc_object_create( p_filter, sizeof( projectm_thread_t ) );
187 vlc_object_attach( p_sys->p_thread, p_filter );
188 p_thread->i_width = var_CreateGetInteger( p_filter, "projectm-width" );
189 p_thread->i_height = var_CreateGetInteger( p_filter, "projectm-height" );
191 /* Create the openGL provider */
192 int i_ret = initOpenGL( p_sys->p_thread );
193 if( i_ret != VLC_SUCCESS )
195 vlc_object_detach( p_sys->p_thread );
196 vlc_object_release( p_sys->p_thread );
201 p_thread->i_channels = aout_FormatNbChannels( &p_filter->input );
202 p_thread->psz_config = var_CreateGetString( p_filter, "projectm-config" );
203 vlc_mutex_init( &p_thread->lock );
204 p_thread->p_buffer = NULL;
205 p_thread->i_buffer_size = 0;
206 p_thread->i_nb_samples = 0;
208 /* Create the thread */
209 if( vlc_thread_create( p_thread, "projectm update thread", Thread,
210 VLC_THREAD_PRIORITY_LOW ) )
212 msg_Err( p_filter, "cannot launch the projectm thread" );
213 vlc_object_detach( p_thread );
214 vlc_object_release( p_thread );
225 * @param p_this: the filter object
227 static void Close( vlc_object_t *p_this )
229 aout_filter_t *p_filter = (aout_filter_t *)p_this;
230 aout_filter_sys_t *p_sys = p_filter->p_sys;
231 projectm_thread_t *p_thread = p_sys->p_thread;
233 /* Stop the thread */
234 vlc_object_kill( p_thread );
235 vlc_thread_join( p_thread );
237 /* Free the ressources */
238 vlc_mutex_destroy( &p_thread->lock );
239 free( p_thread->p_buffer );
240 free( p_thread->psz_config );
242 vlc_object_detach( p_thread );
243 vlc_object_release( p_thread );
250 * Do the actual work with the new sample
251 * @param p_aout: audio output object
252 * @param p_filter: filter object
253 * @param p_in_buf: input buffer
254 * @param p_out_buf: output buffer
256 static void DoWork( aout_instance_t *p_aout, aout_filter_t *p_filter,
257 aout_buffer_t *p_in_buf, aout_buffer_t *p_out_buf )
259 projectm_thread_t *p_thread = p_filter->p_sys->p_thread;
261 p_out_buf->i_nb_samples = p_in_buf->i_nb_samples;
262 p_out_buf->i_nb_bytes = p_in_buf->i_nb_bytes;
264 vlc_mutex_lock( &p_thread->lock );
265 if( p_thread->i_buffer_size > 0 )
267 p_thread->p_buffer[0] = 0;
268 p_thread->i_nb_samples = __MIN( p_thread->i_buffer_size,
269 p_in_buf->i_nb_samples );
270 for( int i = 0; i < p_thread->i_nb_samples; i++ )
271 p_thread->p_buffer[i] = p_in_buf->p_buffer[i];
274 vlc_mutex_unlock( &p_thread->lock );
281 * ProjectM update thread which do the rendering
282 * @param p_this: the p_thread object
284 static void* Thread( vlc_object_t *p_this )
286 /* we don't want to be interupted in this thread */
287 int cancel = vlc_savecancel();
288 projectm_thread_t *p_thread = (projectm_thread_t *)p_this;
290 /* Initialize the opengl provider for this thread */
291 p_thread->p_opengl->pf_init( p_thread->p_opengl );
293 /* Create the projectM object */
294 p_thread->p_projectm = new projectM( p_thread->psz_config );
295 p_thread->i_buffer_size = p_thread->p_projectm->pcm()->maxsamples;
296 p_thread->p_buffer = (float*)malloc( p_thread->i_buffer_size *
299 /* TODO: Give to projectm the name of the input
300 p_thread->p_projectm->projectM_setTitle( "" ); */
302 /* Reset the dislay to get the right size */
303 p_thread->p_projectm->projectM_resetGL( p_thread->i_width,
304 p_thread->i_height );
306 while( vlc_object_alive( p_thread ) )
308 /* Manage the events */
309 p_thread->p_opengl->pf_manage( p_thread->p_opengl );
310 /* Render the image and swap the buffers */
311 vlc_mutex_lock( &p_thread->lock );
312 if( p_thread->i_nb_samples > 0 )
313 p_thread->p_projectm->pcm()->addPCMfloat( p_thread->p_buffer,
314 p_thread->i_nb_samples );
316 p_thread->p_projectm->renderFrame();
317 p_thread->p_opengl->pf_swap( p_thread->p_opengl );
318 vlc_mutex_unlock( &p_thread->lock );
320 /* TODO: use a fps limiter */
326 delete p_thread->p_projectm;
328 /* Free the openGL provider */
329 module_unneed( p_thread->p_opengl, p_thread->p_module );
330 vlc_object_detach( p_thread->p_opengl );
331 vlc_object_release( p_thread->p_opengl );
334 vlc_restorecancel( cancel );