Ticket #2381: videoout_corevideo.3.cpp

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