|
MythTV
0.26-pre
|
00001 /****************************************************************************** 00002 * = NAME 00003 * videoout_quartz.cpp 00004 * 00005 * = DESCRIPTION 00006 * Basic video for Mac OS X, using an unholy amalgamation of QuickTime, 00007 * QuickDraw, Quartz/Core Graphics, and undocumented DVD playback APIs. 00008 * 00009 * = POSSIBLE ENHANCEMENTS 00010 * - Expand choices for the possibility of multiple displays 00011 * - Improve performance of zoomed aspect modes 00012 * - Other viewing options? 00013 * 00014 * = KNOWN BUGS 00015 * - Drawing on the desktop only updates the main screen? 00016 * - The floating window can only be created on the main screen? 00017 * - The fullscreen window doesn't display anything when created 00018 * on any screen except, you guessed it, the main screen. 00019 * 00020 * = REVISION 00021 * $Id$ 00022 * 00023 * = AUTHORS 00024 * Nigel Pearson, Jeremiah Morris 00025 *****************************************************************************/ 00026 00027 // **************************************************************************** 00028 // Configuration: 00029 00030 // Default numbers of buffers from some of the other videoout modules: 00031 const int kNumBuffers = 31; 00032 const int kNeedFreeFrames = 1; 00033 const int kPrebufferFramesNormal = 12; 00034 const int kPrebufferFramesSmall = 4; 00035 const int kKeepPrebuffer = 2; 00036 00037 // **************************************************************************** 00038 00039 #include <map> 00040 #include <vector> 00041 #include <iostream> 00042 #include <algorithm> 00043 using namespace std; 00044 00045 #include "DisplayRes.h" 00046 #include "yuv2rgb.h" 00047 #include "mythcorecontext.h" 00048 #include "filtermanager.h" 00049 #define AVCODEC_AVCODEC_H // prevent clash with QuickTime CodecType 00050 #include "videoout_quartz.h" 00051 00052 #include "util-osx.h" 00053 00054 #ifdef USING_QUARTZ_VIDEO 00055 #import <QuartzCore/CoreVideo.h> 00056 #else 00057 #import <CoreGraphics/CGBase.h> 00058 #import <CoreGraphics/CGDisplayConfiguration.h> 00059 #import <CoreGraphics/CGImage.h> 00060 #endif 00061 #import <Carbon/Carbon.h> 00062 #import <QuickTime/QuickTime.h> 00063 00064 #include "osd.h" 00065 #include "mythconfig.h" 00066 #include "mythlogging.h" 00067 #include "videodisplayprofile.h" 00068 00069 #define LOC QString("VideoOutputQuartz::") 00070 00077 class VideoOutputQuartzView 00078 { 00079 public: 00080 VideoOutputQuartzView(QuartzData *pData); 00081 virtual ~VideoOutputQuartzView(); 00082 00083 virtual bool Init(void); 00084 00085 virtual void SetFrameSkip(int numskip); 00086 virtual void Show(void); 00087 00088 virtual void InputChanged(int width, int height, float aspect, 00089 MythCodecID av_codec_id); 00090 virtual void MoveResize(QRect newRect); 00091 00092 virtual void HideForGUI(void); 00093 virtual void ShowAfterGUI(QRect size); 00094 00095 protected: 00096 virtual bool Begin(void); 00097 virtual void End(void); 00098 virtual void Transform(QRect newRect); 00099 virtual void BlankScreen(bool deferred); 00100 00101 // Subclasses implement the following methods: 00102 virtual bool BeginPort(void) = 0; // Set up a valid thePort object 00103 virtual void EndPort(void) = 0; // Release thePort 00104 00105 char * name; // Name, for verbose output 00106 00107 QuartzData * parentData; // information about video source is here 00108 00109 CGrafPtr thePort; // QuickDraw graphics port 00110 QRect m_desired; // Desired output size characteristics 00111 ImageSequence theCodec; // QuickTime sequence ID 00112 RgnHandle theMask; // clipping region 00113 00114 int frameSkip; // do we skip frames? 00115 int frameCounter; // keep track of skip status 00116 bool drawBlank; // draw a blank frame before next Show 00117 00121 bool applyMoveResize; 00122 00123 QMutex viewLock; 00124 }; 00125 00126 00127 /* 00128 * The floating window class needs an event callback. 00129 */ 00130 OSStatus VoqvFloater_Callback(EventHandlerCallRef inHandlerCallRef, 00131 EventRef inEvent, 00132 void *inUserData); 00133 00134 /* 00135 * The class containing most of VideoOutputQuartz's variables 00136 */ 00137 class QuartzData 00138 { 00139 public: 00140 QuartzData() : 00141 srcWidth(0), srcHeight(0), 00142 srcAspect(1.3333f), srcMode(kAspect_Off), 00143 00144 pixelData(0), pixelSize(0), 00145 pixelLock(), 00146 00147 window(0), 00148 screen(0), refreshRate(60.0f), 00149 00150 drawInWindow(false), windowedMode(false), 00151 scaleUpVideo(false), correctGamma(false), 00152 convertI420to2VUY(NULL), 00153 00154 embeddedView(NULL) 00155 {;} 00156 ~QuartzData() { ClearViews(); } 00157 00158 void ClearViews(void) 00159 { 00160 vector<VideoOutputQuartzView*>::iterator it = views.begin(); 00161 for (; it != views.end(); ++it) 00162 delete *it; 00163 views.clear(); 00164 } 00165 00166 // Stored information about the media stream: 00167 int srcWidth, 00168 srcHeight; 00169 float srcAspect; 00170 int srcMode; // letterbox mode 00171 00172 // Pixel storage for the media stream: 00173 ImageDescriptionHandle imgDesc; // source description header 00174 char * pixelData; // storage for one frame 00175 size_t pixelSize; // size of one frame 00176 QMutex pixelLock; // to update pixels safely 00177 00178 // Information about the display: 00179 WindowRef window; // MythTV window 00180 Rect windowBounds; // dimensions, to restore size later 00181 CGDirectDisplayID screen; // screen containing main window 00182 float refreshRate; // for screen above 00183 00184 // Global preferences: 00185 bool drawInWindow; // Fullscreen or in GUI view? 00186 bool windowedMode; // GUI runs in window? 00187 bool scaleUpVideo; // Enlarge video as needed? 00188 bool correctGamma; // Video gamma correction 00189 conv_i420_2vuy_fun convertI420to2VUY; // I420 -> 2VUY conversion function 00190 00191 // Output viewports: 00192 vector<VideoOutputQuartzView*> views; // current views 00193 00194 // Embedding: 00195 VideoOutputQuartzView * embeddedView; // special embedded widget 00196 }; 00197 00198 VideoOutputQuartzView::VideoOutputQuartzView(QuartzData *pData) 00199 : name(NULL), parentData(pData), 00200 thePort(NULL), theCodec(0), theMask(NULL), frameSkip(1), frameCounter(0), 00201 drawBlank(true), applyMoveResize(false) 00202 { 00203 } 00204 00205 VideoOutputQuartzView::~VideoOutputQuartzView() 00206 { 00207 End(); 00208 } 00209 00212 bool VideoOutputQuartzView::Begin(void) 00213 { 00214 viewLock.lock(); 00215 if (!thePort) 00216 { 00217 LOG(VB_GENERAL, LOG_ERR, 00218 QString("VOQV::Begin(%1) - No graphics port available") 00219 .arg(name)); 00220 viewLock.unlock(); 00221 return false; 00222 } 00223 00224 // Set initial output size 00225 Rect portBounds; 00226 GetPortBounds(thePort, &portBounds); 00227 LOG(VB_PLAYBACK, LOG_INFO, QString("%0Viewport currently %1,%2 -> %3,%4") 00228 .arg(name).arg(portBounds.left).arg(portBounds.top) 00229 .arg(portBounds.right).arg(portBounds.bottom)); 00230 m_desired.setWidth(portBounds.right); 00231 m_desired.setHeight(portBounds.bottom); 00232 #if 0 00233 // The clipping mask 00234 theMask = NewRgn(); 00235 SetRectRgn(theMask, m_desired.left(), m_desired.top(), 00236 m_desired.width(), m_desired.height()); 00237 #endif 00238 00239 // create the decompressor 00240 if (DecompressSequenceBeginS(&theCodec, 00241 parentData->imgDesc, 00242 NULL, 00243 0, 00244 thePort, 00245 NULL, 00246 NULL, 00247 NULL, 00248 srcCopy, 00249 theMask, 00250 0, 00251 codecNormalQuality, 00252 bestSpeedCodec)) 00253 { 00254 LOG(VB_GENERAL, LOG_ERR, 00255 QString("VOQV::Begin(%1) - DecompressSequenceBeginS failed") 00256 .arg(name)); 00257 viewLock.unlock(); 00258 return false; 00259 } 00260 00261 // Turn off gamma correction unless requested 00262 if (!parentData->correctGamma) 00263 QTSetPixMapHandleRequestedGammaLevel(GetPortPixMap(thePort), 00264 kQTUseSourceGammaLevel); 00265 00266 SetDSequenceFlags(theCodec, 00267 codecDSequenceFlushInsteadOfDirtying, 00268 codecDSequenceFlushInsteadOfDirtying); 00269 viewLock.unlock(); 00270 00271 // set transformation matrix 00272 Transform(m_desired); 00273 00274 return true; 00275 } 00276 00278 void VideoOutputQuartzView::End(void) 00279 { 00280 viewLock.lock(); 00281 // Destroy codec 00282 if (theCodec) 00283 { 00284 CDSequenceEnd(theCodec); 00285 theCodec = 0; 00286 if (theMask) 00287 { 00288 DisposeRgn(theMask); 00289 theMask = NULL; 00290 } 00291 } 00292 viewLock.unlock(); 00293 } 00294 00296 void VideoOutputQuartzView::Transform(QRect newRect) 00297 { 00298 MatrixRecord matrix; 00299 SetIdentityMatrix(&matrix); 00300 00301 int x, y, w, h, sw, sh; 00302 x = newRect.left(); 00303 y = newRect.top(); 00304 w = newRect.width(); 00305 h = newRect.height(); 00306 sw = parentData->srcWidth; 00307 sh = parentData->srcHeight; 00308 00309 // constants for transformation operations 00310 Fixed one, zero; 00311 one = Long2Fix(1); 00312 zero = Long2Fix(0); 00313 00314 LOG(VB_PLAYBACK, LOG_INFO, QString("%0Viewport is %1 x %2") 00315 .arg(name).arg(w).arg(h)); 00316 LOG(VB_PLAYBACK, LOG_INFO, QString("%0Image is %1 x %2") 00317 .arg(name).arg(sw).arg(sh)); 00318 00319 double hscale = (double) w / sw; 00320 double vscale = (double) h / sh; 00321 00322 // cap zooming if we requested it 00323 if (!parentData->scaleUpVideo) 00324 { 00325 double maxScale = fmax(hscale, vscale); 00326 hscale /= maxScale; 00327 vscale /= maxScale; 00328 } 00329 00330 if ((hscale < 0.99) || (hscale > 1.01) || 00331 (vscale < 0.99) || (vscale > 1.01)) 00332 { 00333 LOG(VB_PLAYBACK, LOG_INFO, QString("%0Scaling to %1 x %2 of original") 00334 .arg(name).arg(hscale).arg(vscale)); 00335 ScaleMatrix(&matrix, 00336 X2Fix(hscale), 00337 X2Fix(vscale), 00338 zero, zero); 00339 00340 // reset sw, sh for new apparent width/height 00341 sw = (int)(sw * hscale); 00342 sh = (int)(sh * vscale); 00343 } 00344 00345 // center image in viewport 00346 if ((h != sh) || (w != sw)) 00347 { 00348 LOG(VB_PLAYBACK, LOG_INFO, QString("%0Centering with %1, %2") 00349 .arg(name).arg((w - sw)/2.0).arg((h - sh)/2.0)); 00350 TranslateMatrix(&matrix, X2Fix((w - sw) / 2.0), X2Fix((h - sh) / 2.0)); 00351 } 00352 00353 // apply graphics port or embedding offset 00354 if (x || y) 00355 { 00356 LOG(VB_PLAYBACK, LOG_INFO, QString("%0Translating to %1, %2") 00357 .arg(name).arg(x).arg(y)); 00358 TranslateMatrix(&matrix, Long2Fix(x), Long2Fix(y)); 00359 } 00360 00361 // Apply the transformation 00362 viewLock.lock(); 00363 SetDSequenceMatrix(theCodec, &matrix); 00364 viewLock.unlock(); 00365 BlankScreen(true); // clean up screen of artifacts at next Show 00366 } 00367 00368 void VideoOutputQuartzView::BlankScreen(bool deferred) 00369 { 00370 if (deferred) 00371 { 00372 drawBlank = true; 00373 return; 00374 } 00375 00376 viewLock.lock(); 00377 if (thePort) 00378 { 00379 SetPort(thePort); 00380 00381 // set clipping rectangle 00382 Rect clipRect; 00383 if (m_desired.width() && m_desired.height()) 00384 { 00385 clipRect.left = m_desired.left(); 00386 clipRect.top = m_desired.top(); 00387 clipRect.right = m_desired.right(); 00388 clipRect.bottom = m_desired.bottom(); 00389 } 00390 else 00391 { 00392 GetPortBounds(thePort, &clipRect); 00393 } 00394 RgnHandle clipRgn = NewRgn(); 00395 RectRgn(clipRgn, &clipRect); 00396 00397 // erase our rectangle to black 00398 RGBColor rgbBlack = { 0, 0, 0 }; 00399 RGBBackColor(&rgbBlack); 00400 EraseRect(&clipRect); 00401 QDFlushPortBuffer(thePort, clipRgn); 00402 00403 drawBlank = false; 00404 } 00405 viewLock.unlock(); 00406 } 00407 00408 bool VideoOutputQuartzView::Init(void) 00409 { 00410 return (BeginPort() && Begin()); 00411 } 00412 00413 void VideoOutputQuartzView::SetFrameSkip(int numskip) 00414 { 00415 frameSkip = numskip + 1; 00416 } 00417 00418 void VideoOutputQuartzView::Show(void) 00419 { 00420 if (drawBlank) 00421 BlankScreen(false); 00422 00423 // we only draw when frameCounter is 0 00424 // if (frameSkip == 1), this is every time 00425 frameCounter = (frameCounter + 1) % frameSkip; 00426 if (frameCounter) 00427 return; 00428 00429 viewLock.lock(); 00430 if (theCodec && thePort && parentData->pixelData) 00431 { 00432 CodecFlags outFlags; 00433 00434 // tell QuickTime to draw the current frame 00435 if (DecompressSequenceFrameWhen(theCodec, 00436 (Ptr)parentData->pixelData, 00437 parentData->pixelSize, 00438 0, 00439 &outFlags, 00440 NULL, 00441 NULL)) 00442 { 00443 LOG(VB_GENERAL, LOG_ERR, 00444 QString("VOQV::Show(%1)- DecompressSequenceFrameWhen failed") 00445 .arg(name)); 00446 } 00447 } 00448 viewLock.unlock(); 00449 } 00450 00451 void VideoOutputQuartzView::InputChanged(int width, int height, float aspect, 00452 MythCodecID av_codec_id) 00453 { 00454 (void)width; 00455 (void)height; 00456 (void)aspect; 00457 (void)av_codec_id; 00458 00459 // need to redo codec, but not the port 00460 End(); 00461 Begin(); 00462 } 00463 00464 void VideoOutputQuartzView::MoveResize(QRect newRect) 00465 { 00466 if (applyMoveResize) 00467 Transform(newRect); 00468 } 00469 00472 void VideoOutputQuartzView::HideForGUI(void) 00473 { 00474 // do nothing in default version 00475 } 00476 00479 void VideoOutputQuartzView::ShowAfterGUI(QRect size) 00480 { 00481 // do nothing in default version 00482 } 00483 00487 class VoqvMainWindow : public VideoOutputQuartzView 00488 { 00489 public: 00490 VoqvMainWindow(QuartzData *pData, float alphaBlend = 1.0) 00491 : VideoOutputQuartzView(pData) 00492 { 00493 alpha = fminf(1.0, fmaxf(0.0, alphaBlend)); 00494 applyMoveResize = true; 00495 name = (char *) "Main window: "; 00496 }; 00497 00498 ~VoqvMainWindow() 00499 { 00500 End(); 00501 EndPort(); 00502 }; 00503 00504 protected: 00505 float alpha; 00506 00507 bool BeginPort(void) 00508 { 00509 viewLock.lock(); 00510 thePort = GetWindowPort(parentData->window); 00511 if (!thePort) 00512 { 00513 LOG(VB_GENERAL, LOG_ERR, 00514 "VoqvMainWindow::BeginPort() - GetWindowPort failed"); 00515 viewLock.unlock(); 00516 return false; 00517 } 00518 00519 SetWindowAlpha(parentData->window, alpha); 00520 RGBColor black = { 0, 0, 0 }; 00521 SetWindowContentColor(parentData->window, &black); 00522 viewLock.unlock(); 00523 return true; 00524 }; 00525 00526 bool Begin(void) 00527 { 00528 bool ret = VideoOutputQuartzView::Begin(); 00529 00530 if (ret && (alpha < 0.99)) 00531 { 00532 // change QuickTime mode to transparent 00533 RGBColor black = { 0, 0, 0 }; 00534 viewLock.lock(); 00535 SetDSequenceTransferMode(theCodec, transparent, &black); 00536 viewLock.unlock(); 00537 } 00538 return ret; 00539 }; 00540 00541 void EndPort(void) 00542 { 00543 viewLock.lock(); 00544 SetWindowAlpha(parentData->window, 1.0); 00545 thePort = NULL; 00546 viewLock.unlock(); 00547 }; 00548 00549 void HideForGUI(void) 00550 { 00551 LOG(VB_PLAYBACK, LOG_INFO, "VOQV::HideForGUI() main window"); 00552 End(); 00553 } 00554 00555 void ShowAfterGUI(QRect size) 00556 { 00557 LOG(VB_PLAYBACK, LOG_INFO, "VOQV::ShowAfterGUI() main window"); 00558 Begin(); 00559 Transform(size); 00560 } 00561 }; 00562 00566 class VoqvEmbedded : public VideoOutputQuartzView 00567 { 00568 public: 00569 VoqvEmbedded(QuartzData *pData, int x, int y, int w, int h) 00570 : VideoOutputQuartzView(pData) 00571 { 00572 m_desired = QRect(x, y, w, h); 00573 name = (char *) "Embedded window: "; 00574 }; 00575 00576 ~VoqvEmbedded() 00577 { 00578 End(); 00579 EndPort(); 00580 }; 00581 00582 protected: 00583 bool BeginPort(void) 00584 { 00585 viewLock.lock(); 00586 thePort = GetWindowPort(parentData->window); 00587 if (!thePort) 00588 { 00589 LOG(VB_GENERAL, LOG_ERR, 00590 "VoqvEmbedded::BeginPort() - GetWindowPort failed"); 00591 viewLock.unlock(); 00592 return false; 00593 } 00594 00595 // Ensure Qt updates by invalidating the window. 00596 Rect portBounds; 00597 GetPortBounds(thePort, &portBounds); 00598 InvalWindowRect(parentData->window, &portBounds); 00599 00600 viewLock.unlock(); 00601 return true; 00602 }; 00603 00604 // Simple scaler setup that just uses m_desired to fill embed area: 00605 bool Begin(void) 00606 { 00607 viewLock.lock(); 00608 if (DecompressSequenceBeginS(&theCodec, parentData->imgDesc, 00609 NULL, 0, thePort, NULL, NULL, NULL, 00610 srcCopy, theMask, 0, 00611 codecNormalQuality, bestSpeedCodec)) 00612 { 00613 LOG(VB_GENERAL, LOG_ERR, 00614 QString("VOQV::Begin(%1) - DecompressSequenceBeginS failed") 00615 .arg(name)); 00616 viewLock.unlock(); 00617 return false; 00618 } 00619 00620 // Turn off gamma correction unless requested 00621 if (!parentData->correctGamma) 00622 QTSetPixMapHandleRequestedGammaLevel(GetPortPixMap(thePort), 00623 kQTUseSourceGammaLevel); 00624 00625 SetDSequenceFlags(theCodec, 00626 codecDSequenceFlushInsteadOfDirtying, 00627 codecDSequenceFlushInsteadOfDirtying); 00628 viewLock.unlock(); 00629 00630 // set transformation matrix 00631 Transform(m_desired); 00632 00633 return true; 00634 } 00635 00636 void EndPort(void) 00637 { 00638 viewLock.lock(); 00639 thePort = NULL; 00640 viewLock.unlock(); 00641 }; 00642 }; 00643 00647 class VoqvFullscreen : public VideoOutputQuartzView 00648 { 00649 public: 00650 VoqvFullscreen(QuartzData *pData) 00651 : VideoOutputQuartzView(pData) 00652 { 00653 applyMoveResize = true; 00654 name = (char *) "Full screen: "; 00655 }; 00656 00657 ~VoqvFullscreen() 00658 { 00659 End(); 00660 EndPort(); 00661 }; 00662 00663 protected: 00664 CGDirectDisplayID d; 00665 00666 bool BeginPort(void) 00667 { 00668 viewLock.lock(); 00669 d = parentData->screen; 00670 00671 if (CGDisplayCapture(d) != CGDisplayNoErr) 00672 { 00673 LOG(VB_GENERAL, LOG_ERR, 00674 "VoqvFullScreen::BeginPort() - Could not capture display"); 00675 viewLock.unlock(); 00676 return false; 00677 } 00678 00679 // switch screen resolution if desired 00680 if (gCoreContext->GetNumSetting("UseVideoModes", 0)) 00681 { 00682 DisplayRes *disp = DisplayRes::GetDisplayRes(); 00683 disp->SwitchToVideo(parentData->srcWidth, parentData->srcHeight); 00684 } 00685 00686 CGDisplayHideCursor(d); 00687 00688 thePort = CreateNewPortForCGDisplayID((UInt32)d); 00689 if (!thePort) 00690 { 00691 LOG(VB_GENERAL, LOG_ERR, "VoqvFullScreen::BeginPort() - " 00692 "CreateNewPortForCGDisplayID failed"); 00693 viewLock.unlock(); 00694 return false; 00695 } 00696 00697 viewLock.unlock(); 00698 return true; 00699 }; 00700 00701 void EndPort(void) 00702 { 00703 viewLock.lock(); 00704 if (thePort) 00705 { 00706 DisposePort(thePort); 00707 thePort = NULL; 00708 } 00709 00710 // return screen resolution to normal 00711 if (gCoreContext->GetNumSetting("UseVideoModes", 0)) 00712 DisplayRes::GetDisplayRes()->SwitchToGUI(); 00713 00714 if (d) 00715 { 00716 CGDisplayShowCursor(d); 00717 CGDisplayRelease(d); 00718 d = NULL; 00719 } 00720 viewLock.unlock(); 00721 }; 00722 00723 void HideForGUI(void) 00724 { 00725 LOG(VB_PLAYBACK, LOG_INFO, "VOQV::HideForGUI() full screen"); 00726 End(); 00727 EndPort(); 00728 } 00729 00730 void ShowAfterGUI(QRect size) 00731 { 00732 LOG(VB_PLAYBACK, LOG_INFO, "VOQV::ShowAfterGUI() full screen"); 00733 BeginPort(); 00734 Begin(); 00735 Transform(size); 00736 } 00737 }; 00738 00742 class VoqvDock : public VideoOutputQuartzView 00743 { 00744 public: 00745 VoqvDock(QuartzData *pData) 00746 : VideoOutputQuartzView(pData) 00747 { 00748 name = (char *) "Dock icon: "; 00749 }; 00750 00751 ~VoqvDock() 00752 { 00753 End(); 00754 EndPort(); 00755 }; 00756 00757 protected: 00758 bool BeginPort(void) 00759 { 00760 thePort = BeginQDContextForApplicationDockTile(); 00761 if (!thePort) 00762 { 00763 LOG(VB_GENERAL, LOG_ERR, "VoqvDock::BeginPort() - " 00764 "BeginQDContextForApplicationDockTile failed"); 00765 return false; 00766 } 00767 return true; 00768 }; 00769 00770 void EndPort(void) 00771 { 00772 viewLock.lock(); 00773 EndQDContextForApplicationDockTile(thePort); 00774 thePort = NULL; 00775 RestoreApplicationDockTileImage(); 00776 viewLock.unlock(); 00777 }; 00778 }; 00779 00784 class VoqvFloater : public VideoOutputQuartzView 00785 { 00786 public: 00787 VoqvFloater(QuartzData *pData, float alphaBlend = 0.5) 00788 : VideoOutputQuartzView(pData) 00789 { 00790 alpha = fminf(1.0, fmaxf(0.0, alphaBlend)); 00791 resizing = false; 00792 name = (char *) "Floating window: "; 00793 }; 00794 00795 ~VoqvFloater() 00796 { 00797 End(); 00798 EndPort(); 00799 }; 00800 00801 void Show(void) 00802 { 00803 if (resizing) 00804 return; // don't update while user is resizing 00805 00806 VideoOutputQuartzView::Show(); 00807 } 00808 00809 void ResizeChanged(bool startResizing) 00810 { 00811 if (!startResizing) 00812 { 00813 // Resize complete, reset the window drawing transformation 00814 Rect curBounds; 00815 GetPortBounds(thePort, &curBounds); 00816 m_desired.setWidth(curBounds.right - curBounds.left); 00817 m_desired.setHeight(curBounds.bottom - curBounds.top); 00818 SetRectRgn(theMask, m_desired.left(), m_desired.top(), 00819 m_desired.width(), m_desired.height()); 00820 Transform(m_desired); 00821 } 00822 resizing = startResizing; 00823 } 00824 00825 protected: 00826 ToolboxObjectClassRef myClass; 00827 WindowRef window; 00828 float alpha; 00829 bool resizing; 00830 00831 bool BeginPort(void) 00832 { 00833 viewLock.lock(); 00834 00835 Rect bounds; 00836 bounds.top = bounds.left = bounds.right = bounds.bottom = 50; 00837 switch ((int)(10 * parentData->srcAspect)) 00838 { 00839 case 17: 00840 case 18: 00841 bounds.right += 320; 00842 bounds.bottom += 180; 00843 break; 00844 case 13: 00845 bounds.right += 280; 00846 bounds.bottom += 210; 00847 break; 00848 default: 00849 bounds.right += CGDisplayPixelsWide(parentData->screen) / 3; 00850 bounds.bottom += CGDisplayPixelsHigh(parentData->screen) / 3; 00851 } 00852 00853 // custom window definition 00854 EventHandlerUPP myUPP = NewEventHandlerUPP(VoqvFloater_Callback); 00855 EventTypeSpec defEvents[] = 00856 { { kEventClassWindow, kEventWindowHitTest }, 00857 { kEventClassWindow, kEventWindowDrawFrame }, 00858 { kEventClassWindow, kEventWindowClickResizeRgn } }; 00859 RegisterToolboxObjectClass(CFSTR("org.mythtv.myth.VoqvFloater"), 00860 NULL, 00861 3, 00862 defEvents, 00863 myUPP, 00864 this, 00865 &myClass); 00866 WindowDefSpec mySpec; 00867 mySpec.defType = kWindowDefObjectClass; 00868 mySpec.u.classRef = myClass; 00869 if (CreateCustomWindow(&mySpec, 00870 kUtilityWindowClass, 00871 kWindowNoShadowAttribute | 00872 kWindowResizableAttribute | 00873 kWindowStandardHandlerAttribute, 00874 &bounds, 00875 &window)) 00876 { 00877 LOG(VB_GENERAL, LOG_ERR, 00878 "VoqvFloater::BeginPort() - CreateCustomWindow failed"); 00879 viewLock.unlock(); 00880 return false; 00881 } 00882 SetWindowAlpha(window, alpha); 00883 RGBColor black = { 0, 0, 0 }; 00884 SetWindowContentColor(window, &black); 00885 00886 thePort = GetWindowPort(window); 00887 if (!thePort) 00888 { 00889 LOG(VB_GENERAL, LOG_ERR, 00890 "VoqvFloater::BeginPort() - GetWindowPort failed"); 00891 viewLock.unlock(); 00892 return false; 00893 } 00894 00895 viewLock.unlock(); 00896 ShowWindow(window); 00897 // don't lose focus from main window 00898 SelectWindow(parentData->window); 00899 00900 return true; 00901 }; 00902 00903 bool Begin(void) 00904 { 00905 bool ret = VideoOutputQuartzView::Begin(); 00906 00907 if (ret && (alpha < 0.99)) 00908 { 00909 // change QuickTime mode to transparent 00910 RGBColor black = { 0, 0, 0 }; 00911 viewLock.lock(); 00912 SetDSequenceTransferMode(theCodec, transparent, &black); 00913 viewLock.unlock(); 00914 } 00915 return ret; 00916 }; 00917 00918 void EndPort(void) 00919 { 00920 viewLock.lock(); 00921 thePort = NULL; 00922 if (window) 00923 { 00924 DisposeWindow(window); 00925 window = NULL; 00926 } 00927 UnregisterToolboxObjectClass(myClass); 00928 viewLock.unlock(); 00929 }; 00930 }; 00931 00932 // The event callback for the floating window above 00933 OSStatus VoqvFloater_Callback(EventHandlerCallRef inHandlerCallRef, 00934 EventRef inEvent, 00935 void *inUserData) 00936 { 00937 (void)inHandlerCallRef; 00938 VoqvFloater *floater = reinterpret_cast<VoqvFloater*>(inUserData); 00939 WindowRef window; 00940 Point mouseLoc; 00941 Rect winLoc; 00942 WindowDefPartCode where; 00943 00944 switch (GetEventKind(inEvent)) 00945 { 00946 case kEventWindowHitTest: 00947 // gather info about window and click 00948 GetEventParameter(inEvent, 00949 kEventParamDirectObject, 00950 typeWindowRef, 00951 NULL, 00952 sizeof(WindowRef), 00953 NULL, 00954 &window); 00955 GetEventParameter(inEvent, 00956 kEventParamMouseLocation, 00957 typeQDPoint, 00958 NULL, 00959 sizeof(mouseLoc), 00960 NULL, 00961 &mouseLoc); 00962 00963 // see if user hit grow area 00964 GetWindowBounds(window, 00965 kWindowGlobalPortRgn, 00966 &winLoc); 00967 where = wInDrag; 00968 if (mouseLoc.h > (winLoc.right - 12) && 00969 mouseLoc.v > (winLoc.bottom - 12)) 00970 { 00971 where = wInGrow; 00972 } 00973 SetEventParameter(inEvent, 00974 kEventParamWindowDefPart, 00975 typeWindowDefPartCode, 00976 sizeof(WindowDefPartCode), 00977 &where); 00978 break; 00979 00980 case kEventWindowClickResizeRgn: 00981 // gather info about window and click 00982 GetEventParameter(inEvent, 00983 kEventParamDirectObject, 00984 typeWindowRef, 00985 NULL, 00986 sizeof(WindowRef), 00987 NULL, 00988 &window); 00989 GetEventParameter(inEvent, 00990 kEventParamMouseLocation, 00991 typeQDPoint, 00992 NULL, 00993 sizeof(mouseLoc), 00994 NULL, 00995 &mouseLoc); 00996 00997 floater->ResizeChanged(true); 00998 ResizeWindow(window, mouseLoc, NULL, NULL); 00999 floater->ResizeChanged(false); 01000 break; 01001 } 01002 return noErr; 01003 } 01004 01005 01009 class VoqvDesktop : public VideoOutputQuartzView 01010 { 01011 public: 01012 VoqvDesktop(QuartzData *pData) 01013 : VideoOutputQuartzView(pData) 01014 { 01015 name = (char *) "Desktop: "; 01016 }; 01017 01018 ~VoqvDesktop() 01019 { 01020 End(); 01021 EndPort(); 01022 }; 01023 01024 protected: 01025 WindowRef window; 01026 01027 bool BeginPort(void) 01028 { 01029 viewLock.lock(); 01030 01031 Rect bounds; 01032 bounds.top = bounds.left = 0; 01033 bounds.right = CGDisplayPixelsWide(parentData->screen); 01034 bounds.bottom = CGDisplayPixelsHigh(parentData->screen); 01035 if (CreateNewWindow(kPlainWindowClass, 01036 kWindowNoShadowAttribute | 01037 kWindowOpaqueForEventsAttribute, 01038 &bounds, 01039 &window)) 01040 { 01041 LOG(VB_GENERAL, LOG_ERR, 01042 "VoqvDesktop::BeginPort() - CreateNewWindow failed"); 01043 viewLock.unlock(); 01044 return false; 01045 } 01046 WindowGroupRef winGroup; 01047 if (CreateWindowGroup(0, &winGroup)) 01048 { 01049 LOG(VB_GENERAL, LOG_ERR, 01050 "VoqvDesktop::BeginPort() - CreateWindowGroup failed"); 01051 viewLock.unlock(); 01052 return false; 01053 } 01054 SetWindowGroupLevel(winGroup, kCGDesktopIconWindowLevel - 1); 01055 SetWindowGroup(window, winGroup); 01056 RGBColor black = { 0, 0, 0 }; 01057 SetWindowContentColor(window, &black); 01058 01059 thePort = GetWindowPort(window); 01060 if (!thePort) 01061 { 01062 LOG(VB_GENERAL, LOG_ERR, 01063 "VoqvDesktop::BeginPort() - GetWindowPort failed"); 01064 viewLock.unlock(); 01065 return false; 01066 } 01067 viewLock.unlock(); 01068 ShowWindow(window); 01069 // don't lose focus from main window 01070 SelectWindow(parentData->window); 01071 01072 return true; 01073 }; 01074 01075 void EndPort(void) 01076 { 01077 viewLock.lock(); 01078 thePort = NULL; 01079 if (window) 01080 { 01081 DisposeWindow(window); 01082 window = NULL; 01083 } 01084 viewLock.unlock(); 01085 }; 01086 }; 01087 01088 void VideoOutputQuartz::GetRenderOptions(render_opts &opts, 01089 QStringList &cpudeints) 01090 { 01091 opts.renderers->append("quartz-blit"); 01092 opts.deints->insert("quartz-blit", cpudeints); 01093 (*opts.osds)["quartz-blit"].append("softblend"); 01094 (*opts.safe_renderers)["dummy"].append("quartz-blit"); 01095 (*opts.safe_renderers)["nuppel"].append("quartz-blit"); 01096 if (opts.decoders->contains("ffmpeg")) 01097 (*opts.safe_renderers)["ffmpeg"].append("quartz-blit"); 01098 if (opts.decoders->contains("vda")) 01099 (*opts.safe_renderers)["vda"].append("quartz-blit"); 01100 if (opts.decoders->contains("crystalhd")) 01101 (*opts.safe_renderers)["crystalhd"].append("quartz-blit"); 01102 (*opts.render_group)["quartz"].append("quartz-blit"); 01103 opts.priorities->insert("quartz-blit", 70); 01104 } 01105 01109 VideoOutputQuartz::VideoOutputQuartz() : 01110 VideoOutput(), Started(false), data(new QuartzData()) 01111 { 01112 init(&pauseFrame, FMT_YV12, NULL, 0, 0, 0, 0); 01113 } 01114 01115 VideoOutputQuartz::~VideoOutputQuartz() 01116 { 01117 if (data) 01118 { 01119 Exit(); 01120 01121 delete data; 01122 data = NULL; 01123 } 01124 } 01125 01126 void VideoOutputQuartz::VideoAspectRatioChanged(float aspect) 01127 { 01128 LOG(VB_PLAYBACK, LOG_INFO, LOC + 01129 QString("VideoAspectRatioChanged(aspect=%1) [was %2]") 01130 .arg(aspect).arg(data->srcAspect)); 01131 01132 VideoOutput::VideoAspectRatioChanged(aspect); 01133 01134 data->srcAspect = aspect; 01135 data->srcMode = db_aspectoverride; 01136 } 01137 01138 // this is documented in videooutbase.cpp 01139 void VideoOutputQuartz::Zoom(ZoomDirection direction) 01140 { 01141 LOG(VB_PLAYBACK, LOG_INFO, LOC + 01142 QString("Zoom(direction=%1)").arg(direction)); 01143 01144 VideoOutput::Zoom(direction); 01145 MoveResize(); 01146 } 01147 01148 void VideoOutputQuartz::ToggleAdjustFill(AdjustFillMode adjustFill) 01149 { 01150 // Calculate the desired output rectangle for this fill mode. 01151 VideoOutput::ToggleAdjustFill(adjustFill); 01152 01153 // We could change all the views, but the user probably only 01154 // wants the main one (window or fullscreen) to change. 01155 data->views[0]->MoveResize(window.GetDisplayVideoRect()); 01156 } 01157 01158 void VideoOutputQuartz::MoveResize(void) 01159 { 01160 // This recalculates the desired output rectangle, based on 01161 // the user's current aspect/fill/letterbox/zoom settings. 01162 VideoOutput::MoveResize(); 01163 01164 QRect newRect = window.GetDisplayVideoRect(); 01165 01166 vector<VideoOutputQuartzView*>::iterator it; 01167 for (it = data->views.begin(); it != data->views.end(); ++it) 01168 { 01169 (*it)->MoveResize(newRect); 01170 } 01171 } 01172 01173 void VideoOutputQuartz::ToggleAspectOverride(AspectOverrideMode aspectMode) 01174 { 01175 VideoOutput::ToggleAspectOverride(aspectMode); 01176 MoveResize(); 01177 } 01178 01179 bool VideoOutputQuartz::InputChanged(const QSize &input_size, 01180 float aspect, 01181 MythCodecID av_codec_id, 01182 void *codec_private, 01183 bool &aspect_only) 01184 { 01185 LOG(VB_PLAYBACK, LOG_INFO, LOC + 01186 QString("InputChanged(WxH = %1x%2, aspect=%3") 01187 .arg(input_size.width()) 01188 .arg(input_size.height()).arg(aspect)); 01189 01190 bool cid_changed = (video_codec_id != av_codec_id); 01191 bool res_changed = input_size != window.GetActualVideoDim(); 01192 bool asp_changed = aspect != window.GetVideoAspect(); 01193 01194 VideoOutput::InputChanged(input_size, aspect, av_codec_id, codec_private, 01195 aspect_only); 01196 01197 if (!res_changed && !cid_changed) 01198 { 01199 aspect_only = true; 01200 // TODO we should clear our buffers to black here.. 01201 if (asp_changed) 01202 { 01203 MoveResize(); 01204 } 01205 return true; 01206 } 01207 01208 const QSize video_dim = window.GetVideoDispDim(); 01209 01210 DeleteQuartzBuffers(); 01211 01212 data->srcWidth = video_dim.width(); 01213 data->srcHeight = video_dim.height(); 01214 data->srcAspect = aspect; 01215 data->srcMode = db_aspectoverride; 01216 01217 CreateQuartzBuffers(); 01218 01219 vector<VideoOutputQuartzView*>::iterator it = data->views.begin(); 01220 for (; it != data->views.end(); ++it) 01221 { 01222 (*it)->InputChanged( 01223 video_dim.width(), video_dim.height(), aspect, av_codec_id); 01224 } 01225 01226 MoveResize(); 01227 01228 return true; 01229 } 01230 01231 bool VideoOutputQuartz::Init(int width, int height, float aspect, 01232 WId winid, const QRect &win_rect, 01233 MythCodecID codec_id) 01234 { 01235 LOG(VB_PLAYBACK, LOG_INFO, LOC + 01236 QString("Init(WxH %1x%2, aspect=%3, winid=%4\n\t\t\t" 01237 "win_bounds(x %5, y%6, WxH %7x%8))") 01238 .arg(width).arg(height).arg(aspect).arg(winid) 01239 .arg(win_rect.x()).arg(win_rect.y()) 01240 .arg(win_rect.width()).arg(win_rect.height())); 01241 01242 vbuffers.Init(kNumBuffers, true, kNeedFreeFrames, 01243 kPrebufferFramesNormal, kPrebufferFramesSmall, 01244 kKeepPrebuffer); 01245 VideoOutput::Init(width, height, aspect, winid, win_rect, codec_id); 01246 01247 const QSize video_dim = window.GetVideoDim(); 01248 data->srcWidth = video_dim.width(); 01249 data->srcHeight = video_dim.height(); 01250 data->srcAspect = aspect; 01251 data->srcMode = db_aspectoverride; 01252 01253 // Initialize QuickTime 01254 if (EnterMovies()) 01255 { 01256 LOG(VB_GENERAL, LOG_ERR, LOC + "Init() - EnterMovies failed"); 01257 return false; 01258 } 01259 01260 // Find the main window 01261 data->window = FrontNonFloatingWindow(); 01262 if (!data->window) 01263 { 01264 LOG(VB_GENERAL, LOG_ERR, LOC + "Init() - Find window failed"); 01265 return false; 01266 } 01267 01268 // It may be possible to locate a display that best matches the requested 01269 // dimensions and aspect ratio, but for simplicity we will just use the 01270 // display that contains the MythTV window. 01271 01272 if (GetWindowBounds(data->window, 01273 kWindowStructureRgn, &(data->windowBounds))) 01274 { 01275 LOG(VB_GENERAL, LOG_ERR, LOC + "Init() - GetWindowBounds failed"); 01276 return false; 01277 } 01278 CGPoint pt; 01279 pt.x = data->windowBounds.left; 01280 pt.y = data->windowBounds.top; 01281 CGDisplayCount ct; 01282 data->screen = NULL; 01283 if (CGGetDisplaysWithPoint(pt, 1, &data->screen, &ct)) 01284 { 01285 // window is offscreen? use main display instead 01286 data->screen = CGMainDisplayID(); 01287 } 01288 01289 // Find the refresh rate of our screen 01290 CFDictionaryRef m; 01291 m = CGDisplayCurrentMode(data->screen); 01292 data->refreshRate = get_float_CF(m, kCGDisplayRefreshRate); 01293 if (data->refreshRate == 0.0) // LCD display? 01294 data->refreshRate = 150.0; // Divisible by 25Hz and 30Hz 01295 // to minimise AV sync waiting 01296 01297 // Find the display physical aspect ratio 01298 CGSize size_in_mm = CGDisplayScreenSize(data->screen); 01299 if ((size_in_mm.width > 0.0001f) && (size_in_mm.height > 0.0001f)) 01300 { 01301 window.SetDisplayDim(QSize((uint) size_in_mm.width, 01302 (uint) size_in_mm.height)); 01303 window.SetDisplayAspect(size_in_mm.width / size_in_mm.height); 01304 LOG(VB_PLAYBACK, LOG_INFO, 01305 QString("Screen size is %1 x %2 (mm), aspect %3") 01306 .arg(size_in_mm.width).arg(size_in_mm.height) 01307 .arg(size_in_mm.width / size_in_mm.height)); 01308 } 01309 01310 // Global configuration options 01311 data->scaleUpVideo = gCoreContext->GetNumSetting("MacScaleUp", 1); 01312 data->drawInWindow = gCoreContext->GetNumSetting("GuiSizeForTV", 0); 01313 data->windowedMode = gCoreContext->GetNumSetting("RunFrontendInWindow", 0); 01314 data->correctGamma = gCoreContext->GetNumSetting("MacGammaCorrect", 0); 01315 01316 data->convertI420to2VUY = get_i420_2vuy_conv(); 01317 01318 01319 if (data->drawInWindow) 01320 { 01321 // display_aspect and _dim have to be scaled to actual window size 01322 float winWidth = size_in_mm.width * win_rect.width() 01323 / get_int_CF(m, kCGDisplayWidth); 01324 float winHeight = size_in_mm.height * win_rect.height() 01325 / get_int_CF(m, kCGDisplayHeight); 01326 window.SetDisplayDim(QSize(winWidth, winHeight)); 01327 window.SetDisplayAspect(winWidth / winHeight); 01328 LOG(VB_PLAYBACK, LOG_INFO, 01329 QString("Main window is %1 x %2 (mm), aspect %3") 01330 .arg((int)winWidth).arg((int)winHeight) 01331 .arg(winWidth / winHeight)); 01332 } 01333 01334 if (!CreateQuartzBuffers()) 01335 { 01336 LOG(VB_GENERAL, LOG_ERR, LOC + "Init() - CreateQuartzBuffers failed"); 01337 return false; 01338 } 01339 01340 01341 // Create the output view objects 01342 VideoOutputQuartzView *tmp; 01343 if (!data->drawInWindow) 01344 { 01345 // Fullscreen will take over everything 01346 tmp = new VoqvFullscreen(data); 01347 tmp->SetFrameSkip(gCoreContext->GetNumSetting("MacFullSkip", 0)); 01348 data->views.push_back(tmp); 01349 } 01350 else if (!data->windowedMode) 01351 { 01352 // Full GUI is hidden, only show the main window 01353 tmp = new VoqvMainWindow(data, 1.0); 01354 tmp->SetFrameSkip(gCoreContext->GetNumSetting("MacFullSkip", 0)); 01355 data->views.push_back(tmp); 01356 } 01357 else 01358 { 01359 // Full GUI is shown, many output options 01360 if (gCoreContext->GetNumSetting("MacMainEnabled", 1)) 01361 { 01362 float opacity = 01363 gCoreContext->GetNumSetting("MacMainOpacity", 100) / 100.0; 01364 tmp = new VoqvMainWindow(data, opacity); 01365 tmp->SetFrameSkip(gCoreContext->GetNumSetting("MacMainSkip", 0)); 01366 data->views.push_back(tmp); 01367 } 01368 else 01369 { 01370 // If video in the main window is not enabled, 01371 // hide (shrink) it so it is not in the way 01372 LOG(VB_PLAYBACK, LOG_INFO, "Shrinking Main Window to 1x1"); 01373 SizeWindow(data->window, 1, 1, true); 01374 } 01375 if (gCoreContext->GetNumSetting("MacFloatEnabled", 0)) 01376 { 01377 float opacity = 01378 gCoreContext->GetNumSetting("MacFloatOpacity", 100) / 100.0; 01379 tmp = new VoqvFloater(data, opacity); 01380 tmp->SetFrameSkip(gCoreContext->GetNumSetting("MacFloatSkip", 0)); 01381 data->views.push_back(tmp); 01382 } 01383 if (gCoreContext->GetNumSetting("MacDesktopEnabled", 0)) 01384 { 01385 tmp = new VoqvDesktop(data); 01386 tmp->SetFrameSkip(gCoreContext->GetNumSetting("MacDesktopSkip", 0)); 01387 data->views.push_back(tmp); 01388 } 01389 if (gCoreContext->GetNumSetting("MacDockEnabled", 1)) 01390 { 01391 tmp = new VoqvDock(data); 01392 tmp->SetFrameSkip(gCoreContext->GetNumSetting("MacDockSkip", 3)); 01393 data->views.push_back(tmp); 01394 } 01395 } 01396 01397 vector<VideoOutputQuartzView*>::iterator it = data->views.begin(); 01398 for (; it != data->views.end(); ++it) 01399 { 01400 if (!(*it)->Init()) 01401 { 01402 LOG(VB_GENERAL, LOG_ERR, LOC + "Init() - QuartzView Init() failed"); 01403 } 01404 } 01405 01406 MoveResize(); 01407 Started = true; 01408 01409 return true; 01410 } 01411 01412 void VideoOutputQuartz::SetVideoFrameRate(float playback_fps) 01413 { 01414 LOG(VB_PLAYBACK, LOG_INFO, QString("SetVideoFrameRate(%1) - unimplemented?") 01415 .arg(playback_fps)); 01416 } 01417 01418 static QString toCommaList(const QStringList &list) 01419 { 01420 QString ret = ""; 01421 for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it) 01422 ret += *it + ","; 01423 01424 if (ret.length()) 01425 return ret.left(ret.length()-1); 01426 01427 return ""; 01428 } 01429 01430 bool VideoOutputQuartz::CreateQuartzBuffers(void) 01431 { 01432 const QSize video_dim = window.GetVideoDim(); 01433 db_vdisp_profile->SetInput(video_dim); 01434 QStringList renderers = GetAllowedRenderers(video_codec_id, video_dim); 01435 QString renderer = QString::null; 01436 01437 QString tmp = db_vdisp_profile->GetVideoRenderer(); 01438 LOG(VB_PLAYBACK, LOG_INFO, LOC + 01439 QString("CreateQuartzBuffers() render: %1, allowed: %2") 01440 .arg(tmp).arg(toCommaList(renderers))); 01441 01442 if (renderers.contains(tmp)) 01443 renderer = tmp; 01444 else if (!renderers.empty()) 01445 renderer = renderers[0]; 01446 else 01447 { 01448 LOG(VB_GENERAL, LOG_ERR, "Failed to find a video renderer"); 01449 return false; 01450 } 01451 01452 // reset this so that all the prefs are reinitialized 01453 db_vdisp_profile->SetVideoRenderer(renderer); 01454 LOG(VB_GENERAL, LOG_INFO, LOC + "VProf: " + db_vdisp_profile->toString()); 01455 01456 vbuffers.CreateBuffers(FMT_YV12, video_dim.width(), video_dim.height()); 01457 01458 // Set up pause frame 01459 if (pauseFrame.buf) 01460 delete [] pauseFrame.buf; 01461 01462 VideoFrame *scratch = vbuffers.GetScratchFrame(); 01463 01464 init(&pauseFrame, FMT_YV12, new unsigned char[scratch->size], 01465 scratch->width, scratch->height, scratch->size); 01466 01467 pauseFrame.frameNumber = scratch->frameNumber; 01468 01469 01470 // Set up pixel storage and image description for source 01471 data->pixelLock.lock(); 01472 01473 int width, height; 01474 width = data->srcWidth; 01475 height = data->srcHeight; 01476 01477 // Set up description to display YUV data. 01478 // The views will use this to initialize 01479 // their QuickTime decompressor. 01480 data->imgDesc = 01481 (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription)); 01482 HLock((Handle)(data->imgDesc)); 01483 01484 ImageDescription *desc = *data->imgDesc; 01485 01486 desc->idSize = sizeof(ImageDescription); 01487 desc->cType = k422YpCbCr8CodecType; 01488 desc->version = 2; 01489 desc->revisionLevel = 0; 01490 desc->spatialQuality = codecNormalQuality; 01491 desc->width = width; 01492 desc->height = height; 01493 desc->hRes = Long2Fix(72); 01494 desc->vRes = Long2Fix(72); 01495 desc->depth = 24; 01496 desc->frameCount = 0; 01497 desc->dataSize = 0; 01498 desc->clutID = -1; 01499 01500 HUnlock((Handle)(data->imgDesc)); 01501 01502 // Set up storage area for one YUV frame 01503 data->pixelSize = width * height * 2; 01504 data->pixelData = new char[data->pixelSize]; 01505 01506 data->pixelLock.unlock(); 01507 01508 return true; 01509 } 01510 01511 void VideoOutputQuartz::Exit(void) 01512 { 01513 if (Started) 01514 { 01515 Started = false; 01516 01517 // Restore main window 01518 // (assuming it was shrunk i.e. we were not in full screen mode) 01519 if (data->windowedMode) 01520 { 01521 LOG(VB_PLAYBACK, LOG_INFO, 01522 QString("Restoring Main Window to %1x%2") 01523 .arg(data->windowBounds.right - data->windowBounds.left) 01524 .arg(data->windowBounds.bottom - data->windowBounds.top)); 01525 SetWindowBounds(data->window, kWindowStructureRgn, 01526 &(data->windowBounds)); 01527 } 01528 01529 data->ClearViews(); 01530 DeleteQuartzBuffers(); 01531 } 01532 } 01533 01534 void VideoOutputQuartz::DeleteQuartzBuffers() 01535 { 01536 data->pixelLock.lock(); 01537 if (data->imgDesc) 01538 { 01539 DisposeHandle((Handle)(data->imgDesc)); 01540 data->imgDesc = NULL; 01541 } 01542 if (data->pixelData) 01543 { 01544 delete [] data->pixelData; 01545 data->pixelData = NULL; 01546 data->pixelSize = 0; 01547 } 01548 data->pixelLock.unlock(); 01549 01550 if (pauseFrame.buf) 01551 { 01552 delete [] pauseFrame.buf; 01553 init(&pauseFrame, FMT_YV12, NULL, 0, 0, 0, 0); 01554 } 01555 01556 vbuffers.DeleteBuffers(); 01557 } 01558 01559 void VideoOutputQuartz::EmbedInWidget(const QRect &rect) 01560 { 01561 LOG(VB_PLAYBACK, LOG_INFO, LOC + 01562 QString("EmbedInWidget(x=%1, y=%2, w=%3, h=%4)") 01563 .arg(rect.left()).arg(rect.top()) 01564 .arg(rect.width()).arg(rect.height())); 01565 01566 if (window.IsEmbedding()) 01567 return; 01568 01569 VideoOutput::EmbedInWidget(rect); 01570 // Base class has now calculated Aspect/Fill, 01571 // so copy for precision sizing of new widget: 01572 QRect newArea = window.GetDisplayVideoRect(); 01573 LOG(VB_PLAYBACK, LOG_INFO, LOC + 01574 QString("now - EmbedInWidget(x=%1, y=%2, w=%3, h=%4)") 01575 .arg(newArea.left()).arg(newArea.top()) 01576 .arg(newArea.width()).arg(newArea.height())); 01577 01578 data->pixelLock.lock(); 01579 01580 // create embedded widget 01581 data->embeddedView = new VoqvEmbedded(data, newArea.left(), newArea.top(), 01582 newArea.width(), newArea.height()); 01583 if (data->embeddedView) 01584 { 01585 data->embeddedView->Init(); 01586 data->views.push_back(data->embeddedView); 01587 } 01588 01589 data->pixelLock.unlock(); 01590 } 01591 01592 void VideoOutputQuartz::StopEmbedding(void) 01593 { 01594 LOG(VB_PLAYBACK, LOG_INFO, LOC + "StopEmbedding()"); 01595 01596 if (!window.IsEmbedding()) 01597 return; 01598 01599 VideoOutput::StopEmbedding(); 01600 01601 data->pixelLock.lock(); 01602 01603 // delete embedded widget 01604 if (data->embeddedView) 01605 { 01606 vector<VideoOutputQuartzView*>::iterator it = 01607 find(data->views.begin(), data->views.end(), data->embeddedView); 01608 if (it != data->views.end()) 01609 { 01610 delete *it; 01611 data->views.erase(it); 01612 } 01613 data->embeddedView = NULL; 01614 } 01615 01616 data->pixelLock.unlock(); 01617 } 01618 01622 void VideoOutputQuartz::PrepareFrame(VideoFrame *buffer, FrameScanType t, 01623 OSD *osd) 01624 { 01625 (void)osd; 01626 (void)t; 01627 01628 if (buffer) 01629 framesPlayed = buffer->frameNumber + 1; 01630 } 01631 01636 void VideoOutputQuartz::Show(FrameScanType t) 01637 { 01638 (void)t; 01639 01640 data->pixelLock.lock(); 01641 vector<VideoOutputQuartzView*>::iterator it = data->views.begin(); 01642 for (; it != data->views.end(); ++it) 01643 (*it)->Show(); 01644 data->pixelLock.unlock(); 01645 } 01646 01647 void VideoOutputQuartz::DrawUnusedRects(bool) 01648 { 01649 } 01650 01651 void VideoOutputQuartz::UpdatePauseFrame(int64_t &disp_timecode) 01652 { 01653 if (!pauseFrame.buf) 01654 { 01655 LOG(VB_GENERAL, LOG_ERR, LOC + "UpdatePauseFrame() - no buffers?"); 01656 return; 01657 } 01658 01659 VideoFrame *pauseb = vbuffers.GetScratchFrame(); 01660 VideoFrame *pauseu = vbuffers.head(kVideoBuffer_used); 01661 if (pauseu) 01662 CopyFrame(&pauseFrame, pauseu); 01663 else 01664 CopyFrame(&pauseFrame, pauseb); 01665 01666 disp_timecode = pauseFrame.disp_timecode; 01667 } 01668 01673 void VideoOutputQuartz::ProcessFrame(VideoFrame *frame, OSD *osd, 01674 FilterChain *filterList, 01675 const PIPMap &pipPlayers, 01676 FrameScanType scan) 01677 { 01678 if (!frame) 01679 { 01680 frame = vbuffers.GetScratchFrame(); 01681 CopyFrame(vbuffers.GetScratchFrame(), &pauseFrame); 01682 } 01683 01684 if (filterList) 01685 filterList->ProcessFrame(frame); 01686 01687 if (m_deinterlacing && 01688 m_deintFilter != NULL && 01689 m_deinterlaceBeforeOSD) 01690 { 01691 m_deintFilter->ProcessFrame(frame, scan); 01692 } 01693 01694 ShowPIPs(frame, pipPlayers); 01695 if (osd && !window.IsEmbedding()) 01696 DisplayOSD(frame, osd); 01697 01698 if (m_deinterlacing && 01699 m_deintFilter != NULL && 01700 !m_deinterlaceBeforeOSD) 01701 { 01702 m_deintFilter->ProcessFrame(frame, scan); 01703 } 01704 01705 QMutexLocker locker(&data->pixelLock); 01706 if (!data->pixelData) 01707 { 01708 LOG(VB_PLAYBACK, LOG_ERR, LOC + "ProcessFrame(): NULL pixelData!"); 01709 return; 01710 } 01711 01712 // copy data to our buffer 01713 data->convertI420to2VUY( 01714 (unsigned char*) data->pixelData, frame->width<<1, // 2vuy 01715 frame->buf + frame->offsets[0], // Y plane 01716 frame->buf + frame->offsets[1], // U plane 01717 frame->buf + frame->offsets[2], // V plane 01718 frame->pitches[0], frame->pitches[1], frame->pitches[2], 01719 frame->width, frame->height); 01720 } 01721 01723 void VideoOutputQuartz::ResizeForGui(void) 01724 { 01725 data->pixelLock.lock(); 01726 vector<VideoOutputQuartzView*>::iterator it = data->views.begin(); 01727 for (; it != data->views.end(); ++it) 01728 (*it)->HideForGUI(); 01729 data->pixelLock.unlock(); 01730 01731 VideoOutput::ResizeForGui(); 01732 } 01733 01735 void VideoOutputQuartz::ResizeForVideo(uint width, uint height) 01736 { 01737 VideoOutput::ResizeForVideo(width, height); 01738 01739 // Base class gives us the correct dimensions for main window/screen: 01740 QRect size = window.GetDisplayVideoRect(); 01741 01742 data->pixelLock.lock(); 01743 vector<VideoOutputQuartzView*>::iterator it = data->views.begin(); 01744 for (; it != data->views.end(); ++it) 01745 (*it)->ShowAfterGUI(size); 01746 data->pixelLock.unlock(); 01747 } 01748 01749 QStringList VideoOutputQuartz::GetAllowedRenderers( 01750 MythCodecID myth_codec_id, const QSize &video_dim) 01751 { 01752 (void) video_dim; 01753 01754 QStringList list; 01755 01756 if (codec_is_std(myth_codec_id)) 01757 { 01758 list += "quartz-blit"; 01759 } 01760 01761 return list; 01762 } 01763 01764 MythCodecID VideoOutputQuartz::GetBestSupportedCodec( 01765 uint width, uint height, 01766 uint osd_width, uint osd_height, 01767 uint stream_type, uint fourcc) 01768 { 01769 (void) osd_width; 01770 (void) osd_height; 01771 01772 VideoDisplayProfile vdp; 01773 vdp.SetInput(QSize(width, height)); 01774 QString dec = vdp.GetDecoder(); 01775 if (dec == "ffmpeg") 01776 return (MythCodecID)(kCodec_MPEG1 + (stream_type-1)); 01777 return (MythCodecID)(kCodec_MPEG1 + (stream_type-1)); 01778 }
1.7.6.1