]> git.sesse.net Git - vlc/blob - plugins/macosx/vout_macosx.m
7c03d5c344d1d088b51113270a580e717b7092a8
[vlc] / plugins / macosx / vout_macosx.m
1 /*****************************************************************************
2  * vout_macosx.m: MacOS X video output plugin
3  *****************************************************************************
4  * Copyright (C) 2001, 2002 VideoLAN
5  * $Id: vout_macosx.m,v 1.12 2002/07/16 20:41:48 jlj Exp $
6  *
7  * Authors: Colin Delacroix <colin@zoy.org>
8  *          Florian G. Pflug <fgp@phlo.org>
9  *          Jon Lech Johansen <jon-vl@nanocrew.net>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <stdlib.h>                                                /* free() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/vout.h>
35 #include <vlc/aout.h>
36 #include <vlc/intf.h>
37
38 #include <Cocoa/Cocoa.h>
39 #include <QuickTime/QuickTime.h>
40
41 #include "intf_macosx.h"
42 #include "vout_macosx.h"
43
44 #define QT_MAX_DIRECTBUFFERS 10
45
46 struct picture_sys_s
47 {
48     void *p_info;
49     unsigned int i_size;
50
51     /* When using I420 output */
52     PlanarPixmapInfoYUV420 pixmap_i420;
53 };
54
55 /*****************************************************************************
56  * Local prototypes
57  *****************************************************************************/
58 static int  vout_Create    ( vout_thread_t * );
59 static int  vout_Init      ( vout_thread_t * );
60 static void vout_End       ( vout_thread_t * );
61 static void vout_Destroy   ( vout_thread_t * );
62 static int  vout_Manage    ( vout_thread_t * );
63 static void vout_Render    ( vout_thread_t *, picture_t * );
64 static void vout_Display   ( vout_thread_t *, picture_t * );
65
66 static int  CoSendRequest      ( vout_thread_t *, long );
67 static int  CoCreateWindow     ( vout_thread_t * );
68 static int  CoDestroyWindow    ( vout_thread_t * );
69 static int  CoToggleFullscreen ( vout_thread_t * );
70
71 static void QTScaleMatrix      ( vout_thread_t * );
72 static int  QTCreateSequence   ( vout_thread_t * );
73 static void QTDestroySequence  ( vout_thread_t * );
74 static int  QTNewPicture       ( vout_thread_t *, picture_t * );
75 static void QTFreePicture      ( vout_thread_t *, picture_t * );
76
77 /*****************************************************************************
78  * Functions exported as capabilities. They are declared as static so that
79  * we don't pollute the namespace too much.
80  *****************************************************************************/
81 void _M( vout_getfunctions )( function_list_t * p_function_list )
82 {
83     p_function_list->functions.vout.pf_create     = vout_Create;
84     p_function_list->functions.vout.pf_init       = vout_Init;
85     p_function_list->functions.vout.pf_end        = vout_End;
86     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
87     p_function_list->functions.vout.pf_manage     = vout_Manage;
88     p_function_list->functions.vout.pf_render     = vout_Render;
89     p_function_list->functions.vout.pf_display    = vout_Display;
90 }
91
92 /*****************************************************************************
93  * vout_Create: allocates MacOS X video thread output method
94  *****************************************************************************
95  * This function allocates and initializes a MacOS X vout method.
96  *****************************************************************************/
97 static int vout_Create( vout_thread_t *p_vout )
98 {
99     OSErr err;
100
101     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
102     if( p_vout->p_sys == NULL )
103     {
104         msg_Err( p_vout, "out of memory" );
105         return( 1 );
106     }
107
108     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
109
110     p_vout->p_sys->p_intf = vlc_object_find( p_vout, VLC_OBJECT_INTF, 
111                                              FIND_ANYWHERE );
112     if( p_vout->p_sys->p_intf == NULL )
113     {
114         msg_Err( p_vout, "no interface present" );
115         free( p_vout->p_sys );
116         return( 1 );
117     }
118
119     if( p_vout->p_sys->p_intf->p_module == NULL || 
120         strcmp( p_vout->p_sys->p_intf->p_module->psz_object_name, 
121                 MODULE_STRING ) != 0 )
122     {
123         msg_Err( p_vout, "MacOS X interface module required" );
124         vlc_object_release( p_vout->p_sys->p_intf );
125         free( p_vout->p_sys );
126         return( 1 );
127     }
128
129     p_vout->p_sys->h_img_descr = 
130         (ImageDescriptionHandle)NewHandleClear( sizeof(ImageDescription) );
131     p_vout->p_sys->p_matrix = (MatrixRecordPtr)malloc( sizeof(MatrixRecord) );
132
133     p_vout->p_sys->b_mouse_pointer_visible = 1;
134
135     /* set window size */
136     p_vout->p_sys->s_rect.size.width = p_vout->i_window_width;
137     p_vout->p_sys->s_rect.size.height = p_vout->i_window_height;
138
139     if( ( err = EnterMovies() ) != noErr )
140     {
141         msg_Err( p_vout, "EnterMovies failed: %d", err );
142         free( p_vout->p_sys->p_matrix );
143         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
144         free( p_vout->p_sys );
145         return( 1 );
146     } 
147
148     if( vout_ChromaCmp( p_vout->render.i_chroma, FOURCC_I420 ) )
149     {
150         err = FindCodec( kYUV420CodecType, bestSpeedCodec,
151                          nil, &p_vout->p_sys->img_dc );
152         if( err == noErr && p_vout->p_sys->img_dc != 0 )
153         {
154             p_vout->output.i_chroma = FOURCC_I420;
155             p_vout->p_sys->i_codec = kYUV420CodecType;
156         }
157         else
158         {
159             msg_Err( p_vout, "failed to find an appropriate codec" );
160         }
161     }
162     else
163     {
164         msg_Err( p_vout, "chroma 0x%08x not supported",
165                          p_vout->render.i_chroma );
166     }
167
168     if( p_vout->p_sys->img_dc == 0 )
169     {
170         free( p_vout->p_sys->p_matrix );
171         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
172         free( p_vout->p_sys );
173         return( 1 );        
174     }
175
176     if( CoCreateWindow( p_vout ) )
177     {
178         msg_Err( p_vout, "unable to create window" );
179         free( p_vout->p_sys->p_matrix );
180         DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
181         free( p_vout->p_sys ); 
182         return( 1 );
183     }
184
185     return( 0 );
186 }
187
188 /*****************************************************************************
189  * vout_Init: initialize video thread output method
190  *****************************************************************************/
191 static int vout_Init( vout_thread_t *p_vout )
192 {
193     int i_index;
194     picture_t *p_pic;
195
196     I_OUTPUTPICTURES = 0;
197
198     /* Initialize the output structure; we already found a codec,
199      * and the corresponding chroma we will be using. Since we can
200      * arbitrary scale, stick to the coordinates and aspect. */
201     p_vout->output.i_width  = p_vout->render.i_width;
202     p_vout->output.i_height = p_vout->render.i_height;
203     p_vout->output.i_aspect = p_vout->render.i_aspect;
204
205     SetPort( p_vout->p_sys->p_qdport );
206     QTScaleMatrix( p_vout );
207
208     if( QTCreateSequence( p_vout ) )
209     {
210         msg_Err( p_vout, "unable to create sequence" );
211         return( 1 );
212     }
213
214     /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
215     while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
216     {
217         p_pic = NULL;
218
219         /* Find an empty picture slot */
220         for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
221         {
222             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
223             {
224                 p_pic = p_vout->p_picture + i_index;
225                 break;
226             }
227         }
228
229         /* Allocate the picture */
230         if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
231         {
232             break;
233         }
234
235         p_pic->i_status = DESTROYED_PICTURE;
236         p_pic->i_type   = DIRECT_PICTURE;
237
238         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
239
240         I_OUTPUTPICTURES++;
241     }
242
243     return( 0 );
244 }
245
246 /*****************************************************************************
247  * vout_End: terminate video thread output method
248  *****************************************************************************/
249 static void vout_End( vout_thread_t *p_vout )
250 {
251     int i_index;
252
253     QTDestroySequence( p_vout );
254
255     /* Free the direct buffers we allocated */
256     for( i_index = I_OUTPUTPICTURES; i_index; )
257     {
258         i_index--;
259         QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
260     }
261 }
262
263 /*****************************************************************************
264  * vout_Destroy: destroy video thread output method
265  *****************************************************************************/
266 static void vout_Destroy( vout_thread_t *p_vout )
267 {
268     if( CoDestroyWindow( p_vout ) )
269     {
270         msg_Err( p_vout, "unable to destroy window" );
271     }
272
273     ExitMovies();
274
275     free( p_vout->p_sys->p_matrix );
276     DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
277
278     vlc_object_release( p_vout->p_sys->p_intf );
279
280     free( p_vout->p_sys );
281 }
282
283 /*****************************************************************************
284  * vout_Manage: handle events
285  *****************************************************************************
286  * This function should be called regularly by video output thread. It manages
287  * console events. It returns a non null value on error.
288  *****************************************************************************/
289 static int vout_Manage( vout_thread_t *p_vout )
290 {    
291     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
292     {
293         if( CoToggleFullscreen( p_vout ) )  
294         {
295             return( 1 );
296         }
297
298         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
299     }
300
301     if( p_vout->i_changes & VOUT_SIZE_CHANGE ) 
302     {
303         QTScaleMatrix( p_vout );
304         SetDSequenceMatrix( p_vout->p_sys->i_seq, 
305                             p_vout->p_sys->p_matrix );
306  
307         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
308     }
309
310     /* hide/show mouse cursor */
311     if( p_vout->p_sys->b_mouse_moved ||
312         p_vout->p_sys->i_time_mouse_last_moved )
313     {
314         vlc_bool_t b_change = 0;
315
316         if( !p_vout->p_sys->b_mouse_pointer_visible )
317         {
318             CGDisplayShowCursor( kCGDirectMainDisplay );
319             b_change = 1;
320         }
321 #if 0
322         else if( !p_vout->p_sys->b_mouse_moved && 
323             mdate() - p_vout->p_sys->i_time_mouse_last_moved > 2000000 &&
324             p_vout->p_sys->b_mouse_pointer_visible )
325         {
326             CGDisplayHideCursor( kCGDirectMainDisplay );
327             b_change = 1;
328         }
329 #endif
330
331         if( b_change )
332         {
333             p_vout->p_sys->i_time_mouse_last_moved = 0;
334             p_vout->p_sys->b_mouse_moved = 0;
335             p_vout->p_sys->b_mouse_pointer_visible =
336                 !p_vout->p_sys->b_mouse_pointer_visible;
337         }
338     }
339
340     return( 0 );
341 }
342
343 /*****************************************************************************
344  * vout_Render: render previously calculated output
345  *****************************************************************************/
346 static void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
347 {
348     ;
349 }
350
351 /*****************************************************************************
352  * vout_Display: displays previously rendered output
353  *****************************************************************************
354  * This function sends the currently rendered image to the display.
355  *****************************************************************************/
356 static void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
357 {
358     OSErr err;
359     CodecFlags flags;
360
361     if( ( err = DecompressSequenceFrameS( 
362                     p_vout->p_sys->i_seq,
363                     p_pic->p_sys->p_info,
364                     p_pic->p_sys->i_size,                    
365                     codecFlagUseImageBuffer, &flags, nil ) != noErr ) )
366     {
367         msg_Err( p_vout, "DecompressSequenceFrameS failed: %d", err );
368     }
369 }
370
371 /*****************************************************************************
372  * CoSendRequest: send request to interface thread
373  *****************************************************************************
374  * Returns 0 on success, 1 otherwise
375  *****************************************************************************/
376 static int CoSendRequest( vout_thread_t *p_vout, long i_request )
377 {
378     NSArray *o_array;
379     NSPortMessage *o_msg;
380     struct vout_req_s req;
381     struct vout_req_s *p_req = &req;
382     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
383     NSPort *recvPort = [[NSPort port] retain];
384
385     memset( &req, 0, sizeof(req) );
386     req.i_type = i_request;
387     req.p_vout = p_vout;
388
389     req.o_lock = [[NSConditionLock alloc] initWithCondition: 0];
390
391     o_array = [NSArray arrayWithObject:
392         [NSData dataWithBytes: &p_req length: sizeof(void *)]];
393     o_msg = [[NSPortMessage alloc]
394         initWithSendPort: p_vout->p_sys->p_intf->p_sys->o_sendport
395         receivePort: recvPort components: o_array]; 
396
397     [o_msg sendBeforeDate: [NSDate distantPast]];
398
399     [req.o_lock lockWhenCondition: 1];
400     [req.o_lock unlock];
401
402     [o_msg release];
403     [req.o_lock release];
404
405     [recvPort release];
406     [o_pool release];
407
408     return( !req.i_result );
409 }
410
411 /*****************************************************************************
412  * CoCreateWindow: create new window 
413  *****************************************************************************
414  * Returns 0 on success, 1 otherwise
415  *****************************************************************************/
416 static int CoCreateWindow( vout_thread_t *p_vout )
417 {
418     if( CoSendRequest( p_vout, VOUT_REQ_CREATE_WINDOW ) )
419     {
420         msg_Err( p_vout, "CoSendRequest (CREATE_WINDOW) failed" );
421         return( 1 );
422     }
423
424     return( 0 );
425 }
426
427 /*****************************************************************************
428  * CoDestroyWindow: destroy window 
429  *****************************************************************************
430  * Returns 0 on success, 1 otherwise
431  *****************************************************************************/
432 static int CoDestroyWindow( vout_thread_t *p_vout )
433 {
434     if( !p_vout->p_sys->b_mouse_pointer_visible )
435     {
436         CGDisplayShowCursor( kCGDirectMainDisplay );
437         p_vout->p_sys->b_mouse_pointer_visible = 1;
438     }
439
440     if( CoSendRequest( p_vout, VOUT_REQ_DESTROY_WINDOW ) )
441     {
442         msg_Err( p_vout, "CoSendRequest (DESTROY_WINDOW) failed" );
443         return( 1 );
444     }
445
446     return( 0 );
447 }
448
449 /*****************************************************************************
450  * CoToggleFullscreen: toggle fullscreen 
451  *****************************************************************************
452  * Returns 0 on success, 1 otherwise
453  *****************************************************************************/
454 static int CoToggleFullscreen( vout_thread_t *p_vout )
455 {
456     QTDestroySequence( p_vout );
457
458     if( CoDestroyWindow( p_vout ) )
459     {
460         msg_Err( p_vout, "unable to destroy window" );
461         return( 1 );
462     }
463     
464     p_vout->b_fullscreen = !p_vout->b_fullscreen;
465
466     if( p_vout->b_fullscreen )
467     {
468         HideMenuBar();
469     }
470     else
471     {
472         ShowMenuBar();
473     }
474
475     if( CoCreateWindow( p_vout ) )
476     {
477         msg_Err( p_vout, "unable to create window" );
478         return( 1 );
479     }
480
481     SetPort( p_vout->p_sys->p_qdport );
482     QTScaleMatrix( p_vout );
483
484     if( QTCreateSequence( p_vout ) )
485     {
486         msg_Err( p_vout, "unable to create sequence" );
487         return( 1 ); 
488     } 
489
490     return( 0 );
491 }
492
493 /*****************************************************************************
494  * QTScaleMatrix: scale matrix 
495  *****************************************************************************/
496 static void QTScaleMatrix( vout_thread_t *p_vout )
497 {
498     Rect s_rect;
499     int i_width, i_height;
500     Fixed factor_x, factor_y;
501     int i_offset_x = 0;
502     int i_offset_y = 0;
503
504     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
505
506     i_width = s_rect.right - s_rect.left;
507     i_height = s_rect.bottom - s_rect.top;
508
509     if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
510     {
511         int i_adj_width = i_height * p_vout->output.i_aspect /
512                           VOUT_ASPECT_FACTOR;
513
514         factor_x = FixDiv( Long2Fix( i_adj_width ),
515                            Long2Fix( p_vout->output.i_width ) );
516         factor_y = FixDiv( Long2Fix( i_height ),
517                            Long2Fix( p_vout->output.i_height ) );
518
519         i_offset_x = (i_width - i_adj_width) / 2;
520     }
521     else
522     {
523         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
524                            p_vout->output.i_aspect;
525
526         factor_x = FixDiv( Long2Fix( i_width ),
527                            Long2Fix( p_vout->output.i_width ) );
528         factor_y = FixDiv( Long2Fix( i_adj_height ),
529                            Long2Fix( p_vout->output.i_height ) );
530
531         i_offset_y = (i_height - i_adj_height) / 2;
532     }
533
534     SetIdentityMatrix( p_vout->p_sys->p_matrix );
535
536     ScaleMatrix( p_vout->p_sys->p_matrix,
537                  factor_x, factor_y,
538                  Long2Fix(0), Long2Fix(0) );            
539
540     TranslateMatrix( p_vout->p_sys->p_matrix, 
541                      Long2Fix(i_offset_x), 
542                      Long2Fix(i_offset_y) );
543 }
544
545 /*****************************************************************************
546  * QTCreateSequence: create a new sequence 
547  *****************************************************************************
548  * Returns 0 on success, 1 otherwise
549  *****************************************************************************/
550 static int QTCreateSequence( vout_thread_t *p_vout )
551 {
552     OSErr err;
553     ImageDescriptionPtr p_descr;
554
555     HLock( (Handle)p_vout->p_sys->h_img_descr );
556     p_descr = *p_vout->p_sys->h_img_descr;
557
558     p_descr->idSize = sizeof(ImageDescription);
559     p_descr->cType = p_vout->p_sys->i_codec;
560     p_descr->version = 1;
561     p_descr->revisionLevel = 0;
562     p_descr->vendor = 'appl';
563     p_descr->width = p_vout->output.i_width;
564     p_descr->height = p_vout->output.i_height;
565     p_descr->hRes = Long2Fix(72);
566     p_descr->vRes = Long2Fix(72);
567     p_descr->spatialQuality = codecLosslessQuality;
568     p_descr->frameCount = 1;
569     p_descr->clutID = -1;
570     p_descr->dataSize = 0;
571     p_descr->depth = 12;
572
573     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
574
575     if( ( err = DecompressSequenceBeginS( 
576                               &p_vout->p_sys->i_seq,
577                               p_vout->p_sys->h_img_descr,
578                               NULL, 0,
579                               p_vout->p_sys->p_qdport,
580                               NULL, NULL,
581                               p_vout->p_sys->p_matrix,
582                               0, NULL,
583                               codecFlagUseImageBuffer,
584                               codecLosslessQuality,
585                               p_vout->p_sys->img_dc ) ) )
586     {
587         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
588         return( 1 );
589     }
590
591     return( 0 );
592 }
593
594 /*****************************************************************************
595  * QTDestroySequence: destroy sequence 
596  *****************************************************************************/
597 static void QTDestroySequence( vout_thread_t *p_vout )
598 {
599     CDSequenceEnd( p_vout->p_sys->i_seq );
600 }
601
602 /*****************************************************************************
603  * QTNewPicture: allocate a picture
604  *****************************************************************************
605  * Returns 0 on success, 1 otherwise
606  *****************************************************************************/
607 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
608 {
609     int i_width  = p_vout->output.i_width;
610     int i_height = p_vout->output.i_height;
611
612     /* We know the chroma, allocate a buffer which will be used
613      * directly by the decoder */
614     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
615
616     if( p_pic->p_sys == NULL )
617     {
618         return( -1 );
619     }
620
621     switch( p_vout->output.i_chroma )
622     {
623         case FOURCC_I420:
624
625             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
626             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
627
628             /* Allocate the memory buffer */
629             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
630                                           16, i_width * i_height * 3 / 2 );
631
632             /* Y buffer */
633             p_pic->Y_PIXELS = p_pic->p_data; 
634             p_pic->p[Y_PLANE].i_lines = i_height;
635             p_pic->p[Y_PLANE].i_pitch = i_width;
636             p_pic->p[Y_PLANE].i_pixel_bytes = 1;
637             p_pic->p[Y_PLANE].b_margin = 0;
638
639             /* U buffer */
640             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
641             p_pic->p[U_PLANE].i_lines = i_height / 2;
642             p_pic->p[U_PLANE].i_pitch = i_width / 2;
643             p_pic->p[U_PLANE].i_pixel_bytes = 1;
644             p_pic->p[U_PLANE].b_margin = 0;
645
646             /* V buffer */
647             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
648             p_pic->p[V_PLANE].i_lines = i_height / 2;
649             p_pic->p[V_PLANE].i_pitch = i_width / 2;
650             p_pic->p[V_PLANE].i_pixel_bytes = 1;
651             p_pic->p[V_PLANE].b_margin = 0;
652
653             /* We allocated 3 planes */
654             p_pic->i_planes = 3;
655
656 #define P p_pic->p_sys->pixmap_i420
657             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
658                                        - p_pic->p_sys->p_info;
659             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
660                                         - p_pic->p_sys->p_info;
661             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
662                                         - p_pic->p_sys->p_info;
663
664             P.componentInfoY.rowBytes = i_width;
665             P.componentInfoCb.rowBytes = i_width / 2;
666             P.componentInfoCr.rowBytes = i_width / 2;
667 #undef P
668
669             break;
670
671     default:
672         /* Unknown chroma, tell the guy to get lost */
673         free( p_pic->p_sys );
674         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
675                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
676         p_pic->i_planes = 0;
677         return( -1 );
678     }
679
680     return( 0 );
681 }
682
683 /*****************************************************************************
684  * QTFreePicture: destroy a picture allocated with QTNewPicture
685  *****************************************************************************/
686 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
687 {
688     switch( p_vout->output.i_chroma )
689     {
690         case FOURCC_I420:
691             free( p_pic->p_data_orig );
692             break;
693     }
694
695     free( p_pic->p_sys );
696 }
697
698 /*****************************************************************************
699  * VLCWindow implementation
700  *****************************************************************************/
701 @implementation VLCWindow
702
703 - (void)setVout:(vout_thread_t *)_p_vout
704 {
705     p_vout = _p_vout;
706 }
707
708 - (void)toggleFullscreen
709 {
710     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
711 }
712
713 - (BOOL)isFullscreen
714 {
715     return( p_vout->b_fullscreen );
716 }
717
718 - (BOOL)canBecomeKeyWindow
719 {
720     return( YES );
721 }
722
723 - (void)keyDown:(NSEvent *)o_event
724 {
725     unichar key = 0;
726
727     if( [[o_event characters] length] )
728     {
729         key = [[o_event characters] characterAtIndex: 0];
730     }
731
732     switch( key )
733     {
734         case (unichar)0xf700: /* up-arrow */
735         { 
736             aout_thread_t * p_aout = vlc_object_find( p_vout, VLC_OBJECT_AOUT,
737                                                       FIND_ANYWHERE );
738             if( p_aout != NULL )
739             {
740                 if( p_aout->i_volume + VOLUME_STEP <= VOLUME_MAX )
741                 {
742                     p_aout->i_volume += VOLUME_STEP;
743                 }
744  
745                 vlc_object_release( p_aout ); 
746             } 
747         } 
748         break;
749
750         case (unichar)0xf701: /* down-arrow */
751         {
752             aout_thread_t * p_aout = vlc_object_find( p_vout, VLC_OBJECT_AOUT,
753                                                       FIND_ANYWHERE );
754             if( p_aout != NULL )
755             {
756                 if( p_aout->i_volume - VOLUME_STEP >= VOLUME_MIN )
757                 {
758                     p_aout->i_volume -= VOLUME_STEP;
759                 }
760
761                 vlc_object_release( p_aout );
762             }
763         }
764         break;
765
766         case 'f': case 'F':
767             [self toggleFullscreen];
768             break;
769
770         case (unichar)0x1b: /* escape */
771             if( [self isFullscreen] )
772             {
773                 [self toggleFullscreen];
774             }
775             break;
776
777         case 'q': case 'Q':
778             p_vout->p_vlc->b_die = VLC_TRUE;
779             break;
780
781         case ' ':
782             input_SetStatus( p_vout, INPUT_STATUS_PAUSE );
783             break;
784
785         default:
786             [super keyDown: o_event];
787             break;
788     }
789 }
790
791 @end
792
793 /*****************************************************************************
794  * VLCView implementation
795  *****************************************************************************/
796 @implementation VLCView
797
798 - (void)setVout:(vout_thread_t *)_p_vout
799 {
800     p_vout = _p_vout;
801 }
802
803 - (void)drawRect:(NSRect)rect
804 {
805     [[NSColor blackColor] set];
806     NSRectFill( rect );
807     [super drawRect: rect];
808
809     p_vout->i_changes |= VOUT_SIZE_CHANGE;
810 }
811
812 @end