]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutqt.m
ALL: OS X OpenGL provider
[vlc] / modules / gui / macosx / voutqt.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2003 VideoLAN
5  * $Id: vout.m 8351 2004-08-02 13:06:38Z hartman $
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *          Florian G. Pflug <fgp@phlo.org>
9  *          Jon Lech Johansen <jon-vl@nanocrew.net>
10  *          Derk-Jan Hartman <hartman at videolan dot org>
11  *          Eric Petit <titer@m0k.org>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdlib.h>                                                /* free() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <QuickTime/QuickTime.h>
36
37 #include <vlc_keys.h>
38
39 #include "intf.h"
40 #include "vout.h"
41
42 #define QT_MAX_DIRECTBUFFERS 10
43 #define VL_MAX_DISPLAYS 16
44
45 /*****************************************************************************
46  * VLCView interface
47  *****************************************************************************/
48 @interface VLCQTView : NSQuickDrawView
49 {
50     vout_thread_t * p_vout;
51 }
52
53 - (id) initWithVout:(vout_thread_t *)p_vout;
54
55 @end
56
57 struct vout_sys_t
58 {
59     NSAutoreleasePool *o_pool;
60     VLCWindow * o_window;
61     VLCQTView * o_qtview;
62
63     vlc_bool_t  b_saved_frame;
64     NSRect      s_frame;
65
66     CodecType i_codec;
67     CGrafPtr p_qdport;
68     ImageSequence i_seq;
69     MatrixRecordPtr p_matrix;
70     DecompressorComponent img_dc;
71     ImageDescriptionHandle h_img_descr;
72 };
73
74 struct picture_sys_t
75 {
76     void *p_data;
77     unsigned int i_size;
78 };
79
80 /*****************************************************************************
81  * Local prototypes
82  *****************************************************************************/
83
84 static int  InitVideo           ( vout_thread_t * );
85 static void EndVideo            ( vout_thread_t * );
86 static int  ManageVideo         ( vout_thread_t * );
87 static void DisplayVideo        ( vout_thread_t *, picture_t * );
88 static int  ControlVideo        ( vout_thread_t *, int, va_list );
89
90 static int CoToggleFullscreen( vout_thread_t *p_vout );
91 static void QTScaleMatrix       ( vout_thread_t * );
92 static int  QTCreateSequence    ( vout_thread_t * );
93 static void QTDestroySequence   ( vout_thread_t * );
94 static int  QTNewPicture        ( vout_thread_t *, picture_t * );
95 static void QTFreePicture       ( vout_thread_t *, picture_t * );
96
97 /*****************************************************************************
98  * OpenVideo: allocates MacOS X video thread output method
99  *****************************************************************************
100  * This function allocates and initializes a MacOS X vout method.
101  *****************************************************************************/
102 int E_(OpenVideoQT) ( vlc_object_t *p_this )
103 {   
104     vout_thread_t * p_vout = (vout_thread_t *)p_this;
105     vlc_value_t val;
106     OSErr err;
107     int i_timeout;
108
109     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
110     if( p_vout->p_sys == NULL )
111     {
112         msg_Err( p_vout, "out of memory" );
113         return( 1 );
114     }
115
116     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
117
118     /* Wait for a MacOS X interface to appear. Timeout is 2 seconds. */
119     for( i_timeout = 20 ; i_timeout-- ; )
120     {
121         if( NSApp == NULL )
122         {
123             msleep( INTF_IDLE_SLEEP );
124         }
125     }
126
127     if( NSApp == NULL )
128     {
129         /* no MacOS X intf, unable to communicate with MT */
130         msg_Err( p_vout, "no MacOS X interface present" );
131         free( p_vout->p_sys );
132         return( 1 );
133     }
134
135     p_vout->pf_init = InitVideo;
136     p_vout->pf_end = EndVideo;
137     p_vout->pf_manage = ManageVideo;
138     p_vout->pf_render = NULL;
139     p_vout->pf_display = DisplayVideo;
140     p_vout->pf_control = ControlVideo;
141
142     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
143
144     var_Create( p_vout, "macosx-vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
145     var_Create( p_vout, "macosx-vdev", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
146     var_Create( p_vout, "macosx-fill", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
147     var_Create( p_vout, "macosx-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
148     var_Create( p_vout, "macosx-opaqueness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
149     var_Create( p_vout, "macosx-opengl-effect", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
150     
151     /* Initialize QuickTime */
152     p_vout->p_sys->h_img_descr = 
153         (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
154     p_vout->p_sys->p_matrix =
155         (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
156
157     if( ( err = EnterMovies() ) != noErr )
158     {
159         msg_Err( p_vout, "EnterMovies failed: %d", err );
160         free( p_vout->p_sys->p_matrix );
161         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
162         free( p_vout->p_sys );
163         return VLC_EGENERIC;
164     }
165
166     /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
167     vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
168
169     /* Can we find the right chroma ? */
170     err = FindCodec( kComponentVideoUnsigned, bestSpeedCodec,
171                         nil, &p_vout->p_sys->img_dc );
172     
173     vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
174     
175     if( err == noErr && p_vout->p_sys->img_dc != 0 )
176     {
177         p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
178         p_vout->p_sys->i_codec = kComponentVideoUnsigned;
179     }
180     else
181     {
182         msg_Err( p_vout, "failed to find an appropriate codec" );
183     }
184
185     if( p_vout->p_sys->img_dc == 0 )
186     {
187         free( p_vout->p_sys->p_matrix );
188         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
189         free( p_vout->p_sys );
190         return VLC_EGENERIC;        
191     }
192
193     /* Setup the menuitem for the multiple displays. Read the vlc preference (macosx-vdev) for the primary display */
194     NSArray * o_screens = [NSScreen screens];
195     if( [o_screens count] > 0 && var_Type( p_vout, "video-device" ) == 0 )
196     {
197         int i = 1;
198         vlc_value_t val2, text;
199         NSScreen * o_screen;
200
201         var_Get( p_vout, "macosx-vdev", &val );
202
203         var_Create( p_vout, "video-device", VLC_VAR_INTEGER |
204                                             VLC_VAR_HASCHOICE ); 
205         text.psz_string = _("Video device");
206         var_Change( p_vout, "video-device", VLC_VAR_SETTEXT, &text, NULL );
207         
208         NSEnumerator * o_enumerator = [o_screens objectEnumerator];
209
210         while( (o_screen = [o_enumerator nextObject]) != NULL )
211         {
212             char psz_temp[255];
213             NSRect s_rect = [o_screen frame];
214
215             snprintf( psz_temp, sizeof(psz_temp)/sizeof(psz_temp[0])-1, 
216                       "%s %d (%dx%d)", _("Screen"), i,
217                       (int)s_rect.size.width, (int)s_rect.size.height ); 
218
219             text.psz_string = psz_temp;
220             val2.i_int = i;
221             var_Change( p_vout, "video-device",
222                         VLC_VAR_ADDCHOICE, &val2, &text );
223
224             if( ( i - 1 ) == val.i_int )
225             {
226                 var_Set( p_vout, "video-device", val2 );
227             }
228             i++;
229         }
230
231         var_AddCallback( p_vout, "video-device", vout_VarCallback,
232                          NULL );
233
234         val2.b_bool = VLC_TRUE;
235         var_Set( p_vout, "intf-change", val2 );
236     }
237
238     /* Spawn window */
239     p_vout->p_sys->o_window =
240         [[VLCWindow alloc] initWithVout: p_vout frame: nil];
241
242 #define o_qtview p_vout->p_sys->o_qtview
243     o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
244     [p_vout->p_sys->o_window setContentView: o_qtview];
245     [o_qtview autorelease];
246
247     /* Retrieve the QuickDraw port */
248     [o_qtview lockFocus];
249     p_vout->p_sys->p_qdport = [o_qtview qdPort];
250     [o_qtview unlockFocus];
251 #undef o_qtview
252
253     return VLC_SUCCESS;
254 }
255
256 /*****************************************************************************
257  * CloseVideo: destroy video thread output method
258  *****************************************************************************/
259 void E_(CloseVideoQT) ( vlc_object_t *p_this )
260 {
261     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; 
262     vout_thread_t * p_vout = (vout_thread_t *)p_this;
263
264     [p_vout->p_sys->o_window close];
265     [p_vout->p_sys->o_window release];
266
267     /* Clean Up Quicktime environment */
268     ExitMovies();
269     free( p_vout->p_sys->p_matrix );
270     DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
271
272     [o_pool release];
273     free( p_vout->p_sys );
274 }
275
276 /*****************************************************************************
277  * InitVideo: initialize video thread output method
278  *****************************************************************************/
279 static int InitVideo    ( vout_thread_t *p_vout )
280 {
281     picture_t *p_pic;
282     int i_index;
283
284     I_OUTPUTPICTURES = 0;
285
286     /* Initialize the output structure; we already found a codec,
287      * and the corresponding chroma we will be using. Since we can
288      * arbitrary scale, stick to the coordinates and aspect. */
289     p_vout->output.i_width  = p_vout->render.i_width;
290     p_vout->output.i_height = p_vout->render.i_height;
291     p_vout->output.i_aspect = p_vout->render.i_aspect;
292
293     SetPort( p_vout->p_sys->p_qdport );
294     QTScaleMatrix( p_vout );
295
296     if( QTCreateSequence( p_vout ) )
297     {
298         msg_Err( p_vout, "unable to create sequence" );
299         return( 1 );
300     }
301
302     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
303     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
304     {
305         p_pic = NULL;
306
307         /* Find an empty picture slot */
308         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
309         {
310             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
311             {
312                 p_pic = p_vout->p_picture + i_index;
313                 break;
314             }
315         }
316
317         /* Allocate the picture */
318         if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
319         {
320             break;
321         }
322
323         p_pic->i_status = DESTROYED_PICTURE;
324         p_pic->i_type   = DIRECT_PICTURE;
325
326         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
327         I_OUTPUTPICTURES++;
328     }
329     return 0;
330 }
331
332 /*****************************************************************************
333  * EndVideo: terminate video thread output method
334  *****************************************************************************/
335 static void EndVideo( vout_thread_t *p_vout )
336 {
337     int i_index;
338
339     QTDestroySequence( p_vout );
340
341     /* Free the direct buffers we allocated */
342     for( i_index = I_OUTPUTPICTURES; i_index; )
343     {
344         i_index--;
345         QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
346     }
347 }
348
349 /*****************************************************************************
350  * ManageVideo: handle events
351  *****************************************************************************
352  * This function should be called regularly by video output thread. It manages
353  * console events. It returns a non null value on error.
354  *****************************************************************************/
355 static int ManageVideo( vout_thread_t *p_vout )
356 {
357     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
358     {
359         if( CoToggleFullscreen( p_vout ) )  
360         {
361             return( 1 );
362         }
363
364         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
365     }
366
367     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
368     {
369         QTScaleMatrix( p_vout );
370         SetDSequenceMatrix( p_vout->p_sys->i_seq, 
371                             p_vout->p_sys->p_matrix );
372  
373         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
374     }
375
376     [p_vout->p_sys->o_window manage];
377     
378     return( 0 );
379 }
380
381 /*****************************************************************************
382  * vout_Display: displays previously rendered output
383  *****************************************************************************
384  * This function sends the currently rendered image to the display.
385  *****************************************************************************/
386 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
387 {
388     OSErr err;
389     CodecFlags flags;
390
391     if( ( err = DecompressSequenceFrameWhen( 
392                     p_vout->p_sys->i_seq,
393                     p_pic->p_sys->p_data,
394                     p_pic->p_sys->i_size,                    
395                     codecFlagUseImageBuffer, &flags, NULL, NULL ) != noErr ) )
396     {
397         msg_Warn( p_vout, "DecompressSequenceFrameWhen failed: %d", err );
398     }
399     else
400     {
401         QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
402     }
403 }
404
405 /*****************************************************************************
406  * ControlVideo: control facility for the vout
407  *****************************************************************************/
408 static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
409 {
410     vlc_bool_t b_arg;
411
412     switch( i_query )
413     {
414         case VOUT_SET_STAY_ON_TOP:
415             b_arg = va_arg( args, vlc_bool_t );
416             [p_vout->p_sys->o_window setOnTop: b_arg];
417             return VLC_SUCCESS;
418
419         case VOUT_CLOSE:
420         case VOUT_REPARENT:
421         default:
422             return vout_vaControlDefault( p_vout, i_query, args );
423     }
424 }
425
426 /*****************************************************************************
427  * CoToggleFullscreen: toggle fullscreen 
428  *****************************************************************************
429  * Returns 0 on success, 1 otherwise
430  *****************************************************************************/
431 static int CoToggleFullscreen( vout_thread_t *p_vout )
432 {
433     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
434
435     QTDestroySequence( p_vout );
436
437     if( !p_vout->b_fullscreen )
438     {
439         /* Save window size and position */
440         p_vout->p_sys->s_frame.size =
441             [[p_vout->p_sys->o_window contentView] frame].size;
442         p_vout->p_sys->s_frame.origin =
443             [p_vout->p_sys->o_window frame].origin;
444         p_vout->p_sys->b_saved_frame = VLC_TRUE;
445     }
446     [p_vout->p_sys->o_window close];
447
448     p_vout->b_fullscreen = !p_vout->b_fullscreen;
449
450     if( p_vout->p_sys->b_saved_frame )
451     {
452         p_vout->p_sys->o_window = [[VLCWindow alloc]
453             initWithVout: p_vout frame: &p_vout->p_sys->s_frame];
454     }
455     else
456     {
457         p_vout->p_sys->o_window = [[VLCWindow alloc]
458             initWithVout: p_vout frame: nil];
459     }
460
461 #define o_qtview p_vout->p_sys->o_qtview
462     o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
463     [p_vout->p_sys->o_window setContentView: o_qtview];
464     [o_qtview autorelease];
465
466     /* Retrieve the QuickDraw port */
467     [o_qtview lockFocus];
468     p_vout->p_sys->p_qdport = [o_qtview qdPort];
469     [o_qtview unlockFocus];
470 #undef o_qtview
471
472     SetPort( p_vout->p_sys->p_qdport );
473     QTScaleMatrix( p_vout );
474
475     if( QTCreateSequence( p_vout ) )
476     {
477         msg_Err( p_vout, "unable to create sequence" );
478         return( 1 ); 
479     } 
480
481     [o_pool release];
482     return 0;
483 }
484
485 /*****************************************************************************
486  * QTScaleMatrix: scale matrix 
487  *****************************************************************************/
488 static void QTScaleMatrix( vout_thread_t *p_vout )
489 {
490     Rect s_rect;
491     vlc_value_t val;
492     unsigned int i_width, i_height;
493     Fixed factor_x, factor_y;
494     unsigned int i_offset_x = 0;
495     unsigned int i_offset_y = 0;
496
497     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
498
499     i_width = s_rect.right - s_rect.left;
500     i_height = s_rect.bottom - s_rect.top;
501
502     var_Get( p_vout, "macosx-stretch", &val );
503     if( val.b_bool )
504     {
505         factor_x = FixDiv( Long2Fix( i_width ),
506                            Long2Fix( p_vout->output.i_width ) );
507         factor_y = FixDiv( Long2Fix( i_height ),
508                            Long2Fix( p_vout->output.i_height ) );
509                            
510     }
511     else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
512     {
513         int i_adj_width = i_height * p_vout->output.i_aspect /
514                           VOUT_ASPECT_FACTOR;
515
516         factor_x = FixDiv( Long2Fix( i_adj_width ),
517                            Long2Fix( p_vout->output.i_width ) );
518         factor_y = FixDiv( Long2Fix( i_height ),
519                            Long2Fix( p_vout->output.i_height ) );
520
521         i_offset_x = (i_width - i_adj_width) / 2;
522     }
523     else
524     {
525         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
526                            p_vout->output.i_aspect;
527
528         factor_x = FixDiv( Long2Fix( i_width ),
529                            Long2Fix( p_vout->output.i_width ) );
530         factor_y = FixDiv( Long2Fix( i_adj_height ),
531                            Long2Fix( p_vout->output.i_height ) );
532
533         i_offset_y = (i_height - i_adj_height) / 2;
534     }
535     
536     SetIdentityMatrix( p_vout->p_sys->p_matrix );
537
538     ScaleMatrix( p_vout->p_sys->p_matrix,
539                  factor_x, factor_y,
540                  Long2Fix(0), Long2Fix(0) );
541                  
542     TranslateMatrix( p_vout->p_sys->p_matrix,
543                  Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
544 }
545
546 /*****************************************************************************
547  * QTCreateSequence: create a new sequence 
548  *****************************************************************************
549  * Returns 0 on success, 1 otherwise
550  *****************************************************************************/
551 static int QTCreateSequence( vout_thread_t *p_vout )
552 {
553     OSErr err;
554     ImageDescriptionPtr p_descr;
555
556     HLock( (Handle)p_vout->p_sys->h_img_descr );
557     p_descr = *p_vout->p_sys->h_img_descr;
558
559     p_descr->idSize = sizeof(ImageDescription);
560     p_descr->cType = p_vout->p_sys->i_codec;
561     p_descr->version = 2;
562     p_descr->revisionLevel = 0;
563     p_descr->vendor = 'mpla';
564     p_descr->width = p_vout->output.i_width;
565     p_descr->height = p_vout->output.i_height;
566     p_descr->hRes = Long2Fix(72);
567     p_descr->vRes = Long2Fix(72);
568     p_descr->spatialQuality = codecLosslessQuality;
569     p_descr->frameCount = 1;
570     p_descr->clutID = -1;
571     p_descr->dataSize = 0;
572     p_descr->depth = 24;
573
574     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
575
576     if( ( err = DecompressSequenceBeginS( 
577                               &p_vout->p_sys->i_seq,
578                               p_vout->p_sys->h_img_descr,
579                               NULL,
580                               (p_descr->width * p_descr->height * 16) / 8,
581                               p_vout->p_sys->p_qdport,
582                               NULL, NULL,
583                               p_vout->p_sys->p_matrix,
584                               srcCopy, NULL,
585                               codecFlagUseImageBuffer,
586                               codecLosslessQuality,
587                               bestSpeedCodec ) ) )
588     {
589         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
590         return( 1 );
591     }
592
593     return( 0 );
594 }
595
596 /*****************************************************************************
597  * QTDestroySequence: destroy sequence 
598  *****************************************************************************/
599 static void QTDestroySequence( vout_thread_t *p_vout )
600 {
601     CDSequenceEnd( p_vout->p_sys->i_seq );
602 }
603
604 /*****************************************************************************
605  * QTNewPicture: allocate a picture
606  *****************************************************************************
607  * Returns 0 on success, 1 otherwise
608  *****************************************************************************/
609 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
610 {
611     /* We know the chroma, allocate a buffer which will be used
612      * directly by the decoder */
613     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
614
615     if( p_pic->p_sys == NULL )
616     {
617         return( -1 );
618     }
619     
620     vout_InitPicture( VLC_OBJECT( p_vout), p_pic, p_vout->output.i_chroma,
621                       p_vout->output.i_width, p_vout->output.i_height,
622                       p_vout->output.i_aspect );
623
624     switch( p_vout->output.i_chroma )
625     {
626         case VLC_FOURCC('Y','U','Y','2'):
627             p_pic->p_sys->i_size = p_vout->output.i_width * p_vout->output.i_height * 2;
628
629             /* Allocate the memory buffer */
630             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
631                                           16, p_pic->p_sys->i_size );
632
633             p_pic->p[0].p_pixels = p_pic->p_data;
634             p_pic->p[0].i_lines = p_vout->output.i_height;
635             p_pic->p[0].i_pitch = p_vout->output.i_width * 2;
636             p_pic->p[0].i_pixel_pitch = 1;
637             p_pic->p[0].i_visible_pitch = p_vout->output.i_width * 2;
638             p_pic->i_planes = 1;
639
640             p_pic->p_sys->p_data = (void *)p_pic->p[0].p_pixels;
641
642             break;
643
644     default:
645         /* Unknown chroma, tell the guy to get lost */
646         free( p_pic->p_sys );
647         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
648                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
649         p_pic->i_planes = 0;
650         return( -1 );
651     }
652
653     return( 0 );
654 }
655
656 /*****************************************************************************
657  * QTFreePicture: destroy a picture allocated with QTNewPicture
658  *****************************************************************************/
659 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
660 {
661     switch( p_vout->output.i_chroma )
662     {
663         case VLC_FOURCC('I','4','2','0'):
664             free( p_pic->p_data_orig );
665             break;
666     }
667
668     free( p_pic->p_sys );
669 }
670
671 /*****************************************************************************
672  * VLCQTView implementation
673  *****************************************************************************/
674 @implementation VLCQTView
675
676 - (id) initWithVout:(vout_thread_t *)_p_vout
677 {
678     p_vout = _p_vout;
679     return [super init];
680 }
681
682 - (void)drawRect:(NSRect)rect
683 {
684     [[NSColor blackColor] set];
685     NSRectFill( rect );
686     [super drawRect: rect];
687
688     p_vout->i_changes |= VOUT_SIZE_CHANGE;
689 }
690
691 @end