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