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