]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutqt.m
* Update copyright to 2005 where appropriate.
[vlc] / modules / gui / macosx / voutqt.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2004 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     vlc_bool_t  b_altivec;
65     NSRect      s_frame;
66
67     CodecType i_codec;
68     CGrafPtr p_qdport;
69     ImageSequence i_seq;
70     MatrixRecordPtr p_matrix;
71     DecompressorComponent img_dc;
72     ImageDescriptionHandle h_img_descr;
73 };
74
75 struct picture_sys_t
76 {
77     void *p_data;
78     unsigned int i_size;
79     
80     /* When using I420 output */
81     PlanarPixmapInfoYUV420 pixmap_i420;
82 };
83
84 /*****************************************************************************
85  * Local prototypes
86  *****************************************************************************/
87
88 static int  InitVideo           ( vout_thread_t * );
89 static void EndVideo            ( vout_thread_t * );
90 static int  ManageVideo         ( vout_thread_t * );
91 static void DisplayVideo        ( vout_thread_t *, picture_t * );
92 static int  ControlVideo        ( vout_thread_t *, int, va_list );
93
94 static int CoToggleFullscreen( vout_thread_t *p_vout );
95 static void QTScaleMatrix       ( vout_thread_t * );
96 static int  QTCreateSequence    ( vout_thread_t * );
97 static void QTDestroySequence   ( vout_thread_t * );
98 static int  QTNewPicture        ( vout_thread_t *, picture_t * );
99 static void QTFreePicture       ( vout_thread_t *, picture_t * );
100
101 /*****************************************************************************
102  * OpenVideo: allocates MacOS X video thread output method
103  *****************************************************************************
104  * This function allocates and initializes a MacOS X vout method.
105  *****************************************************************************/
106 int E_(OpenVideoQT) ( vlc_object_t *p_this )
107 {   
108     vout_thread_t * p_vout = (vout_thread_t *)p_this;
109     OSErr err;
110
111     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
112     if( p_vout->p_sys == NULL )
113     {
114         msg_Err( p_vout, "out of memory" );
115         return( 1 );
116     }
117
118     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
119
120     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
121
122     p_vout->pf_init = InitVideo;
123     p_vout->pf_end = EndVideo;
124     p_vout->pf_manage = ManageVideo;
125     p_vout->pf_render = NULL;
126     p_vout->pf_display = DisplayVideo;
127     p_vout->pf_control = ControlVideo;
128
129     /* Spawn window */
130     p_vout->p_sys->o_window =
131         [[VLCWindow alloc] initWithVout: p_vout frame: nil];
132     if( !p_vout->p_sys->o_window )
133     {
134         return VLC_EGENERIC;
135     }
136
137     p_vout->p_sys->b_altivec = p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_ALTIVEC;
138     msg_Dbg( p_vout, "We do%s have Altivec", p_vout->p_sys->b_altivec ? "" : "n't" );
139     
140     /* Initialize QuickTime */
141     p_vout->p_sys->h_img_descr = 
142         (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
143     p_vout->p_sys->p_matrix =
144         (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
145
146     if( ( err = EnterMovies() ) != noErr )
147     {
148         msg_Err( p_vout, "EnterMovies failed: %d", err );
149         free( p_vout->p_sys->p_matrix );
150         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
151         free( p_vout->p_sys );
152         return VLC_EGENERIC;
153     }
154
155     /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
156     vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
157
158     /* Can we find the right chroma ? */
159     if( p_vout->p_sys->b_altivec )
160     {
161         err = FindCodec( kYUVSPixelFormat, bestSpeedCodec,
162                         nil, &p_vout->p_sys->img_dc );
163     }
164     else
165     {
166         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
167                         nil, &p_vout->p_sys->img_dc );
168     }
169     vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
170     
171     if( err == noErr && p_vout->p_sys->img_dc != 0 )
172     {
173         if( p_vout->p_sys->b_altivec )
174         {
175             p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
176             p_vout->p_sys->i_codec = kYUVSPixelFormat;
177         }
178         else
179         {
180             p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
181             p_vout->p_sys->i_codec = kYUV420CodecType;
182         }
183     }
184     else
185     {
186         msg_Err( p_vout, "failed to find an appropriate codec" );
187     }
188
189     if( p_vout->p_sys->img_dc == 0 )
190     {
191         free( p_vout->p_sys->p_matrix );
192         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
193         free( p_vout->p_sys );
194         return VLC_EGENERIC;        
195     }
196
197 #define o_qtview p_vout->p_sys->o_qtview
198     o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
199     [p_vout->p_sys->o_window setContentView: o_qtview];
200     [o_qtview autorelease];
201
202     /* Retrieve the QuickDraw port */
203     [o_qtview lockFocus];
204     p_vout->p_sys->p_qdport = [o_qtview qdPort];
205     [o_qtview unlockFocus];
206 #undef o_qtview
207
208     return VLC_SUCCESS;
209 }
210
211 /*****************************************************************************
212  * CloseVideo: destroy video thread output method
213  *****************************************************************************/
214 void E_(CloseVideoQT) ( vlc_object_t *p_this )
215 {
216     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init]; 
217     vout_thread_t * p_vout = (vout_thread_t *)p_this;
218
219     [p_vout->p_sys->o_window close];
220
221     /* Clean Up Quicktime environment */
222     ExitMovies();
223     free( p_vout->p_sys->p_matrix );
224     DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
225
226     [o_pool release];
227     free( p_vout->p_sys );
228 }
229
230 /*****************************************************************************
231  * InitVideo: initialize video thread output method
232  *****************************************************************************/
233 static int InitVideo    ( vout_thread_t *p_vout )
234 {
235     picture_t *p_pic;
236     int i_index;
237
238     I_OUTPUTPICTURES = 0;
239
240     /* Initialize the output structure; we already found a codec,
241      * and the corresponding chroma we will be using. Since we can
242      * arbitrary scale, stick to the coordinates and aspect. */
243     p_vout->output.i_width  = p_vout->render.i_width;
244     p_vout->output.i_height = p_vout->render.i_height;
245     p_vout->output.i_aspect = p_vout->render.i_aspect;
246
247     SetPort( p_vout->p_sys->p_qdport );
248     QTScaleMatrix( p_vout );
249
250     if( QTCreateSequence( p_vout ) )
251     {
252         msg_Err( p_vout, "unable to create sequence" );
253         return( 1 );
254     }
255
256     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
257     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
258     {
259         p_pic = NULL;
260
261         /* Find an empty picture slot */
262         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
263         {
264             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
265             {
266                 p_pic = p_vout->p_picture + i_index;
267                 break;
268             }
269         }
270
271         /* Allocate the picture */
272         if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
273         {
274             break;
275         }
276
277         p_pic->i_status = DESTROYED_PICTURE;
278         p_pic->i_type   = DIRECT_PICTURE;
279
280         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
281         I_OUTPUTPICTURES++;
282     }
283     return 0;
284 }
285
286 /*****************************************************************************
287  * EndVideo: terminate video thread output method
288  *****************************************************************************/
289 static void EndVideo( vout_thread_t *p_vout )
290 {
291     int i_index;
292
293     QTDestroySequence( p_vout );
294
295     /* Free the direct buffers we allocated */
296     for( i_index = I_OUTPUTPICTURES; i_index; )
297     {
298         i_index--;
299         QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
300     }
301 }
302
303 /*****************************************************************************
304  * ManageVideo: handle events
305  *****************************************************************************
306  * This function should be called regularly by video output thread. It manages
307  * console events. It returns a non null value on error.
308  *****************************************************************************/
309 static int ManageVideo( vout_thread_t *p_vout )
310 {
311     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
312     {
313         if( CoToggleFullscreen( p_vout ) )  
314         {
315             return( 1 );
316         }
317
318         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
319     }
320
321     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
322     {
323         QTScaleMatrix( p_vout );
324         SetDSequenceMatrix( p_vout->p_sys->i_seq, 
325                             p_vout->p_sys->p_matrix );
326  
327         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
328     }
329
330     [p_vout->p_sys->o_window manage];
331     
332     return( 0 );
333 }
334
335 /*****************************************************************************
336  * vout_Display: displays previously rendered output
337  *****************************************************************************
338  * This function sends the currently rendered image to the display.
339  *****************************************************************************/
340 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
341 {
342     OSErr err;
343     CodecFlags flags;
344
345     if( ( err = DecompressSequenceFrameWhen( 
346                     p_vout->p_sys->i_seq,
347                     p_pic->p_sys->p_data,
348                     p_pic->p_sys->i_size,                    
349                     codecFlagUseImageBuffer, &flags, NULL, NULL ) != noErr ) )
350     {
351         msg_Warn( p_vout, "DecompressSequenceFrameWhen failed: %d", err );
352     }
353     else
354     {
355         QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
356     }
357 }
358
359 /*****************************************************************************
360  * ControlVideo: control facility for the vout
361  *****************************************************************************/
362 static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
363 {
364     vlc_bool_t b_arg;
365
366     switch( i_query )
367     {
368         case VOUT_SET_STAY_ON_TOP:
369             b_arg = va_arg( args, vlc_bool_t );
370             [p_vout->p_sys->o_window setOnTop: b_arg];
371             return VLC_SUCCESS;
372
373         case VOUT_CLOSE:
374         case VOUT_REPARENT:
375         default:
376             return vout_vaControlDefault( p_vout, i_query, args );
377     }
378 }
379
380 /*****************************************************************************
381  * CoToggleFullscreen: toggle fullscreen 
382  *****************************************************************************
383  * Returns 0 on success, 1 otherwise
384  *****************************************************************************/
385 static int CoToggleFullscreen( vout_thread_t *p_vout )
386 {
387     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
388
389     QTDestroySequence( p_vout );
390
391     if( !p_vout->b_fullscreen )
392     {
393         /* Save window size and position */
394         p_vout->p_sys->s_frame.size =
395             [[p_vout->p_sys->o_window contentView] frame].size;
396         p_vout->p_sys->s_frame.origin =
397             [p_vout->p_sys->o_window frame].origin;
398         p_vout->p_sys->b_saved_frame = VLC_TRUE;
399     }
400     [p_vout->p_sys->o_window close];
401
402     p_vout->b_fullscreen = !p_vout->b_fullscreen;
403
404     if( p_vout->p_sys->b_saved_frame )
405     {
406         p_vout->p_sys->o_window = [[VLCWindow alloc]
407             initWithVout: p_vout frame: &p_vout->p_sys->s_frame];
408     }
409     else
410     {
411         p_vout->p_sys->o_window = [[VLCWindow alloc]
412             initWithVout: p_vout frame: nil];
413     }
414
415 #define o_qtview p_vout->p_sys->o_qtview
416     o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
417     [p_vout->p_sys->o_window setContentView: o_qtview];
418     [o_qtview autorelease];
419
420     /* Retrieve the QuickDraw port */
421     [o_qtview lockFocus];
422     p_vout->p_sys->p_qdport = [o_qtview qdPort];
423     [o_qtview unlockFocus];
424 #undef o_qtview
425
426     SetPort( p_vout->p_sys->p_qdport );
427     QTScaleMatrix( p_vout );
428
429     if( QTCreateSequence( p_vout ) )
430     {
431         msg_Err( p_vout, "unable to create sequence" );
432         return( 1 ); 
433     } 
434
435     [o_pool release];
436     return 0;
437 }
438
439 /*****************************************************************************
440  * QTScaleMatrix: scale matrix 
441  *****************************************************************************/
442 static void QTScaleMatrix( vout_thread_t *p_vout )
443 {
444     Rect s_rect;
445     vlc_value_t val;
446     unsigned int i_width, i_height;
447     Fixed factor_x, factor_y;
448     unsigned int i_offset_x = 0;
449     unsigned int i_offset_y = 0;
450
451     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
452
453     i_width = s_rect.right - s_rect.left;
454     i_height = s_rect.bottom - s_rect.top;
455
456     var_Get( p_vout, "macosx-stretch", &val );
457     if( val.b_bool )
458     {
459         factor_x = FixDiv( Long2Fix( i_width ),
460                            Long2Fix( p_vout->output.i_width ) );
461         factor_y = FixDiv( Long2Fix( i_height ),
462                            Long2Fix( p_vout->output.i_height ) );
463                            
464     }
465     else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
466     {
467         int i_adj_width = i_height * p_vout->output.i_aspect /
468                           VOUT_ASPECT_FACTOR;
469
470         factor_x = FixDiv( Long2Fix( i_adj_width ),
471                            Long2Fix( p_vout->output.i_width ) );
472         factor_y = FixDiv( Long2Fix( i_height ),
473                            Long2Fix( p_vout->output.i_height ) );
474
475         i_offset_x = (i_width - i_adj_width) / 2;
476     }
477     else
478     {
479         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
480                            p_vout->output.i_aspect;
481
482         factor_x = FixDiv( Long2Fix( i_width ),
483                            Long2Fix( p_vout->output.i_width ) );
484         factor_y = FixDiv( Long2Fix( i_adj_height ),
485                            Long2Fix( p_vout->output.i_height ) );
486
487         i_offset_y = (i_height - i_adj_height) / 2;
488     }
489     
490     SetIdentityMatrix( p_vout->p_sys->p_matrix );
491
492     ScaleMatrix( p_vout->p_sys->p_matrix,
493                  factor_x, factor_y,
494                  Long2Fix(0), Long2Fix(0) );
495                  
496     TranslateMatrix( p_vout->p_sys->p_matrix,
497                  Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
498 }
499
500 /*****************************************************************************
501  * QTCreateSequence: create a new sequence 
502  *****************************************************************************
503  * Returns 0 on success, 1 otherwise
504  *****************************************************************************/
505 static int QTCreateSequence( vout_thread_t *p_vout )
506 {
507     OSErr err;
508     ImageDescriptionPtr p_descr;
509
510     HLock( (Handle)p_vout->p_sys->h_img_descr );
511     p_descr = *p_vout->p_sys->h_img_descr;
512
513     p_descr->idSize = sizeof(ImageDescription);
514     p_descr->cType = p_vout->p_sys->i_codec;
515     p_descr->version = 2;
516     p_descr->revisionLevel = 0;
517     p_descr->vendor = 'mpla';
518     p_descr->width = p_vout->output.i_width;
519     p_descr->height = p_vout->output.i_height;
520     p_descr->hRes = Long2Fix(72);
521     p_descr->vRes = Long2Fix(72);
522     p_descr->spatialQuality = codecLosslessQuality;
523     p_descr->frameCount = 1;
524     p_descr->clutID = -1;
525     p_descr->dataSize = 0;
526     p_descr->depth = 24;
527
528     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
529
530     if( ( err = DecompressSequenceBeginS( 
531                               &p_vout->p_sys->i_seq,
532                               p_vout->p_sys->h_img_descr,
533                               NULL,
534                               (p_descr->width * p_descr->height * 16) / 8,
535                               p_vout->p_sys->p_qdport,
536                               NULL, NULL,
537                               p_vout->p_sys->p_matrix,
538                               srcCopy, NULL,
539                               codecFlagUseImageBuffer,
540                               codecLosslessQuality,
541                               bestSpeedCodec ) ) )
542     {
543         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
544         return( 1 );
545     }
546
547     return( 0 );
548 }
549
550 /*****************************************************************************
551  * QTDestroySequence: destroy sequence 
552  *****************************************************************************/
553 static void QTDestroySequence( vout_thread_t *p_vout )
554 {
555     CDSequenceEnd( p_vout->p_sys->i_seq );
556 }
557
558 /*****************************************************************************
559  * QTNewPicture: allocate a picture
560  *****************************************************************************
561  * Returns 0 on success, 1 otherwise
562  *****************************************************************************/
563 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
564 {
565     /* We know the chroma, allocate a buffer which will be used
566      * directly by the decoder */
567     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
568
569     if( p_pic->p_sys == NULL )
570     {
571         return( -1 );
572     }
573
574     vout_InitPicture( VLC_OBJECT( p_vout), p_pic, p_vout->output.i_chroma,
575                       p_vout->output.i_width, p_vout->output.i_height,
576                       p_vout->output.i_aspect );
577
578     switch( p_vout->output.i_chroma )
579     {
580         case VLC_FOURCC('Y','U','Y','2'):
581             p_pic->p_sys->i_size = p_vout->output.i_width * p_vout->output.i_height * 2;
582
583             /* Allocate the memory buffer */
584             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
585                                           16, p_pic->p_sys->i_size );
586
587             p_pic->p[0].p_pixels = p_pic->p_data;
588             p_pic->p[0].i_lines = p_vout->output.i_height;
589             p_pic->p[0].i_visible_lines = p_vout->output.i_height;
590             p_pic->p[0].i_pitch = p_vout->output.i_width * 2;
591             p_pic->p[0].i_pixel_pitch = 1;
592             p_pic->p[0].i_visible_pitch = p_vout->output.i_width * 2;
593             p_pic->i_planes = 1;
594
595             p_pic->p_sys->p_data = (void *)p_pic->p[0].p_pixels;
596
597             break;
598             
599         case VLC_FOURCC('I','4','2','0'):
600             p_pic->p_sys->p_data = (void *)&p_pic->p_sys->pixmap_i420;
601             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
602             
603             /* Allocate the memory buffer */
604             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
605                                           16, p_vout->output.i_width * p_vout->output.i_height * 3 / 2 );
606
607             /* Y buffer */
608             p_pic->Y_PIXELS = p_pic->p_data; 
609             p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
610             p_pic->p[Y_PLANE].i_visible_lines = p_vout->output.i_height;
611             p_pic->p[Y_PLANE].i_pitch = p_vout->output.i_width;
612             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
613             p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width;
614
615             /* U buffer */
616             p_pic->U_PIXELS = p_pic->Y_PIXELS + p_vout->output.i_height * p_vout->output.i_width;
617             p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
618             p_pic->p[U_PLANE].i_visible_lines = p_vout->output.i_height / 2;
619             p_pic->p[U_PLANE].i_pitch = p_vout->output.i_width / 2;
620             p_pic->p[U_PLANE].i_pixel_pitch = 1;
621             p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2;
622
623             /* V buffer */
624             p_pic->V_PIXELS = p_pic->U_PIXELS + p_vout->output.i_height * p_vout->output.i_width / 4;
625             p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
626             p_pic->p[V_PLANE].i_visible_lines = p_vout->output.i_height / 2;
627             p_pic->p[V_PLANE].i_pitch = p_vout->output.i_width / 2;
628             p_pic->p[V_PLANE].i_pixel_pitch = 1;
629             p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2;
630
631             /* We allocated 3 planes */
632             p_pic->i_planes = 3;
633
634 #define P p_pic->p_sys->pixmap_i420
635             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
636                                        - p_pic->p_sys->p_data;
637             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
638                                         - p_pic->p_sys->p_data;
639             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
640                                         - p_pic->p_sys->p_data;
641
642             P.componentInfoY.rowBytes = p_vout->output.i_width;
643             P.componentInfoCb.rowBytes = p_vout->output.i_width / 2;
644             P.componentInfoCr.rowBytes = p_vout->output.i_width / 2;
645 #undef P
646             break;
647         
648         default:
649             /* Unknown chroma, tell the guy to get lost */
650             free( p_pic->p_sys );
651             msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
652                      p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
653             p_pic->i_planes = 0;
654             return( -1 );
655     }
656
657     return( 0 );
658 }
659
660 /*****************************************************************************
661  * QTFreePicture: destroy a picture allocated with QTNewPicture
662  *****************************************************************************/
663 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
664 {
665     switch( p_vout->output.i_chroma )
666     {
667         case VLC_FOURCC('I','4','2','0'):
668             free( p_pic->p_data_orig );
669             break;
670     }
671
672     free( p_pic->p_sys );
673 }
674
675 /*****************************************************************************
676  * VLCQTView implementation
677  *****************************************************************************/
678 @implementation VLCQTView
679
680 - (id) initWithVout:(vout_thread_t *)_p_vout
681 {
682     p_vout = _p_vout;
683     return [super init];
684 }
685
686 - (void)drawRect:(NSRect)rect
687 {
688     [[NSColor blackColor] set];
689     NSRectFill( rect );
690     [super drawRect: rect];
691
692     p_vout->i_changes |= VOUT_SIZE_CHANGE;
693 }
694
695 @end