00001
00002 #include "mythcontext.h"
00003 #include "tv.h"
00004 #include "openglvideo.h"
00005 #include "myth_imgconvert.h"
00006 #include "mythrender_opengl.h"
00007
00008
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
00155 if (options.contains("preferycbcr"))
00156 preferYCBCR = true;
00157
00158
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
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
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
00191 if ((GL_BGRA != videoTextureType) && (MYTHTV_UYVY != videoTextureType))
00192 colourSpace->SetSupportedAttributes(kPictureAttributeSupported_None);
00193
00194
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
00205 if ((GL_RGBA != videoTextureType) && (defaultUpsize != kGLFilterBicubic) &&
00206 (gl_features & kGLExtRect))
00207 {
00208 textureType = gl_context->GetTextureType(textureRects);
00209 }
00210
00211
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
00283 bool resize_up = ((video_disp_dim.height() < display_video_rect.height()) ||
00284 (video_disp_dim.width() < display_video_rect.width())) && allow;
00285
00286
00287 bool resize_down = (video_disp_dim.height() > display_video_rect.height()) &&
00288 deinterlacing && allow;
00289
00290
00291
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
00328
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;
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
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
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
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
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
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
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
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
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
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
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
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
01024 uint textures[4];
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
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 }