MythTV  0.26-pre
videoout_quartz.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends