MythTV  0.26-pre
openglvideo.cpp
Go to the documentation of this file.
00001 // MythTV headers
00002 #include "mythcontext.h"
00003 #include "tv.h"
00004 #include "openglvideo.h"
00005 #include "myth_imgconvert.h"
00006 #include "mythrender_opengl.h"
00007 
00008 // AVLib header
00009 extern "C" {
00010 #include "libavcodec/avcodec.h"
00011 }
00012 
00013 #define LOC QString("GLVid: ")
00014 #define COLOUR_UNIFORM "m_colourMatrix"
00015 
00016 class OpenGLFilter
00017 {
00018     public:
00019         vector<GLuint> fragmentPrograms;
00020         uint           numInputs;
00021         vector<GLuint> frameBuffers;
00022         vector<GLuint> frameBufferTextures;
00023         DisplayBuffer  outputBuffer;
00024 };
00025 
00065 OpenGLVideo::OpenGLVideo() :
00066     gl_context(NULL),         video_disp_dim(0,0),
00067     video_dim(0,0),           viewportSize(0,0),
00068     masterViewportSize(0,0),  display_visible_rect(0,0,0,0),
00069     display_video_rect(0,0,0,0), video_rect(0,0,0,0),
00070     frameBufferRect(0,0,0,0), softwareDeinterlacer(QString::null),
00071     hardwareDeinterlacer(QString::null), hardwareDeinterlacing(false),
00072     colourSpace(NULL),        viewportControl(false),
00073     inputTextureSize(0,0),    currentFrameNum(0),
00074     inputUpdated(false),      refsNeeded(0),
00075     textureRects(false),      textureType(GL_TEXTURE_2D),
00076     helperTexture(0),         defaultUpsize(kGLFilterResize),
00077     gl_features(0),           videoTextureType(GL_BGRA),
00078     preferYCBCR(false)
00079 {
00080 }
00081 
00082 OpenGLVideo::~OpenGLVideo()
00083 {
00084     OpenGLLocker ctx_lock(gl_context);
00085     Teardown();
00086 }
00087 
00088 void OpenGLVideo::Teardown(void)
00089 {
00090     if (helperTexture)
00091         gl_context->DeleteTexture(helperTexture);
00092     helperTexture = 0;
00093 
00094     DeleteTextures(&inputTextures);
00095     DeleteTextures(&referenceTextures);
00096 
00097     while (!filters.empty())
00098     {
00099         RemoveFilter(filters.begin()->first);
00100         filters.erase(filters.begin());
00101     }
00102 }
00103 
00126 bool OpenGLVideo::Init(MythRenderOpenGL *glcontext, VideoColourSpace *colourspace,
00127                        QSize videoDim, QSize videoDispDim,
00128                        QRect displayVisibleRect,
00129                        QRect displayVideoRect, QRect videoRect,
00130                        bool viewport_control, QString options,
00131                        bool hw_accel)
00132 {
00133     if (!glcontext)
00134         return false;
00135 
00136     gl_context            = glcontext;
00137     OpenGLLocker ctx_lock(gl_context);
00138 
00139     video_dim             = videoDim;
00140     video_disp_dim        = videoDispDim;
00141     display_visible_rect  = displayVisibleRect;
00142     display_video_rect    = displayVideoRect;
00143     video_rect            = videoRect;
00144     masterViewportSize    = QSize(1920, 1080);
00145     frameBufferRect       = QRect(QPoint(0,0), video_disp_dim);
00146     softwareDeinterlacer  = "";
00147     hardwareDeinterlacing = false;
00148     colourSpace           = colourspace;
00149     viewportControl       = viewport_control;
00150     inputTextureSize      = QSize(0,0);
00151     currentFrameNum       = -1;
00152     inputUpdated          = false;
00153 
00154     // OpenGL-Lite - use implementation specific extensions for updating frames
00155     if (options.contains("preferycbcr"))
00156         preferYCBCR = true;
00157 
00158     // Set OpenGL feature support
00159     gl_features = gl_context->GetFeatures();
00160 
00161     if (viewportControl)
00162         gl_context->SetFence();
00163 
00164     SetViewPort(display_visible_rect.size());
00165 
00166     bool shaders = (gl_features & kGLExtFragProg) || (gl_features & kGLSL);
00167     bool fbos    = gl_features & kGLExtFBufObj;
00168     bool uyvy    = !getenv("OPENGL_NOUYVY");
00169     bool ycbcr   = (gl_features & kGLMesaYCbCr) || (gl_features & kGLAppleYCbCr);
00170 
00171     // warn about the lite profile when it offers no benefit
00172     if (!ycbcr && preferYCBCR)
00173     {
00174         LOG(VB_GENERAL, LOG_WARNING, LOC +
00175             "You have selected the opengl-lite profile but no required OpenGL "
00176             "extensions are available.");
00177     }
00178 
00179     // decide on best video input texture format
00180     videoTextureType = GL_BGRA;
00181     if (hw_accel)
00182         videoTextureType = GL_RGBA;
00183     else if ((shaders && fbos && uyvy) && !(ycbcr && preferYCBCR))
00184         videoTextureType = MYTHTV_UYVY;
00185     else if ((!shaders || preferYCBCR) && (gl_features & kGLMesaYCbCr))
00186         videoTextureType = GL_YCBCR_MESA;
00187     else if ((!shaders || preferYCBCR) && (gl_features & kGLAppleYCbCr))
00188         videoTextureType = GL_YCBCR_422_APPLE;
00189 
00190     // colourspace adjustments require shaders to operate on YUV textures
00191     if ((GL_BGRA != videoTextureType) && (MYTHTV_UYVY != videoTextureType))
00192         colourSpace->SetSupportedAttributes(kPictureAttributeSupported_None);
00193 
00194     // turn on bicubic filtering
00195     if (options.contains("openglbicubic"))
00196     {
00197         if (shaders && fbos)
00198             defaultUpsize = kGLFilterBicubic;
00199         else
00200             LOG(VB_PLAYBACK, LOG_ERR, LOC +
00201                 "No OpenGL feature support for Bicubic filter.");
00202     }
00203 
00204     // decide on best input texture type
00205     if ((GL_RGBA != videoTextureType) && (defaultUpsize != kGLFilterBicubic) &&
00206         (gl_features & kGLExtRect))
00207     {
00208         textureType = gl_context->GetTextureType(textureRects);
00209     }
00210 
00211     // Create initial input texture and associated filter stage
00212     GLuint tex = CreateVideoTexture(video_dim, inputTextureSize);
00213     bool    ok = false;
00214 
00215     if ((GL_BGRA == videoTextureType) || (MYTHTV_UYVY == videoTextureType))
00216         ok = tex && AddFilter(kGLFilterYUV2RGB);
00217     else
00218         ok = tex && AddFilter(kGLFilterResize);
00219 
00220     if (ok)
00221     {
00222         if (GL_RGBA == videoTextureType)
00223             LOG(VB_GENERAL, LOG_INFO, LOC + "Using raw RGBA input textures.");
00224         else if ((GL_YCBCR_MESA == videoTextureType) ||
00225                  (GL_YCBCR_422_APPLE == videoTextureType))
00226             LOG(VB_GENERAL, LOG_INFO, LOC +
00227                 "Using YCbCr->BGRA input textures.");
00228         else if (MYTHTV_UYVY == videoTextureType)
00229             LOG(VB_GENERAL, LOG_INFO, LOC +
00230                 "Using custom UYVY input textures.");
00231         else
00232             LOG(VB_GENERAL, LOG_INFO, LOC +
00233                 "Using plain BGRA input textures.");
00234         inputTextures.push_back(tex);
00235     }
00236     else
00237         Teardown();
00238 
00239     if (filters.empty())
00240     {
00241         LOG(VB_PLAYBACK, LOG_INFO, LOC +
00242                 "Failed to setup colourspace conversion.\n\t\t\t"
00243                 "Falling back to software conversion.\n\t\t\t"
00244                 "Any opengl filters will also be disabled.");
00245 
00246         videoTextureType = GL_BGRA;
00247         GLuint bgra32tex = CreateVideoTexture(video_dim, inputTextureSize);
00248 
00249         if (bgra32tex && AddFilter(kGLFilterResize))
00250         {
00251             inputTextures.push_back(bgra32tex);
00252             colourSpace->SetSupportedAttributes(kPictureAttributeSupported_None);
00253         }
00254         else
00255         {
00256             LOG(VB_GENERAL, LOG_ERR, LOC + "Fatal error");
00257             Teardown();
00258             return false;
00259         }
00260     }
00261 
00262     bool mmx = false;
00263 #ifdef MMX
00264     mmx = true;
00265 #endif
00266 
00267     CheckResize(false);
00268 
00269     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("MMX: %1 PBO: %2")
00270             .arg(mmx).arg((gl_features & kGLExtPBufObj) > 0));
00271 
00272     return true;
00273 }
00274 
00280 void OpenGLVideo::CheckResize(bool deinterlacing, bool allow)
00281 {
00282     // to improve performance on slower cards
00283     bool resize_up = ((video_disp_dim.height() < display_video_rect.height()) ||
00284                      (video_disp_dim.width() < display_video_rect.width())) && allow;
00285 
00286     // to ensure deinterlacing works correctly
00287     bool resize_down = (video_disp_dim.height() > display_video_rect.height()) &&
00288                         deinterlacing && allow;
00289 
00290     // UYVY packed pixels must be sampled exactly and any overscan settings will
00291     // break sampling - so always force an extra stage
00292     resize_down |= videoTextureType == MYTHTV_UYVY;
00293 
00294     if (resize_up && (defaultUpsize == kGLFilterBicubic))
00295     {
00296         RemoveFilter(kGLFilterResize);
00297         filters.erase(kGLFilterResize);
00298         AddFilter(kGLFilterBicubic);
00299         OptimiseFilters();
00300         return;
00301     }
00302 
00303     if ((resize_up && (defaultUpsize == kGLFilterResize)) || resize_down)
00304     {
00305         RemoveFilter(kGLFilterBicubic);
00306         filters.erase(kGLFilterBicubic);
00307         AddFilter(kGLFilterResize);
00308         OptimiseFilters();
00309         return;
00310     }
00311 
00312     RemoveFilter(kGLFilterBicubic);
00313     filters.erase(kGLFilterBicubic);
00314     OptimiseFilters();
00315 }
00316 
00323 bool OpenGLVideo::OptimiseFilters(void)
00324 {
00325     glfilt_map_t::reverse_iterator it;
00326 
00327     // add/remove required frame buffer objects
00328     // and link filters
00329     uint buffers_needed = 1;
00330     bool last_filter    = true;
00331     for (it = filters.rbegin(); it != filters.rend(); ++it)
00332     {
00333         if (!last_filter)
00334         {
00335             it->second->outputBuffer = kFrameBufferObject;
00336             uint buffers_have = it->second->frameBuffers.size();
00337             int buffers_diff = buffers_needed - buffers_have;
00338             if (buffers_diff > 0)
00339             {
00340                 uint tmp_buf, tmp_tex;
00341                 for (int i = 0; i < buffers_diff; i++)
00342                 {
00343                     if (!AddFrameBuffer(tmp_buf, tmp_tex, video_disp_dim))
00344                         return false;
00345                     else
00346                     {
00347                         it->second->frameBuffers.push_back(tmp_buf);
00348                         it->second->frameBufferTextures.push_back(tmp_tex);
00349                     }
00350                 }
00351             }
00352             else if (buffers_diff < 0)
00353             {
00354                 for (int i = 0; i > buffers_diff; i--)
00355                 {
00356                     OpenGLFilter *filt = it->second;
00357 
00358                     gl_context->DeleteFrameBuffer(
00359                         filt->frameBuffers.back());
00360                     gl_context->DeleteTexture(
00361                         filt->frameBufferTextures.back());
00362 
00363                     filt->frameBuffers.pop_back();
00364                     filt->frameBufferTextures.pop_back();
00365                 }
00366             }
00367         }
00368         else
00369         {
00370             it->second->outputBuffer = kDefaultBuffer;
00371             last_filter = false;
00372         }
00373         buffers_needed = it->second->numInputs;
00374     }
00375 
00376     SetFiltering();
00377 
00378     return true;
00379 }
00380 
00386 void OpenGLVideo::SetFiltering(void)
00387 {
00388     if (filters.size() < 2)
00389     {
00390         SetTextureFilters(&inputTextures, GL_LINEAR, GL_CLAMP_TO_EDGE);
00391         SetTextureFilters(&referenceTextures, GL_LINEAR, GL_CLAMP_TO_EDGE);
00392         return;
00393     }
00394 
00395     SetTextureFilters(&inputTextures, GL_NEAREST, GL_CLAMP_TO_EDGE);
00396     SetTextureFilters(&referenceTextures, GL_NEAREST, GL_CLAMP_TO_EDGE);
00397 
00398     glfilt_map_t::reverse_iterator rit;
00399     int last_filter = 0;
00400 
00401     for (rit = filters.rbegin(); rit != filters.rend(); ++rit)
00402     {
00403         if (last_filter == 1)
00404         {
00405             SetTextureFilters(&(rit->second->frameBufferTextures),
00406                               GL_LINEAR, GL_CLAMP_TO_EDGE);
00407         }
00408         else if (last_filter > 1)
00409         {
00410             SetTextureFilters(&(rit->second->frameBufferTextures),
00411                               GL_NEAREST, GL_CLAMP_TO_EDGE);
00412         }
00413         ++last_filter;
00414     }
00415 }
00416 
00422 bool OpenGLVideo::AddFilter(OpenGLFilterType filter)
00423 {
00424     if (filters.count(filter))
00425         return true;
00426 
00427     if (!(gl_features & kGLExtFBufObj) && (filter == kGLFilterResize) &&
00428         !filters.empty())
00429     {
00430         LOG(VB_PLAYBACK, LOG_ERR, LOC +
00431             "GL_EXT_framebuffer_object not available "
00432             "for scaling/resizing filter.");
00433         return false;
00434     }
00435 
00436     if (!((gl_features & kGLExtFragProg) && (gl_features & kGLExtFBufObj)) &&
00437         filter == kGLFilterBicubic)
00438     {
00439         LOG(VB_PLAYBACK, LOG_ERR, LOC +
00440             "Features not available for bicubic filter.");
00441         return false;
00442     }
00443 
00444     if (!(gl_features & kGLExtFragProg) && !(gl_features & kGLSL) &&
00445         (filter == kGLFilterYUV2RGB))
00446     {
00447         LOG(VB_PLAYBACK, LOG_ERR, LOC +
00448             "No shader support for OpenGL deinterlacing.");
00449         return false;
00450     }
00451 
00452     bool success = true;
00453 
00454     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Creating %1 filter.")
00455             .arg(FilterToString(filter)));
00456 
00457     OpenGLFilter *temp = new OpenGLFilter();
00458 
00459     temp->numInputs = 1;
00460     GLuint program = 0;
00461 
00462     if (filter == kGLFilterBicubic)
00463     {
00464         if (helperTexture)
00465             gl_context->DeleteTexture(helperTexture);
00466 
00467         helperTexture = gl_context->CreateHelperTexture();
00468         if (!helperTexture)
00469             success = false;
00470     }
00471 
00472     if (success && (filter != kGLFilterNone) && (filter != kGLFilterResize))
00473     {
00474         program = AddFragmentProgram(filter);
00475         if (!program)
00476             success = false;
00477         else
00478             temp->fragmentPrograms.push_back(program);
00479     }
00480 
00481     if (success)
00482     {
00483         temp->outputBuffer = kDefaultBuffer;
00484         temp->frameBuffers.clear();
00485         temp->frameBufferTextures.clear();
00486         filters[filter] = temp;
00487         success &= OptimiseFilters();
00488     }
00489 
00490     if (!success)
00491     {
00492         RemoveFilter(filter);
00493         filters.erase(filter);
00494         delete temp; // If temp wasn't added to the filter list, we need to delete
00495         return false;
00496     }
00497 
00498     return true;
00499 }
00500 
00501 bool OpenGLVideo::RemoveFilter(OpenGLFilterType filter)
00502 {
00503     if (!filters.count(filter))
00504         return true;
00505 
00506     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Removing %1 filter")
00507             .arg(FilterToString(filter)));
00508 
00509     vector<GLuint> temp;
00510     vector<GLuint>::iterator it;
00511 
00512     temp = filters[filter]->fragmentPrograms;
00513     for (it = temp.begin(); it != temp.end(); ++it)
00514         gl_context->DeleteShaderObject(*it);
00515     filters[filter]->fragmentPrograms.clear();
00516 
00517     temp = filters[filter]->frameBuffers;
00518     for (it = temp.begin(); it != temp.end(); ++it)
00519         gl_context->DeleteFrameBuffer(*it);
00520     filters[filter]->frameBuffers.clear();
00521 
00522     DeleteTextures(&(filters[filter]->frameBufferTextures));
00523 
00524     delete filters[filter];
00525     filters[filter] = NULL;
00526 
00527     return true;
00528 }
00529 
00530 void OpenGLVideo::TearDownDeinterlacer(void)
00531 {
00532     if (!filters.count(kGLFilterYUV2RGB))
00533         return;
00534 
00535     OpenGLFilter *tmp = filters[kGLFilterYUV2RGB];
00536 
00537     if (tmp->fragmentPrograms.size() == 3)
00538     {
00539         gl_context->DeleteShaderObject(tmp->fragmentPrograms[2]);
00540         tmp->fragmentPrograms.pop_back();
00541     }
00542 
00543     if (tmp->fragmentPrograms.size() == 2)
00544     {
00545         gl_context->DeleteShaderObject(tmp->fragmentPrograms[1]);
00546         tmp->fragmentPrograms.pop_back();
00547     }
00548 
00549     DeleteTextures(&referenceTextures);
00550     refsNeeded = 0;
00551 }
00552 
00561 bool OpenGLVideo::AddDeinterlacer(const QString &deinterlacer)
00562 {
00563     if (!(gl_features & kGLExtFragProg) && !(gl_features & kGLSL))
00564     {
00565         LOG(VB_PLAYBACK, LOG_ERR, LOC +
00566             "No shader support for OpenGL deinterlacing.");
00567         return false;
00568     }
00569 
00570     OpenGLLocker ctx_lock(gl_context);
00571 
00572     if (!filters.count(kGLFilterYUV2RGB))
00573     {
00574         LOG(VB_PLAYBACK, LOG_ERR, LOC +
00575             "No YUV2RGB filter stage for OpenGL deinterlacing%1.");
00576         return false;
00577     }
00578 
00579     if (hardwareDeinterlacer == deinterlacer)
00580         return true;
00581 
00582     TearDownDeinterlacer();
00583 
00584     bool success = true;
00585 
00586     uint ref_size = 2;
00587 
00588     if (deinterlacer == "openglbobdeint" ||
00589         deinterlacer == "openglonefield" ||
00590         deinterlacer == "opengllinearblend" ||
00591         deinterlacer == "opengldoubleratelinearblend" ||
00592         deinterlacer == "opengldoubleratefieldorder")
00593     {
00594         ref_size = 0;
00595     }
00596 
00597     refsNeeded = ref_size;
00598     if (ref_size > 0)
00599     {
00600         for (; ref_size > 0; ref_size--)
00601         {
00602             GLuint tex = CreateVideoTexture(video_dim, inputTextureSize);
00603             if (tex)
00604             {
00605                 referenceTextures.push_back(tex);
00606             }
00607             else
00608             {
00609                 success = false;
00610             }
00611         }
00612     }
00613 
00614     uint prog1 = AddFragmentProgram(kGLFilterYUV2RGB,
00615                                     deinterlacer, kScan_Interlaced);
00616     uint prog2 = AddFragmentProgram(kGLFilterYUV2RGB,
00617                                     deinterlacer, kScan_Intr2ndField);
00618 
00619     if (prog1 && prog2)
00620     {
00621         filters[kGLFilterYUV2RGB]->fragmentPrograms.push_back(prog1);
00622         filters[kGLFilterYUV2RGB]->fragmentPrograms.push_back(prog2);
00623     }
00624     else
00625     {
00626         success = false;
00627     }
00628 
00629     if (success)
00630     {
00631         CheckResize(hardwareDeinterlacing);
00632         hardwareDeinterlacer = deinterlacer;
00633         return true;
00634     }
00635 
00636     hardwareDeinterlacer = "";
00637     TearDownDeinterlacer();
00638 
00639     return false;
00640 }
00641 
00648 uint OpenGLVideo::AddFragmentProgram(OpenGLFilterType name,
00649                                      QString deint, FrameScanType field)
00650 {
00651     if (!gl_context)
00652         return 0;
00653 
00654     QString vertex, fragment;
00655     if (gl_features & kGLSL)
00656     {
00657         GetProgramStrings(vertex, fragment, name, deint, field);
00658     }
00659     else if (gl_features & kGLExtFragProg)
00660     {
00661         fragment = GetProgramString(name, deint, field);
00662     }
00663     else
00664     {
00665         LOG(VB_PLAYBACK, LOG_ERR, LOC + "No OpenGL shader/program support");
00666         return 0;
00667     }
00668 
00669     return gl_context->CreateShaderObject(vertex, fragment);
00670 }
00671 
00678 bool OpenGLVideo::AddFrameBuffer(uint &framebuffer,
00679                                  uint &texture, QSize vid_size)
00680 {
00681     if (!(gl_features & kGLExtFBufObj))
00682     {
00683         LOG(VB_PLAYBACK, LOG_ERR, LOC + "Framebuffer binding not supported.");
00684         return false;
00685     }
00686 
00687     texture = gl_context->CreateTexture(vid_size, false, textureType);
00688 
00689     bool ok = gl_context->CreateFrameBuffer(framebuffer, texture);
00690 
00691     if (!ok)
00692         gl_context->DeleteTexture(texture);
00693 
00694     return ok;
00695 }
00696 
00697 void OpenGLVideo::SetViewPort(const QSize &viewPortSize)
00698 {
00699     uint w = max(viewPortSize.width(),  video_disp_dim.width());
00700     uint h = max(viewPortSize.height(), video_disp_dim.height());
00701 
00702     viewportSize = QSize(w, h);
00703 
00704     if (!viewportControl)
00705         return;
00706 
00707     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Viewport: %1x%2") .arg(w).arg(h));
00708     gl_context->SetViewPort(QRect(QPoint(),viewportSize));
00709 }
00710 
00717 uint OpenGLVideo::CreateVideoTexture(QSize size, QSize &tex_size)
00718 {
00719     uint tmp_tex = 0;
00720     bool use_pbo = gl_features & kGLExtPBufObj;
00721     if (GL_YCBCR_MESA == videoTextureType)
00722     {
00723         tmp_tex = gl_context->CreateTexture(size, use_pbo, textureType,
00724                                             GL_UNSIGNED_SHORT_8_8_MESA,
00725                                             GL_YCBCR_MESA, GL_YCBCR_MESA);
00726     }
00727     else if (GL_YCBCR_422_APPLE == videoTextureType)
00728     {
00729         tmp_tex = gl_context->CreateTexture(size, use_pbo, textureType,
00730                                             GL_UNSIGNED_SHORT_8_8_MESA,
00731                                             GL_YCBCR_422_APPLE, GL_RGBA);
00732     }
00733     else if (MYTHTV_UYVY == videoTextureType)
00734     {
00735         QSize fix(size.width() / 2, size.height());
00736         tmp_tex = gl_context->CreateTexture(fix, use_pbo, textureType,
00737                                             GL_UNSIGNED_BYTE, GL_RGBA, GL_RGBA);
00738     }
00739     else
00740         tmp_tex = gl_context->CreateTexture(size, use_pbo, textureType);
00741 
00742     tex_size = gl_context->GetTextureSize(textureType, size);
00743     if (!tmp_tex)
00744         return 0;
00745 
00746     return tmp_tex;
00747 }
00748 
00749 QSize OpenGLVideo::GetTextureSize(const QSize &size)
00750 {
00751     if (textureRects)
00752         return size;
00753 
00754     int w = 64;
00755     int h = 64;
00756 
00757     while (w < size.width())
00758     {
00759         w *= 2;
00760     }
00761 
00762     while (h < size.height())
00763     {
00764         h *= 2;
00765     }
00766 
00767     return QSize(w, h);
00768 }
00769 
00770 uint OpenGLVideo::GetInputTexture(void) const
00771 {
00772     return inputTextures[0];
00773 }
00774 
00775 uint OpenGLVideo::GetTextureType(void) const
00776 {
00777     return textureType;
00778 }
00779 
00780 void OpenGLVideo::SetInputUpdated(void)
00781 {
00782     inputUpdated = true;
00783 }
00784 
00792 void OpenGLVideo::UpdateInputFrame(const VideoFrame *frame, bool soft_bob)
00793 {
00794     OpenGLLocker ctx_lock(gl_context);
00795 
00796     if (frame->width  != video_dim.width()  ||
00797         frame->height != video_dim.height() ||
00798         frame->width  < 1 || frame->height < 1 ||
00799         frame->codec != FMT_YV12)
00800     {
00801         return;
00802     }
00803     if (hardwareDeinterlacing)
00804         RotateTextures();
00805 
00806     // We need to convert frames here to avoid dependencies in MythRenderOpenGL
00807     void* buf = gl_context->GetTextureBuffer(inputTextures[0]);
00808     if (!buf)
00809         return;
00810 
00811     if (!filters.count(kGLFilterYUV2RGB) ||
00812         MYTHTV_UYVY == videoTextureType)
00813     {
00814         // software conversion
00815         AVPicture img_in, img_out;
00816         PixelFormat out_fmt = PIX_FMT_BGRA;
00817         if ((GL_YCBCR_MESA == videoTextureType) ||
00818             (GL_YCBCR_422_APPLE == videoTextureType) ||
00819             (MYTHTV_UYVY == videoTextureType))
00820         {
00821             out_fmt = PIX_FMT_UYVY422;
00822         }
00823 
00824         avpicture_fill(&img_out, (uint8_t *)buf, out_fmt,
00825                        frame->width, frame->height);
00826         avpicture_fill(&img_in, (uint8_t *)frame->buf, PIX_FMT_YUV420P,
00827                        frame->width, frame->height);
00828         myth_sws_img_convert(&img_out, out_fmt, &img_in, PIX_FMT_YUV420P,
00829                        frame->width, frame->height);
00830     }
00831     else if (frame->interlaced_frame && !soft_bob)
00832     {
00833         pack_yv12interlaced(frame->buf, (unsigned char*)buf, frame->offsets,
00834                             frame->pitches, video_dim);
00835     }
00836     else
00837     {
00838         pack_yv12progressive(frame->buf, (unsigned char*)buf, frame->offsets,
00839                              frame->pitches, video_dim);
00840     }
00841 
00842     gl_context->UpdateTexture(inputTextures[0], buf);
00843     inputUpdated = true;
00844 }
00845 
00846 void OpenGLVideo::SetDeinterlacing(bool deinterlacing)
00847 {
00848     hardwareDeinterlacing = deinterlacing;
00849     OpenGLLocker ctx_lock(gl_context);
00850     CheckResize(hardwareDeinterlacing);
00851 }
00852 
00853 void OpenGLVideo::SetSoftwareDeinterlacer(const QString &filter)
00854 {
00855     if (softwareDeinterlacer != filter)
00856         CheckResize(false, filter != "bobdeint");
00857     softwareDeinterlacer = filter;
00858     softwareDeinterlacer.detach();
00859 }
00860 
00877 void OpenGLVideo::PrepareFrame(bool topfieldfirst, FrameScanType scan,
00878                                bool softwareDeinterlacing,
00879                                long long frame, StereoscopicMode stereo,
00880                                bool draw_border)
00881 {
00882     if (inputTextures.empty() || filters.empty())
00883         return;
00884 
00885     OpenGLLocker ctx_lock(gl_context);
00886 
00887     // we need to special case software bobdeint for 1080i
00888     bool softwarebob = softwareDeinterlacer == "bobdeint" &&
00889                        softwareDeinterlacing;
00890 
00891     vector<GLuint> inputs = inputTextures;
00892     QSize inputsize = inputTextureSize;
00893     QSize realsize  = GetTextureSize(video_disp_dim);
00894 
00895     glfilt_map_t::iterator it;
00896     for (it = filters.begin(); it != filters.end(); ++it)
00897     {
00898         OpenGLFilterType type = it->first;
00899         OpenGLFilter *filter = it->second;
00900 
00901         bool actual = softwarebob && (filter->outputBuffer == kDefaultBuffer);
00902 
00903         // texture coordinates
00904         float trueheight = (float)(actual ? video_dim.height() :
00905                                             video_disp_dim.height());
00906         float width = video_disp_dim.width();
00907         if ((type == kGLFilterYUV2RGB) && (videoTextureType == MYTHTV_UYVY))
00908             width /= 2.0f;
00909 
00910         QRectF trect(QPoint(0, 0), QSize(width, trueheight));
00911 
00912         // only apply overscan on last filter
00913         if (filter->outputBuffer == kDefaultBuffer)
00914             trect.setCoords(video_rect.left(),  video_rect.top(),
00915                             video_rect.left() + video_rect.width(),
00916                             video_rect.top()  + video_rect.height());
00917 
00918         if (!textureRects && (inputsize.height() > 0))
00919             trueheight /= inputsize.height();
00920 
00921         // software bobdeint
00922         if (actual)
00923         {
00924             bool top = (scan == kScan_Intr2ndField && topfieldfirst) ||
00925                        (scan == kScan_Interlaced && !topfieldfirst);
00926             bool bot = (scan == kScan_Interlaced && topfieldfirst) ||
00927                        (scan == kScan_Intr2ndField && !topfieldfirst);
00928             bool first = filters.size() < 2;
00929             float bob = (trueheight / (float)video_disp_dim.height()) / 4.0f;
00930             if ((top && !first) || (bot && first))
00931             {
00932                 trect.setBottom(trect.bottom() / 2);
00933                 trect.setTop(trect.top() / 2);
00934                 trect.adjust(0, bob, 0, bob);
00935             }
00936             if ((bot && !first) || (top && first))
00937             {
00938                 trect.setTop((trueheight / 2) + (trect.top() / 2));
00939                 trect.setBottom((trueheight / 2) + (trect.bottom() / 2));
00940                 trect.adjust(0, -bob, 0, -bob);
00941             }
00942         }
00943 
00944         // discard stereoscopic fields
00945         if (filter->outputBuffer == kDefaultBuffer)
00946         {
00947             if (kStereoscopicModeSideBySideDiscard == stereo)
00948                 trect = QRectF(trect.left() / 2.0f,  trect.top(),
00949                                trect.width() / 2.0f, trect.height());
00950             if (kStereoscopicModeTopAndBottomDiscard == stereo)
00951                 trect = QRectF(trect.left(),  trect.top() / 2.0f,
00952                                trect.width(), trect.height() / 2.0f);
00953         }
00954 
00955         // vertex coordinates
00956         QRect display = (filter->outputBuffer == kDefaultBuffer) ?
00957                          display_video_rect : frameBufferRect;
00958         QRect visible = (filter->outputBuffer == kDefaultBuffer) ?
00959                          display_visible_rect : frameBufferRect;
00960         QRectF vrect(display);
00961 
00962         // invert if first filter
00963         if (it == filters.begin())
00964         {
00965             if (filters.size() > 1)
00966             {
00967                 vrect.setTop((visible.height()) - display.top());
00968                 vrect.setBottom(vrect.top() - (display.height()));
00969             }
00970             else
00971             {
00972                 vrect.setBottom(display.top());
00973                 vrect.setTop(display.top() + (display.height()));
00974             }
00975         }
00976 
00977         // hardware bobdeint
00978         if (filter->outputBuffer == kDefaultBuffer &&
00979             hardwareDeinterlacing &&
00980             hardwareDeinterlacer == "openglbobdeint")
00981         {
00982             float bob = ((float)display.height() / (float)video_rect.height())
00983                         / 2.0f;
00984             float field = kScan_Interlaced ? -1.0f : 1.0f;
00985             bob = bob * (topfieldfirst ? field : -field);
00986             vrect.adjust(0, bob, 0, bob);
00987         }
00988 
00989         uint target = 0;
00990         // bind correct frame buffer (default onscreen) and set viewport
00991         switch (filter->outputBuffer)
00992         {
00993             case kDefaultBuffer:
00994                 gl_context->BindFramebuffer(0);
00995                 if (viewportControl)
00996                     gl_context->SetViewPort(QRect(QPoint(), display_visible_rect.size()));
00997                 else
00998                     gl_context->SetViewPort(QRect(QPoint(), masterViewportSize));
00999                 break;
01000             case kFrameBufferObject:
01001                 if (!filter->frameBuffers.empty())
01002                 {
01003                     gl_context->BindFramebuffer(filter->frameBuffers[0]);
01004                     gl_context->SetViewPort(QRect(QPoint(), frameBufferRect.size()));
01005                     target = filter->frameBuffers[0];
01006                 }
01007                 break;
01008 
01009             default:
01010                 continue;
01011         }
01012 
01013         if (draw_border && filter->outputBuffer == kDefaultBuffer)
01014         {
01015             QRectF piprectf = vrect.adjusted(-10, -10, +10, +10);
01016             QRect  piprect(piprectf.left(), piprectf.top(),
01017                            piprectf.width(), piprectf.height());
01018             static const QPen nopen(Qt::NoPen);
01019             static const QBrush redbrush(QBrush(QColor(127, 0, 0, 255)));
01020             gl_context->DrawRect(piprect, redbrush, nopen, 255);
01021         }
01022 
01023         // bind correct textures
01024         uint textures[4]; // NB
01025         uint texture_count = 0;
01026         for (uint i = 0; i < inputs.size(); i++)
01027             textures[texture_count++] = inputs[i];
01028 
01029         if (!referenceTextures.empty() &&
01030             hardwareDeinterlacing &&
01031             type == kGLFilterYUV2RGB)
01032         {
01033             for (uint i = 0; i < referenceTextures.size(); i++)
01034                 textures[texture_count++] = referenceTextures[i];
01035         }
01036 
01037         if (helperTexture && type == kGLFilterBicubic)
01038             textures[texture_count++] = helperTexture;
01039 
01040         // enable fragment program and set any environment variables
01041         GLuint program = 0;
01042         if ((type != kGLFilterNone) && (type != kGLFilterResize))
01043         {
01044             GLuint prog_ref = 0;
01045 
01046             if (type == kGLFilterYUV2RGB)
01047             {
01048                 if (hardwareDeinterlacing &&
01049                     filter->fragmentPrograms.size() == 3 &&
01050                     !refsNeeded)
01051                 {
01052                     if (scan == kScan_Interlaced)
01053                         prog_ref = topfieldfirst ? 1 : 2;
01054                     else if (scan == kScan_Intr2ndField)
01055                         prog_ref = topfieldfirst ? 2 : 1;
01056                 }
01057             }
01058             program = filter->fragmentPrograms[prog_ref];
01059         }
01060 
01061         if (type == kGLFilterYUV2RGB)
01062             gl_context->SetShaderParams(program, colourSpace->GetMatrix(),
01063                                         COLOUR_UNIFORM);
01064 
01065         gl_context->DrawBitmap(textures, texture_count, target, &trect, &vrect,
01066                                program);
01067 
01068         inputs = filter->frameBufferTextures;
01069         inputsize = realsize;
01070     }
01071 
01072     currentFrameNum = frame;
01073     inputUpdated = false;
01074 }
01075 
01076 void OpenGLVideo::RotateTextures(void)
01077 {
01078     if (referenceTextures.size() < 2)
01079         return;
01080 
01081     if (refsNeeded > 0)
01082         refsNeeded--;
01083 
01084     GLuint tmp = referenceTextures[referenceTextures.size() - 1];
01085 
01086     for (uint i = referenceTextures.size() - 1; i > 0;  i--)
01087         referenceTextures[i] = referenceTextures[i - 1];
01088 
01089     referenceTextures[0] = inputTextures[0];
01090     inputTextures[0] = tmp;
01091 }
01092 
01093 void OpenGLVideo::DeleteTextures(vector<GLuint> *textures)
01094 {
01095     if ((*textures).empty())
01096         return;
01097 
01098     for (uint i = 0; i < (*textures).size(); i++)
01099         gl_context->DeleteTexture((*textures)[i]);
01100     (*textures).clear();
01101 }
01102 
01103 void OpenGLVideo::SetTextureFilters(vector<GLuint> *textures,
01104                                     int filt, int wrap)
01105 {
01106     if (textures->empty())
01107         return;
01108 
01109     for (uint i = 0; i < textures->size(); i++)
01110         gl_context->SetTextureFilters((*textures)[i], filt, wrap);
01111 }
01112 
01113 OpenGLFilterType OpenGLVideo::StringToFilter(const QString &filter)
01114 {
01115     OpenGLFilterType ret = kGLFilterNone;
01116 
01117     if (filter.contains("master"))
01118         ret = kGLFilterYUV2RGB;
01119     else if (filter.contains("resize"))
01120         ret = kGLFilterResize;
01121     else if (filter.contains("bicubic"))
01122         ret = kGLFilterBicubic;
01123 
01124     return ret;
01125 }
01126 
01127 QString OpenGLVideo::FilterToString(OpenGLFilterType filt)
01128 {
01129     switch (filt)
01130     {
01131         case kGLFilterNone:
01132             break;
01133         case kGLFilterYUV2RGB:
01134             return "master";
01135         case kGLFilterResize:
01136             return "resize";
01137         case kGLFilterBicubic:
01138             return "bicubic";
01139     }
01140 
01141     return "";
01142 }
01143 
01144 static const QString attrib_fast =
01145 "ATTRIB tex   = fragment.texcoord[0];\n"
01146 "PARAM yuv[3] = { program.local[0..2] };\n";
01147 
01148 static const QString tex_fast =
01149 "TEX res, tex, texture[0], %1;\n";
01150 
01151 static const QString var_fast =
01152 "TEMP tmp, res;\n";
01153 
01154 static const QString var_col =
01155 "TEMP col;\n";
01156 
01157 static const QString select_col =
01158 "MUL col, tex.xxxx, %8;\n"
01159 "FRC col, col;\n"
01160 "SUB col, col, 0.5;\n"
01161 "CMP res, col, res.rabg, res.rgba;\n";
01162 
01163 static const QString end_fast =
01164 "DPH tmp.r, res.arbg, yuv[0];\n"
01165 "DPH tmp.g, res.arbg, yuv[1];\n"
01166 "DPH tmp.b, res.arbg, yuv[2];\n"
01167 "MOV tmp.a, 1.0;\n"
01168 "MOV result.color, tmp;\n";
01169 
01170 static const QString var_deint =
01171 "TEMP other, current, mov, prev;\n";
01172 
01173 static const QString field_calc =
01174 "MUL prev, tex.yyyy, %2;\n"
01175 "FRC prev, prev;\n"
01176 "SUB prev, prev, 0.5;\n";
01177 
01178 static const QString bobdeint[2] = {
01179 field_calc +
01180 "ADD other, tex, {0.0, %3, 0.0, 0.0};\n"
01181 "MIN other, other, {10000.0, %9, 10000.0, 10000.0};\n"
01182 "TEX other, other, texture[0], %1;\n"
01183 "CMP res, prev, res, other;\n",
01184 field_calc +
01185 "SUB other, tex, {0.0, %3, 0.0, 0.0};\n"
01186 "TEX other, other, texture[0], %1;\n"
01187 "CMP res, prev, other, res;\n"
01188 };
01189 
01190 static const QString deint_end_top =
01191 "CMP res,  prev, current, other;\n";
01192 
01193 static const QString deint_end_bot =
01194 "CMP res,  prev, other, current;\n";
01195 
01196 static const QString linearblend[2] = {
01197 "TEX current, tex, texture[0], %1;\n"
01198 "ADD other, tex, {0.0, %3, 0.0, 0.0};\n"
01199 "MIN other, other, {10000.0, %9, 10000.0, 10000.0};\n"
01200 "TEX other, other, texture[0], %1;\n"
01201 "SUB mov, tex, {0.0, %3, 0.0, 0.0};\n"
01202 "TEX mov, mov, texture[0], %1;\n"
01203 "LRP other, 0.5, other, mov;\n"
01204 + field_calc + deint_end_top,
01205 
01206 "TEX current, tex, texture[0], %1;\n"
01207 "SUB other, tex, {0.0, %3, 0.0, 0.0};\n"
01208 "TEX other, other, texture[0], %1;\n"
01209 "ADD mov, tex, {0.0, %3, 0.0, 0.0};\n"
01210 "TEX mov, mov, texture[0], %1;\n"
01211 "LRP other, 0.5, other, mov;\n"
01212 + field_calc + deint_end_bot
01213 };
01214 
01215 static const QString kerneldeint[2] = {
01216 "TEX current, tex, texture[1], %1;\n"
01217 "TEX prev, tex, texture[2], %1;\n"
01218 "MUL other, 0.125, prev;\n"
01219 "MAD other, 0.125, current, other;\n"
01220 "ADD prev, tex, {0.0, %3, 0.0, 0.0};\n"
01221 "MIN prev, prev, {10000.0, %9, 10000.0, 10000.0};\n"
01222 "TEX prev, prev, texture[1], %1;\n"
01223 "MAD other, 0.5, prev, other;\n"
01224 "SUB prev, tex, {0.0, %3, 0.0, 0.0};\n"
01225 "TEX prev, prev, texture[1], %1;\n"
01226 "MAD other, 0.5, prev, other;\n"
01227 "ADD prev, tex, {0.0, %4, 0.0, 0.0};\n"
01228 "TEX tmp, prev, texture[1], %1;\n"
01229 "MAD other, -0.0625, tmp, other;\n"
01230 "TEX tmp, prev, texture[2], %1;\n"
01231 "MAD other, -0.0625, tmp, other;\n"
01232 "SUB prev, tex, {0.0, %4, 0.0, 0.0};\n"
01233 "TEX tmp, prev, texture[1], %1;\n"
01234 "MAD other, -0.0625, tmp, other;\n"
01235 "TEX tmp, prev, texture[2], %1;\n"
01236 "MAD other, -0.0625, tmp, other;\n"
01237 + field_calc + deint_end_top,
01238 
01239 "TEX current, tex, texture[1], %1;\n"
01240 "MUL other, 0.125, res;\n"
01241 "MAD other, 0.125, current, other;\n"
01242 "ADD prev, tex, {0.0, %3, 0.0, 0.0};\n"
01243 "TEX prev, prev, texture[1], %1;\n"
01244 "MAD other, 0.5, prev, other;\n"
01245 "SUB prev, tex, {0.0, %3, 0.0, 0.0};\n"
01246 "TEX prev, prev, texture[1], %1;\n"
01247 "MAD other, 0.5, prev, other;\n"
01248 "ADD prev, tex, {0.0, %4, 0.0, 0.0};\n"
01249 "TEX tmp, prev, texture[1], %1;\n"
01250 "MAD other, -0.0625, tmp, other;\n"
01251 "TEX tmp, prev, texture[0], %1;\n"
01252 "MAD other, -0.0625, tmp, other;\n"
01253 "SUB prev, tex, {0.0, %4, 0.0, 0.0};\n"
01254 "TEX tmp, prev, texture[1], %1;\n"
01255 "MAD other, -0.0625, tmp, other;\n"
01256 "TEX tmp, prev, texture[0], %1;\n"
01257 "MAD other, -0.0625, tmp, other;\n"
01258 + field_calc + deint_end_bot
01259 };
01260 
01261 static const QString bicubic =
01262 "TEMP coord, coord2, cdelta, parmx, parmy, a, b, c, d;\n"
01263 "MAD coord.xy, fragment.texcoord[0], {%6, %7}, {0.5, 0.5};\n"
01264 "TEX parmx, coord.x, texture[1], 1D;\n"
01265 "TEX parmy, coord.y, texture[1], 1D;\n"
01266 "MUL cdelta.xz, parmx.rrgg, {-%5, 0, %5, 0};\n"
01267 "MUL cdelta.yw, parmy.rrgg, {0, -%3, 0, %3};\n"
01268 "ADD coord, fragment.texcoord[0].xyxy, cdelta.xyxw;\n"
01269 "ADD coord2, fragment.texcoord[0].xyxy, cdelta.zyzw;\n"
01270 "TEX a, coord.xyxy, texture[0], 2D;\n"
01271 "TEX b, coord.zwzw, texture[0], 2D;\n"
01272 "TEX c, coord2.xyxy, texture[0], 2D;\n"
01273 "TEX d, coord2.zwzw, texture[0], 2D;\n"
01274 "LRP a, parmy.b, a, b;\n"
01275 "LRP c, parmy.b, c, d;\n"
01276 "LRP result.color, parmx.b, a, c;\n";
01277 
01278 QString OpenGLVideo::GetProgramString(OpenGLFilterType name,
01279                                       QString deint, FrameScanType field)
01280 {
01281     QString ret =
01282         "!!ARBfp1.0\n"
01283         "OPTION ARB_precision_hint_fastest;\n";
01284 
01285     switch (name)
01286     {
01287         case kGLFilterYUV2RGB:
01288         {
01289             bool need_tex = true;
01290             bool packed = MYTHTV_UYVY == videoTextureType;
01291             QString deint_bit = "";
01292             if (deint != "")
01293             {
01294                 uint tmp_field = 0;
01295                 if (field == kScan_Intr2ndField)
01296                     tmp_field = 1;
01297                 if (deint == "openglbobdeint" ||
01298                     deint == "openglonefield" ||
01299                     deint == "opengldoubleratefieldorder")
01300                 {
01301                     deint_bit = bobdeint[tmp_field];
01302                 }
01303                 else if (deint == "opengllinearblend" ||
01304                          deint == "opengldoubleratelinearblend")
01305                 {
01306                     deint_bit = linearblend[tmp_field];
01307                     if (!tmp_field) { need_tex = false; }
01308                 }
01309                 else if (deint == "openglkerneldeint" ||
01310                          deint == "opengldoubleratekerneldeint")
01311                 {
01312                     deint_bit = kerneldeint[tmp_field];
01313                     if (!tmp_field) { need_tex = false; }
01314                 }
01315                 else
01316                 {
01317                     LOG(VB_PLAYBACK, LOG_ERR, LOC +
01318                         "Unrecognised OpenGL deinterlacer");
01319                 }
01320             }
01321 
01322             ret += attrib_fast;
01323             ret += (deint != "") ? var_deint : "";
01324             ret += packed ? var_col : "";
01325             ret += var_fast + (need_tex ? tex_fast : "");
01326             ret += deint_bit;
01327             ret += packed ? select_col : "";
01328             ret += end_fast;
01329         }
01330             break;
01331 
01332         case kGLFilterNone:
01333         case kGLFilterResize:
01334             break;
01335 
01336         case kGLFilterBicubic:
01337 
01338             ret += bicubic;
01339             break;
01340 
01341         default:
01342             LOG(VB_PLAYBACK, LOG_ERR, LOC + "Unknown fragment program.");
01343             break;
01344     }
01345 
01346     CustomiseProgramString(ret);
01347     ret += "END";
01348 
01349     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 fragment program %2")
01350                 .arg(FilterToString(name)).arg(deint));
01351 
01352     return ret;
01353 }
01354 
01355 void OpenGLVideo::CustomiseProgramString(QString &string)
01356 {
01357     string.replace("%1", textureRects ? "RECT" : "2D");
01358 
01359     if (!textureRects)
01360     {
01361         string.replace("GLSL_SAMPLER", "sampler2D");
01362         string.replace("GLSL_TEXTURE", "texture2D");
01363     }
01364 
01365     float lineHeight = 1.0f;
01366     float colWidth   = 1.0f;
01367     float yselect    = 1.0f;
01368     QSize fb_size = GetTextureSize(video_disp_dim);
01369 
01370     if (!textureRects &&
01371        (inputTextureSize.height() > 0))
01372     {
01373         lineHeight /= inputTextureSize.height();
01374         colWidth   /= inputTextureSize.width();
01375         yselect    /= ((float)inputTextureSize.width() / 2.0f);
01376     }
01377 
01378     float maxheight  = (float)(min(inputTextureSize.height(), 1080) - 1) *
01379                        lineHeight;
01380     float fieldSize = 1.0f / (lineHeight * 2.0);
01381 
01382     string.replace("%2", QString::number(fieldSize, 'f', 8));
01383     string.replace("%3", QString::number(lineHeight, 'f', 8));
01384     string.replace("%4", QString::number(lineHeight * 2.0, 'f', 8));
01385     string.replace("%5", QString::number(colWidth, 'f', 8));
01386     string.replace("%6", QString::number((float)fb_size.width(), 'f', 1));
01387     string.replace("%7", QString::number((float)fb_size.height(), 'f', 1));
01388     string.replace("%8", QString::number(1.0f / yselect, 'f', 8));
01389     string.replace("%9", QString::number(maxheight, 'f', 8));
01390 
01391     string.replace("COLOUR_UNIFORM", COLOUR_UNIFORM);
01392 }
01393 
01394 static const QString YUV2RGBVertexShader =
01395 "GLSL_DEFINES"
01396 "attribute vec2 a_position;\n"
01397 "attribute vec2 a_texcoord0;\n"
01398 "varying   vec2 v_texcoord0;\n"
01399 "uniform   mat4 u_projection;\n"
01400 "void main() {\n"
01401 "    gl_Position = u_projection * vec4(a_position, 0.0, 1.0);\n"
01402 "    v_texcoord0 = a_texcoord0;\n"
01403 "}\n";
01404 
01405 static const QString SelectColumn =
01406 "    if (fract(v_texcoord0.x * %8) < 0.5)\n"
01407 "        yuva = yuva.rabg;\n";
01408 
01409 static const QString YUV2RGBFragmentShader =
01410 "GLSL_DEFINES"
01411 "uniform GLSL_SAMPLER s_texture0;\n"
01412 "uniform mat4 COLOUR_UNIFORM;\n"
01413 "varying vec2 v_texcoord0;\n"
01414 "void main(void)\n"
01415 "{\n"
01416 "    vec4 yuva    = GLSL_TEXTURE(s_texture0, v_texcoord0);\n"
01417 "SELECT_COLUMN"
01418 "    gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n"
01419 "}\n";
01420 
01421 static const QString OneFieldShader[2] = {
01422 "GLSL_DEFINES"
01423 "uniform GLSL_SAMPLER s_texture0;\n"
01424 "uniform mat4 COLOUR_UNIFORM;\n"
01425 "varying vec2 v_texcoord0;\n"
01426 "void main(void)\n"
01427 "{\n"
01428 "    float field = v_texcoord0.y + (step(0.5, fract(v_texcoord0.y * %2)) * %3);\n"
01429 "    field       = clamp(field, 0.0, %9);\n"
01430 "    vec4 yuva   = GLSL_TEXTURE(s_texture0, vec2(v_texcoord0.x, field));\n"
01431 "SELECT_COLUMN"
01432 "    gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n"
01433 "}\n",
01434 
01435 "GLSL_DEFINES"
01436 "uniform GLSL_SAMPLER s_texture0;\n"
01437 "uniform mat4 COLOUR_UNIFORM;\n"
01438 "varying vec2 v_texcoord0;\n"
01439 "void main(void)\n"
01440 "{\n"
01441 "    vec2 field   = vec2(0.0, step(0.5, 1.0 - fract(v_texcoord0.y * %2)) * %3);\n"
01442 "    vec4 yuva    = GLSL_TEXTURE(s_texture0, v_texcoord0 + field);\n"
01443 "SELECT_COLUMN"
01444 "    gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n"
01445 "}\n"
01446 };
01447 
01448 static const QString LinearBlendShader[2] = {
01449 "GLSL_DEFINES"
01450 "uniform GLSL_SAMPLER s_texture0;\n"
01451 "uniform mat4 COLOUR_UNIFORM;\n"
01452 "varying vec2 v_texcoord0;\n"
01453 "void main(void)\n"
01454 "{\n"
01455 "    vec2 line  = vec2(0.0, %3);\n"
01456 "    vec2 line2 = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %3, 0.0, %9));\n"
01457 "    vec4 yuva  = GLSL_TEXTURE(s_texture0, v_texcoord0);\n"
01458 "    vec4 above = GLSL_TEXTURE(s_texture0, line2);\n"
01459 "    vec4 below = GLSL_TEXTURE(s_texture0, v_texcoord0 - line);\n"
01460 "    if (fract(v_texcoord0.y * %2) >= 0.5)\n"
01461 "        yuva = mix(above, below, 0.5);\n"
01462 "SELECT_COLUMN"
01463 "    gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n"
01464 "}\n",
01465 
01466 "GLSL_DEFINES"
01467 "uniform GLSL_SAMPLER s_texture0;\n"
01468 "uniform mat4 COLOUR_UNIFORM;\n"
01469 "varying vec2 v_texcoord0;\n"
01470 "void main(void)\n"
01471 "{\n"
01472 "    vec2 line  = vec2(0.0, %3);\n"
01473 "    vec4 yuva  = GLSL_TEXTURE(s_texture0, v_texcoord0);\n"
01474 "    vec4 above = GLSL_TEXTURE(s_texture0, v_texcoord0 + line);\n"
01475 "    vec4 below = GLSL_TEXTURE(s_texture0, v_texcoord0 - line);\n"
01476 "    if (fract(v_texcoord0.y * %2) < 0.5)\n"
01477 "        yuva = mix(above, below, 0.5);\n"
01478 "SELECT_COLUMN"
01479 "    gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n"
01480 "}\n"
01481 };
01482 
01483 static const QString KernelShader[2] = {
01484 "GLSL_DEFINES"
01485 "uniform GLSL_SAMPLER s_texture1;\n"
01486 "uniform GLSL_SAMPLER s_texture2;\n"
01487 "uniform mat4 COLOUR_UNIFORM;\n"
01488 "varying vec2 v_texcoord0;\n"
01489 "void main(void)\n"
01490 "{\n"
01491 "    vec4 yuva    = GLSL_TEXTURE(s_texture1, v_texcoord0);\n"
01492 "    if (fract(v_texcoord0.y * %2) >= 0.5)\n"
01493 "    {\n"
01494 "        vec2 twoup   = v_texcoord0 - vec2(0.0, %4);\n"
01495 "        vec2 twodown = v_texcoord0 + vec2(0.0, %4);\n"
01496 "        vec2 onedown = vec2(v_texcoord0.x, clamp(v_texcoord0.y + %3, 0.0, %9));\n"
01497 "        vec4 line0   = GLSL_TEXTURE(s_texture1, twoup);\n"
01498 "        vec4 line1   = GLSL_TEXTURE(s_texture1, v_texcoord0 - vec2(0.0, %3));\n"
01499 "        vec4 line3   = GLSL_TEXTURE(s_texture1, onedown);\n"
01500 "        vec4 line4   = GLSL_TEXTURE(s_texture1, twodown);\n"
01501 "        vec4 line00  = GLSL_TEXTURE(s_texture2, twoup);\n"
01502 "        vec4 line20  = GLSL_TEXTURE(s_texture2, v_texcoord0);\n"
01503 "        vec4 line40  = GLSL_TEXTURE(s_texture2, twodown);\n"
01504 "        yuva = (yuva   * 0.125);\n"
01505 "        yuva = (line20 * 0.125) + yuva;\n"
01506 "        yuva = (line1  * 0.5) + yuva;\n"
01507 "        yuva = (line3  * 0.5) + yuva;\n"
01508 "        yuva = (line0  * -0.0625) + yuva;\n"
01509 "        yuva = (line4  * -0.0625) + yuva;\n"
01510 "        yuva = (line00 * -0.0625) + yuva;\n"
01511 "        yuva = (line40 * -0.0625) + yuva;\n"
01512 "    }\n"
01513 "SELECT_COLUMN"
01514 "    gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n"
01515 "}\n",
01516 
01517 "GLSL_DEFINES"
01518 "uniform GLSL_SAMPLER s_texture0;\n"
01519 "uniform GLSL_SAMPLER s_texture1;\n"
01520 "uniform mat4 COLOUR_UNIFORM;\n"
01521 "varying vec2 v_texcoord0;\n"
01522 "void main(void)\n"
01523 "{\n"
01524 "    vec4 yuva    = GLSL_TEXTURE(s_texture1, v_texcoord0);\n"
01525 "    if (fract(v_texcoord0.y * %2) < 0.5)\n"
01526 "    {\n"
01527 "        vec2 twoup   = v_texcoord0 - vec2(0.0, %4);\n"
01528 "        vec2 twodown = v_texcoord0 + vec2(0.0, %4);\n"
01529 "        vec4 line0   = GLSL_TEXTURE(s_texture1, twoup);\n"
01530 "        vec4 line1   = GLSL_TEXTURE(s_texture1, v_texcoord0 - vec2(0.0, %3));\n"
01531 "        vec4 line3   = GLSL_TEXTURE(s_texture1, v_texcoord0 + vec2(0.0, %3));\n"
01532 "        vec4 line4   = GLSL_TEXTURE(s_texture1, twodown);\n"
01533 "        vec4 line00  = GLSL_TEXTURE(s_texture0, twoup);\n"
01534 "        vec4 line20  = GLSL_TEXTURE(s_texture0, v_texcoord0);\n"
01535 "        vec4 line40  = GLSL_TEXTURE(s_texture0, twodown);\n"
01536 "        yuva = (yuva   * 0.125);\n"
01537 "        yuva = (line20 * 0.125) + yuva;\n"
01538 "        yuva = (line1  * 0.5) + yuva;\n"
01539 "        yuva = (line3  * 0.5) + yuva;\n"
01540 "        yuva = (line0  * -0.0625) + yuva;\n"
01541 "        yuva = (line4  * -0.0625) + yuva;\n"
01542 "        yuva = (line00 * -0.0625) + yuva;\n"
01543 "        yuva = (line40 * -0.0625) + yuva;\n"
01544 "    }\n"
01545 "SELECT_COLUMN"
01546 "    gl_FragColor = vec4(yuva.arb, 1.0) * COLOUR_UNIFORM;\n"
01547 "}\n"
01548 };
01549 
01550 static const QString BicubicShader =
01551 "GLSL_DEFINES"
01552 "uniform sampler2D s_texture0;\n"
01553 "uniform sampler1D s_texture1;\n"
01554 "varying vec2 v_texcoord0;\n"
01555 "void main(void)\n"
01556 "{\n"
01557 "    vec2 coord = (v_texcoord0 * vec2(%6, %7)) - vec2(0.5, 0.5);\n"
01558 "    vec4 parmx = texture1D(s_texture1, coord.x);\n"
01559 "    vec4 parmy = texture1D(s_texture1, coord.y);\n"
01560 "    vec2 e_x = vec2(%5, 0.0);\n"
01561 "    vec2 e_y = vec2(0.0, %3);\n"
01562 "    vec2 coord10 = v_texcoord0 + parmx.x * e_x;\n"
01563 "    vec2 coord00 = v_texcoord0 - parmx.y * e_x;\n"
01564 "    vec2 coord11 = coord10     + parmy.x * e_y;\n"
01565 "    vec2 coord01 = coord00     + parmy.x * e_y;\n"
01566 "    coord10      = coord10     - parmy.y * e_y;\n"
01567 "    coord00      = coord00     - parmy.y * e_y;\n"
01568 "    vec4 tex00   = texture2D(s_texture0, coord00);\n"
01569 "    vec4 tex10   = texture2D(s_texture0, coord10);\n"
01570 "    vec4 tex01   = texture2D(s_texture0, coord01);\n"
01571 "    vec4 tex11   = texture2D(s_texture0, coord11);\n"
01572 "    tex00        = mix(tex00, tex01, parmy.z);\n"
01573 "    tex10        = mix(tex10, tex11, parmy.z);\n"
01574 "    gl_FragColor = mix(tex00, tex10, parmx.z);\n"
01575 "}\n";
01576 
01577 void OpenGLVideo::GetProgramStrings(QString &vertex, QString &fragment,
01578                                     OpenGLFilterType filter,
01579                                     QString deint, FrameScanType field)
01580 {
01581     uint bottom = field == kScan_Intr2ndField;
01582     vertex = YUV2RGBVertexShader;
01583     switch (filter)
01584     {
01585         case kGLFilterYUV2RGB:
01586         {
01587             if (deint == "openglonefield" || deint == "openglbobdeint")
01588                 fragment = OneFieldShader[bottom];
01589             else if (deint == "opengllinearblend" ||
01590                      deint == "opengldoubleratelinearblend")
01591                 fragment = LinearBlendShader[bottom];
01592             else if (deint == "openglkerneldeint" ||
01593                      deint == "opengldoubleratekerneldeint")
01594                 fragment = KernelShader[bottom];
01595             else
01596                 fragment = YUV2RGBFragmentShader;
01597 
01598             fragment.replace("SELECT_COLUMN", MYTHTV_UYVY == videoTextureType ?
01599                                               SelectColumn : "");
01600             break;
01601         }
01602         case kGLFilterNone:
01603         case kGLFilterResize:
01604             break;
01605         case kGLFilterBicubic:
01606             fragment = BicubicShader;
01607             break;
01608         default:
01609             LOG(VB_PLAYBACK, LOG_ERR, LOC + "Unknown filter");
01610             break;
01611     }
01612     CustomiseProgramString(vertex);
01613     CustomiseProgramString(fragment);
01614 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends