]> git.sesse.net Git - vlc/blob - src/video_output/video_output.c
* ./plugins/chroma/i420_rgb16.c: 24/32 bpp software YUV.
[vlc] / src / video_output / video_output.c
1 /*****************************************************************************
2  * video_output.c : video output thread
3  * This module describes the programming interface for video output threads.
4  * It includes functions allowing to open a new thread, send pictures to a
5  * thread, and destroy a previously oppened video output thread.
6  *****************************************************************************
7  * Copyright (C) 2000-2001 VideoLAN
8  * $Id: video_output.c,v 1.156 2002/01/12 01:25:57 sam Exp $
9  *
10  * Authors: Vincent Seguin <seguin@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <stdio.h>                                              /* sprintf() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <videolan/vlc.h>
36
37 #ifdef HAVE_SYS_TIMES_H
38 #   include <sys/times.h>
39 #endif
40
41 #include "video.h"
42 #include "video_output.h"
43
44 /*****************************************************************************
45  * Local prototypes
46  *****************************************************************************/
47 static int      InitThread        ( vout_thread_t *p_vout );
48 static void     RunThread         ( vout_thread_t *p_vout );
49 static void     ErrorThread       ( vout_thread_t *p_vout );
50 static void     EndThread         ( vout_thread_t *p_vout );
51 static void     DestroyThread     ( vout_thread_t *p_vout, int i_status );
52 static int      ReduceHeight      ( int );
53
54 /*****************************************************************************
55  * vout_InitBank: initialize the video output bank.
56  *****************************************************************************/
57 void vout_InitBank ( void )
58 {
59     p_vout_bank->i_count = 0;
60
61     vlc_mutex_init( &p_vout_bank->lock );
62 }
63
64 /*****************************************************************************
65  * vout_EndBank: empty the video output bank.
66  *****************************************************************************
67  * This function ends all unused video outputs and empties the bank in
68  * case of success.
69  *****************************************************************************/
70 void vout_EndBank ( void )
71 {
72     /* Ask all remaining video outputs to die */
73     while( p_vout_bank->i_count )
74     {
75         vout_DestroyThread(
76                 p_vout_bank->pp_vout[ --p_vout_bank->i_count ], NULL );
77     }
78
79     vlc_mutex_destroy( &p_vout_bank->lock );
80 }
81
82 /*****************************************************************************
83  * vout_CreateThread: creates a new video output thread
84  *****************************************************************************
85  * This function creates a new video output thread, and returns a pointer
86  * to its description. On error, it returns NULL.
87  * If pi_status is NULL, then the function will block until the thread is ready.
88  * If not, it will be updated using one of the THREAD_* constants.
89  *****************************************************************************/
90 vout_thread_t * vout_CreateThread   ( int *pi_status,
91                                       int i_width, int i_height,
92                                       u32 i_chroma, int i_aspect )
93 {
94     vout_thread_t * p_vout;                             /* thread descriptor */
95     int             i_status;                               /* thread status */
96     int             i_index;                                /* loop variable */
97     char          * psz_plugin;
98     probedata_t     data;
99
100     /* Allocate descriptor */
101     p_vout = (vout_thread_t *) malloc( sizeof(vout_thread_t) );
102     if( p_vout == NULL )
103     {
104         intf_ErrMsg( "vout error: vout thread creation returned %s",
105                      strerror(ENOMEM) );
106         return( NULL );
107     }
108
109     /* Choose the best module */
110     psz_plugin = main_GetPszVariable( VOUT_FILTER_VAR, "" );
111     if( psz_plugin[0] == '\0' )
112     {
113         psz_plugin = main_GetPszVariable( VOUT_METHOD_VAR, "" );
114     }
115
116     data.vout.i_chroma = i_chroma;
117     p_vout->p_module
118         = module_Need( MODULE_CAPABILITY_VOUT, psz_plugin, &data );
119
120     if( p_vout->p_module == NULL )
121     {
122         intf_ErrMsg( "vout error: no suitable vout module" );
123         free( p_vout );
124         return( NULL );
125     }
126
127 #define f p_vout->p_module->p_functions->vout.functions.vout
128     p_vout->pf_create     = f.pf_create;
129     p_vout->pf_init       = f.pf_init;
130     p_vout->pf_end        = f.pf_end;
131     p_vout->pf_destroy    = f.pf_destroy;
132     p_vout->pf_manage     = f.pf_manage;
133     p_vout->pf_render     = f.pf_render;
134     p_vout->pf_display    = f.pf_display;
135 #undef f
136
137     /* Initialize thread properties - thread id and locks will be initialized
138      * later */
139     p_vout->b_die               = 0;
140     p_vout->b_error             = 0;
141     p_vout->b_active            = 0;
142     p_vout->pi_status           = (pi_status != NULL) ? pi_status : &i_status;
143     *p_vout->pi_status          = THREAD_CREATE;
144
145     /* Initialize pictures and subpictures - translation tables and functions
146      * will be initialized later in InitThread */
147     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++)
148     {
149         p_vout->p_picture[i_index].i_status = FREE_PICTURE;
150         p_vout->p_picture[i_index].i_type   = EMPTY_PICTURE;
151     }
152
153     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
154     {
155         p_vout->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
156         p_vout->p_subpicture[i_index].i_type   = EMPTY_SUBPICTURE;
157     }
158
159     /* Initialize the rendering heap */
160     p_vout->i_heap_size = 0;
161
162     I_RENDERPICTURES = 0;
163     p_vout->render.i_width    = i_width;
164     p_vout->render.i_height   = i_height;
165     p_vout->render.i_chroma   = i_chroma;
166     p_vout->render.i_aspect   = i_aspect;
167
168     /* Initialize misc stuff */
169     p_vout->i_changes    = 0;
170     p_vout->f_gamma      = 0;
171     p_vout->b_grayscale  = 0;
172     p_vout->b_info       = 0;
173     p_vout->b_interface  = 0;
174     p_vout->b_scale      = 1;
175     p_vout->b_fullscreen = main_GetIntVariable( VOUT_FULLSCREEN_VAR,
176                                                 VOUT_FULLSCREEN_DEFAULT );
177     p_vout->render_time  = 10;
178
179     /* Create thread and set locks */
180     vlc_mutex_init( &p_vout->picture_lock );
181     vlc_mutex_init( &p_vout->subpicture_lock );
182     vlc_mutex_init( &p_vout->change_lock );
183
184     if( vlc_thread_create( &p_vout->thread_id, "video output",
185                            (void *) RunThread, (void *) p_vout) )
186     {
187         intf_ErrMsg("vout error: %s", strerror(ENOMEM));
188         p_vout->pf_destroy( p_vout );
189         module_Unneed( p_vout->p_module );
190         free( p_vout );
191         return( NULL );
192     }
193
194     /* If status is NULL, wait until the thread is created */
195     if( pi_status == NULL )
196     {
197         do
198         {
199             msleep( THREAD_SLEEP );
200         }while( (i_status != THREAD_READY) && (i_status != THREAD_ERROR)
201                 && (i_status != THREAD_FATAL) );
202         if( i_status != THREAD_READY )
203         {
204             return( NULL );
205         }
206     }
207
208     return( p_vout );
209 }
210
211 /*****************************************************************************
212  * vout_DestroyThread: destroys a previously created thread
213  *****************************************************************************
214  * Destroy a terminated thread.
215  * The function will request a destruction of the specified thread. If pi_error
216  * is NULL, it will return once the thread is destroyed. Else, it will be
217  * update using one of the THREAD_* constants.
218  *****************************************************************************/
219 void vout_DestroyThread( vout_thread_t *p_vout, int *pi_status )
220 {
221     int     i_status;                                       /* thread status */
222
223     /* Set status */
224     p_vout->pi_status = (pi_status != NULL) ? pi_status : &i_status;
225     *p_vout->pi_status = THREAD_DESTROY;
226
227     /* Request thread destruction */
228     p_vout->b_die = 1;
229
230     /* If status is NULL, wait until thread has been destroyed */
231     if( pi_status == NULL )
232     {
233         do
234         {
235             msleep( THREAD_SLEEP );
236         } while( (i_status != THREAD_OVER) && (i_status != THREAD_ERROR)
237                  && (i_status != THREAD_FATAL) );
238     }
239 }
240
241 /*****************************************************************************
242  * InitThread: initialize video output thread
243  *****************************************************************************
244  * This function is called from RunThread and performs the second step of the
245  * initialization. It returns 0 on success. Note that the thread's flag are not
246  * modified inside this function.
247  *****************************************************************************/
248 static int InitThread( vout_thread_t *p_vout )
249 {
250     int i, i_pgcd;
251     probedata_t data;
252
253     /* Update status */
254     *p_vout->pi_status = THREAD_START;
255
256     vlc_mutex_lock( &p_vout->change_lock );
257
258 #ifdef STATS
259     p_vout->c_loops = 0;
260 #endif
261
262     /* Initialize output method, it issues its own error messages */
263     if( p_vout->pf_init( p_vout ) )
264     {
265         vlc_mutex_unlock( &p_vout->change_lock );
266         return( 1 );
267     }
268
269     if( !I_OUTPUTPICTURES )
270     {
271         intf_ErrMsg( "vout error: plugin was unable to allocate at least "
272                      "one direct buffer" );
273         p_vout->pf_end( p_vout );
274         vlc_mutex_unlock( &p_vout->change_lock );
275         return( 1 );
276     }
277
278     intf_WarnMsg( 1, "vout info: got %i direct buffer(s)", I_OUTPUTPICTURES );
279
280     i_pgcd = ReduceHeight( p_vout->render.i_aspect );
281     intf_WarnMsg( 1, "vout info: picture in %ix%i, chroma 0x%.8x (%4.4s), "
282                      "aspect ratio %i:%i",
283                   p_vout->render.i_width, p_vout->render.i_height,
284                   p_vout->render.i_chroma, (char*)&p_vout->render.i_chroma,
285                   p_vout->render.i_aspect / i_pgcd,
286                   VOUT_ASPECT_FACTOR / i_pgcd );
287
288     i_pgcd = ReduceHeight( p_vout->output.i_aspect );
289     intf_WarnMsg( 1, "vout info: picture out %ix%i, chroma 0x%.8x (%4.4s), "
290                      "aspect ratio %i:%i",
291                   p_vout->output.i_width, p_vout->output.i_height,
292                   p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma,
293                   p_vout->output.i_aspect / i_pgcd,
294                   VOUT_ASPECT_FACTOR / i_pgcd );
295
296     /* Check whether we managed to create direct buffers similar to
297      * the render buffers, ie same size, chroma and aspect ratio */
298     if( ( p_vout->output.i_width == p_vout->render.i_width )
299      && ( p_vout->output.i_height == p_vout->render.i_height )
300      && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) )
301      && ( p_vout->output.i_aspect == p_vout->render.i_aspect ) )
302     {
303         /* Cool ! We have direct buffers, we can ask the decoder to
304          * directly decode into them ! Map the first render buffers to
305          * the first direct buffers, but keep the first direct buffer
306          * for memcpy operations */
307         p_vout->b_direct = 1;
308
309         intf_WarnMsg( 2, "vout info: direct render, mapping "
310                          "render pictures 0-%i to system pictures 1-%i",
311                          VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
312
313         for( i = 1; i < VOUT_MAX_PICTURES; i++ )
314         {
315             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
316             I_RENDERPICTURES++;
317         }
318
319     }
320     else
321     {
322         /* Rats... Something is wrong here, we could not find an output
323          * plugin able to directly render what we decode. See if we can
324          * find a chroma plugin to do the conversion */
325         p_vout->b_direct = 0;
326
327         /* Choose the best module */
328         data.chroma.p_render = &p_vout->render;
329         data.chroma.p_output = &p_vout->output;
330         p_vout->chroma.p_module
331             = module_Need( MODULE_CAPABILITY_CHROMA, NULL, &data );
332
333         if( p_vout->chroma.p_module == NULL )
334         {
335             intf_ErrMsg( "vout error: no chroma module for %4.4s to %4.4s",
336                          &p_vout->render.i_chroma, &p_vout->output.i_chroma );
337             p_vout->pf_end( p_vout );
338             vlc_mutex_unlock( &p_vout->change_lock );
339             return( 1 );
340         }
341
342 #define f p_vout->chroma.p_module->p_functions->chroma.functions.chroma
343         p_vout->chroma.pf_init       = f.pf_init;
344         p_vout->chroma.pf_end        = f.pf_end;
345 #undef f
346
347         if( p_vout->chroma.pf_init( p_vout ) )
348         {
349             intf_ErrMsg( "vout error: could not initialize chroma module" );
350             module_Unneed( p_vout->chroma.p_module );
351             p_vout->pf_end( p_vout );
352             vlc_mutex_unlock( &p_vout->change_lock );
353             return( 1 );
354         }
355
356         if( I_OUTPUTPICTURES < VOUT_MAX_PICTURES )
357         {
358             intf_WarnMsg( 2, "vout info: indirect render, mapping "
359                              "render pictures %i-%i to system pictures %i-%i",
360                              I_OUTPUTPICTURES - 1, VOUT_MAX_PICTURES - 2,
361                              I_OUTPUTPICTURES, VOUT_MAX_PICTURES - 1 );
362         }
363         else
364         {
365             intf_WarnMsg( 2, "vout info: indirect render, no system "
366                              "pictures needed, we have %i directbuffers",
367                              I_OUTPUTPICTURES );
368         }
369
370         /* Append render buffers after the direct buffers */
371         for( i = I_OUTPUTPICTURES; i < VOUT_MAX_PICTURES; i++ )
372         {
373             PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
374             I_RENDERPICTURES++;
375         }
376     }
377
378     /* Mark thread as running and return */
379     p_vout->b_active = 1;
380     *p_vout->pi_status = THREAD_READY;
381
382     intf_DbgMsg("thread ready");
383     return( 0 );
384 }
385
386 /*****************************************************************************
387  * RunThread: video output thread
388  *****************************************************************************
389  * Video output thread. This function does only returns when the thread is
390  * terminated. It handles the pictures arriving in the video heap and the
391  * display device events.
392  *****************************************************************************/
393 static void RunThread( vout_thread_t *p_vout)
394 {
395     int             i_index;                                /* index in heap */
396     mtime_t         current_date;                            /* current date */
397     mtime_t         display_date;                            /* display date */
398
399     picture_t *     p_picture;                            /* picture pointer */
400     picture_t *     p_directbuffer;              /* direct buffer to display */
401
402     subpicture_t *  p_subpic;                          /* subpicture pointer */
403
404     /* Create and initialize system-dependant method - this function issues its
405      * own error messages */
406     if( p_vout->pf_create( p_vout ) )
407     {
408         DestroyThread( p_vout, THREAD_ERROR );
409         return;
410     }
411
412     /*
413      * Initialize thread
414      */
415     p_vout->b_error = InitThread( p_vout );
416     if( p_vout->b_error )
417     {
418         /* Destroy thread structures allocated by Create and InitThread */
419         p_vout->pf_destroy( p_vout );
420
421         DestroyThread( p_vout, THREAD_ERROR );
422         return;
423     }
424
425     /*
426      * Main loop - it is not executed if an error occured during
427      * initialization
428      */
429     while( (!p_vout->b_die) && (!p_vout->b_error) )
430     {
431         /* Initialize loop variables */
432         display_date = 0;
433         current_date = mdate();
434
435 #ifdef STATS
436         p_vout->c_loops++;
437         if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
438         {
439             intf_Msg( "vout stats: picture heap: %d/%d",
440                       I_RENDERPICTURES, p_vout->i_heap_size );
441         }
442 #endif
443
444         /*
445          * Find the picture to display - this operation does not need lock,
446          * since only READY_PICTUREs are handled
447          */
448         p_picture = NULL;
449
450         for( i_index = 0;
451              i_index < I_RENDERPICTURES;
452              i_index++ )
453         {
454             if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
455                 && ( (p_picture == NULL) ||
456                      (PP_RENDERPICTURE[i_index]->date < display_date) ) )
457             {
458                 p_picture = PP_RENDERPICTURE[i_index];
459                 display_date = p_picture->date;
460             }
461         }
462
463         if( p_picture != NULL )
464         {
465             /* Compute FPS rate */
466             p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
467                 = display_date;
468
469             if( display_date < current_date + p_vout->render_time )
470             {
471                 /* Picture is late: it will be destroyed and the thread
472                  * will directly choose the next picture */
473                 vlc_mutex_lock( &p_vout->picture_lock );
474                 if( p_picture->i_refcount )
475                 {
476                     /* Pretend we displayed the picture, but don't destroy
477                      * it since the decoder might still need it. */
478                     p_picture->i_status = DISPLAYED_PICTURE;
479                 }
480                 else
481                 {
482                     /* Destroy the picture without displaying it */
483                     p_picture->i_status = DESTROYED_PICTURE;
484                     p_vout->i_heap_size--;
485                 }
486                 intf_WarnMsg( 1, "vout warning: late picture skipped (%p)",
487                               p_picture );
488                 vlc_mutex_unlock( &p_vout->picture_lock );
489
490                 continue;
491             }
492             else if( display_date > current_date + VOUT_DISPLAY_DELAY )
493             {
494                 /* A picture is ready to be rendered, but its rendering date
495                  * is far from the current one so the thread will perform an
496                  * empty loop as if no picture were found. The picture state
497                  * is unchanged */
498                 p_picture    = NULL;
499                 display_date = 0;
500             }
501         }
502
503         /*
504          * Check for subpictures to display
505          */
506         p_subpic = vout_SortSubPictures( p_vout, display_date );
507
508         /*
509          * Perform rendering
510          */
511         p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
512
513         /*
514          * Call the plugin-specific rendering method
515          */
516         if( p_picture != NULL )
517         {
518             /* Render the direct buffer returned by vout_RenderPicture */
519             p_vout->pf_render( p_vout, p_directbuffer );
520         }
521
522         /*
523          * Sleep, wake up
524          */
525         if( display_date != 0 )
526         {
527             /* Store render time using Bresenham algorithm */
528             p_vout->render_time += mdate() - current_date;
529             p_vout->render_time >>= 1;
530         }
531
532         /* Give back change lock */
533         vlc_mutex_unlock( &p_vout->change_lock );
534
535         /* Sleep a while or until a given date */
536         if( display_date != 0 )
537         {
538             mwait( display_date - VOUT_MWAIT_TOLERANCE );
539         }
540         else
541         {
542             msleep( VOUT_IDLE_SLEEP );
543         }
544
545         /* On awakening, take back lock and send immediately picture
546          * to display. */
547         vlc_mutex_lock( &p_vout->change_lock );
548
549 #ifdef TRACE_VOUT
550         intf_DbgMsg( "picture %p, subpicture %p", p_picture, p_subpic );
551 #endif
552
553         /*
554          * Display the previously rendered picture
555          */
556         if( p_picture != NULL )
557         {
558             /* Display the direct buffer returned by vout_RenderPicture */
559             p_vout->pf_display( p_vout, p_directbuffer );
560
561             /* Remove picture from heap */
562             vlc_mutex_lock( &p_vout->picture_lock );
563             if( p_picture->i_refcount )
564             {
565                 p_picture->i_status = DISPLAYED_PICTURE;
566             }
567             else
568             {
569                 p_picture->i_status = DESTROYED_PICTURE;
570                 p_vout->i_heap_size--;
571             }
572             vlc_mutex_unlock( &p_vout->picture_lock );
573         }
574
575         /*
576          * Check events and manage thread
577          */
578         if( p_vout->pf_manage( p_vout ) )
579         {
580             /* A fatal error occured, and the thread must terminate immediately,
581              * without displaying anything - setting b_error to 1 causes the
582              * immediate end of the main while() loop. */
583             p_vout->b_error = 1;
584         }
585     }
586
587     /*
588      * Error loop - wait until the thread destruction is requested
589      */
590     if( p_vout->b_error )
591     {
592         ErrorThread( p_vout );
593     }
594
595     /* End of thread */
596     EndThread( p_vout );
597
598     /* Destroy method-dependant resources */
599     p_vout->pf_destroy( p_vout );
600
601     /* Destroy thread structures allocated by CreateThread */
602     DestroyThread( p_vout, THREAD_OVER );
603     intf_DbgMsg( "thread end" );
604 }
605
606 /*****************************************************************************
607  * ErrorThread: RunThread() error loop
608  *****************************************************************************
609  * This function is called when an error occured during thread main's loop. The
610  * thread can still receive feed, but must be ready to terminate as soon as
611  * possible.
612  *****************************************************************************/
613 static void ErrorThread( vout_thread_t *p_vout )
614 {
615     /* Wait until a `die' order */
616     while( !p_vout->b_die )
617     {
618         /* Sleep a while */
619         msleep( VOUT_IDLE_SLEEP );
620     }
621 }
622
623 /*****************************************************************************
624  * EndThread: thread destruction
625  *****************************************************************************
626  * This function is called when the thread ends after a sucessful
627  * initialization. It frees all ressources allocated by InitThread.
628  *****************************************************************************/
629 static void EndThread( vout_thread_t *p_vout )
630 {
631     int     i_index;                                        /* index in heap */
632
633     /* Store status */
634     *p_vout->pi_status = THREAD_END;
635
636 #ifdef STATS
637     {
638         struct tms cpu_usage;
639         times( &cpu_usage );
640
641         intf_Msg( "vout stats: cpu usage (user: %d, system: %d)",
642                   cpu_usage.tms_utime, cpu_usage.tms_stime );
643     }
644 #endif
645
646     if( !p_vout->b_direct )
647     {
648         p_vout->chroma.pf_end( p_vout );
649         module_Unneed( p_vout->chroma.p_module );
650     }
651
652     /* Destroy all remaining pictures */
653     for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
654     {
655         if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
656         {
657             free( p_vout->p_picture[i_index].p_data );
658         }
659     }
660
661     /* Destroy all remaining subpictures */
662     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
663     {
664         if( p_vout->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
665         {
666             free( p_vout->p_subpicture[i_index].p_data );
667         }
668     }
669
670     /* Destroy translation tables */
671     p_vout->pf_end( p_vout );
672
673     /* Release the change lock */
674     vlc_mutex_unlock( &p_vout->change_lock );
675 }
676
677 /*****************************************************************************
678  * DestroyThread: thread destruction
679  *****************************************************************************
680  * This function is called when the thread ends. It frees all ressources
681  * allocated by CreateThread. Status is available at this stage.
682  *****************************************************************************/
683 static void DestroyThread( vout_thread_t *p_vout, int i_status )
684 {
685     int *pi_status;                                         /* status adress */
686
687     /* Store status adress */
688     pi_status = p_vout->pi_status;
689
690     /* Destroy the locks */
691     vlc_mutex_destroy( &p_vout->picture_lock );
692     vlc_mutex_destroy( &p_vout->subpicture_lock );
693     vlc_mutex_destroy( &p_vout->change_lock );
694
695     /* Release the module */
696     module_Unneed( p_vout->p_module );
697
698     /* Free structure */
699     free( p_vout );
700     *pi_status = i_status;
701 }
702
703 static int ReduceHeight( int i_ratio )
704 {
705     int i_dummy = VOUT_ASPECT_FACTOR;
706     int i_pgcd  = 1;
707  
708     if( !i_ratio )
709     {
710         return i_pgcd;
711     }
712
713     /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */
714     while( !(i_ratio & 1) && !(i_dummy & 1) )
715     {
716         i_ratio >>= 1;
717         i_dummy >>= 1;
718         i_pgcd  <<= 1;
719     }
720
721     while( !(i_ratio % 3) && !(i_ratio % 3) )
722     {
723         i_ratio /= 3;
724         i_dummy /= 3;
725         i_pgcd  *= 3;
726     }
727
728     while( !(i_ratio % 5) && !(i_ratio % 5) )
729     {
730         i_ratio /= 5;
731         i_dummy /= 5;
732         i_pgcd  *= 5;
733     }
734
735     return i_pgcd;
736 }
737