Ticket #2381: videoout_corevideo.2.cpp

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