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/mouse.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;
135 kNormalScreenCmd
= 3,
136 kDoubleScreenCmd
= 4,
145 #include "osdep/keycodes.h"
147 //PROTOTYPE/////////////////////////////////////////////////////////////////
148 static OSStatus
KeyEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
);
149 static OSStatus
MouseEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
);
150 static OSStatus
WindowEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
);
151 void window_resized(void);
152 void window_ontop(void);
153 void window_fullscreen(void);
154 void window_panscan(void);
156 static void draw_alpha(int x0
, int y0
, int w
, int h
, unsigned char *src
, unsigned char *srca
, int stride
)
158 switch (image_format
)
161 vo_draw_alpha_rgb32(w
, h
, src
, srca
, stride
, image_data
+ 4 * (y0
* imgRect
.right
+ x0
), 4 * imgRect
.right
);
166 vo_draw_alpha_yv12(w
, h
, src
, srca
, stride
, ((char *)P
) + be2me_32(P
->componentInfoY
.offset
) + x0
+ y0
* imgRect
.right
, imgRect
.right
);
169 vo_draw_alpha_uyvy(w
, h
, src
, srca
, stride
, ((char *)P
) + (x0
+ y0
* imgRect
.right
) * 2, imgRect
.right
* 2);
172 vo_draw_alpha_yuy2(w
, h
, src
, srca
, stride
, ((char *)P
) + (x0
+ y0
* imgRect
.right
) * 2, imgRect
.right
* 2);
177 //default keyboard event handler
178 static OSStatus
KeyEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
180 OSStatus result
= noErr
;
181 UInt32
class = GetEventClass(event
);
182 UInt32 kind
= GetEventKind(event
);
184 result
= CallNextEventHandler(nextHandler
, event
);
186 if (class == kEventClassKeyboard
)
190 UInt32 macKeyModifiers
;
192 GetEventParameter(event
, kEventParamKeyMacCharCodes
, typeChar
, NULL
, sizeof(macCharCodes
), NULL
, &macCharCodes
);
193 GetEventParameter(event
, kEventParamKeyCode
, typeUInt32
, NULL
, sizeof(macKeyCode
), NULL
, &macKeyCode
);
194 GetEventParameter(event
, kEventParamKeyModifiers
, typeUInt32
, NULL
, sizeof(macKeyModifiers
), NULL
, &macKeyModifiers
);
196 if (macKeyModifiers
!= 256)
198 if (kind
== kEventRawKeyRepeat
|| kind
== kEventRawKeyDown
)
200 int key
= convert_key(macKeyCode
, macCharCodes
);
203 mplayer_put_key(key
);
206 else if (macKeyModifiers
== 256)
208 switch (macCharCodes
)
210 case '[': SetWindowAlpha(theWindow
, winAlpha
-= 0.05); break;
211 case ']': SetWindowAlpha(theWindow
, winAlpha
+= 0.05); break;
215 result
= eventNotHandledErr
;
221 //default mouse event handler
222 static OSStatus
MouseEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
224 OSStatus result
= noErr
;
225 UInt32
class = GetEventClass(event
);
226 UInt32 kind
= GetEventKind(event
);
228 result
= CallNextEventHandler(nextHandler
, event
);
230 if (class == kEventClassMouse
)
236 GetEventParameter(event
, kEventParamMouseLocation
, typeQDPoint
, 0, sizeof(mousePos
), 0, &mousePos
);
237 GetEventParameter(event
, kEventParamWindowMouseLocation
, typeQDPoint
, 0, sizeof(winMousePos
), 0, &winMousePos
);
241 case kEventMouseMoved
:
245 CGDisplayShowCursor(displayId
);
251 case kEventMouseWheelMoved
:
256 GetEventParameter(event
, kEventParamMouseWheelDelta
, typeSInt32
, 0, sizeof(wheel
), 0, &wheel
);
258 part
= FindWindow(mousePos
, &tmpWin
);
260 if (part
== inContent
)
263 mplayer_put_key(MOUSE_BTN3
);
265 mplayer_put_key(MOUSE_BTN4
);
270 case kEventMouseDown
:
273 EventMouseButton button
;
277 GetWindowPortBounds(theWindow
, &bounds
);
278 GetEventParameter(event
, kEventParamMouseButton
, typeMouseButton
, 0, sizeof(button
), 0, &button
);
280 part
= FindWindow(mousePos
, &tmpWin
);
281 if (kind
== kEventMouseUp
)
283 if (part
!= inContent
)
287 case kEventMouseButtonPrimary
:
288 mplayer_put_key(MOUSE_BTN0
);
290 case kEventMouseButtonSecondary
:
291 mplayer_put_key(MOUSE_BTN2
);
293 case kEventMouseButtonTertiary
:
294 mplayer_put_key(MOUSE_BTN1
);
298 result
= eventNotHandledErr
;
303 if (winMousePos
.h
> bounds
.right
- 15 && winMousePos
.v
> bounds
.bottom
)
309 ResizeWindow(theWindow
, mousePos
, NULL
, &newSize
);
312 else if (part
== inMenuBar
)
314 MenuSelect(mousePos
);
317 else if (part
== inContent
)
321 case kEventMouseButtonPrimary
:
322 mplayer_put_key(MOUSE_BTN0
| MP_KEY_DOWN
);
324 case kEventMouseButtonSecondary
:
325 mplayer_put_key(MOUSE_BTN2
| MP_KEY_DOWN
);
327 case kEventMouseButtonTertiary
:
328 mplayer_put_key(MOUSE_BTN1
| MP_KEY_DOWN
);
332 result
= eventNotHandledErr
;
339 case kEventMouseDragged
:
343 result
= eventNotHandledErr
;
351 static void set_winSizeMult(float mult
)
353 int d_width
, d_height
;
354 aspect(&d_width
, &d_height
, A_NOZOOM
);
363 SizeWindow(theWindow
, d_width
* mult
, d_height
* mult
, 1);
367 //default window event handler
368 static OSStatus
WindowEventHandler(EventHandlerCallRef nextHandler
, EventRef event
, void *userData
)
370 OSStatus result
= noErr
;
371 UInt32
class = GetEventClass(event
);
372 UInt32 kind
= GetEventKind(event
);
374 result
= CallNextEventHandler(nextHandler
, event
);
376 if (class == kEventClassCommand
)
378 HICommand theHICommand
;
380 GetEventParameter(event
, kEventParamDirectObject
, typeHICommand
, NULL
, sizeof(theHICommand
), NULL
, &theHICommand
);
382 switch (theHICommand
.commandID
)
385 mplayer_put_key(KEY_CLOSE_WIN
);
389 set_winSizeMult(0.5);
392 case kNormalScreenCmd
:
396 case kDoubleScreenCmd
:
406 vo_keepaspect
= !vo_keepaspect
;
407 CheckMenuItem(aspectMenu
, 1, vo_keepaspect
);
412 change_movie_aspect(-1);
416 change_movie_aspect(4.0 / 3.0);
420 change_movie_aspect(16.0 / 9.0);
424 vo_panscan
= !vo_panscan
;
425 CheckMenuItem(aspectMenu
, 2, vo_panscan
);
431 result
= eventNotHandledErr
;
435 else if (class == kEventClassWindow
)
438 Rect rectWindow
= { 0, 0, 0, 0 };
440 GetEventParameter(event
, kEventParamDirectObject
, typeWindowRef
, NULL
, sizeof(window
), NULL
, &window
);
444 GetWindowBounds(window
, kWindowGlobalPortRgn
, &rectWindow
);
449 case kEventWindowClosed
:
451 mplayer_put_key(KEY_CLOSE_WIN
);
455 case kEventWindowZoomed
:
456 case kEventWindowBoundsChanged
:
463 result
= eventNotHandledErr
;
471 static void quartz_CreateWindow(uint32_t d_width
, uint32_t d_height
, WindowAttributes windowAttrs
)
473 CFStringRef titleKey
;
474 CFStringRef windowTitle
;
478 CFStringRef movMenuTitle
;
479 CFStringRef aspMenuTitle
;
481 const EventTypeSpec win_events
[] = {
482 {kEventClassWindow
, kEventWindowClosed
},
483 {kEventClassWindow
, kEventWindowBoundsChanged
},
484 {kEventClassCommand
, kEventCommandProcess
}
487 const EventTypeSpec key_events
[] = {
488 {kEventClassKeyboard
, kEventRawKeyDown
},
489 {kEventClassKeyboard
, kEventRawKeyRepeat
}
492 const EventTypeSpec mouse_events
[] = {
493 {kEventClassMouse
, kEventMouseMoved
},
494 {kEventClassMouse
, kEventMouseWheelMoved
},
495 {kEventClassMouse
, kEventMouseDown
},
496 {kEventClassMouse
, kEventMouseUp
},
497 {kEventClassMouse
, kEventMouseDragged
}
500 SetRect(&winRect
, 0, 0, d_width
, d_height
);
501 SetRect(&oldWinRect
, 0, 0, d_width
, d_height
);
502 SetRect(&dstRect
, 0, 0, d_width
, d_height
);
507 // Create Window Menu
508 CreateStandardWindowMenu(0, &windMenu
);
509 InsertMenu(windMenu
, 0);
512 CreateNewMenu(1004, 0, &movMenu
);
513 movMenuTitle
= CFSTR("Movie");
514 SetMenuTitleWithCFString(movMenu
, movMenuTitle
);
516 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Half Size"), 0, kHalfScreenCmd
, &index
);
517 SetMenuItemCommandKey(movMenu
, index
, 0, '0');
519 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Normal Size"), 0, kNormalScreenCmd
, &index
);
520 SetMenuItemCommandKey(movMenu
, index
, 0, '1');
522 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Double Size"), 0, kDoubleScreenCmd
, &index
);
523 SetMenuItemCommandKey(movMenu
, index
, 0, '2');
525 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Full Size"), 0, kFullScreenCmd
, &index
);
526 SetMenuItemCommandKey(movMenu
, index
, 0, 'F');
528 AppendMenuItemTextWithCFString(movMenu
, NULL
, kMenuItemAttrSeparator
, 0, &index
);
530 AppendMenuItemTextWithCFString(movMenu
, CFSTR("Aspect Ratio"), 0, 0, &index
);
532 //// Create Aspect Ratio Sub Menu
533 CreateNewMenu(0, 0, &aspectMenu
);
534 aspMenuTitle
= CFSTR("Aspect Ratio");
535 SetMenuTitleWithCFString(aspectMenu
, aspMenuTitle
);
536 SetMenuItemHierarchicalMenu(movMenu
, 6, aspectMenu
);
538 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("Keep"), 0, kKeepAspectCmd
, &index
);
539 CheckMenuItem(aspectMenu
, 1, vo_keepaspect
);
540 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("Pan-Scan"), 0, kPanScanCmd
, &index
);
541 CheckMenuItem(aspectMenu
, 2, vo_panscan
);
542 AppendMenuItemTextWithCFString(aspectMenu
, NULL
, kMenuItemAttrSeparator
, 0, &index
);
543 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("Original"), 0, kAspectOrgCmd
, &index
);
544 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("4:3"), 0, kAspectFullCmd
, &index
);
545 AppendMenuItemTextWithCFString(aspectMenu
, CFSTR("16:9"), 0, kAspectWideCmd
, &index
);
547 InsertMenu(movMenu
, GetMenuID(windMenu
)); //insert before Window menu
552 CreateNewWindow(kDocumentWindowClass
, windowAttrs
, &winRect
, &theWindow
);
554 CreateWindowGroup(0, &winGroup
);
555 SetWindowGroup(theWindow
, winGroup
);
558 titleKey
= CFSTR("MPlayer - The Movie Player");
559 windowTitle
= CFCopyLocalizedString(titleKey
, NULL
);
560 result
= SetWindowTitleWithCFString(theWindow
, windowTitle
);
562 CFRelease(windowTitle
);
564 // Install event handler
565 InstallApplicationEventHandler(NewEventHandlerUPP(KeyEventHandler
), GetEventTypeCount(key_events
), key_events
, NULL
, NULL
);
566 InstallApplicationEventHandler(NewEventHandlerUPP(MouseEventHandler
), GetEventTypeCount(mouse_events
), mouse_events
, NULL
, NULL
);
567 InstallWindowEventHandler(theWindow
, NewEventHandlerUPP(WindowEventHandler
), GetEventTypeCount(win_events
), win_events
, theWindow
, NULL
);
570 static void update_screen_info(void)
573 CGDisplayCount displayCount
;
574 CGDirectDisplayID
*displays
;
575 // Display IDs might not be consecutive, get the list of all devices up to # device_id
576 displayCount
= device_id
+ 1;
577 displays
= malloc(sizeof(*displays
) * displayCount
);
578 if (kCGErrorSuccess
!= CGGetActiveDisplayList(displayCount
, displays
, &displayCount
) || displayCount
< device_id
+ 1) {
579 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: Device ID %d do not exist, falling back to main device.\n", device_id
);
580 displayId
= kCGDirectMainDisplay
;
585 displayId
= displays
[device_id
];
589 displayRect
= CGDisplayBounds(displayId
);
590 xinerama_x
= displayRect
.origin
.x
;
591 xinerama_y
= displayRect
.origin
.y
;
592 vo_screenwidth
= displayRect
.size
.width
;
593 vo_screenheight
= displayRect
.size
.height
;
594 aspect_save_screenres(vo_screenwidth
, vo_screenheight
);
597 static void free_video_specific(void)
599 if (seqId
) CDSequenceEnd(seqId
);
605 CGDataProviderRelease(dataProviderRef
);
606 dataProviderRef
= NULL
;
607 CGImageRelease(image
);
611 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
)
613 WindowAttributes windowAttrs
;
617 free_video_specific();
619 vo_dwidth
= d_width
*= winSizeMult
;
620 vo_dheight
= d_height
*= winSizeMult
;
621 config_movie_aspect((float)d_width
/ d_height
);
623 // misc mplayer setup/////////////////////////////////////////////////////
624 SetRect(&imgRect
, 0, 0, width
, height
);
625 switch (image_format
)
638 image_size
= (imgRect
.right
* imgRect
.bottom
* image_depth
+ 7) / 8;
640 image_data
= malloc(image_size
);
642 // Create player window//////////////////////////////////////////////////
643 windowAttrs
= kWindowStandardDocumentAttributes
644 | kWindowStandardHandlerAttribute
645 | kWindowLiveResizeAttribute
;
647 windowAttrs
&= ~kWindowResizableAttribute
;
649 if (theWindow
== NULL
)
651 CGContextRef context
;
653 quartz_CreateWindow(d_width
, d_height
, windowAttrs
);
655 if (theWindow
== NULL
)
657 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: Couldn't create window !!!!!\n");
660 tmpBounds
= CGRectMake(0, 0, winRect
.right
, winRect
.bottom
);
661 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
662 CGContextFillRect(context
, tmpBounds
);
663 QDEndCGContext(GetWindowPort(theWindow
), &context
);
667 HideWindow(theWindow
);
668 ChangeWindowAttributes(theWindow
, ~windowAttrs
, windowAttrs
);
669 SetRect(&winRect
, 0, 0, d_width
, d_height
);
670 SetRect(&oldWinRect
, 0, 0, d_width
, d_height
);
671 SizeWindow(theWindow
, d_width
, d_height
, 1);
674 switch (image_format
)
678 CGContextRef context
;
680 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
682 dataProviderRef
= CGDataProviderCreateWithData(0, image_data
, imgRect
.right
* imgRect
.bottom
* 4, 0);
684 image
= CGImageCreate(imgRect
.right
,
688 (imgRect
.right
* 32 + 7) / 8,
689 CGColorSpaceCreateDeviceRGB(),
690 kCGImageAlphaNoneSkipFirst
,
691 dataProviderRef
, 0, 1, kCGRenderingIntentDefault
);
693 QDEndCGContext(GetWindowPort(theWindow
), &context
);
705 if (!EnterMoviesDone
)
707 qterr
= EnterMovies();
715 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: EnterMovies (%d)\n", qterr
);
720 SetIdentityMatrix(&matrix
);
722 if (d_width
!= width
|| d_height
!= height
)
724 ScaleMatrix(&matrix
, FixDiv(Long2Fix(d_width
), Long2Fix(width
)), FixDiv(Long2Fix(d_height
), Long2Fix(height
)), 0, 0);
727 yuv_qt_stuff
.desc
= (ImageDescriptionHandle
) NewHandleClear(sizeof(ImageDescription
));
729 yuv_qt_stuff
.extension_colr
= NewHandleClear(sizeof(NCLCColorInfoImageDescriptionExtension
));
730 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->colorParamType
= kVideoColorInfoImageDescriptionExtensionType
;
731 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->primaries
= 2;
732 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->transferFunction
= 2;
733 ((NCLCColorInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_colr
))->matrix
= 2;
735 yuv_qt_stuff
.extension_fiel
= NewHandleClear(sizeof(FieldInfoImageDescriptionExtension
));
736 ((FieldInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_fiel
))->fieldCount
= 1;
737 ((FieldInfoImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_fiel
))->fieldOrderings
= 0;
739 yuv_qt_stuff
.extension_clap
= NewHandleClear(sizeof(CleanApertureImageDescriptionExtension
));
740 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureWidthN
= imgRect
.right
;
741 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureWidthD
= 1;
742 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureHeightN
= imgRect
.bottom
;
743 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->cleanApertureHeightD
= 1;
744 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->horizOffN
= 0;
745 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->horizOffD
= 1;
746 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->vertOffN
= 0;
747 ((CleanApertureImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_clap
))->vertOffD
= 1;
749 yuv_qt_stuff
.extension_pasp
= NewHandleClear(sizeof(PixelAspectRatioImageDescriptionExtension
));
750 ((PixelAspectRatioImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_pasp
))->hSpacing
= 1;
751 ((PixelAspectRatioImageDescriptionExtension
*) (*yuv_qt_stuff
.extension_pasp
))->vSpacing
= 1;
753 (*yuv_qt_stuff
.desc
)->idSize
= sizeof(ImageDescription
);
754 (*yuv_qt_stuff
.desc
)->cType
= image_qtcodec
;
755 (*yuv_qt_stuff
.desc
)->version
= 2;
756 (*yuv_qt_stuff
.desc
)->revisionLevel
= 0;
757 (*yuv_qt_stuff
.desc
)->vendor
= 'mpla';
758 (*yuv_qt_stuff
.desc
)->width
= imgRect
.right
;
759 (*yuv_qt_stuff
.desc
)->height
= imgRect
.bottom
;
760 (*yuv_qt_stuff
.desc
)->hRes
= Long2Fix(72);
761 (*yuv_qt_stuff
.desc
)->vRes
= Long2Fix(72);
762 (*yuv_qt_stuff
.desc
)->temporalQuality
= 0;
763 (*yuv_qt_stuff
.desc
)->spatialQuality
= codecLosslessQuality
;
764 (*yuv_qt_stuff
.desc
)->frameCount
= 1;
765 (*yuv_qt_stuff
.desc
)->dataSize
= 0;
766 (*yuv_qt_stuff
.desc
)->depth
= 24;
767 (*yuv_qt_stuff
.desc
)->clutID
= -1;
769 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_colr
, kColorInfoImageDescriptionExtension
);
772 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [colr] (%d)\n", qterr
);
775 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_fiel
, kFieldInfoImageDescriptionExtension
);
778 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [fiel] (%d)\n", qterr
);
781 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_clap
, kCleanApertureImageDescriptionExtension
);
784 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [clap] (%d)\n", qterr
);
787 qterr
= AddImageDescriptionExtension(yuv_qt_stuff
.desc
, yuv_qt_stuff
.extension_pasp
, kCleanApertureImageDescriptionExtension
);
790 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: AddImageDescriptionExtension [pasp] (%d)\n", qterr
);
792 P
= calloc(sizeof(PlanarPixmapInfoYUV420
) + image_size
, 1);
793 switch (image_format
)
798 P
->componentInfoY
.offset
= be2me_32(sizeof(PlanarPixmapInfoYUV420
));
799 P
->componentInfoCb
.offset
= be2me_32(be2me_32(P
->componentInfoY
.offset
) + image_size
/ 2);
800 P
->componentInfoCr
.offset
= be2me_32(be2me_32(P
->componentInfoCb
.offset
) + image_size
/ 4);
801 P
->componentInfoY
.rowBytes
= be2me_32(imgRect
.right
);
802 P
->componentInfoCb
.rowBytes
= be2me_32(imgRect
.right
/ 2);
803 P
->componentInfoCr
.rowBytes
= be2me_32(imgRect
.right
/ 2);
804 image_buffer_size
= image_size
+ sizeof(PlanarPixmapInfoYUV420
);
808 image_buffer_size
= image_size
;
812 qterr
= DecompressSequenceBeginS(&seqId
,
816 GetWindowPort(theWindow
),
819 d_width
!= width
|| d_height
!= height
?
824 codecLosslessQuality
,
829 mp_msg(MSGT_VO
, MSGL_FATAL
, "Quartz error: DecompressSequenceBeginS (%d)\n", qterr
);
837 RepositionWindow(theWindow
, NULL
, kWindowCenterOnMainScreen
);
838 ShowWindow(theWindow
);
850 SetWindowGroupLevel(winGroup
, CGWindowLevelForKey(levelList
[winLevel
]));
859 static void check_events(void)
862 EventTargetRef theTarget
;
866 theTarget
= GetEventDispatcherTarget();
867 theErr
= ReceiveNextEvent(0, 0, kEventDurationNoWait
, true, &theEvent
);
868 if (theErr
== noErr
&& theEvent
!= NULL
)
870 SendEventToEventTarget(theEvent
, theTarget
);
871 ReleaseEvent(theEvent
);
875 static void draw_osd(void)
877 vo_draw_text(imgRect
.right
, imgRect
.bottom
, draw_alpha
);
880 static void flip_page(void)
884 if (theWindow
== NULL
)
887 switch (image_format
)
891 CGContextRef context
;
893 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
894 CGContextDrawImage(context
, bounds
, image
);
895 QDEndCGContext(GetWindowPort(theWindow
), &context
);
907 CodecFlags flags
= 0;
909 qterr
= DecompressSequenceFrameWhen(seqId
,
912 0, //codecFlagUseImageBuffer,
918 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: DecompressSequenceFrameWhen in flip_page (%d) flags:0x%08x\n", qterr
, flags
);
926 CGContextRef context
;
928 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
930 CGContextBeginPath(context
);
931 CGContextSetAllowsAntialiasing(context
, false);
932 //CGContextSaveGState(context);
935 CGContextSetRGBStrokeColor(context
, 0.2, 0.2, 0.2, 0.5);
936 CGContextMoveToPoint(context
, winRect
.right
- 1, 1); CGContextAddLineToPoint(context
, winRect
.right
- 1, 1);
937 CGContextMoveToPoint(context
, winRect
.right
- 1, 5); CGContextAddLineToPoint(context
, winRect
.right
- 5, 1);
938 CGContextMoveToPoint(context
, winRect
.right
- 1, 9); CGContextAddLineToPoint(context
, winRect
.right
- 9, 1);
939 CGContextStrokePath(context
);
942 CGContextSetRGBStrokeColor(context
, 0.4, 0.4, 0.4, 0.5);
943 CGContextMoveToPoint(context
, winRect
.right
- 1, 2); CGContextAddLineToPoint(context
, winRect
.right
- 2, 1);
944 CGContextMoveToPoint(context
, winRect
.right
- 1, 6); CGContextAddLineToPoint(context
, winRect
.right
- 6, 1);
945 CGContextMoveToPoint(context
, winRect
.right
- 1, 10); CGContextAddLineToPoint(context
, winRect
.right
- 10, 1);
946 CGContextStrokePath(context
);
949 CGContextSetRGBStrokeColor(context
, 0.6, 0.6, 0.6, 0.5);
950 CGContextMoveToPoint(context
, winRect
.right
- 1, 3); CGContextAddLineToPoint(context
, winRect
.right
- 3, 1);
951 CGContextMoveToPoint(context
, winRect
.right
- 1, 7); CGContextAddLineToPoint(context
, winRect
.right
- 7, 1);
952 CGContextMoveToPoint(context
, winRect
.right
- 1, 11); CGContextAddLineToPoint(context
, winRect
.right
- 11, 1);
953 CGContextStrokePath(context
);
955 // CGContextRestoreGState( context );
956 CGContextFlush(context
);
957 QDEndCGContext(GetWindowPort(theWindow
), &context
);
960 curTime
= TickCount() / 60;
962 // auto hide mouse cursor (and future on-screen control?)
963 if (vo_quartz_fs
&& !mouseHide
)
965 if (curTime
- lastMouseHide
>= 5 || lastMouseHide
== 0)
967 CGDisplayHideCursor(displayId
);
969 lastMouseHide
= curTime
;
972 // update activity every 30 seconds to prevent
973 // screensaver from starting up.
974 if (curTime
- lastScreensaverUpdate
>= 30 || lastScreensaverUpdate
== 0)
976 UpdateSystemActivity(UsrActivity
);
977 lastScreensaverUpdate
= curTime
;
981 static int draw_slice(uint8_t * src
[], int stride
[], int w
, int h
, int x
, int y
)
983 switch (image_format
)
987 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoY
.offset
) + x
+ imgRect
.right
* y
, src
[0], w
, h
, imgRect
.right
, stride
[0]);
988 x
=x
/2;y
=y
/2;w
=w
/2;h
=h
/2;
990 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCb
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[1], w
, h
, imgRect
.right
/ 2, stride
[1]);
991 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCr
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[2], w
, h
, imgRect
.right
/ 2, stride
[2]);
995 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoY
.offset
) + x
+ imgRect
.right
* y
, src
[0], w
, h
, imgRect
.right
, stride
[0]);
996 x
=x
/2;y
=y
/2;w
=w
/2;h
=h
/2;
998 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCr
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[1], w
, h
, imgRect
.right
/ 2, stride
[1]);
999 memcpy_pic(((char *)P
) + be2me_32(P
->componentInfoCb
.offset
) + x
+ imgRect
.right
/ 2 * y
, src
[2], w
, h
, imgRect
.right
/ 2, stride
[2]);
1005 static int draw_frame(uint8_t * src
[])
1007 switch (image_format
)
1010 fast_memcpy(image_data
, src
[0], image_size
);
1015 memcpy_pic(((char *)P
), src
[0], imgRect
.right
* 2, imgRect
.bottom
, imgRect
.right
* 2, imgRect
.right
* 2);
1021 static int query_format(uint32_t format
)
1023 image_format
= format
;
1026 if (format
== IMGFMT_RGB32
)
1028 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
1031 if (format
== IMGFMT_YV12
|| format
== IMGFMT_IYUV
|| format
== IMGFMT_I420
)
1033 image_qtcodec
= kMpegYUV420CodecType
; //kYUV420CodecType ?;
1034 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
| VFCAP_ACCEPT_STRIDE
;
1037 if (format
== IMGFMT_YUY2
)
1039 image_qtcodec
= kComponentVideoUnsigned
;
1040 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
1043 if (format
== IMGFMT_UYVY
)
1045 image_qtcodec
= k422YpCbCr8CodecType
;
1046 return VFCAP_CSP_SUPPORTED
| VFCAP_OSD
| VFCAP_HWSCALE_UP
| VFCAP_HWSCALE_DOWN
;
1052 static void uninit(void)
1054 free_video_specific();
1055 if (EnterMoviesDone
)
1057 EnterMoviesDone
= 0;
1062 static int preinit(const char *arg
)
1068 char *parse_pos
= (char *)&arg
[0];
1070 while (parse_pos
[0] && !parse_err
)
1072 if (strncmp(parse_pos
, "device_id=", 10) == 0)
1074 parse_pos
= &parse_pos
[10];
1075 device_id
= strtol(parse_pos
, &parse_pos
, 0);
1077 if (strncmp(parse_pos
, "fs_res=", 7) == 0)
1079 parse_pos
= &parse_pos
[7];
1080 fs_res_x
= strtol(parse_pos
, &parse_pos
, 0);
1081 parse_pos
= &parse_pos
[1];
1082 fs_res_y
= strtol(parse_pos
, &parse_pos
, 0);
1084 if (parse_pos
[0] == ':')
1085 parse_pos
= &parse_pos
[1];
1086 else if (parse_pos
[0])
1091 #if !defined (CONFIG_MACOSX_FINDER) || !defined (CONFIG_SDL)
1092 // this chunk of code is heavily based off SDL_macosx.m from SDL
1093 // the CPSEnableForegroundOperation that was here before is private and shouldn't be used
1094 // replaced by a call to the 10.3+ TransformProcessType
1096 ProcessSerialNumber myProc
, frProc
;
1099 if (GetFrontProcess(&frProc
) == noErr
)
1101 if (GetCurrentProcess(&myProc
) == noErr
)
1103 if (SameProcess(&frProc
, &myProc
, &sameProc
) == noErr
&& !sameProc
)
1105 TransformProcessType(&myProc
, kProcessTransformToForegroundApplication
);
1107 SetFrontProcess(&myProc
);
1116 static uint32_t draw_yuv_image(mp_image_t
* mpi
)
1118 // ATM we're only called for planar IMGFMT
1119 // drawing is done directly in P
1120 // and displaying is in flip_page.
1121 return get_image_done
? VO_TRUE
: VO_FALSE
;
1124 static uint32_t get_yuv_image(mp_image_t
* mpi
)
1126 if (mpi
->type
!= MP_IMGTYPE_EXPORT
) return VO_FALSE
;
1128 if (mpi
->imgfmt
!= image_format
) return VO_FALSE
;
1130 if (mpi
->flags
& MP_IMGFLAG_PLANAR
)
1132 if (mpi
->num_planes
!= 3)
1134 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: only 3 planes allowed in get_yuv_image for planar (%d) \n", mpi
->num_planes
);
1138 mpi
->planes
[0] = ((char *)P
) + be2me_32(P
->componentInfoY
.offset
);
1139 mpi
->stride
[0] = imgRect
.right
;
1140 mpi
->width
= imgRect
.right
;
1142 if (mpi
->flags
& MP_IMGFLAG_SWAPPED
)
1145 mpi
->planes
[1] = ((char *)P
) + be2me_32(P
->componentInfoCb
.offset
);
1146 mpi
->planes
[2] = ((char *)P
) + be2me_32(P
->componentInfoCr
.offset
);
1147 mpi
->stride
[1] = imgRect
.right
/ 2;
1148 mpi
->stride
[2] = imgRect
.right
/ 2;
1153 mpi
->planes
[1] = ((char *)P
) + be2me_32(P
->componentInfoCr
.offset
);
1154 mpi
->planes
[2] = ((char *)P
) + be2me_32(P
->componentInfoCb
.offset
);
1155 mpi
->stride
[1] = imgRect
.right
/ 2;
1156 mpi
->stride
[2] = imgRect
.right
/ 2;
1159 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
1166 if (mpi
->num_planes
!= 1)
1168 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: only 1 plane allowed in get_yuv_image for packed (%d) \n", mpi
->num_planes
);
1172 mpi
->planes
[0] = (char *)P
;
1173 mpi
->stride
[0] = imgRect
.right
* 2;
1174 mpi
->width
= imgRect
.right
;
1175 mpi
->flags
|= MP_IMGFLAG_DIRECT
;
1182 static int control(uint32_t request
, void *data
)
1186 case VOCTRL_PAUSE
: return int_pause
= 1;
1187 case VOCTRL_RESUME
: return int_pause
= 0;
1188 case VOCTRL_FULLSCREEN
: vo_fs
= !vo_fs
; window_fullscreen(); return VO_TRUE
;
1189 case VOCTRL_ONTOP
: vo_ontop
= !vo_ontop
; window_ontop(); return VO_TRUE
;
1190 case VOCTRL_QUERY_FORMAT
: return query_format(*(uint32_t *) data
);
1191 case VOCTRL_GET_PANSCAN
: return VO_TRUE
;
1192 case VOCTRL_SET_PANSCAN
: window_panscan(); return VO_TRUE
;
1194 case VOCTRL_GET_IMAGE
:
1195 switch (image_format
)
1202 return get_yuv_image(data
);
1207 case VOCTRL_DRAW_IMAGE
:
1208 switch (image_format
)
1215 return draw_yuv_image(data
);
1220 case VOCTRL_UPDATE_SCREENINFO
:
1221 update_screen_info();
1227 void window_resized(void)
1234 CGContextRef context
;
1236 GetWindowPortBounds(theWindow
, &winRect
);
1237 d_width
= vo_dwidth
= winRect
.right
;
1238 d_height
= vo_dheight
= winRect
.bottom
;
1241 aspect(&d_width
, &d_height
, A_WINZOOM
);
1242 SetRect(&dstRect
, (vo_dwidth
- d_width
) / 2, (vo_dheight
- d_height
) / 2, d_width
, d_height
);
1244 switch (image_format
)
1248 bounds
= CGRectMake(dstRect
.left
, dstRect
.top
, dstRect
.right
- dstRect
.left
, dstRect
.bottom
- dstRect
.top
);
1257 long scale_X
= FixDiv(Long2Fix(dstRect
.right
- dstRect
.left
), Long2Fix(imgRect
.right
));
1258 long scale_Y
= FixDiv(Long2Fix(dstRect
.bottom
- dstRect
.top
), Long2Fix(imgRect
.bottom
));
1260 SetIdentityMatrix(&matrix
);
1261 if (dstRect
.right
- dstRect
.left
!= imgRect
.right
|| dstRect
.bottom
- dstRect
.right
!= imgRect
.bottom
)
1263 ScaleMatrix(&matrix
, scale_X
, scale_Y
, 0, 0);
1265 if (vo_dwidth
> d_width
|| vo_dheight
> d_height
)
1267 TranslateMatrix(&matrix
, Long2Fix(dstRect
.left
), Long2Fix(dstRect
.top
));
1271 SetDSequenceMatrix(seqId
, &matrix
);
1279 tmpBounds
= CGRectMake(0, 0, winRect
.right
, winRect
.bottom
);
1280 QDBeginCGContext(GetWindowPort(theWindow
), &context
);
1281 CGContextFillRect(context
, tmpBounds
);
1282 QDEndCGContext(GetWindowPort(theWindow
), &context
);
1285 void window_ontop(void)
1289 // Cycle between level
1294 SetWindowGroupLevel(winGroup
, CGWindowLevelForKey(levelList
[winLevel
]));
1297 void window_fullscreen(void)
1304 if (displayId
== kCGDirectMainDisplay
)
1306 SetSystemUIMode(kUIModeAllHidden
, kUIOptionAutoShowMenuBar
);
1307 CGDisplayHideCursor(displayId
);
1311 if (fs_res_x
!= 0 || fs_res_y
!= 0)
1313 CFDictionaryRef mode
;
1314 size_t desiredBitDepth
= 32;
1315 boolean_t exactMatch
;
1317 originalMode
= CGDisplayCurrentMode(displayId
);
1319 mode
= CGDisplayBestModeForParameters(displayId
, desiredBitDepth
, fs_res_x
, fs_res_y
, &exactMatch
);
1325 // Warn if the mode doesn't match exactly
1326 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
));
1329 CGDisplayCapture(displayId
);
1330 CGDisplaySwitchToMode(displayId
, mode
);
1334 mp_msg(MSGT_VO
, MSGL_ERR
, "Quartz error: can't switch to fullscreen \n");
1337 // Get Main device info///////////////////////////////////////////////////
1338 update_screen_info();
1341 // save old window size
1344 GetWindowPortBounds(theWindow
, &oldWinRect
);
1345 GetWindowBounds(theWindow
, kWindowContentRgn
, &oldWinBounds
);
1348 ChangeWindowAttributes(theWindow
, kWindowNoShadowAttribute
, 0);
1353 else //go back to windowed mode
1356 if (originalMode
!= NULL
)
1358 CGDisplaySwitchToMode(displayId
, originalMode
);
1359 CGDisplayRelease(displayId
);
1361 // Get Main device info///////////////////////////////////////////////////
1362 update_screen_info();
1364 originalMode
= NULL
;
1366 SetSystemUIMode(kUIModeNormal
, 0);
1368 // show mouse cursor
1369 CGDisplayShowCursor(displayId
);
1372 // revert window to previous setting
1373 ChangeWindowAttributes(theWindow
, 0, kWindowNoShadowAttribute
);
1374 SizeWindow(theWindow
, oldWinRect
.right
, oldWinRect
.bottom
, 1);
1375 MoveWindow(theWindow
, oldWinBounds
.left
, oldWinBounds
.top
, 1);
1380 void window_panscan(void)
1385 CheckMenuItem(aspectMenu
, 2, 1);
1387 CheckMenuItem(aspectMenu
, 2, 0);
1391 MoveWindow(theWindow
, xinerama_x
- (vo_panscan_x
>> 1), xinerama_y
- (vo_panscan_y
>> 1), 1);
1392 SizeWindow(theWindow
, vo_screenwidth
+ vo_panscan_x
, vo_screenheight
+ vo_panscan_y
, 1);