Ticket #2381: videoout_corevideo.2.cpp

File videoout_corevideo.2.cpp, 27.9 KB (added by awk@…, 20 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