* ./README.MacOSX.rtf : updated README.
[vlc.git] / 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.16 2002/07/25 22:48:56 massiot 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_t
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, VLC_FOURCC('I','4','2','0') ) )
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 = VLC_FOURCC('I','4','2','0');
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     else
370     {
371         QDFlushPortBuffer( p_vout->p_sys->p_qdport, nil );
372     }
373 }
374
375 /*****************************************************************************
376  * CoSendRequest: send request to interface thread
377  *****************************************************************************
378  * Returns 0 on success, 1 otherwise
379  *****************************************************************************/
380 static int CoSendRequest( vout_thread_t *p_vout, long i_request )
381 {
382     NSArray *o_array;
383     NSPortMessage *o_msg;
384     struct vout_req_t req;
385     struct vout_req_t *p_req = &req;
386     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
387     NSPort *recvPort = [[NSPort port] retain];
388
389     memset( &req, 0, sizeof(req) );
390     req.i_type = i_request;
391     req.p_vout = p_vout;
392
393     req.o_lock = [[NSConditionLock alloc] initWithCondition: 0];
394
395     o_array = [NSArray arrayWithObject:
396         [NSData dataWithBytes: &p_req length: sizeof(void *)]];
397     o_msg = [[NSPortMessage alloc]
398         initWithSendPort: p_vout->p_sys->p_intf->p_sys->o_sendport
399         receivePort: recvPort components: o_array]; 
400
401     [o_msg sendBeforeDate: [NSDate distantPast]];
402
403     [req.o_lock lockWhenCondition: 1];
404     [req.o_lock unlock];
405
406     [o_msg release];
407     [req.o_lock release];
408
409     [recvPort release];
410     [o_pool release];
411
412     return( !req.i_result );
413 }
414
415 /*****************************************************************************
416  * CoCreateWindow: create new window 
417  *****************************************************************************
418  * Returns 0 on success, 1 otherwise
419  *****************************************************************************/
420 static int CoCreateWindow( vout_thread_t *p_vout )
421 {
422     if( CoSendRequest( p_vout, VOUT_REQ_CREATE_WINDOW ) )
423     {
424         msg_Err( p_vout, "CoSendRequest (CREATE_WINDOW) failed" );
425         return( 1 );
426     }
427
428     return( 0 );
429 }
430
431 /*****************************************************************************
432  * CoDestroyWindow: destroy window 
433  *****************************************************************************
434  * Returns 0 on success, 1 otherwise
435  *****************************************************************************/
436 static int CoDestroyWindow( vout_thread_t *p_vout )
437 {
438     if( !p_vout->p_sys->b_mouse_pointer_visible )
439     {
440         CGDisplayShowCursor( kCGDirectMainDisplay );
441         p_vout->p_sys->b_mouse_pointer_visible = 1;
442     }
443
444     if( CoSendRequest( p_vout, VOUT_REQ_DESTROY_WINDOW ) )
445     {
446         msg_Err( p_vout, "CoSendRequest (DESTROY_WINDOW) failed" );
447         return( 1 );
448     }
449
450     return( 0 );
451 }
452
453 /*****************************************************************************
454  * CoToggleFullscreen: toggle fullscreen 
455  *****************************************************************************
456  * Returns 0 on success, 1 otherwise
457  *****************************************************************************/
458 static int CoToggleFullscreen( vout_thread_t *p_vout )
459 {
460     QTDestroySequence( p_vout );
461
462     if( CoDestroyWindow( p_vout ) )
463     {
464         msg_Err( p_vout, "unable to destroy window" );
465         return( 1 );
466     }
467     
468     p_vout->b_fullscreen = !p_vout->b_fullscreen;
469
470     if( p_vout->b_fullscreen )
471     {
472         HideMenuBar();
473     }
474     else
475     {
476         ShowMenuBar();
477     }
478
479     if( CoCreateWindow( p_vout ) )
480     {
481         msg_Err( p_vout, "unable to create window" );
482         return( 1 );
483     }
484
485     SetPort( p_vout->p_sys->p_qdport );
486     QTScaleMatrix( p_vout );
487
488     if( QTCreateSequence( p_vout ) )
489     {
490         msg_Err( p_vout, "unable to create sequence" );
491         return( 1 ); 
492     } 
493
494     return( 0 );
495 }
496
497 /*****************************************************************************
498  * QTScaleMatrix: scale matrix 
499  *****************************************************************************/
500 static void QTScaleMatrix( vout_thread_t *p_vout )
501 {
502     Rect s_rect;
503     int i_width, i_height;
504     Fixed factor_x, factor_y;
505     int i_offset_x = 0;
506     int i_offset_y = 0;
507
508     GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
509
510     i_width = s_rect.right - s_rect.left;
511     i_height = s_rect.bottom - s_rect.top;
512
513     if( i_height * p_vout->output.i_aspect < i_width * VOUT_ASPECT_FACTOR )
514     {
515         int i_adj_width = i_height * p_vout->output.i_aspect /
516                           VOUT_ASPECT_FACTOR;
517
518         factor_x = FixDiv( Long2Fix( i_adj_width ),
519                            Long2Fix( p_vout->output.i_width ) );
520         factor_y = FixDiv( Long2Fix( i_height ),
521                            Long2Fix( p_vout->output.i_height ) );
522
523         i_offset_x = (i_width - i_adj_width) / 2;
524     }
525     else
526     {
527         int i_adj_height = i_width * VOUT_ASPECT_FACTOR /
528                            p_vout->output.i_aspect;
529
530         factor_x = FixDiv( Long2Fix( i_width ),
531                            Long2Fix( p_vout->output.i_width ) );
532         factor_y = FixDiv( Long2Fix( i_adj_height ),
533                            Long2Fix( p_vout->output.i_height ) );
534
535         i_offset_y = (i_height - i_adj_height) / 2;
536     }
537
538     SetIdentityMatrix( p_vout->p_sys->p_matrix );
539
540     ScaleMatrix( p_vout->p_sys->p_matrix,
541                  factor_x, factor_y,
542                  Long2Fix(0), Long2Fix(0) );            
543
544     TranslateMatrix( p_vout->p_sys->p_matrix, 
545                      Long2Fix(i_offset_x), 
546                      Long2Fix(i_offset_y) );
547 }
548
549 /*****************************************************************************
550  * QTCreateSequence: create a new sequence 
551  *****************************************************************************
552  * Returns 0 on success, 1 otherwise
553  *****************************************************************************/
554 static int QTCreateSequence( vout_thread_t *p_vout )
555 {
556     OSErr err;
557     ImageDescriptionPtr p_descr;
558
559     HLock( (Handle)p_vout->p_sys->h_img_descr );
560     p_descr = *p_vout->p_sys->h_img_descr;
561
562     p_descr->idSize = sizeof(ImageDescription);
563     p_descr->cType = p_vout->p_sys->i_codec;
564     p_descr->version = 1;
565     p_descr->revisionLevel = 0;
566     p_descr->vendor = 'appl';
567     p_descr->width = p_vout->output.i_width;
568     p_descr->height = p_vout->output.i_height;
569     p_descr->hRes = Long2Fix(72);
570     p_descr->vRes = Long2Fix(72);
571     p_descr->spatialQuality = codecLosslessQuality;
572     p_descr->frameCount = 1;
573     p_descr->clutID = -1;
574     p_descr->dataSize = 0;
575     p_descr->depth = 24;
576
577     HUnlock( (Handle)p_vout->p_sys->h_img_descr );
578
579     if( ( err = DecompressSequenceBeginS( 
580                               &p_vout->p_sys->i_seq,
581                               p_vout->p_sys->h_img_descr,
582                               NULL, 0,
583                               p_vout->p_sys->p_qdport,
584                               NULL, NULL,
585                               p_vout->p_sys->p_matrix,
586                               0, NULL,
587                               codecFlagUseImageBuffer,
588                               codecLosslessQuality,
589                               p_vout->p_sys->img_dc ) ) )
590     {
591         msg_Err( p_vout, "DecompressSequenceBeginS failed: %d", err );
592         return( 1 );
593     }
594
595     return( 0 );
596 }
597
598 /*****************************************************************************
599  * QTDestroySequence: destroy sequence 
600  *****************************************************************************/
601 static void QTDestroySequence( vout_thread_t *p_vout )
602 {
603     CDSequenceEnd( p_vout->p_sys->i_seq );
604 }
605
606 /*****************************************************************************
607  * QTNewPicture: allocate a picture
608  *****************************************************************************
609  * Returns 0 on success, 1 otherwise
610  *****************************************************************************/
611 static int QTNewPicture( vout_thread_t *p_vout, picture_t *p_pic )
612 {
613     int i_width  = p_vout->output.i_width;
614     int i_height = p_vout->output.i_height;
615
616     /* We know the chroma, allocate a buffer which will be used
617      * directly by the decoder */
618     p_pic->p_sys = malloc( sizeof( picture_sys_t ) );
619
620     if( p_pic->p_sys == NULL )
621     {
622         return( -1 );
623     }
624
625     switch( p_vout->output.i_chroma )
626     {
627         case VLC_FOURCC('I','4','2','0'):
628
629             p_pic->p_sys->p_info = (void *)&p_pic->p_sys->pixmap_i420;
630             p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
631
632             /* Allocate the memory buffer */
633             p_pic->p_data = vlc_memalign( &p_pic->p_data_orig,
634                                           16, i_width * i_height * 3 / 2 );
635
636             /* Y buffer */
637             p_pic->Y_PIXELS = p_pic->p_data; 
638             p_pic->p[Y_PLANE].i_lines = i_height;
639             p_pic->p[Y_PLANE].i_pitch = i_width;
640             p_pic->p[Y_PLANE].i_pixel_pitch = 1;
641             p_pic->p[Y_PLANE].i_visible_pitch = i_width;
642
643             /* U buffer */
644             p_pic->U_PIXELS = p_pic->Y_PIXELS + i_height * i_width;
645             p_pic->p[U_PLANE].i_lines = i_height / 2;
646             p_pic->p[U_PLANE].i_pitch = i_width / 2;
647             p_pic->p[U_PLANE].i_pixel_pitch = 1;
648             p_pic->p[U_PLANE].i_visible_pitch = i_width / 2;
649
650             /* V buffer */
651             p_pic->V_PIXELS = p_pic->U_PIXELS + i_height * i_width / 4;
652             p_pic->p[V_PLANE].i_lines = i_height / 2;
653             p_pic->p[V_PLANE].i_pitch = i_width / 2;
654             p_pic->p[V_PLANE].i_pixel_pitch = 1;
655             p_pic->p[V_PLANE].i_visible_pitch = i_width / 2;
656
657             /* We allocated 3 planes */
658             p_pic->i_planes = 3;
659
660 #define P p_pic->p_sys->pixmap_i420
661             P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
662                                        - p_pic->p_sys->p_info;
663             P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
664                                         - p_pic->p_sys->p_info;
665             P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
666                                         - p_pic->p_sys->p_info;
667
668             P.componentInfoY.rowBytes = i_width;
669             P.componentInfoCb.rowBytes = i_width / 2;
670             P.componentInfoCr.rowBytes = i_width / 2;
671 #undef P
672
673             break;
674
675     default:
676         /* Unknown chroma, tell the guy to get lost */
677         free( p_pic->p_sys );
678         msg_Err( p_vout, "never heard of chroma 0x%.8x (%4.4s)",
679                  p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
680         p_pic->i_planes = 0;
681         return( -1 );
682     }
683
684     return( 0 );
685 }
686
687 /*****************************************************************************
688  * QTFreePicture: destroy a picture allocated with QTNewPicture
689  *****************************************************************************/
690 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
691 {
692     switch( p_vout->output.i_chroma )
693     {
694         case VLC_FOURCC('I','4','2','0'):
695             free( p_pic->p_data_orig );
696             break;
697     }
698
699     free( p_pic->p_sys );
700 }
701
702 /*****************************************************************************
703  * VLCWindow implementation
704  *****************************************************************************/
705 @implementation VLCWindow
706
707 - (void)setVout:(vout_thread_t *)_p_vout
708 {
709     p_vout = _p_vout;
710 }
711
712 - (void)toggleFullscreen
713 {
714     p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
715 }
716
717 - (BOOL)isFullscreen
718 {
719     return( p_vout->b_fullscreen );
720 }
721
722 - (BOOL)canBecomeKeyWindow
723 {
724     return( YES );
725 }
726
727 - (void)keyDown:(NSEvent *)o_event
728 {
729     unichar key = 0;
730
731     if( [[o_event characters] length] )
732     {
733         key = [[o_event characters] characterAtIndex: 0];
734     }
735
736     switch( key )
737     {
738         case (unichar)0xf700: /* up-arrow */
739         { 
740             aout_thread_t * p_aout = vlc_object_find( p_vout, VLC_OBJECT_AOUT,
741                                                       FIND_ANYWHERE );
742             if( p_aout != NULL )
743             {
744                 if( p_aout->i_volume + VOLUME_STEP <= VOLUME_MAX )
745                 {
746                     p_aout->i_volume += VOLUME_STEP;
747                 }
748  
749                 vlc_object_release( p_aout ); 
750             } 
751         } 
752         break;
753
754         case (unichar)0xf701: /* down-arrow */
755         {
756             aout_thread_t * p_aout = vlc_object_find( p_vout, VLC_OBJECT_AOUT,
757                                                       FIND_ANYWHERE );
758             if( p_aout != NULL )
759             {
760                 if( p_aout->i_volume - VOLUME_STEP >= VOLUME_MIN )
761                 {
762                     p_aout->i_volume -= VOLUME_STEP;
763                 }
764
765                 vlc_object_release( p_aout );
766             }
767         }
768         break;
769
770         case 'f': case 'F':
771             [self toggleFullscreen];
772             break;
773
774         case (unichar)0x1b: /* escape */
775             if( [self isFullscreen] )
776             {
777                 [self toggleFullscreen];
778             }
779             break;
780
781         case 'q': case 'Q':
782             p_vout->p_vlc->b_die = VLC_TRUE;
783             break;
784
785         case ' ':
786             input_SetStatus( p_vout, INPUT_STATUS_PAUSE );
787             break;
788
789         default:
790             [super keyDown: o_event];
791             break;
792     }
793 }
794
795 @end
796
797 /*****************************************************************************
798  * VLCView implementation
799  *****************************************************************************/
800 @implementation VLCView
801
802 - (void)setVout:(vout_thread_t *)_p_vout
803 {
804     p_vout = _p_vout;
805 }
806
807 - (void)drawRect:(NSRect)rect
808 {
809     [[NSColor blackColor] set];
810     NSRectFill( rect );
811     [super drawRect: rect];
812
813     p_vout->i_changes |= VOUT_SIZE_CHANGE;
814 }
815
816 @end