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