Ticket #2381: videoout_corevideo.cpp

File videoout_corevideo.cpp, 26.0 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// ****************************************************************************
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