Ticket #2649: videoout_opengl.cpp

File videoout_opengl.cpp, 28.9 KB (added by anonymous, 20 years ago)
Line 
1#include <cmath>
2
3using namespace std;
4
5// MythTV videout headers
6#include "videoout_opengl.h"
7#include "util-x11.h"
8#include "videodisplayprofile.h"
9
10// MythTV General headers
11#include "../libavcodec/avcodec.h"
12#include "mythconfig.h"
13#include "mythcontext.h"
14#include "filtermanager.h"
15#include "NuppelVideoPlayer.h"
16#define IGNORE_TV_PLAY_REC
17#include "tv.h"
18
19#define LOC QString("VideoOutputOpengl: ")
20#define LOC_ERR QString("VideoOutputOpengl Error: ")
21
22#ifndef GL_TEXTURE_RECTANGLE_ARB
23#define GL_TEXTURE_RECTANGLE_ARB 0x84F5
24#endif
25
26#ifndef GL_TEXTURE_RECTANGLE_EXT
27#define GL_TEXTURE_RECTANGLE_EXT 0x84F5
28#endif
29
30#ifndef GL_TEXTURE_RECTANGLE_NV
31#define GL_TEXTURE_RECTANGLE_NV 0x84F5
32#endif
33
34VideoOutputOpengl::VideoOutputOpengl()
35 : VideoOutput(),
36 display_res(NULL), global_lock(true),
37
38 XJ_win(0), XJ_curwin(0), XJ_disp(NULL),
39 XJ_screen_num(0), XJ_started(false),
40 my_context(NULL), max_texture_size(0), texture_rects(false),
41 my_gl_texture(GL_TEXTURE_2D), actually_draw_pip(false)
42{
43 bzero(&av_pause_frame, sizeof(av_pause_frame));
44
45 // If using custom display resolutions, display_res will point
46 // to a singleton instance of the DisplayRes class
47 if (gContext->GetNumSetting("UseVideoModes", 0))
48 display_res = DisplayRes::GetDisplayRes();
49
50 use_colourcontrols = gContext->GetNumSetting(
51 "UseOutputPictureControls", 0);
52 allowpreviewepg = false;
53}
54
55VideoOutputOpengl::~VideoOutputOpengl()
56{
57 if (my_context)
58 X11S(glXDestroyContext(XJ_disp, my_context));
59 if (XJ_win)
60 X11S(XDestroyWindow(XJ_disp, XJ_win));
61
62 DeleteBuffers(true);
63
64 // Switch back to desired resolution for GUI
65 if (display_res)
66 display_res->SwitchToGUI();
67}
68
69bool VideoOutputOpengl::Init(int width, int height, float aspect,
70 WId winid, int winx, int winy, int winw,
71 int winh, WId embedid)
72{
73 if (winid <= 0)
74 {
75 VERBOSE(VB_PLAYBACK, LOC_ERR + "Invalid Window ID.");
76 return false;
77 }
78 XJ_disp = MythXOpenDisplay();
79 if (!XJ_disp)
80 {
81 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to open display.");
82 }
83
84 X11L;
85 XJ_screen_num = DefaultScreen(XJ_disp);
86 XJ_curwin = winid;
87 XJ_win = winid;
88 X11U;
89
90 // Basic setup
91 VideoOutput::Init(width, height, aspect,
92 winid, winx, winy, winw, winh,
93 embedid);
94 if (!InitGlxContext(winid, display_visible_rect.width(),
95 display_visible_rect.height()))
96 return false;
97 // Set resolution/measurements (check XRandR, Xinerama, config settings)
98 InitDisplayMeasurements(width, height);
99 MoveResize();
100
101 if (!CheckExtensions())
102 return false;
103 InitOpenGL();
104 if (!CreateVideoTexture(&glVideo, video_dim, display_video_rect))
105 return false;
106 if (!LoadFragmentProgram())
107 return false;
108 if (!InitSetupBuffers())
109 return false;
110
111 XJ_started = true;
112 db_vdisp_profile->SetVideoRenderer("opengl");
113 return true;
114}
115
116bool VideoOutputOpengl::CheckExtensions()
117{
118 X11L;
119 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
120 QString extensions(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)));
121 glXMakeContextCurrent( XJ_disp, None, None, NULL );
122 X11U;
123
124 texture_rects = true;
125 if (extensions.contains("GL_NV_texture_rectangle"))
126 {
127 VERBOSE(VB_PLAYBACK, LOC + "Using NV NPOT texture extension");
128 my_gl_texture = GL_TEXTURE_RECTANGLE_NV;
129 }
130 else if (extensions.contains("GL_ARB_texture_rectangle"))
131 {
132 VERBOSE(VB_PLAYBACK, LOC + "Using ARB NPOT texture extension");
133 my_gl_texture = GL_TEXTURE_RECTANGLE_ARB;
134 }
135 else if (extensions.contains("GL_EXT_texture_rectangle"))
136 {
137 VERBOSE(VB_PLAYBACK, LOC + "Using EXT NPOT texture extension");
138 my_gl_texture = GL_TEXTURE_RECTANGLE_EXT;
139 }
140 else
141 {
142 texture_rects = false;
143 }
144
145 if (!extensions.contains("GL_ARB_fragment_program"))
146 {
147 VERBOSE(VB_GENERAL, "GL_ARB_fragment_program not available.");
148 return false;
149 }
150 return true;
151}
152
153void VideoOutputOpengl::SetViewPort()
154{
155 X11L;
156 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
157
158 glViewport( 0, 0, display_visible_rect.width(), display_visible_rect.height() );
159 glMatrixMode( GL_PROJECTION );
160 glLoadIdentity();
161 glOrtho( 0, display_visible_rect.width(), display_visible_rect.height(), 0, 1, -1 );
162 glMatrixMode( GL_MODELVIEW );
163 glLoadIdentity();
164 glFlush();
165
166 glXMakeContextCurrent( XJ_disp, None, None, NULL );
167 X11U;
168}
169
170void VideoOutputOpengl::InitOpenGL()
171{
172 SetViewPort();
173
174 X11L;
175 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
176
177 glDisable( GL_BLEND );
178 glDisable( GL_DEPTH_TEST );
179 glDepthMask( GL_FALSE );
180 glDisable( GL_CULL_FACE );
181 glEnable( my_gl_texture );
182
183 glShadeModel( GL_FLAT );
184 glDisable( GL_POLYGON_SMOOTH );
185 glDisable( GL_LINE_SMOOTH );
186 glDisable( GL_POINT_SMOOTH );
187
188 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
189 glClear( GL_COLOR_BUFFER_BIT );
190
191 glFlush();
192
193 glGenTextures( 1, &glVideo.tex );
194 glGenTextures( 1, &glPip.tex );
195
196 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max_texture_size );
197 if (max_texture_size == 0)
198 max_texture_size = 512;
199 VERBOSE(VB_PLAYBACK, LOC +
200 QString("Maximum supported texture size: %1 x %2")
201 .arg(max_texture_size).arg(max_texture_size));
202 VERBOSE(VB_PLAYBACK, LOC + "OpenGL state initialised.");
203
204 glXMakeContextCurrent( XJ_disp, None, None, NULL );
205 X11U;
206}
207
208bool VideoOutputOpengl::LoadFragmentProgram()
209{
210 GLint errorpos;
211 QString program =
212 "!!ARBfp1.0\n"
213 "OPTION ARB_precision_hint_fastest;"
214 "ATTRIB ytex = fragment.texcoord[0];"
215 "PARAM off = program.env[1];"
216 "TEMP res, tmp, tmp2;"
217 "TEX res, ytex, texture[0], %1;"
218 "MAD tmp2, ytex, {0.5, 0.5}, off.wyww;"
219 "TEX tmp.x, tmp2, texture[0], %1;"
220 "ADD tmp2, tmp2, off.xwww;"
221 "TEX tmp.y, tmp2, texture[0], %1;";
222 QString prog_colour_adjust =
223 "PARAM adj = program.env[0];"
224 "SUB res, res, 0.5;"
225 "MAD res, res, adj.yyyy, adj.xxxx;"
226 "SUB tmp, tmp, { 0.5, 0.5 };"
227 "MAD tmp, adj.zzzz, tmp, 0.5;";
228 QString prog_convert =
229 "MAD res, res, 1.164, -0.063;"
230 "SUB tmp, tmp, { 0.5, 0.5 };"
231 "MAD res, { 0, -.392, 2.017 }, tmp.xxxw, res;"
232 "MAD result.color, { 1.596, -.813, 0 }, tmp.yyyw, res;"
233 "END";
234
235 program.replace("%1", texture_rects ? "RECT" : "2D");
236 if (use_colourcontrols)
237 program += prog_colour_adjust;
238 program += prog_convert;
239
240 X11L;
241 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
242 glGenProgramsARB ( 1, &frag_prog );
243 glBindProgramARB ( GL_FRAGMENT_PROGRAM_ARB, frag_prog );
244 glProgramStringARB ( GL_FRAGMENT_PROGRAM_ARB,
245 GL_PROGRAM_FORMAT_ASCII_ARB,
246 program.length(), program.latin1() );
247 glGetIntegerv ( GL_PROGRAM_ERROR_POSITION_ARB, &errorpos );
248 glEnable ( GL_FRAGMENT_PROGRAM_ARB );
249
250 glXMakeContextCurrent( XJ_disp, None, None, NULL );
251 X11U;
252
253 if (errorpos != -1)
254 {
255 VERBOSE(VB_PLAYBACK, LOC_ERR +
256 QString("Fragment Program compile error: position %1:'%2'")
257 .arg(errorpos)
258 .arg(program.mid(errorpos)));
259 return false;
260 }
261 VERBOSE(VB_PLAYBACK, LOC + "Fragment program loaded.");
262 return true;
263}
264
265bool VideoOutputOpengl::CreateVideoTexture(GLframe *frame, QSize size, QRect pos)
266{
267 int width, height;
268
269 if (texture_rects)
270 {
271 width = size.width();
272 height = size.height() * 3 / 2;
273 }
274 else
275 {
276 width = height = kMinOpenglTexSize;
277 while (width < size.width()) { width *= 2; }
278 while (height < size.height()) { height *= 2; }
279
280 if ((size.height() * 1.5) > height)
281 height *= 2;
282 }
283
284 if (width > max_texture_size || height > max_texture_size)
285 {
286 VERBOSE(VB_PLAYBACK, LOC_ERR +
287 "Frame larger than maximum texture size.");
288 return false;
289 }
290
291 if (width != frame->tex_width || height != frame->tex_height)
292 {
293 scratchspace = new unsigned char[(width
294 * height * 4) + 4];
295 memset(scratchspace, 0 , width * height * 4);
296
297 if (!CreateTexture(width, height, &frame->tex, scratchspace))
298 {
299 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create texture.");
300 return false;
301 }
302 delete scratchspace;
303 }
304
305 frame->position = pos;
306 frame->vid_width = size.width();
307 frame->vid_height = size.height();
308 frame->tex_width = width;
309 frame->tex_height = height;
310 frame->uOffset = size.width() * size.height();
311 frame->vOffset = frame->uOffset * 5 / 4;
312 VERBOSE(VB_PLAYBACK, LOC + QString("Created texture (%1x%2)")
313 .arg(width).arg(height));
314 return true;
315}
316
317bool VideoOutputOpengl::CreateTexture(GLint width,
318 GLint height,
319 GLuint *tex,
320 unsigned char *data)
321{
322 GLint check;
323
324 X11L;
325 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
326
327 glBindTexture( my_gl_texture, *tex );
328 glTexParameteri (my_gl_texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
329 glTexParameteri (my_gl_texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
330 glTexParameteri (my_gl_texture, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
331 glTexParameteri (my_gl_texture, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
332 glTexImage2D( my_gl_texture, 0, GL_RGBA8,
333 width, height,
334 0, GL_RGB , GL_UNSIGNED_BYTE, data );
335 glGetTexLevelParameteriv(my_gl_texture, 0, GL_TEXTURE_WIDTH, &check);
336 glXMakeContextCurrent(XJ_disp, None, None, NULL);
337 X11U;
338 if (check !=width)
339 return false;
340 return true;
341}
342
343inline void DrawVideoQuad(GLframe frame, bool texture_rects,
344 int my_gl_texture)
345{
346 float right = (float)frame.vid_width;
347 float bottom = (float)frame.vid_height;
348 if (!texture_rects)
349 {
350 right /= frame.tex_width;
351 bottom /= frame.tex_height;
352 }
353
354 glProgramEnvParameter4fARB (GL_FRAGMENT_PROGRAM_ARB, 1,
355 right / 2.0f, bottom, 0.0f, 0.0f);
356 glBindTexture( my_gl_texture, frame.tex );
357 glBegin( GL_QUADS );
358 glTexCoord2f(0.0f, 0.0f);
359 glVertex2f( frame.position.left(), frame.position.top());
360 glTexCoord2f(right, 0.0f);
361 glVertex2f( frame.position.right(), frame.position.top());
362 glTexCoord2f(right, bottom);
363 glVertex2f( frame.position.right(), frame.position.bottom());
364 glTexCoord2f(0.0f, bottom);
365 glVertex2f( frame.position.left(), frame.position.bottom());
366 glEnd();
367}
368
369void VideoOutputOpengl::PrepareFrame(VideoFrame *buffer, FrameScanType t)
370{
371 (void) t;
372
373 if (!buffer)
374 buffer = vbuffers.GetScratchFrame();
375
376 framesPlayed = buffer->frameNumber + 1;
377
378 // TODO should cope with YUV422P, rgb24, argb32 etc
379 if (buffer->codec != FMT_YV12)
380 return;
381
382 UpdateVideoTexture(glVideo, buffer);
383
384 X11L;
385 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
386 glClear(GL_COLOR_BUFFER_BIT);
387
388 if (use_colourcontrols)
389 {
390 glProgramEnvParameter4fARB (GL_FRAGMENT_PROGRAM_ARB, 0,
391 ((float) db_pict_attr[kPictureAttribute_Brightness] / 50 ) - 0.5,
392 ((float) db_pict_attr[kPictureAttribute_Contrast] / 50),
393 ((float) db_pict_attr[kPictureAttribute_Colour] / 50),
394 0.0f);
395 }
396
397 DrawVideoQuad(glVideo, texture_rects, my_gl_texture);
398 if (actually_draw_pip)
399 DrawVideoQuad(glPip, texture_rects, my_gl_texture);
400 glFlush();
401
402 glXMakeContextCurrent(XJ_disp, None, None, NULL);
403 X11U;
404}
405
406void VideoOutputOpengl::ShowPip(VideoFrame *frame, NuppelVideoPlayer *pipplayer)
407{
408 (void) frame;
409 if (!pipplayer)
410 {
411 actually_draw_pip = false;
412 glPip.vid_width = glPip.vid_height = 0;
413 return;
414 }
415
416 actually_draw_pip = false;
417 int pipw, piph;
418 VideoFrame *pipimage = pipplayer->GetCurrentFrame(pipw, piph);
419 float pipVideoAspect = pipplayer->GetVideoAspect();
420 uint pipVideoWidth = pipplayer->GetVideoWidth();
421 uint pipVideoHeight = pipplayer->GetVideoHeight();
422
423 // If PiP is not initialized to values we like, silently ignore the frame.
424 if ((pipVideoAspect <= 0) || !pipimage ||
425 !pipimage->buf || pipimage->codec != FMT_YV12)
426 {
427 pipplayer->ReleaseCurrentFrame(pipimage);
428 return;
429 }
430
431 QRect position;
432 int tmph = (display_visible_rect.height() * db_pip_size) / 100;
433 float pixel_adj = ((float)display_visible_rect.width() /
434 (float)display_visible_rect.height()) / display_aspect;
435 position.setHeight(tmph);
436 position.setWidth((int)(tmph * pipVideoAspect * pixel_adj));
437
438 // Figure out where to put the Picture-in-Picture window
439 int xoff = (int)(display_visible_rect.width() * 0.07); // inside 'safe area'
440 int yoff = (int)(display_visible_rect.height() * 0.07);
441 switch (db_pip_location)
442 {
443 default:
444 case kPIPTopLeft:
445 break;
446 case kPIPBottomLeft:
447 yoff = display_visible_rect.height() - position.height() - yoff;
448 break;
449 case kPIPTopRight:
450 xoff = display_visible_rect.width() - position.width() - xoff;
451 break;
452 case kPIPBottomRight:
453 xoff = display_visible_rect.width() - position.width() - xoff;
454 yoff = display_visible_rect.height() - position.height() - xoff;
455 break;
456 }
457 position.moveBy(xoff, yoff);
458
459 if (glPip.vid_width != pipVideoWidth ||
460 glPip.vid_height != pipVideoHeight)
461 {
462 if (!CreateVideoTexture(&glPip, QSize(pipVideoWidth, pipVideoHeight), position))
463 {
464 pipplayer->ReleaseCurrentFrame(pipimage);
465 return;
466 }
467 }
468 glPip.position = position;
469 UpdateVideoTexture(glPip, pipimage);
470 actually_draw_pip = true;
471 pipplayer->ReleaseCurrentFrame(pipimage);
472}
473
474void VideoOutputOpengl::UpdateVideoTexture(GLframe vid, VideoFrame *buffer)
475{
476 if ((buffer->width * buffer->height) != vid.uOffset)
477 return;
478 X11L;
479 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
480
481 glActiveTexture( GL_TEXTURE0);
482 glBindTexture( my_gl_texture, vid.tex );
483 glTexSubImage2D( my_gl_texture, 0,
484 0,
485 0,
486 vid.vid_width,
487 vid.vid_height,
488 GL_LUMINANCE,
489 GL_UNSIGNED_BYTE,
490 buffer->buf);
491 glTexSubImage2D( my_gl_texture, 0,
492 0,
493 vid.vid_height ,
494 vid.vid_width / 2,
495 vid.vid_height / 2,
496 GL_LUMINANCE,
497 GL_UNSIGNED_BYTE,
498 buffer->buf + vid.uOffset);
499 glTexSubImage2D( my_gl_texture, 0,
500 vid.vid_width / 2,
501 vid.vid_height ,
502 vid.vid_width / 2,
503 vid.vid_height / 2,
504 GL_LUMINANCE,
505 GL_UNSIGNED_BYTE,
506 buffer->buf + vid.vOffset);
507 glXMakeContextCurrent(XJ_disp, None, None, NULL);
508 X11U;
509}
510
511void VideoOutputOpengl::Show(FrameScanType )
512{
513 X11L;
514 glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context);
515 glXSwapBuffers(XJ_disp, XJ_curwin);
516 glXMakeContextCurrent(XJ_disp, None, None, NULL);
517 X11U;
518}
519
520void VideoOutputOpengl::DrawUnusedRects(bool sync)
521{
522 (void) sync;
523}
524
525void VideoOutputOpengl::Zoom(int direction)
526{
527 VideoOutput::Zoom(direction);
528 MoveResize();
529}
530
531void VideoOutputOpengl::InputChanged(int width, int height,
532 float aspect, MythCodecID av_codec_id)
533{
534 VERBOSE(VB_PLAYBACK, LOC + QString("Input changed (%1x%2:%3)")
535 .arg(width).arg(height).arg(aspect));
536 VideoOutput::InputChanged(width, height, aspect, av_codec_id);
537 ResizeForVideo((uint) width, (uint) height);
538 MoveResize();
539 CreateVideoTexture(&glVideo, QSize(width, height), display_video_rect);
540 CreateBuffers();
541 CreatePauseFrame();
542}
543
544void VideoOutputOpengl::MoveResize(void)
545{
546 VideoOutput::MoveResize();
547 glVideo.position = display_video_rect;
548 SetViewPort();
549}
550
551bool VideoOutputOpengl::InitSetupBuffers(void)
552{
553 vbuffers.Init(31, true, 1, 12, 4, 2, false);
554 CreateBuffers();
555 CreatePauseFrame();
556 return true;
557}
558
559void VideoOutputOpengl::CreateBuffers(void)
560{
561 if (!vbuffers.CreateBuffers(glVideo.vid_width, glVideo.vid_height))
562 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create buffers");
563
564}
565
566void VideoOutputOpengl::CreatePauseFrame(void)
567{
568 vbuffers.LockFrame(&av_pause_frame, "CreatePauseFrame");
569 if (av_pause_frame.buf)
570 {
571 delete [] av_pause_frame.buf;
572 av_pause_frame.buf = NULL;
573 }
574 av_pause_frame.height = vbuffers.GetScratchFrame()->height;
575 av_pause_frame.width = vbuffers.GetScratchFrame()->width;
576 av_pause_frame.bpp = vbuffers.GetScratchFrame()->bpp;
577 av_pause_frame.size = vbuffers.GetScratchFrame()->size;
578 av_pause_frame.frameNumber = vbuffers.GetScratchFrame()->frameNumber;
579 av_pause_frame.buf = new unsigned char[av_pause_frame.size];
580 av_pause_frame.qscale_table = NULL;
581 av_pause_frame.qstride = 0;
582
583 vbuffers.UnlockFrame(&av_pause_frame, "CreatePauseFrame");
584}
585
586void VideoOutputOpengl::DeleteBuffers(bool delete_pause_frame)
587{
588 DiscardFrames(true);
589 vbuffers.DeleteBuffers();
590 if (delete_pause_frame)
591 {
592 if (av_pause_frame.buf)
593 {
594 delete [] av_pause_frame.buf;
595 av_pause_frame.buf = NULL;
596 }
597 if (av_pause_frame.qscale_table)
598 {
599 delete [] av_pause_frame.qscale_table;
600 av_pause_frame.qscale_table = NULL;
601 }
602 }
603}
604
605void VideoOutputOpengl::EmbedInWidget(WId wid, int x, int y, int w, int h)
606{
607 (void) wid;
608 (void) x;
609 (void) y;
610 (void) w;
611 (void) h;
612}
613
614void VideoOutputOpengl::StopEmbedding(void)
615{
616}
617
618float VideoOutputOpengl::GetDisplayAspect(void)
619{
620 return display_aspect;
621}
622
623void VideoOutputOpengl::UpdatePauseFrame(void)
624{
625 vbuffers.LockFrame(&av_pause_frame, "UpdatePauseFrame -- pause");
626
627 vbuffers.begin_lock(kVideoBuffer_used);
628 VideoFrame *used_frame = NULL;
629 if (vbuffers.size(kVideoBuffer_used) > 0)
630 {
631 used_frame = vbuffers.head(kVideoBuffer_used);
632 if (!vbuffers.TryLockFrame(used_frame, "UpdatePauseFrame -- used"))
633 used_frame = NULL;
634 }
635 if (used_frame)
636 {
637 CopyFrame(&av_pause_frame, used_frame);
638 vbuffers.UnlockFrame(used_frame, "UpdatePauseFrame -- used");
639 }
640 vbuffers.end_lock();
641 if (!used_frame &&
642 vbuffers.TryLockFrame(vbuffers.GetScratchFrame(),
643 "UpdatePauseFrame -- scratch"))
644 {
645 vbuffers.GetScratchFrame()->frameNumber = framesPlayed - 1;
646 CopyFrame(&av_pause_frame, vbuffers.GetScratchFrame());
647 vbuffers.UnlockFrame(vbuffers.GetScratchFrame(),
648 "UpdatePauseFrame -- scratch");
649 }
650 vbuffers.UnlockFrame(&av_pause_frame, "UpdatePauseFrame - used");
651}
652
653void VideoOutputOpengl::ProcessFrame(VideoFrame *frame, OSD *osd,
654 FilterChain *filterList,
655 NuppelVideoPlayer *pipPlayer)
656{
657 bool pauseframe = false;
658 if (!frame)
659 {
660 frame = vbuffers.GetScratchFrame();
661 CopyFrame(vbuffers.GetScratchFrame(), &av_pause_frame);
662 pauseframe = true;
663 }
664
665 if (filterList)
666 filterList->ProcessFrame(frame);
667
668 if (m_deinterlacing && m_deintFilter != NULL && m_deinterlaceBeforeOSD &&
669 !pauseframe)
670 {
671 m_deintFilter->ProcessFrame(frame);
672 }
673
674 ShowPip(frame, pipPlayer);
675 DisplayOSD(frame, osd);
676
677 if (m_deinterlacing && m_deintFilter != NULL && !m_deinterlaceBeforeOSD &&
678 !pauseframe)
679 {
680 m_deintFilter->ProcessFrame(frame);
681 }
682}
683
684int VideoOutputOpengl::SetPictureAttribute(int attributeType, int newValue)
685{
686 if (kPictureAttribute_Hue == attributeType)
687 return -1;
688 SetPictureAttributeDBValue(attributeType, newValue);
689 return newValue;
690}
691
692bool VideoOutputOpengl::InitGlxContext(WId winid, int width, int height)
693{
694 // set up the opengl rendering environment
695
696 int ndummy;
697 int ret;
698 X11S(ret = glXQueryExtension(XJ_disp, &ndummy, &ndummy));
699 if (!ret)
700 {
701 VERBOSE(VB_PLAYBACK, LOC_ERR + "OpenGL extension not present.");
702 return false;
703 }
704
705 int attribList[] = {GLX_RGBA,
706 GLX_DEPTH_SIZE, 0,
707 GLX_DOUBLEBUFFER, 1,
708 GLX_RED_SIZE, 1,
709 GLX_GREEN_SIZE, 1,
710 GLX_BLUE_SIZE, 1,
711 None};
712
713 XVisualInfo *vis;
714 XSetWindowAttributes swa;
715 Window w;
716
717 X11S(vis = glXChooseVisual(XJ_disp, XJ_screen_num, attribList));
718 if (vis == NULL)
719 {
720 VERBOSE(VB_PLAYBACK, LOC_ERR + "No appropriate visual found");
721 return false;
722 }
723 X11S(swa.colormap = XCreateColormap(XJ_disp,
724 RootWindow(XJ_disp, vis->screen),
725 vis->visual, AllocNone));
726 if (swa.colormap == 0)
727 {
728 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create colormap");
729 return false;
730 }
731
732 X11S(w = XCreateWindow(XJ_disp, winid, 0, 0,
733 width, height, 0, vis->depth,
734 InputOutput, vis->visual, CWColormap, &swa));
735 if (w == 0)
736 {
737 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create window");
738 return false;
739 }
740
741 XJ_curwin = XJ_win = w;
742
743 X11S(my_context = glXCreateContext(XJ_disp, vis, None, GL_TRUE));
744 if (my_context == NULL)
745 {
746 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to create Glx context");
747 return false;
748 }
749 X11S(ret = glXMakeContextCurrent(XJ_disp, XJ_curwin, XJ_curwin, my_context));
750 if (!ret)
751 {
752 VERBOSE(VB_PLAYBACK, LOC_ERR + "Failed to make Glx context current.");
753 return false;
754 }
755
756 X11S(glXMakeContextCurrent(XJ_disp, None, None, NULL));
757 X11S(XFree(vis));
758 X11S(XMapWindow(XJ_disp, XJ_win));
759 VERBOSE(VB_PLAYBACK, LOC + QString("Created window (%1 x %2)").arg(width).arg(height));
760 return true;
761}
762
763int VideoOutputOpengl::GetRefreshRate(void)
764{
765 if (!XJ_started)
766 return -1;
767
768 XF86VidModeModeLine mode_line;
769 int dot_clock;
770
771 int ret = False;
772 X11S(ret = XF86VidModeGetModeLine(XJ_disp, XJ_screen_num,
773 &dot_clock, &mode_line));
774 if (!ret)
775 {
776 VERBOSE(VB_IMPORTANT, LOC_ERR + "GetRefreshRate(): "
777 "X11 ModeLine query failed");
778 return -1;
779 }
780
781 double rate = (double)((double)(dot_clock * 1000.0) /
782 (double)(mode_line.htotal * mode_line.vtotal));
783
784 // Assume 60Hz if we can't otherwise determine it.
785 if (rate == 0)
786 rate = 60;
787
788 if (rate < 20 || rate > 200)
789 {
790 VERBOSE(VB_PLAYBACK, LOC + QString("Unreasonable refresh rate %1Hz "
791 "reported by X").arg(rate));
792 rate = 60;
793 }
794
795 rate = 1000000.0 / rate;
796
797 return (int)rate;
798
799}
800void VideoOutputOpengl::ResizeForGui(void)
801{
802 if (display_res)
803 display_res->SwitchToGUI();
804}
805
806void VideoOutputOpengl::ResizeForVideo(uint width, uint height)
807{
808 if ((width == 1920 || width == 1440) && height == 1088)
809 height = 1080; // ATSC 1920x1080
810
811 if (display_res && display_res->SwitchToVideo(width, height))
812 {
813 // Switching to custom display resolution succeeded
814 // Make a note of the new size
815 display_dim = QSize(display_res->GetPhysicalWidth(),
816 display_res->GetPhysicalHeight());
817 display_aspect = display_res->GetAspectRatio();
818
819 bool fullscreen = !gContext->GetNumSetting("GuiSizeForTV", 0);
820
821 // if width && height are zero users expect fullscreen playback
822 if (!fullscreen)
823 {
824 int gui_width = 0, gui_height = 0;
825 gContext->GetResolutionSetting("Gui", gui_width, gui_height);
826 fullscreen |= (0 == gui_width && 0 == gui_height);
827 }
828
829 if (fullscreen)
830 {
831 QSize sz(display_res->GetWidth(), display_res->GetHeight());
832 display_visible_rect = QRect(QPoint(0,0), sz);
833 // Resize X window to fill new resolution
834 X11S(XMoveResizeWindow(XJ_disp, XJ_win,
835 display_visible_rect.left(),
836 display_visible_rect.top(),
837 display_visible_rect.width(),
838 display_visible_rect.height()));
839 }
840 }
841}
842
843void VideoOutputOpengl::ResizeForVideo(void)
844{
845 ResizeForVideo(glVideo.vid_width, glVideo.vid_height);
846}
847
848void VideoOutputOpengl::InitDisplayMeasurements(uint width, uint height)
849{
850 if (display_res)
851 {
852 // The very first Resize needs to be the maximum possible
853 // desired res, because X will mask off anything outside
854 // the initial dimensions
855 X11S(XMoveResizeWindow(XJ_disp, XJ_win, 0, 0,
856 display_res->GetMaxWidth(),
857 display_res->GetMaxHeight()));
858 ResizeForVideo(width, height);
859 }
860 else
861 {
862 display_dim = QSize(DisplayWidthMM(XJ_disp, XJ_screen_num),
863 DisplayHeightMM(XJ_disp, XJ_screen_num));
864
865 if (db_display_dim.width() > 0 && db_display_dim.height() > 0)
866 display_dim = db_display_dim;
867 }
868
869 // Fetch pixel width and height of the display
870 int xbase, ybase, w, h;
871 gContext->GetScreenBounds(xbase, ybase, w, h);
872 // Determine window dimensions in pixels
873 int window_w = w, window_h = h;
874 if (gContext->GetNumSetting("GuiSizeForTV", 0))
875 gContext->GetResolutionSetting("Gui", window_w, window_h);
876 else
877 gContext->GetScreenBounds(xbase, ybase, window_w, window_h);
878 window_w = (window_w) ? window_w : w;
879 window_h = (window_h) ? window_h : h;
880 float pixel_aspect = ((float)w) / ((float)h);
881
882 VERBOSE(VB_PLAYBACK, LOC + QString(
883 "Pixel dimensions: Screen %1x%2, window %3x%4")
884 .arg(w).arg(h).arg(window_w).arg(window_h));
885
886 // Determine if we are using Xinerama
887 int event_base, error_base;
888 bool usingXinerama = false;
889 X11S(usingXinerama =
890 (XineramaQueryExtension(XJ_disp, &event_base, &error_base) &&
891 XineramaIsActive(XJ_disp)));
892
893 // If the dimensions are invalid, assume square pixels and 17" screen.
894 // Only print warning if this isn't Xinerama, we will fix Xinerama later.
895 if (((display_dim.width() <= 0) || (display_dim.height() <= 0)) &&
896 !usingXinerama)
897 {
898 VERBOSE(VB_GENERAL, LOC + "Physical size of display unknown."
899 "\n\t\t\tAssuming 17\" monitor with square pixels.");
900 display_dim.setHeight(300);
901 display_dim.setWidth((int) round(300 * pixel_aspect));
902 }
903
904 // If we are using Xinerama the display dimensions can not be trusted.
905 // We need to use the Xinerama monitor aspect ratio from the DB to set
906 // the physical screen width. This assumes the height is correct, which
907 // is more or less true in the typical side-by-side monitor setup.
908 if (usingXinerama)
909 {
910 float displayAspect = gContext->GetFloatSettingOnHost(
911 "XineramaMonitorAspectRatio",
912 gContext->GetHostName(), pixel_aspect);
913 int height = display_dim.height();
914 if (height <= 0)
915 display_dim.setHeight(height = 300);
916 display_dim.setWidth((int) round(height * displayAspect));
917 }
918
919 VERBOSE(VB_PLAYBACK, LOC +
920 QString("Estimated display dimensions: %1x%2 mm Aspect: %3")
921 .arg(display_dim.width()).arg(display_dim.height())
922 .arg(((float) display_dim.width()) / display_dim.height()));
923
924 // We must now scale the display measurements to our window size.
925 // If we are running fullscreen this is a no-op.
926 display_dim = QSize((display_dim.width() * window_w) / w,
927 (display_dim.height() * window_h) / h);
928
929 // Now that we know the physical monitor size, we can
930 // calculate the display aspect ratio pretty simply...
931 display_aspect = ((float)display_dim.width()) / display_dim.height();
932
933 // If we are using XRandR, use the aspect ratio from it instead...
934 if (display_res)
935 display_aspect = display_res->GetAspectRatio();
936
937 VERBOSE(VB_PLAYBACK, LOC +
938 QString("Estimated window dimensions: %1x%2 mm Aspect: %3")
939 .arg(display_dim.width()).arg(display_dim.height())
940 .arg(display_aspect));
941}
942