playtree: make some char * function arguments const
[mplayer.git] / libvo / vo_quartz.c
blob9098598aa82db4e44898530079f794c426b4bcca
1 /*
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.
19 /**
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
30 -fix RGB32
31 -(add sugestion here)
34 //SYS
35 #include <stdio.h>
37 //OSX
38 #include <Carbon/Carbon.h>
39 #include <QuickTime/QuickTime.h>
41 //MPLAYER
42 #include "config.h"
43 #include "fastmemcpy.h"
44 #include "video_out.h"
45 #include "video_out_internal.h"
46 #include "aspect.h"
47 #include "mp_msg.h"
48 #include "m_option.h"
49 #include "mp_fifo.h"
50 #include "mpbswap.h"
51 #include "sub/sub.h"
53 #include "input/input.h"
54 #include "input/keycodes.h"
56 #include "osx_common.h"
58 static const vo_info_t info =
60 "Mac OSX (Quartz)",
61 "quartz",
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;
77 static struct
79 ImageDescriptionHandle desc;
80 Handle extension_colr;
81 Handle extension_fiel;
82 Handle extension_clap;
83 Handle extension_pasp;
84 } yuv_qt_stuff;
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;
92 int levelList[] =
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;
131 enum
133 kHalfScreenCmd = 2,
134 kNormalScreenCmd = 3,
135 kDoubleScreenCmd = 4,
136 kFullScreenCmd = 5,
137 kKeepAspectCmd = 6,
138 kAspectOrgCmd = 7,
139 kAspectFullCmd = 8,
140 kAspectWideCmd = 9,
141 kPanScanCmd = 10
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)
157 case IMGFMT_RGB32:
158 vo_draw_alpha_rgb32(w, h, src, srca, stride, image_data + 4 * (y0 * imgRect.right + x0), 4 * imgRect.right);
159 break;
160 case IMGFMT_YV12:
161 case IMGFMT_IYUV:
162 case IMGFMT_I420:
163 vo_draw_alpha_yv12(w, h, src, srca, stride, ((char *)P) + be2me_32(P->componentInfoY.offset) + x0 + y0 * imgRect.right, imgRect.right);
164 break;
165 case IMGFMT_UYVY:
166 vo_draw_alpha_uyvy(w, h, src, srca, stride, ((char *)P) + (x0 + y0 * imgRect.right) * 2, imgRect.right * 2);
167 break;
168 case IMGFMT_YUY2:
169 vo_draw_alpha_yuy2(w, h, src, srca, stride, ((char *)P) + (x0 + y0 * imgRect.right) * 2, imgRect.right * 2);
170 break;
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)
185 char macCharCodes;
186 UInt32 macKeyCode;
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);
199 if (key != -1)
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;
211 else
212 result = eventNotHandledErr;
215 return result;
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)
229 WindowPtr tmpWin;
230 Point mousePos;
231 Point winMousePos;
233 GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(mousePos), 0, &mousePos);
234 GetEventParameter(event, kEventParamWindowMouseLocation, typeQDPoint, 0, sizeof(winMousePos), 0, &winMousePos);
236 switch (kind)
238 case kEventMouseMoved:
240 if (vo_quartz_fs)
242 CGDisplayShowCursor(displayId);
243 mouseHide = FALSE;
246 break;
248 case kEventMouseWheelMoved:
250 int wheel;
251 short part;
253 GetEventParameter(event, kEventParamMouseWheelDelta, typeSInt32, 0, sizeof(wheel), 0, &wheel);
255 part = FindWindow(mousePos, &tmpWin);
257 if (part == inContent)
259 if (wheel > 0)
260 mplayer_put_key(MOUSE_BTN3);
261 else
262 mplayer_put_key(MOUSE_BTN4);
265 break;
267 case kEventMouseDown:
268 case kEventMouseUp:
270 EventMouseButton button;
271 short part;
272 Rect bounds;
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)
281 break;
282 switch (button)
284 case kEventMouseButtonPrimary:
285 mplayer_put_key(MOUSE_BTN0);
286 break;
287 case kEventMouseButtonSecondary:
288 mplayer_put_key(MOUSE_BTN2);
289 break;
290 case kEventMouseButtonTertiary:
291 mplayer_put_key(MOUSE_BTN1);
292 break;
294 default:
295 result = eventNotHandledErr;
296 break;
298 break;
300 if (winMousePos.h > bounds.right - 15 && winMousePos.v > bounds.bottom)
302 if (!vo_quartz_fs)
304 Rect newSize;
306 ResizeWindow(theWindow, mousePos, NULL, &newSize);
309 else if (part == inMenuBar)
311 MenuSelect(mousePos);
312 HiliteMenu(0);
314 else if (part == inContent)
316 switch (button)
318 case kEventMouseButtonPrimary:
319 mplayer_put_key(MOUSE_BTN0 | MP_KEY_DOWN);
320 break;
321 case kEventMouseButtonSecondary:
322 mplayer_put_key(MOUSE_BTN2 | MP_KEY_DOWN);
323 break;
324 case kEventMouseButtonTertiary:
325 mplayer_put_key(MOUSE_BTN1 | MP_KEY_DOWN);
326 break;
328 default:
329 result = eventNotHandledErr;
330 break;
334 break;
336 case kEventMouseDragged:
337 break;
339 default:
340 result = eventNotHandledErr;
341 break;
345 return result;
348 static void set_winSizeMult(float mult)
350 int d_width, d_height;
351 aspect(&d_width, &d_height, A_NOZOOM);
353 if (vo_quartz_fs)
355 vo_fs = !vo_fs;
356 window_fullscreen();
359 winSizeMult = mult;
360 SizeWindow(theWindow, d_width * mult, d_height * mult, 1);
361 window_resized();
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)
381 case kHICommandQuit:
382 mplayer_put_key(KEY_CLOSE_WIN);
383 break;
385 case kHalfScreenCmd:
386 set_winSizeMult(0.5);
387 break;
389 case kNormalScreenCmd:
390 set_winSizeMult(1);
391 break;
393 case kDoubleScreenCmd:
394 set_winSizeMult(2);
395 break;
397 case kFullScreenCmd:
398 vo_fs = !vo_fs;
399 window_fullscreen();
400 break;
402 case kKeepAspectCmd:
403 vo_keepaspect = !vo_keepaspect;
404 CheckMenuItem(aspectMenu, 1, vo_keepaspect);
405 window_resized();
406 break;
408 case kAspectOrgCmd:
409 change_movie_aspect(-1);
410 break;
412 case kAspectFullCmd:
413 change_movie_aspect(4.0 / 3.0);
414 break;
416 case kAspectWideCmd:
417 change_movie_aspect(16.0 / 9.0);
418 break;
420 case kPanScanCmd:
421 vo_panscan = !vo_panscan;
422 CheckMenuItem(aspectMenu, 2, vo_panscan);
423 window_panscan();
424 window_resized();
425 break;
427 default:
428 result = eventNotHandledErr;
429 break;
432 else if (class == kEventClassWindow)
434 WindowRef window;
435 Rect rectWindow = { 0, 0, 0, 0 };
437 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(window), NULL, &window);
439 if (window)
441 GetWindowBounds(window, kWindowGlobalPortRgn, &rectWindow);
444 switch (kind)
446 case kEventWindowClosed:
447 theWindow = NULL;
448 mplayer_put_key(KEY_CLOSE_WIN);
449 break;
451 // resize window
452 case kEventWindowZoomed:
453 case kEventWindowBoundsChanged:
454 window_resized();
455 flip_page();
456 window_resized();
457 break;
459 default:
460 result = eventNotHandledErr;
461 break;
465 return result;
468 static void quartz_CreateWindow(uint32_t d_width, uint32_t d_height, WindowAttributes windowAttrs)
470 CFStringRef titleKey;
471 CFStringRef windowTitle;
472 OSStatus result;
474 MenuItemIndex index;
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);
501 // Clear Menu Bar
502 ClearMenuBar();
504 // Create Window Menu
505 CreateStandardWindowMenu(0, &windMenu);
506 InsertMenu(windMenu, 0);
508 // Create Movie Menu
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
546 DrawMenuBar();
548 // create window
549 CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow);
551 CreateWindowGroup(0, &winGroup);
552 SetWindowGroup(theWindow, winGroup);
554 // Set window title
555 titleKey = CFSTR("MPlayer - The Movie Player");
556 windowTitle = CFCopyLocalizedString(titleKey, NULL);
557 result = SetWindowTitleWithCFString(theWindow, windowTitle);
558 CFRelease(titleKey);
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)
569 CGRect displayRect;
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;
578 device_id = 0;
580 else
582 displayId = displays[device_id];
584 free(displays);
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);
597 seqId = 0;
598 free(image_data);
599 image_data = NULL;
600 free(P);
601 P = NULL;
602 CGDataProviderRelease(dataProviderRef);
603 dataProviderRef = NULL;
604 CGImageRelease(image);
605 image = NULL;
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;
611 OSErr qterr;
612 CGRect tmpBounds;
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)
624 case IMGFMT_RGB32:
625 image_depth = 32;
626 break;
627 case IMGFMT_YV12:
628 case IMGFMT_IYUV:
629 case IMGFMT_I420:
630 case IMGFMT_UYVY:
631 case IMGFMT_YUY2:
632 image_depth = 16;
633 break;
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");
655 return -1;
657 tmpBounds = CGRectMake(0, 0, winRect.right, winRect.bottom);
658 QDBeginCGContext(GetWindowPort(theWindow), &context);
659 CGContextFillRect(context, tmpBounds);
660 QDEndCGContext(GetWindowPort(theWindow), &context);
662 else
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)
673 case IMGFMT_RGB32:
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,
682 imgRect.bottom,
684 image_depth,
685 (imgRect.right * 32 + 7) / 8,
686 CGColorSpaceCreateDeviceRGB(),
687 kCGImageAlphaNoneSkipFirst,
688 dataProviderRef, 0, 1, kCGRenderingIntentDefault);
690 QDEndCGContext(GetWindowPort(theWindow), &context);
691 break;
694 case IMGFMT_YV12:
695 case IMGFMT_IYUV:
696 case IMGFMT_I420:
697 case IMGFMT_UYVY:
698 case IMGFMT_YUY2:
700 get_image_done = 0;
702 if (!EnterMoviesDone)
704 qterr = EnterMovies();
705 EnterMoviesDone = 1;
707 else
708 qterr = 0;
710 if (qterr)
712 mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: EnterMovies (%d)\n", qterr);
713 return -1;
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);
767 if (qterr)
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);
773 if (qterr)
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);
779 if (qterr)
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);
785 if (qterr)
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)
792 case IMGFMT_YV12:
793 case IMGFMT_IYUV:
794 case IMGFMT_I420:
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);
802 break;
803 case IMGFMT_UYVY:
804 case IMGFMT_YUY2:
805 image_buffer_size = image_size;
806 break;
809 qterr = DecompressSequenceBeginS(&seqId,
810 yuv_qt_stuff.desc,
811 (char *)P,
812 image_buffer_size,
813 GetWindowPort(theWindow),
814 NULL,
815 NULL,
816 d_width != width || d_height != height ?
817 &matrix : NULL,
818 srcCopy,
819 NULL,
821 codecLosslessQuality,
822 bestSpeedCodec);
824 if (qterr)
826 mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: DecompressSequenceBeginS (%d)\n", qterr);
827 return -1;
830 break;
833 // Show window
834 RepositionWindow(theWindow, NULL, kWindowCenterOnMainScreen);
835 ShowWindow(theWindow);
837 if (vo_fs)
838 window_fullscreen();
840 if (vo_ontop)
841 window_ontop();
843 if (vo_rootwin)
845 vo_fs = TRUE;
846 winLevel = 0;
847 SetWindowGroupLevel(winGroup, CGWindowLevelForKey(levelList[winLevel]));
848 window_fullscreen();
851 window_resized();
853 return 0;
856 static void check_events(void)
858 EventRef theEvent;
859 EventTargetRef theTarget;
860 OSStatus theErr;
862 // Get event
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)
879 int curTime;
881 if (theWindow == NULL)
882 return;
884 switch (image_format)
886 case IMGFMT_RGB32:
888 CGContextRef context;
890 QDBeginCGContext(GetWindowPort(theWindow), &context);
891 CGContextDrawImage(context, bounds, image);
892 QDEndCGContext(GetWindowPort(theWindow), &context);
894 break;
896 case IMGFMT_YV12:
897 case IMGFMT_IYUV:
898 case IMGFMT_I420:
899 case IMGFMT_UYVY:
900 case IMGFMT_YUY2:
901 if (EnterMoviesDone)
903 OSErr qterr;
904 CodecFlags flags = 0;
906 qterr = DecompressSequenceFrameWhen(seqId,
907 (char *)P,
908 image_buffer_size,
909 0, //codecFlagUseImageBuffer,
910 &flags,
911 NULL,
912 NULL);
913 if (qterr)
915 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: DecompressSequenceFrameWhen in flip_page (%d) flags:0x%08x\n", qterr, flags);
918 break;
921 if (!vo_quartz_fs)
923 CGContextRef context;
925 QDBeginCGContext(GetWindowPort(theWindow), &context);
926 // render resize box
927 CGContextBeginPath(context);
928 CGContextSetAllowsAntialiasing(context, false);
929 //CGContextSaveGState(context);
931 // line white
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);
938 // line gray
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);
945 // line black
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);
965 mouseHide = TRUE;
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)
982 case IMGFMT_YV12:
983 case IMGFMT_I420:
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]);
989 return 0;
991 case IMGFMT_IYUV:
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]);
997 return 0;
999 return -1;
1002 static int draw_frame(uint8_t * src[])
1004 switch (image_format)
1006 case IMGFMT_RGB32:
1007 fast_memcpy(image_data, src[0], image_size);
1008 return 0;
1010 case IMGFMT_UYVY:
1011 case IMGFMT_YUY2:
1012 memcpy_pic(((char *)P), src[0], imgRect.right * 2, imgRect.bottom, imgRect.right * 2, imgRect.right * 2);
1013 return 0;
1015 return -1;
1018 static int query_format(uint32_t format)
1020 image_format = format;
1021 image_qtcodec = 0;
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;
1046 return 0;
1049 static void uninit(void)
1051 free_video_specific();
1052 if (EnterMoviesDone)
1053 ExitMovies();
1054 EnterMoviesDone = 0;
1056 ShowMenuBar();
1059 static int preinit(const char *arg)
1061 int parse_err = 0;
1063 if(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])
1084 parse_err = 1;
1088 osx_foreground_hack();
1090 return 0;
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);
1112 return VO_FALSE;
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)
1121 // I420
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;
1127 else
1129 // YV12
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;
1137 get_image_done = 1;
1138 return VO_TRUE;
1140 else
1142 // doesn't work yet
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);
1146 return VO_FALSE;
1149 mpi->planes[0] = (char *)P;
1150 mpi->stride[0] = imgRect.right * 2;
1151 mpi->width = imgRect.right;
1152 mpi->flags |= MP_IMGFLAG_DIRECT;
1153 get_image_done = 1;
1154 return VO_TRUE;
1156 return VO_FALSE;
1159 static int control(uint32_t request, void *data)
1161 switch (request)
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)
1174 case IMGFMT_YV12:
1175 case IMGFMT_IYUV:
1176 case IMGFMT_I420:
1177 case IMGFMT_UYVY:
1178 case IMGFMT_YUY2:
1179 return get_yuv_image(data);
1180 break;
1181 default:
1182 break;
1184 case VOCTRL_DRAW_IMAGE:
1185 switch (image_format)
1187 case IMGFMT_YV12:
1188 case IMGFMT_IYUV:
1189 case IMGFMT_I420:
1190 case IMGFMT_UYVY:
1191 case IMGFMT_YUY2:
1192 return draw_yuv_image(data);
1193 break;
1194 default:
1195 break;
1197 case VOCTRL_UPDATE_SCREENINFO:
1198 update_screen_info();
1199 return VO_TRUE;
1201 return VO_NOTIMPL;
1204 void window_resized(void)
1206 uint32_t d_width;
1207 uint32_t d_height;
1209 CGRect tmpBounds;
1211 CGContextRef context;
1213 GetWindowPortBounds(theWindow, &winRect);
1214 d_width = vo_dwidth = winRect.right;
1215 d_height = vo_dheight = winRect.bottom;
1217 if (vo_keepaspect)
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)
1223 case IMGFMT_RGB32:
1225 bounds = CGRectMake(dstRect.left, dstRect.top, dstRect.right - dstRect.left, dstRect.bottom - dstRect.top);
1226 break;
1228 case IMGFMT_YV12:
1229 case IMGFMT_IYUV:
1230 case IMGFMT_I420:
1231 case IMGFMT_UYVY:
1232 case IMGFMT_YUY2:
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);
1249 break;
1251 default:
1252 break;
1255 // Clear Background
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)
1264 if (!vo_quartz_fs)
1266 // Cycle between level
1267 winLevel++;
1268 if (winLevel > 2)
1269 winLevel = 1;
1271 SetWindowGroupLevel(winGroup, CGWindowLevelForKey(levelList[winLevel]));
1274 void window_fullscreen(void)
1276 // go fullscreen
1277 if (vo_fs)
1279 if (winLevel != 0)
1281 if (displayId == kCGDirectMainDisplay)
1283 SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1284 CGDisplayHideCursor(displayId);
1285 mouseHide = TRUE;
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);
1298 if (mode != NULL)
1300 if (!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);
1309 else
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
1319 if (!vo_quartz_fs)
1321 GetWindowPortBounds(theWindow, &oldWinRect);
1322 GetWindowBounds(theWindow, kWindowContentRgn, &oldWinBounds);
1324 // go fullscreen
1325 ChangeWindowAttributes(theWindow, kWindowNoShadowAttribute, 0);
1327 vo_quartz_fs = 1;
1328 window_panscan();
1330 else //go back to windowed mode
1332 vo_quartz_fs = 0;
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);
1347 mouseHide = FALSE;
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);
1354 window_resized();
1357 void window_panscan(void)
1359 panscan_calc();
1361 if (vo_panscan > 0)
1362 CheckMenuItem(aspectMenu, 2, 1);
1363 else
1364 CheckMenuItem(aspectMenu, 2, 0);
1366 if (vo_quartz_fs)
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);