Ticket #2649: videoout_opengl.cpp

File videoout_opengl.cpp, 28.9 KB (added by anonymous, 17 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