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>
32 #include <vlc_filter.h>
34 #include <libprojectM/projectM.hpp>
37 /*****************************************************************************
39 *****************************************************************************/
40 static int Open ( vlc_object_t * );
41 static void Close ( vlc_object_t * );
43 #define CONFIG_TEXT N_("projectM configuration file")
44 #define CONFIG_LONGTEXT N_("File that will be used to configure the projectM " \
47 #define WIDTH_TEXT N_("Video width")
48 #define WIDTH_LONGTEXT N_("The width of the video window, in pixels.")
50 #define HEIGHT_TEXT N_("Video height")
51 #define HEIGHT_LONGTEXT N_("The height of the video window, in pixels.")
54 set_shortname( N_("projectM"))
55 set_description( N_("libprojectM effect") )
56 set_capability( "visualization2", 0 )
57 set_category( CAT_AUDIO )
58 set_subcategory( SUBCAT_AUDIO_VISUAL )
59 add_file( "projectm-config", "/usr/share/projectM/config.inp", NULL,
60 CONFIG_TEXT, CONFIG_LONGTEXT, true )
61 add_integer( "projectm-width", 800, NULL, WIDTH_TEXT, WIDTH_LONGTEXT,
63 add_integer( "projectm-height", 640, NULL, HEIGHT_TEXT, HEIGHT_LONGTEXT,
65 add_shortcut( "projectm" )
66 set_callbacks( Open, Close )
70 /*****************************************************************************
72 *****************************************************************************/
80 /* video output module and opengl provider */
81 vout_thread_t *p_opengl;
84 /* libprojectM objects */
102 static block_t *DoWork( filter_t *, block_t * );
103 static void *Thread( void * );
107 * Init the openGL context
108 * p_thread: projectm thread object
109 * @return VLC_SUCCESS or vlc error codes
111 static int initOpenGL( filter_t *p_filter )
113 filter_sys_t *p_sys = p_filter->p_sys;
116 (vout_thread_t *)vlc_object_create( p_filter, sizeof(vout_thread_t) );
117 if( !p_sys->p_opengl )
120 vlc_object_attach( p_sys->p_opengl, p_filter );
122 /* Initialize the opengl object */
123 video_format_Setup( &p_sys->p_opengl->fmt_in, VLC_CODEC_RGB32,
124 p_sys->i_width, p_sys->i_height, 1 );
125 p_sys->p_opengl->i_window_width = p_sys->i_width;
126 p_sys->p_opengl->i_window_height = p_sys->i_height;
127 p_sys->p_opengl->render.i_width = p_sys->i_width;
128 p_sys->p_opengl->render.i_height = p_sys->i_height;
129 p_sys->p_opengl->render.i_aspect = VOUT_ASPECT_FACTOR;
130 p_sys->p_opengl->b_fullscreen = false;
131 p_sys->p_opengl->b_autoscale = true;
132 p_sys->p_opengl->i_alignment = 0;
133 p_sys->p_opengl->fmt_in.i_sar_num = 1;
134 p_sys->p_opengl->fmt_in.i_sar_den = 1;
135 p_sys->p_opengl->fmt_render = p_sys->p_opengl->fmt_in;
137 /* Ask for the opengl provider */
138 p_sys->p_module = module_need( p_sys->p_opengl, "opengl provider",
140 if( !p_sys->p_module )
142 msg_Err( p_filter, "unable to initialize OpenGL" );
143 vlc_object_detach( p_sys->p_opengl );
144 vlc_object_release( p_sys->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 filter_t *p_filter = (filter_t *)p_this;
162 /* Test the audio format */
163 if( p_filter->fmt_in.audio.i_format != VLC_CODEC_FL32 ||
164 p_filter->fmt_out.audio.i_format != VLC_CODEC_FL32 )
166 msg_Warn( p_filter, "bad input or output format" );
169 if( !AOUT_FMTS_SIMILAR( &p_filter->fmt_in.audio, &p_filter->fmt_out.audio ) )
171 msg_Warn( p_filter, "input and outut are not similar" );
175 p_filter->pf_audio_filter = DoWork;
177 p_sys = p_filter->p_sys = (filter_sys_t*)malloc( sizeof( *p_sys ) );
181 /* Create the object for the thread */
182 vlc_sem_init( &p_sys->ready, 0 );
183 p_sys->b_error = false;
184 p_sys->i_width = var_CreateGetInteger( p_filter, "projectm-width" );
185 p_sys->i_height = var_CreateGetInteger( p_filter, "projectm-height" );
186 p_sys->i_channels = aout_FormatNbChannels( &p_filter->fmt_in.audio );
187 p_sys->psz_config = var_CreateGetString( p_filter, "projectm-config" );
188 vlc_mutex_init( &p_sys->lock );
189 p_sys->p_buffer = NULL;
190 p_sys->i_buffer_size = 0;
191 p_sys->i_nb_samples = 0;
193 /* Create the thread */
194 if( vlc_clone( &p_sys->thread, Thread, p_filter, VLC_THREAD_PRIORITY_LOW ) )
197 vlc_sem_wait( &p_sys->ready );
200 vlc_join( p_sys->thread, NULL );
207 vlc_sem_destroy( &p_sys->ready );
215 * @param p_this: the filter object
217 static void Close( vlc_object_t *p_this )
219 filter_t *p_filter = (filter_t *)p_this;
220 filter_sys_t *p_sys = p_filter->p_sys;
222 /* Stop the thread */
223 vlc_cancel( p_sys->thread );
224 vlc_join( p_sys->thread, NULL );
226 /* Free the ressources */
227 vlc_sem_destroy( &p_sys->ready );
228 vlc_mutex_destroy( &p_sys->lock );
229 free( p_sys->p_buffer );
230 free( p_sys->psz_config );
236 * Do the actual work with the new sample
237 * @param p_aout: audio output object
238 * @param p_filter: filter object
239 * @param p_in_buf: input buffer
240 * @param p_out_buf: output buffer
242 static block_t *DoWork( filter_t *p_filter, block_t *p_in_buf )
244 filter_sys_t *p_sys = p_filter->p_sys;
246 vlc_mutex_lock( &p_sys->lock );
247 if( p_sys->i_buffer_size > 0 )
249 p_sys->p_buffer[0] = 0;
250 p_sys->i_nb_samples = __MIN( p_sys->i_buffer_size,
251 p_in_buf->i_nb_samples );
252 for( int i = 0; i < p_sys->i_nb_samples; i++ )
253 p_sys->p_buffer[i] = p_in_buf->p_buffer[i];
255 vlc_mutex_unlock( &p_sys->lock );
261 * Clean up function when Thread() is cancelled.
263 static void ThreadCleanup( void *p_data )
265 filter_t *p_filter = (filter_t*)p_data;
266 filter_sys_t *p_sys = p_filter->p_sys;
269 delete p_sys->p_projectm;
271 /* Free the openGL provider */
272 module_unneed( p_sys->p_opengl, p_sys->p_module );
273 vlc_object_detach( p_sys->p_opengl );
274 vlc_object_release( p_sys->p_opengl );
278 * ProjectM update thread which do the rendering
279 * @param p_this: the p_thread object
281 static void *Thread( void *p_data )
283 filter_t *p_filter = (filter_t*)p_data;
284 filter_sys_t *p_sys = p_filter->p_sys;
285 int cancel = vlc_savecancel();
287 /* Create the openGL provider */
288 if( initOpenGL( p_filter ) )
290 p_sys->b_error = true;
291 vlc_sem_post( &p_sys->ready );
294 vlc_cleanup_push( ThreadCleanup, p_filter );
296 /* Initialize the opengl provider for this thread */
297 p_sys->p_opengl->pf_init( p_sys->p_opengl );
299 /* Create the projectM object */
300 p_sys->p_projectm = new projectM( p_sys->psz_config );
301 p_sys->i_buffer_size = p_sys->p_projectm->pcm()->maxsamples;
302 p_sys->p_buffer = (float*)calloc( p_sys->i_buffer_size,
305 vlc_sem_post( &p_sys->ready );
307 /* TODO: Give to projectm the name of the input
308 p_sys->p_projectm->projectM_setTitle( "" ); */
310 /* Reset the dislay to get the right size */
311 p_sys->p_projectm->projectM_resetGL( p_sys->i_width,
316 /* Manage the events */
317 p_sys->p_opengl->pf_manage( p_sys->p_opengl );
318 /* Render the image and swap the buffers */
319 vlc_mutex_lock( &p_sys->lock );
320 if( p_sys->i_nb_samples > 0 )
321 p_sys->p_projectm->pcm()->addPCMfloat( p_sys->p_buffer,
322 p_sys->i_nb_samples );
324 p_sys->p_projectm->renderFrame();
325 p_sys->p_opengl->pf_swap( p_sys->p_opengl );
326 vlc_mutex_unlock( &p_sys->lock );
328 /* TODO: use a fps limiter */
329 vlc_restorecancel( cancel );
331 cancel = vlc_savecancel();