Ticket #2381: videoout_corevideo.3.cpp

File videoout_corevideo.3.cpp, 27.8 KB (added by awk@…, 14 years ago)

Implement UpdatePauseFrame? fully

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