Ticket #2381: videoout_corevideo.cpp

File videoout_corevideo.cpp, 26.0 KB (added by awk@…, 14 years ago)
Line 
1/******************************************************************************
2 * = NAME
3 * videoout_corevideo.cpp
4 *
5 * = DESCRIPTION
6 * Present video frames on screen using CoreVideo and CoreImage
7 *
8 * = REVISION
9 * $Id$
10 *
11 * = AUTHORS
12 * Andrew Kimpton
13 *****************************************************************************/
14
15
16// Typical call sequence
17// VideoOutputCoreVideo::VideoOutputCoreVideo
18// VideoOutputCoreVideo::Init
19// VideoOutputCoreVideo::VideoAspectRatioChanged
20
21// ****************************************************************************
22// Configuration:
23
24// Default numbers of buffers from some of the other videoout modules:
25const int kNumBuffers      = 31;
26const int kNeedFreeFrames  = 1;
27const int kPrebufferFramesNormal = 12;
28const int kPrebufferFramesSmall = 4;
29const int kKeepPrebuffer   = 2;
30
31// ****************************************************************************
32
33#include "videoout_corevideo.h"
34#include "mythcontext.h"
35#include "filtermanager.h"
36#include "util-osx.h"
37#include "yuv2rgb.h"
38
39#include <math.h>
40#include <Carbon/Carbon.h>
41#include <QuickTime/QuickTime.h>
42#include <AGL/agl.h>
43#include <OpenGL/OpenGL.h>
44#include <OpenGL/glu.h>
45
46class VideoOutputCoreVideoRep
47{
48    private:
49        friend class VideoOutputCoreVideo;
50       
51        VideoOutputCoreVideoRep();
52        ~VideoOutputCoreVideoRep();
53       
54        bool Init(int width, int height, float aspect, WId winid,
55                int winx, int winy, int winw, int winh, WId embedid = 0);
56        void PrepareFrame(VideoFrame *buffer, FrameScanType t);
57        void Show(FrameScanType);
58
59        void InputChanged(int width, int height, float aspect,
60                        MythCodecID av_codec_id);
61        void VideoAspectRatioChanged(float aspect);
62        void Zoom(int direction);
63
64        void EmbedInWidget(WId wid, int x, int y, int w, int h);
65        void StopEmbedding(void);
66
67        int GetRefreshRate(void);
68
69        void DrawUnusedRects(bool sync = true);
70
71        void UpdatePauseFrame(void);
72        void ProcessFrame(VideoFrame *frame, OSD *osd,
73                        FilterChain *filterList,
74                        NuppelVideoPlayer *pipPlayer);
75
76        bool SetupOpenGL();
77        void InitializeGLView();
78        bool CreateCoreVideoBuffers();
79        void DeleteCoreVideoBuffers();
80        void UpdateTransformMatrix();
81
82        // Global preferences:
83        bool               mScaleUpVideo;      // Enlarge video as needed?
84        yuv2vuy_fun mYUVConverter;  // 420 -> 2vuy conversion function
85
86        // CoreVideo, OpenGL & QuickDraw data
87        WindowRef           mMainWindow;
88        AGLContext          mAGLContext;
89        CVDisplayLinkRef    mDisplayLink;
90        CGLPixelFormatObj   mCGLPixelFormat;
91        CGLContextObj       mCGLContext;
92   
93        CVPixelBufferRef        mCurrentFrameBuffer;
94        CVOpenGLTextureCacheRef mTextureCache;
95        CVOpenGLTextureRef      mTexture;
96
97        CGRect mTextureFrame;
98
99        GLfloat mLowerLeft[2];
100        GLfloat mLowerRight[2];
101        GLfloat mUpperRight[2];
102        GLfloat mUpperLeft[2];
103
104        int mWidth;
105        int mHeight;
106        float mAspect;
107        float mSrcAspect;
108        int mSrcMode;
109        int mSrcWidth;
110        int mSrcHeight;
111   
112        int                mDesiredWidth,
113                           mDesiredHeight,
114                           mDesiredXoff,
115                           mDesiredYoff;   // output size characteristics
116       
117        // Zoom preferences:
118        int                mZoomedIn;          // These mirror the videooutbase
119        int                mZoomedUp;          // variables, for the benefit of
120        int                mZoomedRight;       // the views
121
122        int mRefreshRate;
123       
124        char *mBitmapData;
125        size_t mBitmapDataSize;
126};
127
128/*
129 * VideoOutputCoreVideo implementation
130 */
131VideoOutputCoreVideo::VideoOutputCoreVideo(void)
132                 : VideoOutput()
133{
134    init(&mPauseFrame, FMT_YV12, NULL, 0, 0, 0, 0);
135
136    mCoreVideoRep = new VideoOutputCoreVideoRep;
137}
138
139VideoOutputCoreVideo::~VideoOutputCoreVideo()
140{
141    if (mPauseFrame.buf)
142        delete [] mPauseFrame.buf;
143
144    vbuffers.DeleteBuffers();
145
146    delete mCoreVideoRep;
147}
148
149void VideoOutputCoreVideo::VideoAspectRatioChanged(float aspect)
150{
151    VideoOutput::VideoAspectRatioChanged(aspect);
152
153    mCoreVideoRep->VideoAspectRatioChanged(aspect);
154}
155
156void VideoOutputCoreVideo::Zoom(int direction)
157{
158    VERBOSE(VB_PLAYBACK,
159            QString("VideoOutputCoreVideo::Zoom(direction=%1)").arg(direction));
160
161    VideoOutput::Zoom(direction);
162    mCoreVideoRep->Zoom(direction);
163}
164
165void VideoOutputCoreVideo::InputChanged(int width, int height, float aspect,
166                                     MythCodecID av_codec_id)
167{
168    VERBOSE(VB_PLAYBACK,
169            QString("VideoOutputCoreVideo::InputChanged(width=%1, height=%2, aspect=%3")
170                   .arg(width).arg(height).arg(aspect));
171    VideoOutput::InputChanged(width, height, aspect, av_codec_id);
172    mCoreVideoRep->InputChanged(width, height, aspect, av_codec_id);
173}
174
175int VideoOutputCoreVideo::GetRefreshRate(void)
176{
177    return mCoreVideoRep->GetRefreshRate();
178}
179
180bool VideoOutputCoreVideo::Init(int width, int height, float aspect,
181                             WId winid, int winx, int winy,
182                             int winw, int winh, WId embedid)
183{
184    VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideo::Init(width=%1, height=%2, aspect=%3, winid=%4\n winx=%5, winy=%6, winw=%7, winh=%8, WId embedid=%9)")
185                   .arg(width)
186                   .arg(height)
187                   .arg(aspect)
188                   .arg(winid)
189                   .arg(winx)
190                   .arg(winy)
191                   .arg(winw)
192                   .arg(winh)
193                   .arg(embedid));
194
195    vbuffers.Init(kNumBuffers, true, kNeedFreeFrames,
196                  kPrebufferFramesNormal, kPrebufferFramesSmall,
197                  kKeepPrebuffer);
198    VideoOutput::Init(width, height, aspect, winid,
199                      winx, winy, winw, winh, embedid);
200
201    vbuffers.CreateBuffers(video_dim.width(), video_dim.height());
202
203    // Set up pause frame
204    if (mPauseFrame.buf)
205      delete [] mPauseFrame.buf;
206
207    VideoFrame *scratch = vbuffers.GetScratchFrame();
208
209    init(&mPauseFrame, FMT_YV12, new unsigned char[scratch->size],
210       scratch->width, scratch->height, scratch->bpp, scratch->size);
211
212    mPauseFrame.frameNumber = scratch->frameNumber;
213
214    bool repInited =  mCoreVideoRep->Init(width, height, aspect, winid, winx, winy, winw, winh, embedid);
215    if (repInited)
216    {
217        MoveResize();
218    }
219    return repInited;
220}
221
222void VideoOutputCoreVideo::EmbedInWidget(WId wid, int x, int y, int w, int h)
223{
224    VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideo::EmbedInWidget(wid=%1, x=%2, y=%3, w=%4, h=%5)")
225                   .arg(wid)
226                   .arg(x)
227                   .arg(y)
228                   .arg(w)
229                   .arg(h));
230
231    if (embedding)
232        return;
233
234    VideoOutput::EmbedInWidget(wid, x, y, w, h);
235
236    mCoreVideoRep->EmbedInWidget(wid, x, y, w, h);
237}
238
239void VideoOutputCoreVideo::StopEmbedding(void)
240{
241    VERBOSE(VB_PLAYBACK,
242        QString("VideoOutputCoreVideo::StopEmbedding()"));
243
244    if (!embedding)
245        return;
246
247    VideoOutput::StopEmbedding();
248
249    mCoreVideoRep->StopEmbedding();
250}
251
252void VideoOutputCoreVideo::PrepareFrame(VideoFrame *buffer, FrameScanType t)
253{
254    (void)t;
255
256    if (!buffer)
257        buffer = vbuffers.GetScratchFrame();
258
259    framesPlayed = buffer->frameNumber + 1;
260}
261
262void VideoOutputCoreVideo::Show(FrameScanType t)
263{
264    mCoreVideoRep->Show(t);
265}
266
267void VideoOutputCoreVideo::DrawUnusedRects(bool)
268{
269}
270
271void VideoOutputCoreVideo::UpdatePauseFrame(void)
272{
273    VideoFrame *pauseb = vbuffers.GetScratchFrame();
274    VideoFrame *pauseu = vbuffers.head(kVideoBuffer_used);
275    if (pauseu)
276        memcpy(mPauseFrame.buf, pauseu->buf, pauseu->size);
277    else
278        memcpy(mPauseFrame.buf, pauseb->buf, pauseb->size);
279}
280
281void VideoOutputCoreVideo::ProcessFrame(VideoFrame *frame, OSD *osd,
282                                     FilterChain *filterList,
283                                     NuppelVideoPlayer *pipPlayer)
284{
285    if (!frame)
286    {
287        frame = vbuffers.GetScratchFrame();
288        CopyFrame(vbuffers.GetScratchFrame(), &mPauseFrame);
289    }
290
291    if (filterList)
292        filterList->ProcessFrame(frame);
293
294    if (m_deinterlacing &&
295        m_deintFilter != NULL &&
296        m_deinterlaceBeforeOSD)
297    {
298        m_deintFilter->ProcessFrame(frame);
299    }
300
301    ShowPip(frame, pipPlayer);
302    DisplayOSD(frame, osd);
303
304    if (m_deinterlacing &&
305        m_deintFilter != NULL &&
306        !m_deinterlaceBeforeOSD)
307    {
308        m_deintFilter->ProcessFrame(frame);
309    }
310
311    mCoreVideoRep->ProcessFrame(frame, osd, filterList, pipPlayer);
312}
313
314bool VideoOutputCoreVideo::IsCoreVideoSupported()
315{
316    bool supported = false;
317    CGDirectDisplayID displayID = NULL;
318    Rect windowBounds;
319   
320    if (GetWindowBounds( FrontNonFloatingWindow(), kWindowStructureRgn, &windowBounds) == noErr)
321    {
322        CGPoint pt;
323        pt.x = windowBounds.left;
324        pt.y = windowBounds.top;
325        CGDisplayCount ct;
326        CGGetDisplaysWithPoint(pt, 1, &displayID, &ct);
327    }
328    if (!displayID)
329        displayID = CGMainDisplayID();
330   
331    if (displayID && CGDisplayUsesOpenGLAcceleration( displayID ))
332        supported = true;
333       
334    return supported;
335}
336VideoOutputCoreVideoRep::VideoOutputCoreVideoRep() : mAGLContext(NULL), mRefreshRate(0)
337{
338    mDesiredXoff = mDesiredYoff = mDesiredWidth = mDesiredHeight = 0;
339}
340
341VideoOutputCoreVideoRep::~VideoOutputCoreVideoRep()
342{
343    aglDestroyContext(mAGLContext);
344    DeleteCoreVideoBuffers();
345}
346
347bool VideoOutputCoreVideoRep::Init(int width, int height, float aspect, WId winid, int winx, int winy, int winw, int winh, WId embedid)
348{
349    (void)winid; // unused
350    (void)winx; // unused
351    (void)winy; // unused
352    (void)winw; // unused
353    (void)winh; // unused
354    (void)embedid; // unused
355   
356    mYUVConverter = get_yuv2vuy_conv();
357
358    mWidth  = width;
359    mHeight = height;
360    mAspect = aspect;
361
362    mSrcWidth  = width;
363    mSrcHeight = height;
364    mSrcAspect = aspect;
365    mSrcMode   = kLetterbox_Off;
366   
367    mZoomedIn = 0;
368    mZoomedUp = 0;
369    mZoomedRight = 0;
370   
371    // Global configuration options
372    mScaleUpVideo = gContext->GetNumSetting("MacScaleUp", 1);
373
374
375    mTextureFrame = CGRectMake(0, 0, mWidth, mHeight);
376   
377    mMainWindow = FrontNonFloatingWindow();
378 
379    if (!SetupOpenGL())
380    {
381        VERBOSE(VB_IMPORTANT, "VideoOutCoreVideo::Init - SetupOpenGL failed");
382        return false;
383    }
384   
385    InitializeGLView();
386   
387    if (!CreateCoreVideoBuffers())
388    {
389        VERBOSE(VB_IMPORTANT, "VideoOutCoreVideo::Init - CreateCoreVideoBuffers failed");
390        return false;
391    }
392
393    Rect windowBounds;
394    CGDirectDisplayID screen = NULL;
395    if (GetWindowBounds( mMainWindow, kWindowStructureRgn, &windowBounds) == noErr)
396    {
397        CGPoint pt;
398        pt.x = windowBounds.left;
399        pt.y = windowBounds.top;
400        CGDisplayCount ct;
401        CGGetDisplaysWithPoint(pt, 1, &screen, &ct);
402    }
403
404    if (screen == NULL)
405        screen = CGMainDisplayID();
406
407    // Find the refresh rate of our screen
408    CFDictionaryRef m;
409    m = CGDisplayCurrentMode(screen);
410    mRefreshRate = get_float_CF(m, kCGDisplayRefreshRate);
411    if (mRefreshRate == 0.0)    // LCD display?
412        mRefreshRate = 60.0; 
413
414    UpdateTransformMatrix();
415
416    return true;
417}
418
419void VideoOutputCoreVideoRep::PrepareFrame(VideoFrame *buffer, FrameScanType t)
420{
421    VERBOSE(VB_IMPORTANT, "Implement VideoOutputCoreVideoRep::PrepareFrame");
422}
423void VideoOutputCoreVideoRep::Show(FrameScanType)
424{
425    if (!mAGLContext)
426        return;
427       
428    aglSetCurrentContext(mAGLContext);
429   
430    glClear(GL_COLOR_BUFFER_BIT);       
431
432    glEnable(CVOpenGLTextureGetTarget(mTexture));
433    glBindTexture(CVOpenGLTextureGetTarget(mTexture), CVOpenGLTextureGetName(mTexture));
434
435    glColor3f(1,1,1);
436    glBegin(GL_QUADS);
437    glTexCoord2f(mLowerLeft[0], mLowerLeft[1]); glVertex2i(     mTextureFrame.origin.x, mTextureFrame.origin.y);
438    glTexCoord2f(mUpperLeft[0], mUpperLeft[1]); glVertex2i(     mTextureFrame.origin.x, mTextureFrame.size.height);
439    glTexCoord2f(mUpperRight[0], mUpperRight[1]); glVertex2i(   mTextureFrame.size.width, mTextureFrame.size.height);
440    glTexCoord2f(mLowerRight[0], mLowerRight[1]); glVertex2i(   mTextureFrame.size.width, mTextureFrame.origin.y);
441    glEnd();
442    glDisable(CVOpenGLTextureGetTarget(mTexture));
443    glFlush();
444}
445
446void VideoOutputCoreVideoRep::InputChanged(int width, int height, float aspect, MythCodecID av_codec_id)
447{
448    VERBOSE(VB_IMPORTANT, "Implement VideoOutputCoreVideoRep::InputChanged");
449}
450
451void VideoOutputCoreVideoRep::VideoAspectRatioChanged(float aspect)
452{
453    VERBOSE(VB_PLAYBACK,
454            QString("VideoOutputCoreVideoRep::VideoAspectRatioChanged"
455                    "(aspect=%1) [was %2]")
456            .arg(aspect).arg(mSrcAspect));
457
458    mSrcAspect = aspect;
459    mSrcMode   = kLetterbox_Off;
460    UpdateTransformMatrix();
461}
462
463void VideoOutputCoreVideoRep::Zoom(int direction)
464{
465    VERBOSE(VB_IMPORTANT, "Implement VideoOutputCoreVideoRep::Zoom");
466}
467
468void VideoOutputCoreVideoRep::EmbedInWidget(WId wid, int x, int y, int w, int h)
469{
470    VERBOSE(VB_IMPORTANT, "Implement VideoOutputCoreVideoRep::EmbedInWidget");
471}
472
473void VideoOutputCoreVideoRep::StopEmbedding(void)
474{
475    VERBOSE(VB_IMPORTANT, "Implement VideoOutputCoreVideoRep::StopEmbedding");
476}
477
478int VideoOutputCoreVideoRep::GetRefreshRate(void)
479{
480    return (int) 1000000 / mRefreshRate;      // Rate is in microseconds per frame
481}
482
483void VideoOutputCoreVideoRep::DrawUnusedRects(bool sync)
484{
485    VERBOSE(VB_IMPORTANT, "Implement VideoOutputCoreVideoRep::DrawUnusedRects");
486}
487
488void VideoOutputCoreVideoRep::UpdatePauseFrame(void)
489{
490    VERBOSE(VB_IMPORTANT, "Implement VideoOutputCoreVideoRep::UpdatePauseFrame");
491}
492
493void VideoOutputCoreVideoRep::ProcessFrame(VideoFrame *frame, OSD *osd, FilterChain *filterList, NuppelVideoPlayer *pipPlayer)
494{
495#if 1
496    if (mYUVConverter)
497    {
498        mYUVConverter((uint8_t *)mBitmapData,
499                           frame->buf + frame->offsets[0], // Y
500                           frame->buf + frame->offsets[1], // U
501                           frame->buf + frame->offsets[2], // V
502                           frame->width, frame->height,
503                           (frame->width % 2), (frame->width % 2), 0);
504        // FIXME - These values (stride) should be calculated
505        //         from frame->pitches and frame->width ?
506    }
507    else
508#endif
509        memcpy(mBitmapData, frame->buf, frame->size);
510
511    CVReturn error = CVOpenGLTextureCacheCreateTextureFromImage (NULL, mTextureCache,  mCurrentFrameBuffer,  0, &mTexture);
512    if(error != kCVReturnSuccess)
513        VERBOSE(VB_IMPORTANT,QString("VideoOutputCoreVideRep::ProcessFrame - Failed to create OpenGL texture error = %1").arg(error));
514
515    CVOpenGLTextureGetCleanTexCoords(mTexture, mLowerLeft, mLowerRight, mUpperRight, mUpperLeft);
516}
517
518// Build the transformation matrix to scale the video appropriately.
519void VideoOutputCoreVideoRep::UpdateTransformMatrix()
520{
521    if (!mAGLContext)
522        return;
523       
524    aglSetCurrentContext(mAGLContext);
525
526    InitializeGLView();
527   
528    int x, y, w, h, sw, sh;
529    x = mDesiredXoff;
530    y = mDesiredYoff;
531    w = mDesiredWidth;
532    h = mDesiredHeight;
533    sw = mSrcWidth;
534    sh = mSrcHeight;
535    float aspect = mSrcAspect;
536
537    VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Window is %1 x %2")
538                                .arg(w).arg(h));
539    VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Image is %1 x %2")
540                                .arg(sw).arg(sh));
541
542    gluOrtho2D(0, 0, w, h);
543   
544    // scale for non-square pixels
545    if (fabsf(aspect - (sw * 1.0f / sh)) > 0.01f)
546    {
547        if (mScaleUpVideo)
548        {
549            // scale width up, leave height alone
550            double aspectScale = aspect * sh / sw;
551            VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Scaling to %1 of width")
552                                        .arg(aspectScale));
553            glScalef(aspectScale, 1.0f, 1.0f);
554           
555            // reset sw to be apparent width
556            sw = (int)lroundf(sh * aspect);
557        }
558        else
559        {
560            // scale height down
561            double aspectScale = sw / (aspect * sh);
562            VERBOSE(VB_PLAYBACK,
563                    QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Scaling to %1 of height")
564                           .arg(aspectScale));
565            glScalef(1.0f, aspectScale, 1.0f);
566
567            // reset sw to be apparent width
568            sh = (int)lroundf(sw / aspect);
569        }
570    }
571
572    // figure out how much zooming we want
573    double hscale, vscale;
574    switch (mSrcMode)
575    {
576        case kLetterbox_4_3_Zoom:
577            // height only fills 3/4 of image, zoom up
578            hscale = vscale = h * 1.0 / (sh * 0.75);
579            break;
580        case kLetterbox_16_9_Zoom:
581            // width only fills 3/4 of image, zoom up
582            hscale = vscale = w * 1.0 / (sw * 0.75);
583            break;
584        case kLetterbox_16_9_Stretch:
585            // like 16 x 9 standard, but with a horizontal stretch applied
586            hscale = vscale = fmin(h * 1.0 / sh, w * 1.0 / sw);
587            hscale *= 4.0 / 3.0;
588            break;
589        case kLetterbox_4_3:
590        case kLetterbox_16_9:
591        default:
592            // standard, fill viewport with scaled image
593            hscale = vscale = fmin(h * 1.0 / sh, w * 1.0 / sw);
594            break;
595    }
596    if (mZoomedIn)
597    {
598        hscale *= 1 + (mZoomedIn * .01);
599        vscale *= 1 + (mZoomedIn * .01);
600    }
601
602    // cap zooming if we requested it
603    if (!mScaleUpVideo)
604    {
605        double maxScale = fmax(hscale, vscale);
606        hscale /= maxScale;
607        vscale /= maxScale;
608    }
609
610    if ((hscale < 0.99) || (hscale > 1.01) ||
611        (vscale < 0.99) || (vscale > 1.01))
612    {
613        VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Scaling to %1 x %2 of original")
614                                    .arg(hscale).arg(vscale));
615        glScalef(hscale, vscale, 1.0f);
616
617        // reset sw, sh for new apparent width/height
618        sw = (int)(sw * hscale);
619        sh = (int)(sh * vscale);
620    }
621
622    // center image in viewport
623    if ((h != sh) || (w != sw))
624    {
625        VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Centering with %1, %2")
626                                    .arg((w - sw)/2.0).arg((h - sh)/2.0));
627        glTranslatef((w-sw) / 2.0f, (h-sh) / 2.0f, 0.0f);
628    }
629
630// apply the basic sizing to AccelUtils
631#ifdef CONFIG_MAC_ACCEL
632    AccelUtils *accel = AccelUtils::singleton();
633    if (accel)
634      accel->MoveResize(0, 0, mSrcWidth, mSrcHeight,
635                        (int)((w - sw) / 2.0), (int)((h - sh) / 2.0),
636                        sw, sh);
637#endif
638
639    // apply over/underscan
640    int hscan = gContext->GetNumSetting("HorizScanPercentage", 5);
641    int vscan = gContext->GetNumSetting("VertScanPercentage", 5);
642    if (hscan || vscan)
643    {
644        if (vscan > 0)
645        {
646            vscan *= 2;   // Confusing, but matches X behavior
647        }
648        if (hscan > 0)
649        {
650            hscan *= 2;
651        }
652       
653        VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Overscanning to %1, %2")
654                                    .arg(hscan).arg(vscan));
655        // Translate to new origin, then scale
656        glTranslatef(sw / 2.0f, sh / 2.0f, 0.0f);
657        glScalef(1.0 + (hscan / 50.0), 1.0 + (vscan / 50.0), 1.0f);
658        // ScaleMatrix(&matrix,
659        //             X2Fix((double)(1.0 + (hscan / 50.0))),
660        //             X2Fix((double)(1.0 + (vscan / 50.0))),
661        //             X2Fix(sw / 2.0),
662        //             X2Fix(sh / 2.0));
663    }
664
665    // apply TV mode offset
666    if (1)
667    {
668        int tv_xoff = gContext->GetNumSetting("xScanDisplacement", 0);
669        int tv_yoff = gContext->GetNumSetting("yScanDisplacement", 0);
670        if (tv_xoff || tv_yoff)
671        {
672            VERBOSE(VB_PLAYBACK,
673                    QString("VideoOutputCoreVideoRep::UpdateTransformMatrix TV offset by %1, %2").arg(tv_xoff).arg(tv_yoff));
674            glTranslatef(tv_xoff, tv_yoff, 0.0f);
675        }
676    }
677   
678    // apply zoomed offsets
679    if (mZoomedIn)
680    {
681        // calculate original vs. zoomed dimensions
682        int zw = (int)(sw / (1.0 + (mZoomedIn * .01)));
683        int zh = (int)(sh / (1.0 + (mZoomedIn * .01)));
684               
685        int zoomx = (int)((sw - zw) * mZoomedRight * .005);
686        int zoomy = (int)((sh - zh) * mZoomedUp    * .005);
687       
688        VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Zoom translating to %1, %2")
689                                    .arg(zoomx).arg(zoomy));
690        glTranslatef(zoomx, zoomy, 0.0f);
691    }
692
693    // apply graphics port or embedding offset
694    if (x || y)
695    {
696        VERBOSE(VB_PLAYBACK, QString("VideoOutputCoreVideoRep::UpdateTransformMatrix Translating to %1, %2")
697                                    .arg(x).arg(y));
698        glTranslatef(x, y, 0.0f);
699    }
700}
701
702bool VideoOutputCoreVideoRep::SetupOpenGL()
703{
704      if (!mMainWindow)
705      {
706          VERBOSE(VB_IMPORTANT, "VideoOutputCoreVideo::SetupOpenGL - failed to find Front Non-floating Window");
707          return false;
708      }
709
710    // ***** Set up OpenGL *****
711    GLint swapInterval = 1;
712    GLint surfaceOpacity = 1;
713
714    GLint attributes[] = {
715        AGL_RGBA,
716        AGL_PIXEL_SIZE, 32,
717        AGL_ACCELERATED,
718        AGL_NONE
719    };
720
721    AGLPixelFormat aglPixelFormat;
722
723    // get a pixel format that is appropriate for the attributes specified above
724    aglPixelFormat = aglChoosePixelFormat(NULL, 0, attributes);
725    if (NULL == aglPixelFormat) return false;
726   
727    // create an AGL rendering context
728    mAGLContext = aglCreateContext(aglPixelFormat, NULL);
729    if (NULL == mAGLContext) return false;
730   
731    // now that we have a valid context, we can attach it to the window
732    if (!aglSetDrawable(mAGLContext, GetWindowPort(mMainWindow)))
733        return false;
734   
735    // make sure to set the current context here
736        if (!aglSetCurrentContext(mAGLContext))
737        return false;
738
739    // opaque surface
740    if (!aglSetInteger(mAGLContext, AGL_SURFACE_OPACITY, &surfaceOpacity))
741        return false;
742   
743    // sync to the vertical retrace
744    if (!aglSetInteger(mAGLContext, AGL_SWAP_INTERVAL, &swapInterval))
745        return false;
746   
747    // get the CGL context from the AGL context which we need for QT & Core Image
748    if (!aglGetCGLContext(mAGLContext, (void **)&(mCGLContext)))
749        return false;
750   
751    // get the CGL pixel format from the AGL pixel format which we also need for QT & Core Image
752    if (!aglGetCGLPixelFormat(aglPixelFormat, (void **)&(mCGLPixelFormat)))
753        return false;
754
755    return true;
756}
757// adjust the viewport and projection matrix
758void VideoOutputCoreVideoRep::InitializeGLView()
759{
760    GLfloat minX, minY, maxX, maxY;
761    Rect contentRect;
762
763    GetWindowBounds(mMainWindow, kWindowContentRgn, &contentRect);
764
765    minX = (float)0;
766    minY = (float)0;
767    maxX = (float)(contentRect.right - contentRect.left);
768    maxY = (float)(contentRect.bottom - contentRect.top);
769   
770    mDesiredXoff = mDesiredYoff = 0;
771    mDesiredWidth = (contentRect.right - contentRect.left);
772    mDesiredHeight = (contentRect.bottom - contentRect.top);
773   
774    // for best results when using Core Image to render into an OpenGL context follow these guidelines:
775    // * ensure that the a single unit in the coordinate space of the OpenGL context represents a single pixel in the output device
776    // * the Core Image coordinate space has the origin in the bottom left corner of the screen -- you should configure the OpenGL
777    //   context in the same way
778    // * the OpenGL context blending state is respected by Core Image -- if the image you want to render contains translucent pixels,
779    //   it's best to enable blending using a blend function with the parameters GL_ONE, GL_ONE_MINUS_SRC_ALPHA
780
781    glViewport(0, 0, (GLsizei)(contentRect.right - contentRect.left), (GLsizei)(contentRect.bottom - contentRect.top));  // set the viewport
782
783VERBOSE(VB_IMPORTANT, QString("VideoOutputCoreVideoRep::InitializeGLView - width = %1 height = %2").arg(contentRect.right-contentRect.left).arg(contentRect.bottom-contentRect.top));
784
785    glMatrixMode(GL_MODELVIEW);    // select the modelview matrix
786    glLoadIdentity();              // reset it
787
788    glMatrixMode(GL_PROJECTION);   // select the projection matrix
789    glLoadIdentity();              // reset it
790
791    gluOrtho2D(minX, maxX, minY, maxY);  // define a 2-D orthographic projection matrix
792
793    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
794    glEnable(GL_BLEND);
795}
796
797bool VideoOutputCoreVideoRep::CreateCoreVideoBuffers()
798{
799    OSErr err;
800   
801    // Allocate buffer storage
802    mBitmapDataSize = mWidth * mHeight *  2;
803    mBitmapData = new char[mBitmapDataSize];
804
805    // Create the CoreVideo pixel buffer descriptions and frames
806    err = CVPixelBufferCreateWithBytes( NULL, mWidth, mHeight, k422YpCbCr8CodecType, mBitmapData, mWidth*2, NULL, NULL, NULL, &(mCurrentFrameBuffer));
807    if(err != kCVReturnSuccess)
808    {
809        VERBOSE(VB_IMPORTANT, QString("VideoOutCoreVideo::Init - Failed to create Pixel Buffer err = %1").arg(err));
810        return false;
811    }
812   
813    err = CVOpenGLTextureCacheCreate(NULL, 0, mCGLContext, mCGLPixelFormat, 0, &(mTextureCache));
814    if(err != kCVReturnSuccess)
815    {
816        VERBOSE(VB_IMPORTANT, QString("VideoOutCoreVideo::Init - Failed to create OpenGL texture Cache err = %1").arg(err));
817        return false;
818    }
819   
820    err = CVOpenGLTextureCacheCreateTextureFromImage(   NULL, mTextureCache, mCurrentFrameBuffer, 0, &(mTexture));
821    if(err != kCVReturnSuccess)
822    {
823        VERBOSE(VB_IMPORTANT, QString("VideoOutCoreVideo::Init - Failed to create OpenGL texture err = %1").arg(err));
824        return false;
825    }
826   
827    return true;
828}
829
830void VideoOutputCoreVideoRep::DeleteCoreVideoBuffers()
831{
832    CVOpenGLTextureRelease(mTexture);
833    CVOpenGLTextureCacheRelease(mTextureCache);
834    CVPixelBufferRelease(mCurrentFrameBuffer);
835
836    if (mBitmapData)
837    {
838        delete [] mBitmapData;
839        mBitmapData = NULL;
840        mBitmapDataSize = 0;
841    }
842}
843