1 /*****************************************************************************
2 * vout.m: MacOS X video output module
3 *****************************************************************************
4 * Copyright (C) 2001-2007 the VideoLAN team
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>
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.
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.
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 *****************************************************************************/
29 /*****************************************************************************
31 *****************************************************************************/
32 #include <stdlib.h> /* free() */
36 #include <QuickTime/QuickTime.h>
43 #define QT_MAX_DIRECTBUFFERS 10
44 #define VL_MAX_DISPLAYS 16
46 /*****************************************************************************
48 *****************************************************************************/
49 @interface VLCQTView : NSQuickDrawView <VLCVoutViewResetting>
51 vout_thread_t * p_vout;
54 + (void)resetVout: (vout_thread_t *)p_vout;
55 - (id) initWithVout:(vout_thread_t *)p_vout;
60 NSAutoreleasePool *o_pool;
62 VLCVoutView * o_vout_view;
65 bool b_cpu_has_simd; /* does CPU supports Altivec, MMX, etc... */
71 MatrixRecordPtr p_matrix;
72 DecompressorComponent img_dc;
73 ImageDescriptionHandle h_img_descr;
75 /* video geometry in port */
77 int i_width, i_height;
78 /* Mozilla plugin-related variables */
88 /* When using I420 output */
89 PlanarPixmapInfoYUV420 pixmap_i420;
92 /*****************************************************************************
94 *****************************************************************************/
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 );
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 * );
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 OpenVideoQT ( vlc_object_t *p_this )
119 vout_thread_t * p_vout = (vout_thread_t *)p_this;
121 vlc_value_t value_drawable;
123 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
124 if( p_vout->p_sys == NULL )
126 msg_Err( p_vout, "out of memory" );
130 memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
132 p_vout->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
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;
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 = true;
147 p_vout->p_sys->b_embedded = false;
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" );
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) );
159 if( ( err = EnterMovies() ) != noErr )
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 );
168 /* Damn QT isn't thread safe, so keep a process-wide lock */
169 vlc_mutex_t *p_qtlock = var_AcquireMutex( "quicktime_mutex" );
171 /* Can we find the right chroma ? */
172 if( p_vout->p_sys->b_cpu_has_simd )
174 err = FindCodec( kYUVSPixelFormat, bestSpeedCodec,
175 nil, &p_vout->p_sys->img_dc );
179 err = FindCodec( kYUV420CodecType, bestSpeedCodec,
180 nil, &p_vout->p_sys->img_dc );
182 vlc_mutex_unlock( p_qtlock );
184 if( err == noErr && p_vout->p_sys->img_dc != 0 )
186 if( p_vout->p_sys->b_cpu_has_simd )
188 p_vout->output.i_chroma = VLC_FOURCC('Y','U','Y','2');
189 p_vout->p_sys->i_codec = kYUVSPixelFormat;
193 p_vout->output.i_chroma = VLC_FOURCC('I','4','2','0');
194 p_vout->p_sys->i_codec = kYUV420CodecType;
199 msg_Err( p_vout, "QT doesn't support any appropriate chroma" );
202 if( p_vout->p_sys->img_dc == 0 )
204 free( p_vout->p_sys->p_matrix );
205 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
206 free( p_vout->p_sys );
210 if( p_vout->b_fullscreen || !p_vout->p_sys->b_embedded )
213 #define o_qtview p_vout->p_sys->o_qtview
214 o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
215 [o_qtview autorelease];
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 )
223 [o_qtview lockFocus];
224 p_vout->p_sys->p_qdport = [o_qtview qdPort];
225 [o_qtview unlockFocus];
232 /*****************************************************************************
233 * CloseVideo: destroy video thread output method
234 *****************************************************************************/
235 void CloseVideoQT ( vlc_object_t *p_this )
237 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
238 vout_thread_t * p_vout = (vout_thread_t *)p_this;
240 if( p_vout->b_fullscreen || !p_vout->p_sys->b_embedded )
241 [p_vout->p_sys->o_vout_view closeVout];
243 /* Clean Up Quicktime environment */
245 free( p_vout->p_sys->p_matrix );
246 DisposeHandle( (Handle)p_vout->p_sys->h_img_descr );
249 free( p_vout->p_sys );
252 /*****************************************************************************
253 * InitVideo: initialize video thread output method
254 *****************************************************************************/
255 static int InitVideo ( vout_thread_t *p_vout )
260 I_OUTPUTPICTURES = 0;
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;
269 if( p_vout->b_fullscreen || !p_vout->p_sys->b_embedded )
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;
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
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);
290 QTScaleMatrix( p_vout );
292 if( QTCreateSequence( p_vout ) )
294 msg_Err( p_vout, "unable to initialize QT: QTCreateSequence failed" );
298 /* Try to initialize up to QT_MAX_DIRECTBUFFERS direct buffers */
299 while( I_OUTPUTPICTURES < QT_MAX_DIRECTBUFFERS )
303 /* Find an empty picture slot */
304 for( i_index = 0; i_index < VOUT_MAX_PICTURES; i_index++ )
306 if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
308 p_pic = p_vout->p_picture + i_index;
313 /* Allocate the picture */
314 if( p_pic == NULL || QTNewPicture( p_vout, p_pic ) )
319 p_pic->i_status = DESTROYED_PICTURE;
320 p_pic->i_type = DIRECT_PICTURE;
322 PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
328 /*****************************************************************************
329 * EndVideo: terminate video thread output method
330 *****************************************************************************/
331 static void EndVideo( vout_thread_t *p_vout )
335 QTDestroySequence( p_vout );
337 if( !p_vout->b_fullscreen && p_vout->p_sys->b_embedded )
339 var_DelCallback(p_vout->p_libvlc, "drawableredraw", DrawableRedraw, p_vout);
340 DisposeRgn(p_vout->p_sys->clip_mask);
343 /* Free the direct buffers we allocated */
344 for( i_index = I_OUTPUTPICTURES; i_index; )
347 QTFreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
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 )
359 if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
361 if( CoToggleFullscreen( p_vout ) )
366 p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
369 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
371 if( p_vout->b_fullscreen || !p_vout->p_sys->b_embedded )
373 /* get the geometry from NSQuickDrawView */
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;
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);
390 if( p_vout->i_changes & VOUT_SIZE_CHANGE ||
391 p_vout->i_changes & VOUT_ASPECT_CHANGE )
393 QTScaleMatrix( p_vout );
394 SetDSequenceMatrix( p_vout->p_sys->i_seq,
395 p_vout->p_sys->p_matrix );
397 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
399 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
401 if( p_vout->i_changes & VOUT_ASPECT_CHANGE )
403 p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
407 [p_vout->p_sys->o_vout_view manage];
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 )
421 if( (NULL == p_vout->p_sys->clip_mask) || !EmptyRgn(p_vout->p_sys->clip_mask) )
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 */
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 ) )
440 QDFlushPortBuffer( p_vout->p_sys->p_qdport, p_vout->p_sys->clip_mask );
441 //QDFlushPortBuffer( p_vout->p_sys->p_qdport, NULL );
445 msg_Warn( p_vout, "QT failed to display the frame sequence: %d", err );
447 //SetPortBounds(p_vout->p_sys->p_qdport, &oldBounds);
452 /*****************************************************************************
453 * ControlVideo: control facility for the vout
454 *****************************************************************************/
455 static int ControlVideo( vout_thread_t *p_vout, int i_query, va_list args )
461 case VOUT_SET_STAY_ON_TOP:
462 b_arg = (bool) va_arg( args, int );
463 [p_vout->p_sys->o_vout_view setOnTop: b_arg];
469 return vout_vaControlDefault( p_vout, i_query, args );
473 /*****************************************************************************
474 * CoToggleFullscreen: toggle fullscreen
475 *****************************************************************************
476 * Returns 0 on success, 1 otherwise
477 *****************************************************************************/
478 static int CoToggleFullscreen( vout_thread_t *p_vout )
480 NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
482 p_vout->b_fullscreen = !p_vout->b_fullscreen;
484 if( p_vout->b_fullscreen )
485 [p_vout->p_sys->o_vout_view enterFullscreen];
487 [p_vout->p_sys->o_vout_view leaveFullscreen];
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. */
498 static int DrawableRedraw( vlc_object_t *p_this, const char *psz_name,
499 vlc_value_t oval, vlc_value_t nval, void *param)
501 /* ignore changes until we are ready for them */
502 if( (oval.i_int != nval.i_int) && (nval.i_int == 1) )
504 vout_thread_t *p_vout = (vout_thread_t *)param;
505 /* prevent QT from rendering any more video until we have updated
507 SetEmptyRgn(p_vout->p_sys->clip_mask);
508 SetDSequenceMask(p_vout->p_sys->i_seq,
509 p_vout->p_sys->clip_mask);
511 p_vout->i_changes |= VOUT_SIZE_CHANGE;
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. */
521 static void UpdateEmbeddedGeometry( vout_thread_t *p_vout )
524 vlc_value_t valt, vall, valb, valr, valx, valy, valw, valh,
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 );
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;
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 );
554 /* reset drawableredraw variable indicating we are ready
555 to take changes in video geometry */
557 var_Set( p_vout->p_libvlc, "drawableredraw", val );
560 /*****************************************************************************
561 * QTScaleMatrix: scale matrix
562 *****************************************************************************/
563 static void QTScaleMatrix( vout_thread_t *p_vout )
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;
572 var_Get( p_vout, "macosx-stretch", &val );
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 ) );
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 )
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 );
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 ) );
596 i_offset_x = (i_width - i_adj_width) / 2;
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 );
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 ) );
610 i_offset_y = (i_height - i_adj_height) / 2;
613 SetIdentityMatrix( p_vout->p_sys->p_matrix );
615 ScaleMatrix( p_vout->p_sys->p_matrix,
617 Long2Fix(0), Long2Fix(0) );
619 TranslateMatrix( p_vout->p_sys->p_matrix,
620 Long2Fix(i_offset_x), Long2Fix(i_offset_y) );
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 )
631 ImageDescriptionPtr p_descr;
633 HLock( (Handle)p_vout->p_sys->h_img_descr );
634 p_descr = *p_vout->p_sys->h_img_descr;
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;
651 HUnlock( (Handle)p_vout->p_sys->h_img_descr );
653 if( ( err = DecompressSequenceBeginS(
654 &p_vout->p_sys->i_seq,
655 p_vout->p_sys->h_img_descr,
657 (p_descr->width * p_descr->height * 16) / 8,
658 p_vout->p_sys->p_qdport,
660 p_vout->p_sys->p_matrix,
661 srcCopy, p_vout->p_sys->clip_mask,
662 codecFlagUseImageBuffer,
663 codecLosslessQuality,
666 msg_Err( p_vout, "Failed to initialize QT: DecompressSequenceBeginS failed: %d", err );
673 /*****************************************************************************
674 * QTDestroySequence: destroy sequence
675 *****************************************************************************/
676 static void QTDestroySequence( vout_thread_t *p_vout )
678 CDSequenceEnd( p_vout->p_sys->i_seq );
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 )
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 ) );
692 if( p_pic->p_sys == NULL )
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 );
701 switch( p_vout->output.i_chroma )
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;
706 /* Allocate the memory buffer */
707 p_pic->p_data_orig = p_pic->p_data = malloc( p_pic->p_sys->i_size );
708 /* Memory is always 16-bytes aligned on OSX, so it does not
709 * posix_memalign() */
710 assert( (((uintptr_t)p_pic->p_data) % 16) == 0 );
712 p_pic->p[0].p_pixels = p_pic->p_data;
713 p_pic->p[0].i_lines = p_vout->output.i_height;
714 p_pic->p[0].i_visible_lines = p_vout->output.i_height;
715 p_pic->p[0].i_pitch = p_vout->output.i_width * 2;
716 p_pic->p[0].i_pixel_pitch = 1;
717 p_pic->p[0].i_visible_pitch = p_vout->output.i_width * 2;
720 p_pic->p_sys->p_data = (void *)p_pic->p[0].p_pixels;
724 case VLC_FOURCC('I','4','2','0'):
725 p_pic->p_sys->p_data = (void *)&p_pic->p_sys->pixmap_i420;
726 p_pic->p_sys->i_size = sizeof(PlanarPixmapInfoYUV420);
728 /* Allocate the memory buffer */
729 p_pic->p_data_orig = p_pic->p_data = malloc( p_vout->output.i_width
730 * p_vout->output.i_height * 3 / 2 );
731 /* Memory is always 16-bytes aligned on OSX, so it does not
732 * posix_memalign() */
733 assert( (((uintptr_t)p_pic->p_data) % 16) == 0 );
736 p_pic->Y_PIXELS = p_pic->p_data;
737 p_pic->p[Y_PLANE].i_lines = p_vout->output.i_height;
738 p_pic->p[Y_PLANE].i_visible_lines = p_vout->output.i_height;
739 p_pic->p[Y_PLANE].i_pitch = p_vout->output.i_width;
740 p_pic->p[Y_PLANE].i_pixel_pitch = 1;
741 p_pic->p[Y_PLANE].i_visible_pitch = p_vout->output.i_width;
744 p_pic->U_PIXELS = p_pic->Y_PIXELS + p_vout->output.i_height * p_vout->output.i_width;
745 p_pic->p[U_PLANE].i_lines = p_vout->output.i_height / 2;
746 p_pic->p[U_PLANE].i_visible_lines = p_vout->output.i_height / 2;
747 p_pic->p[U_PLANE].i_pitch = p_vout->output.i_width / 2;
748 p_pic->p[U_PLANE].i_pixel_pitch = 1;
749 p_pic->p[U_PLANE].i_visible_pitch = p_vout->output.i_width / 2;
752 p_pic->V_PIXELS = p_pic->U_PIXELS + p_vout->output.i_height * p_vout->output.i_width / 4;
753 p_pic->p[V_PLANE].i_lines = p_vout->output.i_height / 2;
754 p_pic->p[V_PLANE].i_visible_lines = p_vout->output.i_height / 2;
755 p_pic->p[V_PLANE].i_pitch = p_vout->output.i_width / 2;
756 p_pic->p[V_PLANE].i_pixel_pitch = 1;
757 p_pic->p[V_PLANE].i_visible_pitch = p_vout->output.i_width / 2;
759 /* We allocated 3 planes */
762 #define P p_pic->p_sys->pixmap_i420
763 P.componentInfoY.offset = (void *)p_pic->Y_PIXELS
764 - p_pic->p_sys->p_data;
765 P.componentInfoCb.offset = (void *)p_pic->U_PIXELS
766 - p_pic->p_sys->p_data;
767 P.componentInfoCr.offset = (void *)p_pic->V_PIXELS
768 - p_pic->p_sys->p_data;
770 P.componentInfoY.rowBytes = p_vout->output.i_width;
771 P.componentInfoCb.rowBytes = p_vout->output.i_width / 2;
772 P.componentInfoCr.rowBytes = p_vout->output.i_width / 2;
777 /* Unknown chroma, tell the guy to get lost */
778 free( p_pic->p_sys );
779 msg_Err( p_vout, "Unknown chroma format 0x%.8x (%4.4s)",
780 p_vout->output.i_chroma, (char*)&p_vout->output.i_chroma );
788 /*****************************************************************************
789 * QTFreePicture: destroy a picture allocated with QTNewPicture
790 *****************************************************************************/
791 static void QTFreePicture( vout_thread_t *p_vout, picture_t *p_pic )
793 switch( p_vout->output.i_chroma )
795 case VLC_FOURCC('I','4','2','0'):
796 free( p_pic->p_data_orig );
800 free( p_pic->p_sys );
803 /*****************************************************************************
804 * VLCQTView implementation
805 *****************************************************************************/
806 @implementation VLCQTView
808 /* This function will reset the o_vout_view. It's useful to go fullscreen. */
809 + (void)resetVout: (vout_thread_t *)p_vout
811 QTDestroySequence( p_vout );
813 if( p_vout->b_fullscreen )
815 if( !p_vout->p_sys->b_embedded )
817 /* Save window size and position */
818 p_vout->p_sys->s_frame.size =
819 [p_vout->p_sys->o_vout_view frame].size;
820 p_vout->p_sys->s_frame.origin =
821 [[p_vout->p_sys->o_vout_view getWindow] frame].origin;
822 p_vout->p_sys->b_saved_frame = true;
826 var_DelCallback(p_vout->p_libvlc, "drawableredraw", DrawableRedraw, p_vout);
827 DisposeRgn(p_vout->p_sys->clip_mask);
831 if( p_vout->b_fullscreen || !p_vout->p_sys->b_embedded )
834 p_vout->p_sys->clip_mask = NULL;
835 #define o_qtview p_vout->p_sys->o_qtview
836 o_qtview = [[VLCQTView alloc] initWithVout: p_vout];
837 [o_qtview autorelease];
839 if( p_vout->p_sys->b_saved_frame )
841 p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
843 frame: &p_vout->p_sys->s_frame];
847 p_vout->p_sys->o_vout_view = [VLCVoutView getVoutView: p_vout
848 subView: o_qtview frame: nil];
851 /* Retrieve the QuickDraw port */
852 [o_qtview lockFocus];
853 p_vout->p_sys->p_qdport = [o_qtview qdPort];
854 [o_qtview unlockFocus];
856 GetPortBounds( p_vout->p_sys->p_qdport, &s_rect );
857 p_vout->p_sys->i_origx = s_rect.left;
858 p_vout->p_sys->i_origy = s_rect.top;
859 p_vout->p_sys->i_width = s_rect.right - s_rect.left;
860 p_vout->p_sys->i_height = s_rect.bottom - s_rect.top;
864 /* Create the clipping mask */
865 p_vout->p_sys->clip_mask = NewRgn();
866 UpdateEmbeddedGeometry(p_vout);
867 var_AddCallback(p_vout->p_libvlc, "drawableredraw", DrawableRedraw, p_vout);
869 QTScaleMatrix( p_vout );
871 if( QTCreateSequence( p_vout ) )
873 msg_Err( p_vout, "unable to initialize QT: QTCreateSequence failed" );
878 - (id) initWithVout:(vout_thread_t *)_p_vout
884 - (void)drawRect:(NSRect)rect
886 [[NSColor blackColor] set];
888 [super drawRect: rect];
890 p_vout->i_changes |= VOUT_SIZE_CHANGE;