]> git.sesse.net Git - vlc/blob - modules/gui/macosx/voutqt.m
* First string review of the OSX interface
[vlc] / modules / gui / macosx / voutqt.m
1 /*****************************************************************************
2  * vout.m: MacOS X video output module
3  *****************************************************************************
4  * Copyright (C) 2001-2004 the VideoLAN team
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, 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     VLCQTView * o_qtview;
61     VLCVoutView       * o_vout_view;
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     /* Mozilla plugin-related variables */
75     vlc_bool_t b_embedded;
76     Rect clipping_rect;
77     int portx, porty;
78 };
79
80 struct picture_sys_t
81 {
82     void *p_data;
83     unsigned int i_size;
84     
85     /* When using I420 output */
86     PlanarPixmapInfoYUV420 pixmap_i420;
87 };
88
89 /*****************************************************************************
90  * Local prototypes
91  *****************************************************************************/
92
93 static int  InitVideo           ( vout_thread_t * );
94 static void EndVideo            ( vout_thread_t * );
95 static int  ManageVideo         ( vout_thread_t * );
96 static void DisplayVideo        ( vout_thread_t *, picture_t * );
97 static int  ControlVideo        ( vout_thread_t *, int, va_list );
98
99 static int CoToggleFullscreen( vout_thread_t *p_vout );
100 static void QTScaleMatrix       ( vout_thread_t * );
101 static int  QTCreateSequence    ( vout_thread_t * );
102 static void QTDestroySequence   ( vout_thread_t * );
103 static int  QTNewPicture        ( vout_thread_t *, picture_t * );
104 static void QTFreePicture       ( vout_thread_t *, picture_t * );
105
106 /*****************************************************************************
107  * OpenVideo: allocates MacOS X video thread output method
108  *****************************************************************************
109  * This function allocates and initializes a MacOS X vout method.
110  *****************************************************************************/
111 int E_(OpenVideoQT) ( vlc_object_t *p_this )
112 {
113     vout_thread_t * p_vout = (vout_thread_t *)p_this;
114     OSErr err;
115     vlc_value_t value_drawable;
116
117     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
118     if( p_vout->p_sys == NULL )
119     {
120         msg_Err( p_vout, "out of memory" );
121         return( 1 );
122     }
123
124     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
125
126     p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
127
128     p_vout->pf_init = InitVideo;
129     p_vout->pf_end = EndVideo;
130     p_vout->pf_manage = ManageVideo;
131     p_vout->pf_render = NULL;
132     p_vout->pf_display = DisplayVideo;
133     p_vout->pf_control = ControlVideo;
134
135     /* Are we embedded?  If so, the drawable value will be a pointer to a
136      * CGrafPtr that we're expected to use */
137     var_Get( p_vout->p_vlc, "drawable", &value_drawable );
138     if( value_drawable.i_int != 0 )
139         p_vout->p_sys->b_embedded = VLC_TRUE;
140     else
141         p_vout->p_sys->b_embedded = VLC_FALSE;
142
143     p_vout->p_sys->b_altivec = p_vout->p_libvlc->i_cpu & CPU_CAPABILITY_ALTIVEC;
144     msg_Dbg( p_vout, "We do%s have Altivec", p_vout->p_sys->b_altivec ? "" : "n't" );
145     
146     /* Initialize QuickTime */
147     p_vout->p_sys->h_img_descr = 
148         (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
149     p_vout->p_sys->p_matrix =
150         (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
151
152     if( ( err = EnterMovies() ) != noErr )
153     {
154         msg_Err( p_vout, "QT initialization failed: EnterMovies failed: %d", err );
155         free( p_vout->p_sys->p_matrix );
156         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
157         free( p_vout->p_sys );
158         return VLC_EGENERIC;
159     }
160
161     /* Damn QT isn't thread safe. so keep a lock in the p_vlc object */
162     vlc_mutex_lock( &p_vout->p_vlc->quicktime_lock );
163
164     /* Can we find the right chroma ? */
165     if( p_vout->p_sys->b_altivec )
166     {
167         err = FindCodec( kYUVSPixelFormat, bestSpeedCodec,
168                         nil, &p_vout->p_sys->img_dc );
169     }
170     else
171     {
172         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
173                         nil, &p_vout->p_sys->img_dc );
174     }
175     vlc_mutex_unlock( &p_vout->p_vlc->quicktime_lock );
176     
177     if( err == noErr && p_vout->p_sys->img_dc != 0 )
178     {
179         if( p_vout->p_sys->b_altivec )
180         {
181             p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
182             p_vout->p_sys->i_codec = kYUVSPixelFormat;
183         }
184         else
185         {
186             p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
187             p_vout->p_sys->i_codec = kYUV420CodecType;
188         }
189     }
190     else
191     {
192         msg_Err( p_vout, "QT doesn't support any appropriate chroma" );
193     }
194
195     if( p_vout->p_sys->img_dc == 0 )
196     {
197         free( p_vout->p_sys->p_matrix );
198         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
199         free( p_vout->p_sys );
200         return VLC_EGENERIC;        
201     }
202
203 #define o_qtview p_vout->p_sys->o_qtview
204     o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
205     [o_qtview autorelease];
206
207     if( p_vout->p_sys->b_embedded )
208     {
209         /* Zero the clipping rectangle */
210         p_vout->p_sys->clipping_rect.left = 0;
211         p_vout->p_sys->clipping_rect.right = 0;
212         p_vout->p_sys->clipping_rect.top = 0;
213         p_vout->p_sys->clipping_rect.bottom = 0;
214     }
215     else
216     {
217         /* Spawn window */
218         p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
219                     subView: o_qtview frame: nil];
220         if( !p_vout->p_sys->o_vout_view )
221         {
222             return VLC_EGENERIC;
223         }
224     }
225
226     /* Retrieve the QuickDraw port */
227     if( p_vout->p_sys->b_embedded )
228     {
229         /* Don't need (nor want) to lock the focus, since otherwise we crash
230          * (presumably because we don't own the window, but I'm not sure
231          * if this is the exact reason)  -andrep */
232         p_vout->p_sys->p_qdport = [o_qtview qdPort];
233     }
234     else
235     {
236         [o_qtview lockFocus];
237         p_vout->p_sys->p_qdport = [o_qtview qdPort];
238         [o_qtview unlockFocus];
239     }
240 #undef o_qtview
241
242     return VLC_SUCCESS;
243 }
244
245 /*****************************************************************************
246  * CloseVideo: destroy video thread output method
247  *****************************************************************************/
248 void E_(CloseVideoQT) ( vlc_object_t *p_this )
249 {
250     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
251     vout_thread_t * p_vout = (vout_thread_t *)p_this;
252
253     if( !p_vout->p_sys->b_embedded )
254         [p_vout->p_sys->o_vout_view closeVout];
255
256     /* Clean Up Quicktime environment */
257     ExitMovies();
258     free( p_vout->p_sys->p_matrix );
259     DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
260
261     [o_pool release];
262     free( p_vout->p_sys );
263 }
264
265 /*****************************************************************************
266  * InitVideo: initialize video thread output method
267  *****************************************************************************/
268 static int InitVideo    ( vout_thread_t *p_vout )
269 {
270     picture_t *p_pic;
271     int i_index;
272
273     I_OUTPUTPICTURES = 0;
274
275     /* Initialize the output structure; we already found a codec,
276      * and the corresponding chroma we will be using. Since we can
277      * arbitrary scale, stick to the coordinates and aspect. */
278     p_vout->output.i_width  = p_vout->render.i_width;
279     p_vout->output.i_height = p_vout->render.i_height;
280     p_vout->output.i_aspect = p_vout->render.i_aspect;
281
282     /* If we are embedded (e.g. running as a Mozilla plugin), use the pointer
283      * stored in the "drawable" value as the CGrafPtr for the QuickDraw
284      * graphics port */
285     if( p_vout->p_sys->b_embedded )
286     {
287         vlc_value_t val;
288         var_Get( p_vout->p_vlc, "drawable", &val );
289         p_vout->p_sys->p_qdport = (CGrafPtr) val.i_int;
290     }
291
292     SetPort( p_vout->p_sys->p_qdport );
293     QTScaleMatrix( p_vout );
294
295     if( QTCreateSequence( p_vout ) )
296     {
297         msg_Err( p_vout, "unable to initialize QT: QTCreateSequence failed" );
298         return( 1 );
299     }
300
301     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
302     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
303     {
304         p_pic = NULL;
305
306         /* Find an empty picture slot */
307         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
308         {
309             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
310             {
311                 p_pic = p_vout->p_picture + i_index;
312                 break;
313             }
314         }
315
316         /* Allocate the picture */
317         if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
318         {
319             break;
320         }
321
322         p_pic->i_status = DESTROYED_PICTURE;
323         p_pic->i_type   = DIRECT_PICTURE;
324
325         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
326         I_OUTPUTPICTURES++;
327     }
328     return 0;
329 }
330
331 /*****************************************************************************
332  * EndVideo: terminate video thread output method
333  *****************************************************************************/
334 static void EndVideo( vout_thread_t *p_vout )
335 {
336     int i_index;
337
338     QTDestroySequence( p_vout );
339
340     /* Free the direct buffers we allocated */
341     for( i_index = I_OUTPUTPICTURES; i_index; )
342     {
343         i_index--;
344         QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
345     }
346 }
347
348 /*****************************************************************************
349  * ManageVideo: handle events
350  *****************************************************************************
351  * This function should be called regularly by video output thread. It manages
352  * console events. It returns a non null value on error.
353  *****************************************************************************/
354 static int ManageVideo( vout_thread_t *p_vout )
355 {
356     vlc_value_t val;
357     var_Get( p_vout->p_vlc, "drawableredraw", &val );
358
359     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
360     {
361         if( CoToggleFullscreen( p_vout ) )  
362         {
363             return( 1 );
364         }
365
366         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
367     }
368
369     if( p_vout->p_sys->b_embedded && val.i_int == 1 )
370     {
371         /* If we're embedded, the application is expected to indicate a
372          * window change (move/resize/etc) via the "drawableredraw" value.
373          * If that's the case, set the VOUT_SIZE_CHANGE flag so we do
374          * actually handle the window change. */
375         val.i_int = 0;
376         var_Set( p_vout->p_vlc, "drawableredraw", val );
377
378         p_vout->i_changes |= VOUT_SIZE_CHANGE;
379     }
380
381     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
382     {
383         QTScaleMatrix( p_vout );
384         SetDSequenceMatrix( p_vout->p_sys->i_seq,
385                             p_vout->p_sys->p_matrix );
386         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
387     }
388
389     [p_vout->p_sys->o_vout_view manage];
390
391     return( 0 );
392 }
393
394 /*****************************************************************************
395  * vout_Display: displays previously rendered output
396  *****************************************************************************
397  * This function sends the currently rendered image to the display.
398  *****************************************************************************/
399 static void DisplayVideo( vout_thread_t *p_vout, picture_t *p_pic )
400 {
401     OSErr err;
402     CodecFlags flags;
403
404     Rect saved_rect;
405     RgnHandle saved_clip;
406
407     saved_clip = NewRgn();
408
409     if( p_vout->p_sys->b_embedded )
410     {
411         /* In the Mozilla plugin, the browser also draws things in the windows.
412          * So, we have to update the origin and clipping rectangle for each
413          * picture.  FIXME: The vout should probably lock something ... */
414
415         /* Save the origin and clipping rectangle used by the host application
416          * (e.g. Mozilla), so we can restore it later */
417         GetPortBounds( p_vout->p_sys->p_qdport, &saved_rect );
418         GetClip( saved_clip );
419
420         /* The port gets unlocked at the end of this function */
421         LockPortBits( p_vout->p_sys->p_qdport );
422
423         /* Change the origin and clipping to the coordinates that the embedded
424          * window wants to draw at */
425         SetPort( p_vout->p_sys->p_qdport );
426         SetOrigin( p_vout->p_sys->portx , p_vout->p_sys->porty );
427         ClipRect( &p_vout->p_sys->clipping_rect );
428     }
429
430     if( ( err = DecompressSequenceFrameWhen(
431                     p_vout->p_sys->i_seq,
432                     p_pic->p_sys->p_data,
433                     p_pic->p_sys->i_size,
434                     codecFlagUseImageBuffer, &flags, NULL, NULL ) != noErr ) )
435     {
436         msg_Warn( p_vout, "QT failed to display the frame sequence: %d", err );
437     }
438     else
439     {
440         if( !p_vout->p_sys->b_embedded )
441             QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
442     }
443
444     if( p_vout->p_sys->b_embedded )
445     {
446         /* Restore the origin and clipping rectangle to the settings used
447          * by the host application */
448         SetOrigin( saved_rect.left, saved_rect.top );
449         SetClip( saved_clip );
450
451         UnlockPortBits( p_vout->p_sys->p_qdport );
452     }
453 }
454
455 /*****************************************************************************
456  * ControlVideo: control facility for the vout
457  *****************************************************************************/
458 static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
459 {
460     vlc_bool_t b_arg;
461
462     switch( i_query )
463     {
464         case VOUT_SET_STAY_ON_TOP:
465             b_arg = va_arg( args, vlc_bool_t );
466             [p_vout->p_sys->o_vout_view setOnTop: b_arg];
467             return VLC_SUCCESS;
468
469         case VOUT_CLOSE:
470         case VOUT_REPARENT:
471         default:
472             return vout_vaControlDefault( p_vout, i_query, args );
473     }
474 }
475
476 /*****************************************************************************
477  * CoToggleFullscreen: toggle fullscreen 
478  *****************************************************************************
479  * Returns 0 on success, 1 otherwise
480  *****************************************************************************/
481 static int CoToggleFullscreen( vout_thread_t *p_vout )
482 {
483     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
484
485     QTDestroySequence( p_vout );
486
487     if( !p_vout->b_fullscreen )
488     {
489         /* Save window size and position */
490         p_vout->p_sys->s_frame.size =
491             [p_vout->p_sys->o_vout_view frame].size;
492         p_vout->p_sys->s_frame.origin =
493             [[p_vout->p_sys->o_vout_view getWindow] frame].origin;
494         p_vout->p_sys->b_saved_frame = VLC_TRUE;
495     }
496     [p_vout->p_sys->o_vout_view closeVout];
497
498     p_vout->b_fullscreen = !p_vout->b_fullscreen;
499
500 #define o_qtview p_vout->p_sys->o_qtview
501     o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
502     [o_qtview autorelease];
503     
504     if( p_vout->p_sys->b_saved_frame )
505     {
506         p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
507             subView: o_qtview
508             frame: &p_vout->p_sys->s_frame];
509     }
510     else
511     {
512         p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
513             subView: o_qtview frame: nil];
514     }
515
516     /* Retrieve the QuickDraw port */
517     [o_qtview lockFocus];
518     p_vout->p_sys->p_qdport = [o_qtview qdPort];
519     [o_qtview unlockFocus];
520 #undef o_qtview
521
522     SetPort( p_vout->p_sys->p_qdport );
523     QTScaleMatrix( p_vout );
524
525     if( QTCreateSequence( p_vout ) )
526     {
527         msg_Err( p_vout, "unable to initialize QT: QTCreateSequence failed" );
528         return( 1 ); 
529     } 
530
531     [o_pool release];
532     return 0;
533 }
534
535 /*****************************************************************************
536  * QTScaleMatrix: scale matrix 
537  *****************************************************************************/
538 static void QTScaleMatrix( vout_thread_t *p_vout )
539 {
540     Rect s_rect;
541     vlc_value_t val;
542     unsigned int i_width, i_height;
543     Fixed factor_x, factor_y;
544     unsigned int i_offset_x = 0;
545     unsigned int i_offset_y = 0;
546
547     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
548
549     i_width = s_rect.right - s_rect.left;
550     i_height = s_rect.bottom - s_rect.top;
551
552     if( p_vout->p_sys->b_embedded )
553     {
554         /* Embedded video get their drawing region from the host application
555          * by the drawable values here.  Read those variables, and store them
556          * in the p_vout->p_sys structure so that other functions (such as
557          * DisplayVideo and ManageVideo) can use them later. */
558         vlc_value_t valt, vall, valb, valr, valx, valy, valw, valh,
559                     valportx, valporty;
560
561         var_Get( p_vout->p_vlc, "drawable", &val );
562         var_Get( p_vout->p_vlc, "drawablet", &valt );
563         var_Get( p_vout->p_vlc, "drawablel", &vall );
564         var_Get( p_vout->p_vlc, "drawableb", &valb );
565         var_Get( p_vout->p_vlc, "drawabler", &valr );
566         var_Get( p_vout->p_vlc, "drawablex", &valx );
567         var_Get( p_vout->p_vlc, "drawabley", &valy );
568         var_Get( p_vout->p_vlc, "drawablew", &valw );
569         var_Get( p_vout->p_vlc, "drawableh", &valh );
570         var_Get( p_vout->p_vlc, "drawableportx", &valportx );
571         var_Get( p_vout->p_vlc, "drawableporty", &valporty );
572
573         p_vout->p_sys->portx = valportx.i_int;
574         p_vout->p_sys->porty = valporty.i_int;
575         p_vout->p_sys->p_qdport = (CGrafPtr) val.i_int;
576         i_width = valw.i_int;
577         i_height = valh.i_int;
578
579         p_vout->p_sys->clipping_rect.top = 0;
580         p_vout->p_sys->clipping_rect.left = 0;
581         p_vout->p_sys->clipping_rect.bottom = valb.i_int - valt.i_int;
582         p_vout->p_sys->clipping_rect.right = valr.i_int - vall.i_int;
583     }
584
585     var_Get( p_vout, "macosx-stretch", &val );
586     if( val.b_bool )
587     {
588         factor_x = FixDiv( Long2Fix( i_width ),
589                            Long2Fix( p_vout->output.i_width ) );
590         factor_y = FixDiv( Long2Fix( i_height ),
591                            Long2Fix( p_vout->output.i_height ) );
592
593     }
594     else if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
595     {
596         int i_adj_width = i_height * p_vout->output.i_aspect /
597                           VOUT_ASPECT_FACTOR;
598
599         factor_x = FixDiv( Long2Fix( i_adj_width ),
600                            Long2Fix( p_vout->output.i_width ) );
601         factor_y = FixDiv( Long2Fix( i_height ),
602                            Long2Fix( p_vout->output.i_height ) );
603
604         i_offset_x = (i_width - i_adj_width) / 2;
605     }
606     else
607     {
608         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
609                            p_vout->output.i_aspect;
610
611         factor_x = FixDiv( Long2Fix( i_width ),
612                            Long2Fix( p_vout->output.i_width ) );
613         factor_y = FixDiv( Long2Fix( i_adj_height ),
614                            Long2Fix( p_vout->output.i_height ) );
615
616         i_offset_y = (i_height - i_adj_height) / 2;
617     }
618
619     SetIdentityMatrix( p_vout->p_sys->p_matrix );
620
621     ScaleMatrix( p_vout->p_sys->p_matrix,
622                  factor_x, factor_y,
623                  Long2Fix(0), Long2Fix(0) );
624
625     TranslateMatrix( p_vout->p_sys->p_matrix,
626                  Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
627 }
628
629 /*****************************************************************************
630  * QTCreateSequence: create a new sequence 
631  *****************************************************************************
632  * Returns 0 on success, 1 otherwise
633  *****************************************************************************/
634 static int QTCreateSequence( vout_thread_t *p_vout )
635 {
636     OSErr err;
637     ImageDescriptionPtr p_descr;
638
639     HLock( (Handle)p_vout->p_sys->h_img_descr );
640     p_descr = *p_vout->p_sys->h_img_descr;
641
642     p_descr->idSize = sizeof(ImageDescription);
643     p_descr->cType = p_vout->p_sys->i_codec;
644     p_descr->version = 2;
645     p_descr->revisionLevel = 0;
646     p_descr->vendor = 'mpla';
647     p_descr->width = p_vout->output.i_width;
648     p_descr->height = p_vout->output.i_height;
649     p_descr->hRes = Long2Fix(72);
650     p_descr->vRes = Long2Fix(72);
651     p_descr->spatialQuality = codecLosslessQuality;
652     p_descr->frameCount = 1;
653     p_descr->clutID = -1;
654     p_descr->dataSize = 0;
655     p_descr->depth = 24;
656
657     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
658
659     if( ( err = DecompressSequenceBeginS( 
660                               &p_vout->p_sys->i_seq,
661                               p_vout->p_sys->h_img_descr,
662                               NULL,
663                               (p_descr->width * p_descr->height * 16) / 8,
664                               p_vout->p_sys->p_qdport,
665                               NULL, NULL,
666                               p_vout->p_sys->p_matrix,
667                               srcCopy, NULL,
668                               codecFlagUseImageBuffer,
669                               codecLosslessQuality,
670                               bestSpeedCodec ) ) )
671     {
672         msg_Err( p_vout, "Failed to initialize QT: DecompressSequenceBeginS failed: %d", err );
673         return( 1 );
674     }
675
676     return( 0 );
677 }
678
679 /*****************************************************************************
680  * QTDestroySequence: destroy sequence 
681  *****************************************************************************/
682 static void QTDestroySequence( vout_thread_t *p_vout )
683 {
684     CDSequenceEnd( p_vout->p_sys->i_seq );
685 }
686
687 /*****************************************************************************
688  * QTNewPicture: allocate a picture
689  *****************************************************************************
690  * Returns 0 on success, 1 otherwise
691  *****************************************************************************/
692 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
693 {
694     /* We know the chroma, allocate a buffer which will be used
695      * directly by the decoder */
696     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
697
698     if( p_pic->p_sys == NULL )
699     {
700         return( -1 );
701     }
702
703     vout_InitPicture( VLC_OBJECT( p_vout), p_pic, p_vout->output.i_chroma,
704                       p_vout->output.i_width, p_vout->output.i_height,
705                       p_vout->output.i_aspect );
706
707     switch( p_vout->output.i_chroma )
708     {
709         case VLC_FOURCC('Y','U','Y','2'):
710             p_pic->p_sys->i_size = p_vout->output.i_width * p_vout->output.i_height * 2;
711
712             /* Allocate the memory buffer */
713             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
714                                           16, p_pic->p_sys->i_size );
715
716             p_pic->p[0].p_pixels = p_pic->p_data;
717             p_pic->p[0].i_lines = p_vout->output.i_height;
718             p_pic->p[0].i_visible_lines = p_vout->output.i_height;
719             p_pic->p[0].i_pitch = p_vout->output.i_width * 2;
720             p_pic->p[0].i_pixel_pitch = 1;
721             p_pic->p[0].i_visible_pitch = p_vout->output.i_width * 2;
722             p_pic->i_planes = 1;
723
724             p_pic->p_sys->p_data = (void *)p_pic->p[0].p_pixels;
725
726             break;
727             
728         case VLC_FOURCC('I','4','2','0'):
729             p_pic->p_sys->p_data = (void *)&p_pic->p_sys->pixmap_i420;
730             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
731             
732             /* Allocate the memory buffer */
733             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
734                                           16, p_vout->output.i_width * p_vout->output.i_height * 3 / 2 );
735
736             /* Y buffer */
737             p_pic->Y_PIXELS = p_pic->p_data; 
738             p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
739             p_pic->p[Y_PLANE].i_visible_lines = p_vout->output.i_height;
740             p_pic->p[Y_PLANE].i_pitch = p_vout->output.i_width;
741             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
742             p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width;
743
744             /* U buffer */
745             p_pic->U_PIXELS = p_pic->Y_PIXELS + p_vout->output.i_height * p_vout->output.i_width;
746             p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
747             p_pic->p[U_PLANE].i_visible_lines = p_vout->output.i_height / 2;
748             p_pic->p[U_PLANE].i_pitch = p_vout->output.i_width / 2;
749             p_pic->p[U_PLANE].i_pixel_pitch = 1;
750             p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2;
751
752             /* V buffer */
753             p_pic->V_PIXELS = p_pic->U_PIXELS + p_vout->output.i_height * p_vout->output.i_width / 4;
754             p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
755             p_pic->p[V_PLANE].i_visible_lines = p_vout->output.i_height / 2;
756             p_pic->p[V_PLANE].i_pitch = p_vout->output.i_width / 2;
757             p_pic->p[V_PLANE].i_pixel_pitch = 1;
758             p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2;
759
760             /* We allocated 3 planes */
761             p_pic->i_planes = 3;
762
763 #define P p_pic->p_sys->pixmap_i420
764             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
765                                        - p_pic->p_sys->p_data;
766             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
767                                         - p_pic->p_sys->p_data;
768             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
769                                         - p_pic->p_sys->p_data;
770
771             P.componentInfoY.rowBytes = p_vout->output.i_width;
772             P.componentInfoCb.rowBytes = p_vout->output.i_width / 2;
773             P.componentInfoCr.rowBytes = p_vout->output.i_width / 2;
774 #undef P
775             break;
776         
777         default:
778             /* Unknown chroma, tell the guy to get lost */
779             free( p_pic->p_sys );
780             msg_Err( p_vout, "Unknown chroma format 0x%.8x (%4.4s)",
781                      p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
782             p_pic->i_planes = 0;
783             return( -1 );
784     }
785
786     return( 0 );
787 }
788
789 /*****************************************************************************
790  * QTFreePicture: destroy a picture allocated with QTNewPicture
791  *****************************************************************************/
792 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
793 {
794     switch( p_vout->output.i_chroma )
795     {
796         case VLC_FOURCC('I','4','2','0'):
797             free( p_pic->p_data_orig );
798             break;
799     }
800
801     free( p_pic->p_sys );
802 }
803
804 /*****************************************************************************
805  * VLCQTView implementation
806  *****************************************************************************/
807 @implementation VLCQTView
808
809 - (id) initWithVout:(vout_thread_t *)_p_vout
810 {
811     p_vout = _p_vout;
812     return [super init];
813 }
814
815 - (void)drawRect:(NSRect)rect
816 {
817     [[NSColor blackColor] set];
818     NSRectFill( rect );
819     [super drawRect: rect];
820
821     p_vout->i_changes |= VOUT_SIZE_CHANGE;
822 }
823
824 @end