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