2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 \author Nicolas Plourde <nicolasplourde@gmail.com>
22 Copyright (c) Nicolas Plourde - April 2004
24 YUV support Copyright (C) 2004 Romain Dolbeau <romain@dolbeau.org>
26 \brief MPlayer Mac OSX Quartz video out module.
28 \todo: -screen overlay output
29 -fit osd in black bar when available
38 #include <Carbon/Carbon.h>
39 #include <QuickTime/QuickTime.h>
43 #include "fastmemcpy.h"
44 #include "video_out.h"
45 #include "video_out_internal.h"
53 #include "input/input.h"
54 #include "input/keycodes.h"
56 #include "osx_common.h"
58 static const vo_info_t info
=
62 "Nicolas Plourde <nicolasplourde@hotmail.com>, Romain Dolbeau <romain@dolbeau.org>",
66 const LIBVO_EXTERN(quartz
)
68 static uint32_t image_depth
;
69 static uint32_t image_format
;
70 static uint32_t image_size
;
71 static uint32_t image_buffer_size
;
72 static char *image_data
;
74 static ImageSequence seqId
;
75 static CodecType image_qtcodec
;
76 static PlanarPixmapInfoYUV420
*P
= NULL
;
79 ImageDescriptionHandle desc
;
80 Handle extension_colr
;
81 Handle extension_fiel
;
82 Handle extension_clap
;
83 Handle extension_pasp
;
85 static MatrixRecord matrix
;
86 static int EnterMoviesDone
= 0;
87 static int get_image_done
= 0;
89 static int vo_quartz_fs
; // we are in fullscreen
91 static int winLevel
= 1;
94 kCGDesktopWindowLevelKey
,
95 kCGNormalWindowLevelKey
,
96 kCGScreenSaverWindowLevelKey
99 static int int_pause
= 0;
100 static float winAlpha
= 1;
101 static int mouseHide
= FALSE
;
102 static float winSizeMult
= 1;
104 static int device_id
= 0;
106 static short fs_res_x
= 0;
107 static short fs_res_y
= 0;
109 static WindowRef theWindow
= NULL
;
110 static WindowGroupRef winGroup
= NULL
;
111 static CGRect bounds
;
112 static CGDirectDisplayID displayId
= 0;
113 static CFDictionaryRef originalMode
= NULL
;
115 static CGDataProviderRef dataProviderRef
= NULL
;
116 static CGImageRef image
= NULL
;
118 static Rect imgRect
; // size of the original image (unscaled)
119 static Rect dstRect
; // size of the displayed image (after scaling)
120 static Rect winRect
; // size of the window containg the displayed image (include padding)
121 static Rect oldWinRect
; // size of the window containg the displayed image (include padding) when NOT in FS mode
122 static Rect oldWinBounds
;
124 static MenuRef windMenu
;
125 static MenuRef movMenu
;
126 static MenuRef aspectMenu
;
128 static int lastScreensaverUpdate
= 0;
129 static int lastMouseHide
= 0;
134 kNormalScreenCmd
= 3,
135 kDoubleScreenCmd
= 4,
144 //PROTOTYPE/////////////////////////////////////////////////////////////////
145 static OSStatus
KeyEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
);
146 static OSStatus
MouseEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
);
147 static OSStatus
WindowEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
);
148 void window_resized(void);
149 void window_ontop(void);
150 void window_fullscreen(void);
151 void window_panscan(void);
153 static void draw_alpha(int x0
, int y0
, int w
, int h
, unsigned char *src
, unsigned char *srca
, int stride
)
155 switch (image_format
)
158 vo_draw_alpha_rgb32(w
, h
, src
, srca
, stride
, image_data
+ 4 * (y0
* imgRect
.right
+ x0
), 4 * imgRect
.right
);
163 vo_draw_alpha_yv12(w
, h
, src
, srca
, stride
, ((char *)P
) + be2me_32(P
->componentInfoY
.offset
) + x0
+ y0
* imgRect
.right
, imgRect
.right
);
166 vo_draw_alpha_uyvy(w
, h
, src
, srca
, stride
, ((char *)P
) + (x0
+ y0
* imgRect
.right
) * 2, imgRect
.right
* 2);
169 vo_draw_alpha_yuy2(w
, h
, src
, srca
, stride
, ((char *)P
) + (x0
+ y0
* imgRect
.right
) * 2, imgRect
.right
* 2);
174 //default keyboard event handler
175 static OSStatus
KeyEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
177 OSStatus result
= noErr
;
178 UInt32
class = GetEventClass(event
);
179 UInt32 kind
= GetEventKind(event
);
181 result
= CallNextEventHandler(nextHandler
, event
);
183 if (class == kEventClassKeyboard
)
187 UInt32 macKeyModifiers
;
189 GetEventParameter(event
, kEventParamKeyMacCharCodes
, typeChar
, NULL
, sizeof(macCharCodes
), NULL
, &macCharCodes
);
190 GetEventParameter(event
, kEventParamKeyCode
, typeUInt32
, NULL
, sizeof(macKeyCode
), NULL
, &macKeyCode
);
191 GetEventParameter(event
, kEventParamKeyModifiers
, typeUInt32
, NULL
, sizeof(macKeyModifiers
), NULL
, &macKeyModifiers
);
193 if (macKeyModifiers
!= 256)
195 if (kind
== kEventRawKeyRepeat
|| kind
== kEventRawKeyDown
)
197 int key
= convert_key(macKeyCode
, macCharCodes
);
200 mplayer_put_key(key
);
203 else if (macKeyModifiers
== 256)
205 switch (macCharCodes
)
207 case '[': SetWindowAlpha(theWindow
, winAlpha
-= 0.05); break;
208 case ']': SetWindowAlpha(theWindow
, winAlpha
+= 0.05); break;
212 result
= eventNotHandledErr
;
218 //default mouse event handler
219 static OSStatus
MouseEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
221 OSStatus result
= noErr
;
222 UInt32
class = GetEventClass(event
);
223 UInt32 kind
= GetEventKind(event
);
225 result
= CallNextEventHandler(nextHandler
, event
);
227 if (class == kEventClassMouse
)
233 GetEventParameter(event
, kEventParamMouseLocation
, typeQDPoint
, 0, sizeof(mousePos
), 0, &mousePos
);
234 GetEventParameter(event
, kEventParamWindowMouseLocation
, typeQDPoint
, 0, sizeof(winMousePos
), 0, &winMousePos
);
238 case kEventMouseMoved
:
242 CGDisplayShowCursor(displayId
);
248 case kEventMouseWheelMoved
:
253 GetEventParameter(event
, kEventParamMouseWheelDelta
, typeSInt32
, 0, sizeof(wheel
), 0, &wheel
);
255 part
= FindWindow(mousePos
, &tmpWin
);
257 if (part
== inContent
)
260 mplayer_put_key(MOUSE_BTN3
);
262 mplayer_put_key(MOUSE_BTN4
);
267 case kEventMouseDown
:
270 EventMouseButton button
;
274 GetWindowPortBounds(theWindow
, &bounds
);
275 GetEventParameter(event
, kEventParamMouseButton
, typeMouseButton
, 0, sizeof(button
), 0, &button
);
277 part
= FindWindow(mousePos
, &tmpWin
);
278 if (kind
== kEventMouseUp
)
280 if (part
!= inContent
)
284 case kEventMouseButtonPrimary
:
285 mplayer_put_key(MOUSE_BTN0
);
287 case kEventMouseButtonSecondary
:
288 mplayer_put_key(MOUSE_BTN2
);
290 case kEventMouseButtonTertiary
:
291 mplayer_put_key(MOUSE_BTN1
);
295 result
= eventNotHandledErr
;
300 if (winMousePos
.h
> bounds
.right
- 15 && winMousePos
.v
> bounds
.bottom
)
306 ResizeWindow(theWindow
, mousePos
, NULL
, &newSize
);
309 else if (part
== inMenuBar
)
311 MenuSelect(mousePos
);
314 else if (part
== inContent
)
318 case kEventMouseButtonPrimary
:
319 mplayer_put_key(MOUSE_BTN0
| MP_KEY_DOWN
);
321 case kEventMouseButtonSecondary
:
322 mplayer_put_key(MOUSE_BTN2
| MP_KEY_DOWN
);
324 case kEventMouseButtonTertiary
:
325 mplayer_put_key(MOUSE_BTN1
| MP_KEY_DOWN
);
329 result
= eventNotHandledErr
;
336 case kEventMouseDragged
:
340 result
= eventNotHandledErr
;
348 static void set_winSizeMult(float mult
)
350 int d_width
, d_height
;
351 aspect(&d_width
, &d_height
, A_NOZOOM
);
360 SizeWindow(theWindow
, d_width
* mult
, d_height
* mult
, 1);
364 //default window event handler
365 static OSStatus
WindowEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
367 OSStatus result
= noErr
;
368 UInt32
class = GetEventClass(event
);
369 UInt32 kind
= GetEventKind(event
);
371 result
= CallNextEventHandler(nextHandler
, event
);
373 if (class == kEventClassCommand
)
375 HICommand theHICommand
;
377 GetEventParameter(event
, kEventParamDirectObject
, typeHICommand
, NULL
, sizeof(theHICommand
), NULL
, &theHICommand
);
379 switch (theHICommand
.commandID
)
382 mplayer_put_key(KEY_CLOSE_WIN
);
386 set_winSizeMult(0.5);
389 case kNormalScreenCmd
:
393 case kDoubleScreenCmd
:
403 vo_keepaspect
= !vo_keepaspect
;
404 CheckMenuItem(aspectMenu
, 1, vo_keepaspect
);
409 change_movie_aspect(-1);
413 change_movie_aspect(4.0 / 3.0);
417 change_movie_aspect(16.0 / 9.0);
421 vo_panscan
= !vo_panscan
;
422 CheckMenuItem(aspectMenu
, 2, vo_panscan
);
428 result
= eventNotHandledErr
;
432 else if (class == kEventClassWindow
)
435 Rect rectWindow
= { 0, 0, 0, 0 };
437 GetEventParameter(event
, kEventParamDirectObject
, typeWindowRef
, NULL
, sizeof(window
), NULL
, &window
);
441 GetWindowBounds(window
, kWindowGlobalPortRgn
, &rectWindow
);
446 case kEventWindowClosed
:
448 mplayer_put_key(KEY_CLOSE_WIN
);
452 case kEventWindowZoomed
:
453 case kEventWindowBoundsChanged
:
460 result
= eventNotHandledErr
;
468 static void quartz_CreateWindow(uint32_t d_width
, uint32_t d_height
, WindowAttributes windowAttrs
)
470 CFStringRef titleKey
;
471 CFStringRef windowTitle
;
475 CFStringRef movMenuTitle
;
476 CFStringRef aspMenuTitle
;
478 const EventTypeSpec win_events
[] = {
479 {kEventClassWindow
, kEventWindowClosed
},
480 {kEventClassWindow
, kEventWindowBoundsChanged
},
481 {kEventClassCommand
, kEventCommandProcess
}
484 const EventTypeSpec key_events
[] = {
485 {kEventClassKeyboard
, kEventRawKeyDown
},
486 {kEventClassKeyboard
, kEventRawKeyRepeat
}
489 const EventTypeSpec mouse_events
[] = {
490 {kEventClassMouse
, kEventMouseMoved
},
491 {kEventClassMouse
, kEventMouseWheelMoved
},
492 {kEventClassMouse
, kEventMouseDown
},
493 {kEventClassMouse
, kEventMouseUp
},
494 {kEventClassMouse
, kEventMouseDragged
}
497 SetRect(&winRect
, 0, 0, d_width
, d_height
);
498 SetRect(&oldWinRect
, 0, 0, d_width
, d_height
);
499 SetRect(&dstRect
, 0, 0, d_width
, d_height
);
504 // Create Window Menu
505 CreateStandardWindowMenu(0, &windMenu
);
506 InsertMenu(windMenu
, 0);
509 CreateNewMenu(1004, 0, &movMenu
);
510 movMenuTitle
= CFSTR("Movie");
511 SetMenuTitleWithCFString(movMenu
, movMenuTitle
);
513 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Half Size"), 0, kHalfScreenCmd
, &index
);
514 SetMenuItemCommandKey(movMenu
, index
, 0, '0');
516 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Normal Size"), 0, kNormalScreenCmd
, &index
);
517 SetMenuItemCommandKey(movMenu
, index
, 0, '1');
519 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Double Size"), 0, kDoubleScreenCmd
, &index
);
520 SetMenuItemCommandKey(movMenu
, index
, 0, '2');
522 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Full Size"), 0, kFullScreenCmd
, &index
);
523 SetMenuItemCommandKey(movMenu
, index
, 0, 'F');
525 AppendMenuItemTextWithCFString(movMenu
, NULL
, kMenuItemAttrSeparator
, 0, &index
);
527 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Aspect Ratio"), 0, 0, &index
);
529 //// Create Aspect Ratio Sub Menu
530 CreateNewMenu(0, 0, &aspectMenu
);
531 aspMenuTitle
= CFSTR("Aspect Ratio");
532 SetMenuTitleWithCFString(aspectMenu
, aspMenuTitle
);
533 SetMenuItemHierarchicalMenu(movMenu
, 6, aspectMenu
);
535 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("Keep"), 0, kKeepAspectCmd
, &index
);
536 CheckMenuItem(aspectMenu
, 1, vo_keepaspect
);
537 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("Pan-Scan"), 0, kPanScanCmd
, &index
);
538 CheckMenuItem(aspectMenu
, 2, vo_panscan
);
539 AppendMenuItemTextWithCFString(aspectMenu
, NULL
, kMenuItemAttrSeparator
, 0, &index
);
540 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("Original"), 0, kAspectOrgCmd
, &index
);
541 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("4:3"), 0, kAspectFullCmd
, &index
);
542 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("16:9"), 0, kAspectWideCmd
, &index
);
544 InsertMenu(movMenu
, GetMenuID(windMenu
)); //insert before Window menu
549 CreateNewWindow(kDocumentWindowClass
, windowAttrs
, &winRect
, &theWindow
);
551 CreateWindowGroup(0, &winGroup
);
552 SetWindowGroup(theWindow
, winGroup
);
555 titleKey
= CFSTR("MPlayer - The Movie Player");
556 windowTitle
= CFCopyLocalizedString(titleKey
, NULL
);
557 result
= SetWindowTitleWithCFString(theWindow
, windowTitle
);
559 CFRelease(windowTitle
);
561 // Install event handler
562 InstallApplicationEventHandler(NewEventHandlerUPP(KeyEventHandler
), GetEventTypeCount(key_events
), key_events
, NULL
, NULL
);
563 InstallApplicationEventHandler(NewEventHandlerUPP(MouseEventHandler
), GetEventTypeCount(mouse_events
), mouse_events
, NULL
, NULL
);
564 InstallWindowEventHandler(theWindow
, NewEventHandlerUPP(WindowEventHandler
), GetEventTypeCount(win_events
), win_events
, theWindow
, NULL
);
567 static void update_screen_info(void)
570 CGDisplayCount displayCount
;
571 CGDirectDisplayID
*displays
;
572 // Display IDs might not be consecutive, get the list of all devices up to # device_id
573 displayCount
= device_id
+ 1;
574 displays
= malloc(sizeof(*displays
) * displayCount
);
575 if (kCGErrorSuccess
!= CGGetActiveDisplayList(displayCount
, displays
, &displayCount
) || displayCount
< device_id
+ 1) {
576 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: Device ID %d do not exist, falling back to main device.\n", device_id
);
577 displayId
= kCGDirectMainDisplay
;
582 displayId
= displays
[device_id
];
586 displayRect
= CGDisplayBounds(displayId
);
587 xinerama_x
= displayRect
.origin
.x
;
588 xinerama_y
= displayRect
.origin
.y
;
589 vo_screenwidth
= displayRect
.size
.width
;
590 vo_screenheight
= displayRect
.size
.height
;
591 aspect_save_screenres(vo_screenwidth
, vo_screenheight
);
594 static void free_video_specific(void)
596 if (seqId
) CDSequenceEnd(seqId
);
602 CGDataProviderRelease(dataProviderRef
);
603 dataProviderRef
= NULL
;
604 CGImageRelease(image
);
608 static int config(uint32_t width
, uint32_t height
, uint32_t d_width
, uint32_t d_height
, uint32_t flags
, char *title
, uint32_t format
)
610 WindowAttributes windowAttrs
;
614 free_video_specific();
616 vo_dwidth
= d_width
*= winSizeMult
;
617 vo_dheight
= d_height
*= winSizeMult
;
618 config_movie_aspect((float)d_width
/ d_height
);
620 // misc mplayer setup/////////////////////////////////////////////////////
621 SetRect(&imgRect
, 0, 0, width
, height
);
622 switch (image_format
)
635 image_size
= (imgRect
.right
* imgRect
.bottom
* image_depth
+ 7) / 8;
637 image_data
= malloc(image_size
);
639 // Create player window//////////////////////////////////////////////////
640 windowAttrs
= kWindowStandardDocumentAttributes
641 | kWindowStandardHandlerAttribute
642 | kWindowLiveResizeAttribute
;
644 windowAttrs
&= ~kWindowResizableAttribute
;
646 if (theWindow
== NULL
)
648 CGContextRef context
;
650 quartz_CreateWindow(d_width
, d_height
, windowAttrs
);
652 if (theWindow
== NULL
)
654 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: Couldn't create window !!!!!\n");
657 tmpBounds
= CGRectMake(0, 0, winRect
.right
, winRect
.bottom
);
658 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
659 CGContextFillRect(context
, tmpBounds
);
660 QDEndCGContext(GetWindowPort(theWindow
), &context
);
664 HideWindow(theWindow
);
665 ChangeWindowAttributes(theWindow
, ~windowAttrs
, windowAttrs
);
666 SetRect(&winRect
, 0, 0, d_width
, d_height
);
667 SetRect(&oldWinRect
, 0, 0, d_width
, d_height
);
668 SizeWindow(theWindow
, d_width
, d_height
, 1);
671 switch (image_format
)
675 CGContextRef context
;
677 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
679 dataProviderRef
= CGDataProviderCreateWithData(0, image_data
, imgRect
.right
* imgRect
.bottom
* 4, 0);
681 image
= CGImageCreate(imgRect
.right
,
685 (imgRect
.right
* 32 + 7) / 8,
686 CGColorSpaceCreateDeviceRGB(),
687 kCGImageAlphaNoneSkipFirst
,
688 dataProviderRef
, 0, 1, kCGRenderingIntentDefault
);
690 QDEndCGContext(GetWindowPort(theWindow
), &context
);
702 if (!EnterMoviesDone
)
704 qterr
= EnterMovies();
712 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: EnterMovies (%d)\n", qterr
);
717 SetIdentityMatrix(&matrix
);
719 if (d_width
!= width
|| d_height
!= height
)
721 ScaleMatrix(&matrix
, FixDiv(Long2Fix(d_width
), Long2Fix(width
)), FixDiv(Long2Fix(d_height
), Long2Fix(height
)), 0, 0);
724 yuv_qt_stuff
.desc
= (ImageDescriptionHandle
) NewHandleClear(sizeof(ImageDescription
));
726 yuv_qt_stuff
.extension_colr
= NewHandleClear(sizeof(NCLCColorInfoImageDescriptionExtension
));
727 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->colorParamType
= kVideoColorInfoImageDescriptionExtensionType
;
728 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->primaries
= 2;
729 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->transferFunction
= 2;
730 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->matrix
= 2;
732 yuv_qt_stuff
.extension_fiel
= NewHandleClear(sizeof(FieldInfoImageDescriptionExtension
));
733 ((FieldInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_fiel
))->fieldCount
= 1;
734 ((FieldInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_fiel
))->fieldOrderings
= 0;
736 yuv_qt_stuff
.extension_clap
= NewHandleClear(sizeof(CleanApertureImageDescriptionExtension
));
737 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureWidthN
= imgRect
.right
;
738 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureWidthD
= 1;
739 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureHeightN
= imgRect
.bottom
;
740 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureHeightD
= 1;
741 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->horizOffN
= 0;
742 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->horizOffD
= 1;
743 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->vertOffN
= 0;
744 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->vertOffD
= 1;
746 yuv_qt_stuff
.extension_pasp
= NewHandleClear(sizeof(PixelAspectRatioImageDescriptionExtension
));
747 ((PixelAspectRatioImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_pasp
))->hSpacing
= 1;
748 ((PixelAspectRatioImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_pasp
))->vSpacing
= 1;
750 (*yuv_qt_stuff
.desc
)->idSize
= sizeof(ImageDescription
);
751 (*yuv_qt_stuff
.desc
)->cType
= image_qtcodec
;
752 (*yuv_qt_stuff
.desc
)->version
= 2;
753 (*yuv_qt_stuff
.desc
)->revisionLevel
= 0;
754 (*yuv_qt_stuff
.desc
)->vendor
= 'mpla';
755 (*yuv_qt_stuff
.desc
)->width
= imgRect
.right
;
756 (*yuv_qt_stuff
.desc
)->height
= imgRect
.bottom
;
757 (*yuv_qt_stuff
.desc
)->hRes
= Long2Fix(72);
758 (*yuv_qt_stuff
.desc
)->vRes
= Long2Fix(72);
759 (*yuv_qt_stuff
.desc
)->temporalQuality
= 0;
760 (*yuv_qt_stuff
.desc
)->spatialQuality
= codecLosslessQuality
;
761 (*yuv_qt_stuff
.desc
)->frameCount
= 1;
762 (*yuv_qt_stuff
.desc
)->dataSize
= 0;
763 (*yuv_qt_stuff
.desc
)->depth
= 24;
764 (*yuv_qt_stuff
.desc
)->clutID
= -1;
766 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_colr
, kColorInfoImageDescriptionExtension
);
769 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [colr] (%d)\n", qterr
);
772 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_fiel
, kFieldInfoImageDescriptionExtension
);
775 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [fiel] (%d)\n", qterr
);
778 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_clap
, kCleanApertureImageDescriptionExtension
);
781 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [clap] (%d)\n", qterr
);
784 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_pasp
, kCleanApertureImageDescriptionExtension
);
787 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [pasp] (%d)\n", qterr
);
789 P
= calloc(sizeof(PlanarPixmapInfoYUV420
) + image_size
, 1);
790 switch (image_format
)
795 P
->componentInfoY
.offset
= be2me_32(sizeof(PlanarPixmapInfoYUV420
));
796 P
->componentInfoCb
.offset
= be2me_32(be2me_32(P
->componentInfoY
.offset
) + image_size
/ 2);
797 P
->componentInfoCr
.offset
= be2me_32(be2me_32(P
->componentInfoCb
.offset
) + image_size
/ 4);
798 P
->componentInfoY
.rowBytes
= be2me_32(imgRect
.right
);
799 P
->componentInfoCb
.rowBytes
= be2me_32(imgRect
.right
/ 2);
800 P
->componentInfoCr
.rowBytes
= be2me_32(imgRect
.right
/ 2);
801 image_buffer_size
= image_size
+ sizeof(PlanarPixmapInfoYUV420
);
805 image_buffer_size
= image_size
;
809 qterr
= DecompressSequenceBeginS(&seqId
,
813 GetWindowPort(theWindow
),
816 d_width
!= width
|| d_height
!= height
?
821 codecLosslessQuality
,
826 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: DecompressSequenceBeginS (%d)\n", qterr
);
834 RepositionWindow(theWindow
, NULL
, kWindowCenterOnMainScreen
);
835 ShowWindow(theWindow
);
847 SetWindowGroupLevel(winGroup
, CGWindowLevelForKey(levelList
[winLevel
]));
856 static void check_events(void)
859 EventTargetRef theTarget
;
863 theTarget
= GetEventDispatcherTarget();
864 theErr
= ReceiveNextEvent(0, 0, kEventDurationNoWait
, true, &theEvent
);
865 if (theErr
== noErr
&& theEvent
!= NULL
)
867 SendEventToEventTarget(theEvent
, theTarget
);
868 ReleaseEvent(theEvent
);
872 static void draw_osd(void)
874 vo_draw_text(imgRect
.right
, imgRect
.bottom
, draw_alpha
);
877 static void flip_page(void)
881 if (theWindow
== NULL
)
884 switch (image_format
)
888 CGContextRef context
;
890 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
891 CGContextDrawImage(context
, bounds
, image
);
892 QDEndCGContext(GetWindowPort(theWindow
), &context
);
904 CodecFlags flags
= 0;
906 qterr
= DecompressSequenceFrameWhen(seqId
,
909 0, //codecFlagUseImageBuffer,
915 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: DecompressSequenceFrameWhen in flip_page (%d) flags:0x%08x\n", qterr
, flags
);
923 CGContextRef context
;
925 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
927 CGContextBeginPath(context
);
928 CGContextSetAllowsAntialiasing(context
, false);
929 //CGContextSaveGState(context);
932 CGContextSetRGBStrokeColor(context
, 0.2, 0.2, 0.2, 0.5);
933 CGContextMoveToPoint(context
, winRect
.right
- 1, 1); CGContextAddLineToPoint(context
, winRect
.right
- 1, 1);
934 CGContextMoveToPoint(context
, winRect
.right
- 1, 5); CGContextAddLineToPoint(context
, winRect
.right
- 5, 1);
935 CGContextMoveToPoint(context
, winRect
.right
- 1, 9); CGContextAddLineToPoint(context
, winRect
.right
- 9, 1);
936 CGContextStrokePath(context
);
939 CGContextSetRGBStrokeColor(context
, 0.4, 0.4, 0.4, 0.5);
940 CGContextMoveToPoint(context
, winRect
.right
- 1, 2); CGContextAddLineToPoint(context
, winRect
.right
- 2, 1);
941 CGContextMoveToPoint(context
, winRect
.right
- 1, 6); CGContextAddLineToPoint(context
, winRect
.right
- 6, 1);
942 CGContextMoveToPoint(context
, winRect
.right
- 1, 10); CGContextAddLineToPoint(context
, winRect
.right
- 10, 1);
943 CGContextStrokePath(context
);
946 CGContextSetRGBStrokeColor(context
, 0.6, 0.6, 0.6, 0.5);
947 CGContextMoveToPoint(context
, winRect
.right
- 1, 3); CGContextAddLineToPoint(context
, winRect
.right
- 3, 1);
948 CGContextMoveToPoint(context
, winRect
.right
- 1, 7); CGContextAddLineToPoint(context
, winRect
.right
- 7, 1);
949 CGContextMoveToPoint(context
, winRect
.right
- 1, 11); CGContextAddLineToPoint(context
, winRect
.right
- 11, 1);
950 CGContextStrokePath(context
);
952 // CGContextRestoreGState( context );
953 CGContextFlush(context
);
954 QDEndCGContext(GetWindowPort(theWindow
), &context
);
957 curTime
= TickCount() / 60;
959 // auto hide mouse cursor (and future on-screen control?)
960 if (vo_quartz_fs
&& !mouseHide
)
962 if (curTime
- lastMouseHide
>= 5 || lastMouseHide
== 0)
964 CGDisplayHideCursor(displayId
);
966 lastMouseHide
= curTime
;
969 // update activity every 30 seconds to prevent
970 // screensaver from starting up.
971 if (curTime
- lastScreensaverUpdate
>= 30 || lastScreensaverUpdate
== 0)
973 UpdateSystemActivity(UsrActivity
);
974 lastScreensaverUpdate
= curTime
;
978 static int draw_slice(uint8_t * src
[], int stride
[], int w
, int h
, int x
, int y
)
980 switch (image_format
)
984 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoY
.offset
) + x
+ imgRect
.right
* y
, src
[0], w
, h
, imgRect
.right
, stride
[0]);
985 x
=x
/2;y
=y
/2;w
=w
/2;h
=h
/2;
987 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCb
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[1], w
, h
, imgRect
.right
/ 2, stride
[1]);
988 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCr
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[2], w
, h
, imgRect
.right
/ 2, stride
[2]);
992 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoY
.offset
) + x
+ imgRect
.right
* y
, src
[0], w
, h
, imgRect
.right
, stride
[0]);
993 x
=x
/2;y
=y
/2;w
=w
/2;h
=h
/2;
995 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCr
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[1], w
, h
, imgRect
.right
/ 2, stride
[1]);
996 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCb
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[2], w
, h
, imgRect
.right
/ 2, stride
[2]);
1002 static int draw_frame(uint8_t * src
[])
1004 switch (image_format
)
1007 fast_memcpy(image_data
, src
[0], image_size
);
1012 memcpy_pic(((char *)P
), src
[0], imgRect
.right
* 2, imgRect
.bottom
, imgRect
.right
* 2, imgRect
.right
* 2);
1018 static int query_format(uint32_t format
)
1020 image_format
= format
;
1023 if (format
== IMGFMT_RGB32
)
1025 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
1028 if (format
== IMGFMT_YV12
|| format
== IMGFMT_IYUV
|| format
== IMGFMT_I420
)
1030 image_qtcodec
= kMpegYUV420CodecType
; //kYUV420CodecType ?;
1031 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
1034 if (format
== IMGFMT_YUY2
)
1036 image_qtcodec
= kComponentVideoUnsigned
;
1037 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
1040 if (format
== IMGFMT_UYVY
)
1042 image_qtcodec
= k422YpCbCr8CodecType
;
1043 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
1049 static void uninit(void)
1051 free_video_specific();
1052 if (EnterMoviesDone
)
1054 EnterMoviesDone
= 0;
1059 static int preinit(const char *arg
)
1065 char *parse_pos
= (char *)&arg
[0];
1067 while (parse_pos
[0] && !parse_err
)
1069 if (strncmp(parse_pos
, "device_id=", 10) == 0)
1071 parse_pos
= &parse_pos
[10];
1072 device_id
= strtol(parse_pos
, &parse_pos
, 0);
1074 if (strncmp(parse_pos
, "fs_res=", 7) == 0)
1076 parse_pos
= &parse_pos
[7];
1077 fs_res_x
= strtol(parse_pos
, &parse_pos
, 0);
1078 parse_pos
= &parse_pos
[1];
1079 fs_res_y
= strtol(parse_pos
, &parse_pos
, 0);
1081 if (parse_pos
[0] == ':')
1082 parse_pos
= &parse_pos
[1];
1083 else if (parse_pos
[0])
1088 osx_foreground_hack();
1093 static uint32_t draw_yuv_image(mp_image_t
* mpi
)
1095 // ATM we're only called for planar IMGFMT
1096 // drawing is done directly in P
1097 // and displaying is in flip_page.
1098 return get_image_done
? VO_TRUE
: VO_FALSE
;
1101 static uint32_t get_yuv_image(mp_image_t
* mpi
)
1103 if (mpi
->type
!= MP_IMGTYPE_EXPORT
) return VO_FALSE
;
1105 if (mpi
->imgfmt
!= image_format
) return VO_FALSE
;
1107 if (mpi
->flags
& MP_IMGFLAG_PLANAR
)
1109 if (mpi
->num_planes
!= 3)
1111 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: only 3 planes allowed in get_yuv_image for planar (%d) \n", mpi
->num_planes
);
1115 mpi
->planes
[0] = ((char *)P
) + be2me_32(P
->componentInfoY
.offset
);
1116 mpi
->stride
[0] = imgRect
.right
;
1117 mpi
->width
= imgRect
.right
;
1119 if (mpi
->flags
& MP_IMGFLAG_SWAPPED
)
1122 mpi
->planes
[1] = ((char *)P
) + be2me_32(P
->componentInfoCb
.offset
);
1123 mpi
->planes
[2] = ((char *)P
) + be2me_32(P
->componentInfoCr
.offset
);
1124 mpi
->stride
[1] = imgRect
.right
/ 2;
1125 mpi
->stride
[2] = imgRect
.right
/ 2;
1130 mpi
->planes
[1] = ((char *)P
) + be2me_32(P
->componentInfoCr
.offset
);
1131 mpi
->planes
[2] = ((char *)P
) + be2me_32(P
->componentInfoCb
.offset
);
1132 mpi
->stride
[1] = imgRect
.right
/ 2;
1133 mpi
->stride
[2] = imgRect
.right
/ 2;
1136 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
1143 if (mpi
->num_planes
!= 1)
1145 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: only 1 plane allowed in get_yuv_image for packed (%d) \n", mpi
->num_planes
);
1149 mpi
->planes
[0] = (char *)P
;
1150 mpi
->stride
[0] = imgRect
.right
* 2;
1151 mpi
->width
= imgRect
.right
;
1152 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
1159 static int control(uint32_t request
, void *data
)
1163 case VOCTRL_PAUSE
: return int_pause
= 1;
1164 case VOCTRL_RESUME
: return int_pause
= 0;
1165 case VOCTRL_FULLSCREEN
: vo_fs
= !vo_fs
; window_fullscreen(); return VO_TRUE
;
1166 case VOCTRL_ONTOP
: vo_ontop
= !vo_ontop
; window_ontop(); return VO_TRUE
;
1167 case VOCTRL_QUERY_FORMAT
: return query_format(*(uint32_t *) data
);
1168 case VOCTRL_GET_PANSCAN
: return VO_TRUE
;
1169 case VOCTRL_SET_PANSCAN
: window_panscan(); return VO_TRUE
;
1171 case VOCTRL_GET_IMAGE
:
1172 switch (image_format
)
1179 return get_yuv_image(data
);
1184 case VOCTRL_DRAW_IMAGE
:
1185 switch (image_format
)
1192 return draw_yuv_image(data
);
1197 case VOCTRL_UPDATE_SCREENINFO
:
1198 update_screen_info();
1204 void window_resized(void)
1211 CGContextRef context
;
1213 GetWindowPortBounds(theWindow
, &winRect
);
1214 d_width
= vo_dwidth
= winRect
.right
;
1215 d_height
= vo_dheight
= winRect
.bottom
;
1218 aspect(&d_width
, &d_height
, A_WINZOOM
);
1219 SetRect(&dstRect
, (vo_dwidth
- d_width
) / 2, (vo_dheight
- d_height
) / 2, d_width
, d_height
);
1221 switch (image_format
)
1225 bounds
= CGRectMake(dstRect
.left
, dstRect
.top
, dstRect
.right
- dstRect
.left
, dstRect
.bottom
- dstRect
.top
);
1234 long scale_X
= FixDiv(Long2Fix(dstRect
.right
- dstRect
.left
), Long2Fix(imgRect
.right
));
1235 long scale_Y
= FixDiv(Long2Fix(dstRect
.bottom
- dstRect
.top
), Long2Fix(imgRect
.bottom
));
1237 SetIdentityMatrix(&matrix
);
1238 if (dstRect
.right
- dstRect
.left
!= imgRect
.right
|| dstRect
.bottom
- dstRect
.right
!= imgRect
.bottom
)
1240 ScaleMatrix(&matrix
, scale_X
, scale_Y
, 0, 0);
1242 if (vo_dwidth
> d_width
|| vo_dheight
> d_height
)
1244 TranslateMatrix(&matrix
, Long2Fix(dstRect
.left
), Long2Fix(dstRect
.top
));
1248 SetDSequenceMatrix(seqId
, &matrix
);
1256 tmpBounds
= CGRectMake(0, 0, winRect
.right
, winRect
.bottom
);
1257 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
1258 CGContextFillRect(context
, tmpBounds
);
1259 QDEndCGContext(GetWindowPort(theWindow
), &context
);
1262 void window_ontop(void)
1266 // Cycle between level
1271 SetWindowGroupLevel(winGroup
, CGWindowLevelForKey(levelList
[winLevel
]));
1274 void window_fullscreen(void)
1281 if (displayId
== kCGDirectMainDisplay
)
1283 SetSystemUIMode(kUIModeAllHidden
, kUIOptionAutoShowMenuBar
);
1284 CGDisplayHideCursor(displayId
);
1288 if (fs_res_x
!= 0 || fs_res_y
!= 0)
1290 CFDictionaryRef mode
;
1291 size_t desiredBitDepth
= 32;
1292 boolean_t exactMatch
;
1294 originalMode
= CGDisplayCurrentMode(displayId
);
1296 mode
= CGDisplayBestModeForParameters(displayId
, desiredBitDepth
, fs_res_x
, fs_res_y
, &exactMatch
);
1302 // Warn if the mode doesn't match exactly
1303 mp_msg(MSGT_VO
, MSGL_WARN
, "Quartz warning: did not get exact mode match (got %dx%d) \n", (int)CFDictionaryGetValue(mode
, kCGDisplayWidth
), (int)CFDictionaryGetValue(mode
, kCGDisplayHeight
));
1306 CGDisplayCapture(displayId
);
1307 CGDisplaySwitchToMode(displayId
, mode
);
1311 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: can't switch to fullscreen \n");
1314 // Get Main device info///////////////////////////////////////////////////
1315 update_screen_info();
1318 // save old window size
1321 GetWindowPortBounds(theWindow
, &oldWinRect
);
1322 GetWindowBounds(theWindow
, kWindowContentRgn
, &oldWinBounds
);
1325 ChangeWindowAttributes(theWindow
, kWindowNoShadowAttribute
, 0);
1330 else //go back to windowed mode
1333 if (originalMode
!= NULL
)
1335 CGDisplaySwitchToMode(displayId
, originalMode
);
1336 CGDisplayRelease(displayId
);
1338 // Get Main device info///////////////////////////////////////////////////
1339 update_screen_info();
1341 originalMode
= NULL
;
1343 SetSystemUIMode(kUIModeNormal
, 0);
1345 // show mouse cursor
1346 CGDisplayShowCursor(displayId
);
1349 // revert window to previous setting
1350 ChangeWindowAttributes(theWindow
, 0, kWindowNoShadowAttribute
);
1351 SizeWindow(theWindow
, oldWinRect
.right
, oldWinRect
.bottom
, 1);
1352 MoveWindow(theWindow
, oldWinBounds
.left
, oldWinBounds
.top
, 1);
1357 void window_panscan(void)
1362 CheckMenuItem(aspectMenu
, 2, 1);
1364 CheckMenuItem(aspectMenu
, 2, 0);
1368 MoveWindow(theWindow
, xinerama_x
- (vo_panscan_x
>> 1), xinerama_y
- (vo_panscan_y
>> 1), 1);
1369 SizeWindow(theWindow
, vo_screenwidth
+ vo_panscan_x
, vo_screenheight
+ vo_panscan_y
, 1);