MythTV  master
mythopenglvideo.cpp
Go to the documentation of this file.
1 // C/C++
2 #include <utility>
3 
4 // MythTV
5 #include "mythcontext.h"
6 #include "tv.h"
8 #include "mythavutil.h"
10 #include "mythopenglvideo.h"
11 
12 #define LOC QString("GLVid: ")
13 #define MAX_VIDEO_TEXTURES 10 // YV12 Kernel deinterlacer + 1
14 
26  QSize VideoDim, QSize VideoDispDim,
27  QRect DisplayVisibleRect, QRect DisplayVideoRect, QRect VideoRect,
28  bool ViewportControl, QString Profile)
29  : m_profile(std::move(Profile)),
30  m_render(Render),
31  m_videoDispDim(VideoDispDim),
32  m_videoDim(VideoDim),
33  m_masterViewportSize(DisplayVisibleRect.size()),
34  m_displayVideoRect(DisplayVideoRect),
35  m_videoRect(VideoRect),
36  m_videoColourSpace(ColourSpace),
37  m_viewportControl(ViewportControl),
38  m_inputTextureSize(m_videoDim)
39 {
41  return;
42 
43  OpenGLLocker ctx_lock(m_render);
44  m_render->IncrRef();
45  if (m_render->isOpenGLES())
46  m_gles = m_render->format().majorVersion();
47 
50 
51  // Set OpenGL feature support
54  m_valid = true;
55 
56  m_chromaUpsamplingFilter = gCoreContext->GetBoolSetting("ChromaUpsamplingFilter", true);
57  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Chroma upsampling filter %1")
58  .arg(m_chromaUpsamplingFilter ? "enabled" : "disabled"));
59 }
60 
62 {
65 
66  if (!m_render)
67  return;
68 
72  m_render->DecrRef();
73 }
74 
75 bool MythOpenGLVideo::IsValid(void) const
76 {
77  return m_valid;
78 }
79 
80 void MythOpenGLVideo::UpdateColourSpace(bool PrimariesChanged)
81 {
82  OpenGLLocker locker(m_render);
83 
84  // if input/output type are unset - we haven't created the shaders yet
85  if (PrimariesChanged && (m_outputType != FMT_NONE))
86  {
87  LOG(VB_GENERAL, LOG_INFO, LOC + "Primaries conversion changed - recreating shaders");
89  }
90 
91  float colourgamma = m_videoColourSpace->GetColourGamma();
92  float displaygamma = 1.0F / m_videoColourSpace->GetDisplayGamma();
93  QMatrix4x4 primary = m_videoColourSpace->GetPrimaryMatrix();
94  for (int i = Progressive; i < ShaderCount; ++i)
95  {
97  m_render->SetShaderProgramParams(m_shaders[i], primary, "m_primaryMatrix");
98  if (m_shaders[i])
99  {
100  m_shaders[i]->setUniformValue("m_colourGamma", colourgamma);
101  m_shaders[i]->setUniformValue("m_displayGamma", displaygamma);
102  }
103  }
104 }
105 
107 {
108  if (m_inputTextureSize.isEmpty())
109  return;
110 
111  OpenGLLocker locker(m_render);
112  bool rect = m_textureTarget == QOpenGLTexture::TargetRectangle;
113  GLfloat lineheight = rect ? 1.0F : 1.0F / m_inputTextureSize.height();
114  GLfloat maxheight = rect ? m_videoDispDim.height() : m_videoDispDim.height() / static_cast<GLfloat>(m_inputTextureSize.height());
115  GLfloat fieldsize = rect ? 0.5F : m_inputTextureSize.height() / 2.0F;
116  QVector4D parameters(lineheight, /* lineheight */
117  static_cast<GLfloat>(m_inputTextureSize.width()), /* 'Y' select */
118  maxheight - lineheight, /* maxheight */
119  fieldsize /* fieldsize */);
120 
121  for (int i = Progressive; i < ShaderCount; ++i)
122  {
123  if (m_shaders[i])
124  {
126  m_shaders[i]->setUniformValue("m_frameData", parameters);
127  }
128  }
129 }
130 
132 {
133  m_masterViewportSize = Size;
134 }
135 
136 void MythOpenGLVideo::SetVideoDimensions(const QSize &VideoDim, const QSize &VideoDispDim)
137 {
138  m_videoDim = VideoDim;
139  m_videoDispDim = VideoDispDim;
140 }
141 
142 void MythOpenGLVideo::SetVideoRects(const QRect &DisplayVideoRect, const QRect &VideoRect)
143 {
144  m_displayVideoRect = DisplayVideoRect;
145  m_videoRect = VideoRect;
146 }
147 
148 void MythOpenGLVideo::SetViewportRect(const QRect &DisplayVisibleRect)
149 {
150  SetMasterViewport(DisplayVisibleRect.size());
151 }
152 
153 QString MythOpenGLVideo::GetProfile(void) const
154 {
156  return TypeToProfile(m_inputType);
157  return TypeToProfile(m_outputType);
158 }
159 
160 void MythOpenGLVideo::SetProfile(const QString &Profile)
161 {
162  m_profile = Profile;
163 }
164 
166 {
167  return m_videoDim;
168 }
169 
171 {
172  // If switching off/from basic deinterlacing, then we need to delete and
173  // recreate the input textures and sometimes the shaders as well - so start
174  // from scratch
176  {
177  // Note. Textures will be created with linear filtering - which matches
178  // no resizing - which should be the case for the basic deinterlacer - and
179  // the call to SetupFrameFormat will reset resizing anyway
180  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Removing single field textures");
181  // revert to YUY2 if preferred
182  if ((m_inputType == FMT_YV12) && (m_profile == "opengl"))
185  emit OutputChanged(m_videoDim, m_videoDim, -1.0f);
186  }
189  m_deinterlacer2x = false;
190 }
191 
193  MythDeintType Filter /* = DEINT_SHADER */,
194  bool CreateReferences /* = true */)
195 {
196  if (!Frame)
197  return false;
198 
199  // do we want an opengl shader?
200  // shaders trump CPU deinterlacers if selected and driver deinterlacers will only
201  // be available under restricted circumstances
202  // N.B. there should in theory be no situation in which shader deinterlacing is not
203  // available for software formats, hence there should be no need to fallback to cpu
204 
205  if (!is_interlaced(Scan) || Frame->already_deinterlaced)
206  {
208  return false;
209  }
210 
211  m_deinterlacer2x = true;
212  MythDeintType deinterlacer = GetDoubleRateOption(Frame, Filter);
214  if (other) // another double rate deinterlacer is enabled
215  {
217  return false;
218  }
219 
220  if (!deinterlacer)
221  {
222  m_deinterlacer2x = false;
223  deinterlacer = GetSingleRateOption(Frame, Filter);
225  if (!deinterlacer || other) // no shader deinterlacer needed
226  {
228  return false;
229  }
230  }
231 
232  // if we get this far, we cannot use driver deinterlacers, shader deints
233  // are preferred over cpu, we have a deinterlacer but don't actually care whether
234  // it is single or double rate
235  if (m_deinterlacer == deinterlacer || (m_fallbackDeinterlacer && (m_fallbackDeinterlacer == deinterlacer)))
236  return true;
237 
238  // Lock
239  OpenGLLocker ctx_lock(m_render);
240 
241  // delete old reference textures
244 
245  // For basic deinterlacing of software frames, we now create 2 sets of field
246  // based textures - which is the same approach taken by the CPU based onefield/bob
247  // deinterlacer and the EGL basic deinterlacer. The advantages of this
248  // approach are:-
249  // - no dependent texturing in the samplers (it is just a basic YUV to RGB conversion
250  // in the shader)
251  // - better quality (the onefield shader line doubles but does not have the
252  // implicit interpolation/smoothing of using separate textures directly,
253  // which leads to 'blockiness').
254  // - as we are not sampling other fields, there is no need to use an intermediate
255  // framebuffer to ensure accurate sampling - so we can skip the resize stage.
256  //
257  // YUYV formats are currently not supported as it does not work correctly - force YV12 instead.
258 
259  if (deinterlacer == DEINT_BASIC && format_is_yuv(m_inputType))
260  {
261  if (m_outputType == FMT_YUY2)
262  {
263  LOG(VB_GENERAL, LOG_INFO, LOC + "Forcing OpenGL YV12 for basic deinterlacer");
265  }
267  QSize size(m_videoDim.width(), m_videoDim.height() >> 1);
268  vector<QSize> sizes;
269  sizes.emplace_back(size);
270  // N.B. If we are currently resizing, it will be turned off for this
271  // deinterlacer, so the default linear texture filtering is OK.
273  // nextTextures will hold the other field
275  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 single field textures")
276  .arg(m_inputTextures.size() * 2));
277  // Con VideoOutWindow into display the field only
278  emit OutputChanged(m_videoDim, size, -1.0F);
279  }
280 
281  // sanity check max texture units. Should only be an issue on old hardware (e.g. Pi)
282  int max = m_render->GetMaxTextureUnits();
283  uint refstocreate = ((deinterlacer == DEINT_HIGH) && CreateReferences) ? 2 : 0;
284  int totaltextures = static_cast<int>(planes(m_outputType)) * static_cast<int>(refstocreate + 1);
285  if (totaltextures > max)
286  {
287  m_fallbackDeinterlacer = deinterlacer;
288  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Insufficent texture units for deinterlacer '%1' (%2 < %3)")
289  .arg(DeinterlacerName(deinterlacer | DEINT_SHADER, m_deinterlacer2x)).arg(max).arg(totaltextures));
290  deinterlacer = DEINT_BASIC;
291  LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Falling back to '%1'")
292  .arg(DeinterlacerName(deinterlacer | DEINT_SHADER, m_deinterlacer2x)));
293  }
294 
295  // create new deinterlacers - the old ones will be deleted
296  if (!(CreateVideoShader(InterlacedBot, deinterlacer) && CreateVideoShader(InterlacedTop, deinterlacer)))
297  return false;
298 
299  // create the correct number of reference textures
300  if (refstocreate)
301  {
302  vector<QSize> sizes;
303  sizes.emplace_back(QSize(m_videoDim));
306  // ensure we use GL_NEAREST if resizing is already active
307  if (m_resizing)
308  {
311  }
312  }
313 
314  // ensure they work correctly
315  UpdateColourSpace(false);
317  m_deinterlacer = deinterlacer;
318 
319  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created deinterlacer '%1' (%2->%3)")
322  return true;
323 }
324 
333 {
334  if (!m_render || !(m_features & QOpenGLFunctions::Shaders))
335  return false;
336 
337  // delete the old
338  if (m_shaders[Type])
340  m_shaders[Type] = nullptr;
341 
342  QStringList defines;
343  QString vertex = DefaultVertexShader;
344  QString fragment;
345  int cost = 1;
346 
348  defines << "EXTOES";
349 
350  if ((Default == Type) || (!format_is_yuv(m_outputType)))
351  {
352  QString glsldefines;
353  foreach (QString define, defines)
354  glsldefines += QString("#define MYTHTV_%1\n").arg(define);
355  fragment = glsldefines + YUVFragmentExtensions + RGBFragmentShader;
356 
357 #ifdef USING_MEDIACODEC
359  vertex = MediaCodecVertexShader;
360 #endif
361  }
362  // no interlaced shaders yet (i.e. interlaced chroma upsampling - not deinterlacers)
363  else
364  {
365  fragment = YUVFragmentShader;
366  QString extensions = YUVFragmentExtensions;
367  QString glsldefines;
368 
369  // Any software frames that are not 8bit need to use unsigned integer
370  // samplers with GLES3.x - which need more modern shaders
371  if ((m_gles > 2) && (ColorDepth(m_inputType) > 8))
372  {
373  static const QString glsl300("#version 300 es\n");
374  fragment = GLSL300YUVFragmentShader;
375  extensions = GLSL300YUVFragmentExtensions;
376  vertex = glsl300 + GLSL300VertexShader;
377  glsldefines.append(glsl300);
378  }
379 
380  bool kernel = false;
381  bool topfield = InterlacedTop == Type;
382  bool progressive = (Progressive == Type) || (Deint == DEINT_NONE);
384  {
385  defines << "YV12";
386  cost = 3;
387  }
388  else if (format_is_nv12(m_outputType))
389  {
390  defines << "NV12";
391  cost = 2;
392  }
393  else if (FMT_YUY2 == m_outputType)
394  {
395  defines << "YUY2";
396  }
397 
398 #ifdef USING_VTB
399  // N.B. Rectangular texture support is only currently used for VideoToolBox
400  // video frames which are NV12. Do not use rectangular textures for the 'default'
401  // shaders as it breaks video resizing and would require changes to our
402  // FramebufferObject code.
403  if ((m_textureTarget == QOpenGLTexture::TargetRectangle) && (Default != Type))
404  defines << "RECTS";
405 #endif
406  if (!progressive)
407  {
408  bool basic = Deint == DEINT_BASIC && format_is_yuv(m_inputType);
409  // Chroma upsampling filter
411  m_chromaUpsamplingFilter && !basic)
412  {
413  defines << "CUE";
414  }
415 
416  // field
417  if (topfield && !basic)
418  defines << "TOPFIELD";
419 
420  switch (Deint)
421  {
422  case DEINT_BASIC:
423  if (!basic)
424  {
425  cost *= 2;
426  defines << "ONEFIELD";
427  }
428  break;
429  case DEINT_MEDIUM: cost *= 5; defines << "LINEARBLEND"; break;
430  case DEINT_HIGH: cost *= 15; defines << "KERNEL"; kernel = true; break;
431  default: break;
432  }
433  }
434 
435  // Start building the new fragment shader
436  // We do this in code otherwise the shader string becomes monolithic
437 
438  // 'expand' calls to sampleYUV for multiple planes
439  // do this before we add the samplers
440  int count = static_cast<int>(planes(m_outputType));
441  for (int i = (kernel ? 2 : 0); (i >= 0) && count; i--)
442  {
443  QString find = QString("s_texture%1").arg(i);
444  QStringList replacelist;
445  for (int j = (i * count); j < ((i + 1) * count); ++j)
446  replacelist << QString("s_texture%1").arg(j);
447  fragment.replace(find, replacelist.join(", "));
448  }
449 
450  // 'expand' calls to the kernel function
451  if (kernel && count)
452  {
453  for (int i = 1 ; i >= 0; i--)
454  {
455  QString find1 = QString("sampler2D kernelTex%1").arg(i);
456  QString find2 = QString("kernelTex%1").arg(i);
457  QStringList replacelist1;
458  QStringList replacelist2;
459  for (int j = 0; j < count; ++j)
460  {
461  replacelist1 << QString("sampler2D kernelTexture%1%2").arg(i).arg(j);
462  replacelist2 << QString("kernelTexture%1%2").arg(i).arg(j);
463  }
464  fragment.replace(find1, replacelist1.join(", "));
465  fragment.replace(find2, replacelist2.join(", "));
466  }
467  }
468 
469  // Retrieve colour mappping defines
471 
472  // Add defines
473  foreach (QString define, defines)
474  glsldefines += QString("#define MYTHTV_%1\n").arg(define);
475 
476  // Add the required samplers
477  int start = 0;
478  int end = count;
479  if (kernel)
480  {
481  end *= 3;
482  if (topfield)
483  start += count;
484  else
485  end -= count;
486  }
487  QString glslsamplers;
488  for (int i = start; i < end; ++i)
489  glslsamplers += QString("uniform sampler2D s_texture%1;\n").arg(i);
490 
491  // construct the final shader string
492  fragment = glsldefines + extensions + glslsamplers + fragment;
493  }
494 
495  m_shaderCost[Type] = cost;
496  QOpenGLShaderProgram *program = m_render->CreateShaderProgram(vertex, fragment);
497  if (!program)
498  return false;
499 
500  m_shaders[Type] = program;
501  return true;
502 }
503 
505  QSize Size, GLenum TextureTarget)
506 {
507  QString texnew = (TextureTarget == QOpenGLTexture::TargetRectangle) ? "Rect" :
508  (TextureTarget == GL_TEXTURE_EXTERNAL_OES) ? "OES" : "2D";
509  QString texold = (m_textureTarget == QOpenGLTexture::TargetRectangle) ? "Rect" :
510  (m_textureTarget == GL_TEXTURE_EXTERNAL_OES) ? "OES" : "2D";
511  LOG(VB_GENERAL, LOG_WARNING, LOC +
512  QString("New frame format: %1:%2 %3x%4 (Tex: %5) -> %6:%7 %8x%9 (Tex: %10)")
514  .arg(m_videoDim.width()).arg(m_videoDim.height()).arg(texold)
515  .arg(format_description(InputType)).arg(format_description(OutputType))
516  .arg(Size.width()).arg(Size.height()).arg(texnew));
517 
519 
520  m_inputType = InputType;
521  m_outputType = OutputType;
522  m_textureTarget = TextureTarget;
523  m_videoDim = Size;
524 
525  // This is only currently needed for RGBA32 frames from composed DRM_PRIME
526  // textures that may be half height for simple bob deinterlacing
528  emit OutputChanged(m_videoDim, m_videoDim, -1.0F);
529 
530  if (!format_is_hw(InputType))
531  {
532  vector<QSize> sizes;
533  sizes.push_back(Size);
535  if (m_inputTextures.empty())
536  {
537  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to create input textures");
538  return false;
539  }
540 
541  m_inputTextureSize = m_inputTextures[0]->m_totalSize;
542  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Created %1 input textures for '%2'")
543  .arg(m_inputTextures.size()).arg(GetProfile()));
544  }
545  else
546  {
547  m_inputTextureSize = Size;
548  }
549 
550  // Create shaders
552  return false;
553 
554  UpdateColourSpace(false);
556  return true;
557 }
558 
560 {
561  for (auto & shader : m_shaders)
562  if (shader)
563  m_render->DeleteShaderProgram(shader);
564  memset(m_shaders, 0, sizeof(m_shaders));
565  memset(m_shaderCost, 1, sizeof(m_shaderCost));
571  m_textureTarget = QOpenGLTexture::Target2D;
572  m_inputTextureSize = QSize();
577  m_frameBuffer = nullptr;
578  m_frameBufferTexture = nullptr;
579  // textures are created with Linear filtering - which matches no resize
580  m_resizing = None;
581 }
582 
585 {
586  if (Frame->codec == FMT_NONE)
587  return;
588 
589  // Hardware frames are retrieved/updated in PrepareFrame but we need to
590  // reset software frames now if necessary
591  if (format_is_hw(Frame->codec))
592  {
594  {
595  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Resetting input format");
597  }
598  return;
599  }
600 
601  // Sanitise frame
602  if ((Frame->width < 1) || (Frame->height < 1) || !Frame->buf)
603  {
604  LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid software frame");
605  return;
606  }
607 
608  // Can we render this frame format
609  if (!format_is_yuv(Frame->codec))
610  {
611  LOG(VB_GENERAL, LOG_ERR, LOC + "Frame format is not supported");
612  return;
613  }
614 
615  // lock
616  OpenGLLocker ctx_lock(m_render);
617 
618  // check for input changes
619  if ((Frame->width != m_videoDim.width()) ||
620  (Frame->height != m_videoDim.height()) ||
621  (Frame->codec != m_inputType))
622  {
623  VideoFrameType frametype = Frame->codec;
624  if ((frametype == FMT_YV12) && (m_profile == "opengl"))
625  frametype = FMT_YUY2;
626  QSize size(Frame->width, Frame->height);
627  if (!SetupFrameFormat(Frame->codec, frametype, size, QOpenGLTexture::Target2D))
628  return;
629  }
630 
631  // Setup deinterlacing if required
632  AddDeinterlacer(Frame, Scan);
633 
634  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
635  m_render->logDebugMarker(LOC + "UPDATE_FRAME_START");
636 
638 
639  // Rotate textures if necessary
640  bool current = true;
642  {
643  if (!m_nextTextures.empty() && !m_prevTextures.empty())
644  {
645  if (abs(Frame->frameCounter - m_discontinuityCounter) > 1)
646  ResetTextures();
647  vector<MythVideoTexture*> temp = m_prevTextures;
650  m_nextTextures = temp;
651  current = false;
652  }
653  }
654 
655  m_discontinuityCounter = Frame->frameCounter;
656 
658  {
659  // first field. Fake the pitches
660  int pitches[3];
661  memcpy(pitches, Frame->pitches, sizeof(int) * 3);
662  Frame->pitches[0] = Frame->pitches[0] << 1;
663  Frame->pitches[1] = Frame->pitches[1] << 1;
664  Frame->pitches[2] = Frame->pitches[2] << 1;
666  // second field. Fake the offsets as well.
667  int offsets[3];
668  memcpy(offsets, Frame->offsets, sizeof(int) * 3);
669  Frame->offsets[0] = Frame->offsets[0] + pitches[0];
670  Frame->offsets[1] = Frame->offsets[1] + pitches[1];
671  Frame->offsets[2] = Frame->offsets[2] + pitches[2];
673  memcpy(Frame->pitches, pitches, sizeof(int) * 3);
674  memcpy(Frame->offsets, offsets, sizeof(int) * 3);
675  }
676  else
677  {
679  }
680 
681  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
682  m_render->logDebugMarker(LOC + "UPDATE_FRAME_END");
683 }
684 
686  StereoscopicMode Stereo, bool DrawBorder)
687 {
688  if (!m_render)
689  return;
690 
691  OpenGLLocker locker(m_render);
692 
693  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
694  m_render->logDebugMarker(LOC + "PREP_FRAME_START");
695 
696  // Set required input textures for the last stage
697  // ProcessFrame is always called first, which will create/destroy software
698  // textures as needed
699  bool hwframes = false;
700  bool useframebufferimage = false;
701 
702  // for tiled renderers (e.g. Pi), enable scissoring to try and improve performance
703  // when not full screen and avoid the resize stage unless absolutely necessary
704  bool tiled = m_extraFeatures & kGLTiled;
705 
706  // We lose the pause frame when seeking and using VDPAU/VAAPI/NVDEC direct rendering.
707  // As a workaround, we always use the resize stage so the last displayed frame
708  // should be retained in the Framebuffer used for resizing. If there is
709  // nothing to display, then fallback to this framebuffer.
710  // N.B. this is now strictly necessary with v4l2 and DRM PRIME direct rendering
711  // but ignore now for performance reasons
712  VideoResizing resize = Frame ? (format_is_hwframes(Frame->codec) ? Framebuffer : None) :
714 
715  vector<MythVideoTexture*> inputtextures = m_inputTextures;
716  if (inputtextures.empty())
717  {
718  // Pull in any hardware frames
720  if (!inputtextures.empty())
721  {
722  hwframes = true;
723  QSize newsize = inputtextures[0]->m_size;
724  VideoFrameType newsourcetype = inputtextures[0]->m_frameType;
725  VideoFrameType newtargettype = inputtextures[0]->m_frameFormat;
726  GLenum newtargettexture = inputtextures[0]->m_target;
727  if ((m_outputType != newtargettype) || (m_textureTarget != newtargettexture) ||
728  (m_inputType != newsourcetype) || (newsize != m_inputTextureSize))
729  {
730  SetupFrameFormat(newsourcetype, newtargettype, newsize, newtargettexture);
731  }
732 
733 #ifdef USING_MEDIACODEC
734  // Set the texture transform for mediacodec
736  {
737  if (inputtextures[0]->m_transform && m_shaders[Default])
738  {
740  m_shaders[Default]->setUniformValue("u_transform", *inputtextures[0]->m_transform);
741  }
742  }
743 #endif
744  // Enable deinterlacing for NVDEC, VTB and VAAPI DRM if VPP is not available
745  if (inputtextures[0]->m_allowGLSLDeint)
746  AddDeinterlacer(Frame, Scan, DEINT_SHADER | DEINT_CPU, false); // pickup shader or cpu prefs
747  }
748  else
749  {
750  if ((resize == Framebuffer) && m_frameBuffer && m_frameBufferTexture)
751  {
752  LOG(VB_PLAYBACK, LOG_INFO, "Using existing framebuffer");
753  useframebufferimage = true;
754  }
755  else
756  {
757  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Nothing to display");
758  // if this is live tv startup and the window rect has changed we
759  // must set the viewport
760  m_render->SetViewPort(QRect(QPoint(), m_masterViewportSize));
761  return;
762  }
763  }
764  }
765 
766  // Determine which shader to use. This helps optimise the resize check.
767  bool deinterlacing = false;
768  bool basicdeinterlacing = false;
770  if (m_deinterlacer != DEINT_NONE)
771  {
772  if (Scan == kScan_Interlaced)
773  {
774  program = TopFieldFirst ? InterlacedTop : InterlacedBot;
775  deinterlacing = true;
776  }
777  else if (Scan == kScan_Intr2ndField)
778  {
779  program = TopFieldFirst ? InterlacedBot : InterlacedTop;
780  deinterlacing = true;
781  }
782 
783  // select the correct field for the basic deinterlacer
784  if (deinterlacing && m_deinterlacer == DEINT_BASIC && format_is_yuv(m_inputType))
785  {
786  basicdeinterlacing = true;
787  if (program == InterlacedBot)
788  inputtextures = m_nextTextures;
789  }
790  }
791 
792  // Set deinterlacer type for debug OSD
793  if (deinterlacing && Frame)
794  {
795  Frame->deinterlace_inuse = m_deinterlacer | DEINT_SHADER;
796  Frame->deinterlace_inuse2x = m_deinterlacer2x;
797  }
798 
799  // Decide whether to use render to texture - for performance or quality
800  if (format_is_yuv(m_outputType) && !resize)
801  {
802  // ensure deinterlacing works correctly when down scaling in height
803  // N.B. not needed for the basic deinterlacer
804  if (deinterlacing && !basicdeinterlacing && (m_videoDispDim.height() > m_displayVideoRect.height()))
805  resize |= Deinterlacer;
806  // UYVY packed pixels must be sampled exactly
807  if (FMT_YUY2 == m_outputType)
808  resize |= Sampling;
809  // unsigned integer texture formats need GL_NEAREST sampling
810  if ((m_gles > 2) && (ColorDepth(m_inputType) > 8))
811  resize |= Sampling;
812 
813  // don't enable resizing if the cost of a framebuffer switch may be
814  // prohibitive (e.g. Raspberry Pi/tiled renderers) or for basic deinterlacing,
815  // where we are trying to simplifiy/optimise rendering (and the framebuffer
816  // sizing gets confused by the change to m_videoDispDim)
817  if (!resize && !tiled && !basicdeinterlacing)
818  {
819  // improve performance. This is an educated guess on the relative cost
820  // of render to texture versus straight rendering.
821  int totexture = m_videoDispDim.width() * m_videoDispDim.height() * m_shaderCost[program];
822  int blitcost = m_displayVideoRect.width() * m_displayVideoRect.height() * m_shaderCost[Default];
823  int noresizecost = m_displayVideoRect.width() * m_displayVideoRect.height() * m_shaderCost[program];
824  if ((totexture + blitcost) < noresizecost)
825  resize |= Performance;
826  }
827  }
828 
829  // set software frame filtering if resizing has changed
830  if (!resize && m_resizing)
831  {
832  // remove framebuffer
834  {
836  m_frameBufferTexture = nullptr;
837  }
838  if (m_frameBuffer)
839  {
841  m_frameBuffer = nullptr;
842  }
843  // set filtering
847  m_resizing = None;
848  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Disabled resizing");
849  }
850  else if (!m_resizing && resize)
851  {
852  // framebuffer will be created as needed below
856  m_resizing = resize;
857  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Resizing from %1x%2 to %3x%4 for %5")
858  .arg(m_videoDispDim.width()).arg(m_videoDispDim.height())
859  .arg(m_displayVideoRect.width()).arg(m_displayVideoRect.height())
860  .arg(VideoResizeToString(resize)));
861  }
862 
863  // check hardware frames have the correct filtering
864  if (hwframes)
865  {
866  QOpenGLTexture::Filter filter = resize ? QOpenGLTexture::Nearest : QOpenGLTexture::Linear;
867  if (inputtextures[0]->m_filter != filter)
868  MythVideoTexture::SetTextureFilters(m_render, inputtextures, filter);
869  }
870 
871  if (resize)
872  {
873  // only render to the framebuffer if there is something to update
874  if (!useframebufferimage)
875  {
876  // render to texture stage
877  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
878  m_render->logDebugMarker(LOC + "RENDER_TO_TEXTURE");
879 
880  // we need a framebuffer
881  if (!m_frameBuffer)
882  {
884  if (!m_frameBuffer)
885  return;
886  }
887 
888  // and its associated texture
890  {
891  m_frameBufferTexture = reinterpret_cast<MythVideoTexture*>(m_render->CreateFramebufferTexture(m_frameBuffer));
893  return;
894  m_render->SetTextureFilters(m_frameBufferTexture, QOpenGLTexture::Linear);
895  }
896 
897  // coordinates
898  QRect vrect(QPoint(0, 0), m_videoDispDim);
899  QRect trect = vrect;
900  if (FMT_YUY2 == m_outputType)
901  trect.setWidth(m_videoDispDim.width() >> 1);
902 
903  // framebuffer
905  m_render->SetViewPort(vrect);
906 
907  // bind correct textures
909  uint numtextures = 0;
910  BindTextures(deinterlacing, inputtextures, &textures[0], numtextures);
911 
912  // render
913  m_render->DrawBitmap(textures, numtextures, m_frameBuffer,
914  trect, vrect, m_shaders[program], 0);
915  }
916 
917  // reset for next stage
918  inputtextures.clear();
919  inputtextures.push_back(m_frameBufferTexture);
920  program = Default;
921  deinterlacing = false;
922  }
923 
924  // render to default framebuffer/screen
925  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
926  m_render->logDebugMarker(LOC + "RENDER_TO_SCREEN");
927 
928  // texture coordinates
929  QRect trect(m_videoRect);
930 
931  // discard stereoscopic fields
933  trect = QRect(trect.left() >> 1, trect.top(), trect.width() >> 1, trect.height());
935  trect = QRect(trect.left(), trect.top() >> 1, trect.width(), trect.height() >> 1);
936 
937  // bind default framebuffer
938  m_render->BindFramebuffer(nullptr);
939  m_render->SetViewPort(QRect(QPoint(), m_masterViewportSize));
940 
941  // PiP border
942  if (DrawBorder)
943  {
944  QRect piprect = m_displayVideoRect.adjusted(-10, -10, +10, +10);
945  static const QPen kNopen(Qt::NoPen);
946  static const QBrush kRedBrush(QBrush(QColor(127, 0, 0, 255)));
947  m_render->DrawRect(nullptr, piprect, kRedBrush, kNopen, 255);
948  }
949 
950  // bind correct textures
952  uint numtextures = 0;
953  BindTextures(deinterlacing, inputtextures, &textures[0], numtextures);
954 
955  // rotation
956  if (Frame)
957  m_lastRotation = Frame->rotation;
958 
959  // apply scissoring
960  if (tiled)
961  {
962  // N.B. It's not obvious whether this helps
963  m_render->glEnable(GL_SCISSOR_TEST);
964  m_render->glScissor(m_displayVideoRect.left() - 1, m_displayVideoRect.top() - 1,
965  m_displayVideoRect.width() + 2, m_displayVideoRect.height() + 2);
966  }
967 
968  // draw
969  m_render->DrawBitmap(textures, numtextures, nullptr, trect,
971 
972  // disable scissoring
973  if (tiled)
974  m_render->glDisable(GL_SCISSOR_TEST);
975 
976  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
977  m_render->logDebugMarker(LOC + "PREP_FRAME_END");
978 }
979 
982 {
983  for (auto & texture : m_inputTextures)
984  texture->m_valid = false;
985  for (auto & texture : m_prevTextures)
986  texture->m_valid = false;
987  for (auto & texture : m_nextTextures)
988  texture->m_valid = false;
989 }
990 
991 void MythOpenGLVideo::BindTextures(bool Deinterlacing, vector<MythVideoTexture*> &Current,
992  MythGLTexture **Textures, uint &TextureCount)
993 {
994  bool usecurrent = true;
995  if (Deinterlacing)
996  {
998  {
999  usecurrent = true;
1000  }
1001  else if ((m_nextTextures.size() == Current.size()) && (m_prevTextures.size() == Current.size()))
1002  {
1003  // if we are using reference frames, we want the current frame in the middle
1004  // but next will be the first valid, followed by current...
1005  usecurrent = false;
1006  size_t count = Current.size();
1007  vector<MythVideoTexture*> &current = Current[0]->m_valid ? Current : m_nextTextures;
1008  vector<MythVideoTexture*> &prev = m_prevTextures[0]->m_valid ? m_prevTextures : current;
1009 
1010  for (uint i = 0; i < count; ++i)
1011  Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(prev[i]);
1012  for (uint i = 0; i < count; ++i)
1013  Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(current[i]);
1014  for (uint i = 0; i < count; ++i)
1015  Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(m_nextTextures[i]);
1016  }
1017  }
1018 
1019  if (usecurrent)
1020  for (auto & texture : Current)
1021  Textures[TextureCount++] = reinterpret_cast<MythGLTexture*>(texture);
1022 }
1023 
1025 {
1026  if (format_is_hw(Type))
1027  return "opengl-hw";
1028 
1029  switch (Type)
1030  {
1031  case FMT_YUY2: return "opengl"; // compatibility with old profiles
1032  case FMT_YV12: return "opengl-yv12";
1033  case FMT_NV12: return "opengl-nv12";
1034  default: break;
1035  }
1036  return "opengl";
1037 }
1038 
1039 QString MythOpenGLVideo::VideoResizeToString(VideoResizing Resize)
1040 {
1041  QStringList reasons;
1042  if (Resize & Deinterlacer) reasons << "Deinterlacer";
1043  if (Resize & Sampling) reasons << "Sampling";
1044  if (Resize & Performance) reasons << "Performance";
1045  if (Resize & Framebuffer) reasons << "Framebuffer";
1046  return reasons.join(",");
1047 }
1048 
1049 QOpenGLFramebufferObject* MythOpenGLVideo::CreateVideoFrameBuffer(VideoFrameType OutputType, QSize Size)
1050 {
1051  // Use a 16bit float framebuffer if necessary and available (not GLES2) to maintain precision.
1052  // The depth check will pick up all software formats as well as NVDEC, VideoToolBox and VAAPI DRM.
1053  // VAAPI GLXPixmap and GLXCopy are currently not 10/12bit aware and VDPAU has no 10bit support -
1054  // and all return RGB formats anyway. The MediaCoded texture format is an unknown but resizing will
1055  // never be enabled as it returns an RGB frame - so if MediaCodec uses a 16bit texture, precision
1056  // will be preserved.
1057 
1058  // GLES3.0 needs specific texture formats - needs more work and it
1059  // is currently unclear whether QOpenGLFrameBufferObject has the
1060  // requisite flexibility for those formats.
1061  bool sixteenbitfb = !m_gles;
1062  bool sixteenbitvid = ColorDepth(OutputType) > 8;
1063  GLenum format = (sixteenbitfb && sixteenbitvid) ? QOpenGLTexture::RGBA16_UNorm : 0;
1064  if (format)
1065  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Using 16bit framebuffer texture");
1066  return m_render->CreateFramebuffer(Size, format);
1067 }
void SetTextureFilters(MythGLTexture *Texture, QOpenGLTexture::Filter Filter, QOpenGLTexture::WrapMode Wrap=QOpenGLTexture::ClampToEdge)
int GetExtraFeatures(void) const
QOpenGLShaderProgram * CreateShaderProgram(const QString &Vertex, const QString &Fragment)
static const QString YUVFragmentExtensions
void SetVideoRects(const QRect &DisplayVideoRect, const QRect &VideoRect)
VideoColourSpace contains a QMatrix4x4 that can convert YCbCr data to RGB.
QOpenGLFramebufferObject * CreateFramebuffer(QSize &Size, GLenum InternalFormat=0)
static vector< MythVideoTexture * > CreateTextures(MythRenderOpenGL *Context, VideoFrameType Type, VideoFrameType Format, vector< QSize > Sizes, GLenum Target=QOpenGLTexture::Target2D)
Create a set of textures suitable for the given Type and Format.
void SetShaderProgramParams(QOpenGLShaderProgram *Program, const QMatrix4x4 &Value, const char *Uniform)
static const QString GLSL300YUVFragmentExtensions
QSize m_masterViewportSize
Current viewport into which OpenGL is rendered, usually the window size.
MythVideoTexture * m_frameBufferTexture
MythDeintType GetDoubleRateOption(const VideoFrame *Frame, MythDeintType Type, MythDeintType Override)
Definition: mythframe.cpp:854
vector< MythVideoTexture * > m_nextTextures
Next textures with raw video data.
QSize m_videoDispDim
Useful video frame size e.g. 1920x1080.
float GetColourGamma(void)
static void SetTextureFilters(MythRenderOpenGL *Context, const vector< MythVideoTexture * > &Textures, QOpenGLTexture::Filter Filter, QOpenGLTexture::WrapMode Wrap=QOpenGLTexture::ClampToEdge)
bool EnableShaderProgram(QOpenGLShaderProgram *Program)
QOpenGLFramebufferObject * CreateVideoFrameBuffer(VideoFrameType OutputType, QSize Size)
QOpenGLFunctions::OpenGLFeatures m_features
Default features available from Qt.
void UpdateShaderParameters(void)
QOpenGLShaderProgram * m_shaders[ShaderCount]
void BindFramebuffer(QOpenGLFramebufferObject *Framebuffer)
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
QSize m_inputTextureSize
Actual size of input texture(s)
bool IsValid(void) const
#define GL_TEXTURE_EXTERNAL_OES
void logDebugMarker(const QString &Message)
FrameScanType
Definition: videoouttypes.h:78
VideoFrameType
Definition: mythframe.h:23
void SetVideoDimensions(const QSize &VideoDim, const QSize &VideoDispDim)
MythOpenGLVideo(MythRenderOpenGL *Render, VideoColourSpace *ColourSpace, QSize VideoDim, QSize VideoDispDim, QRect DisplayVisibleRect, QRect DisplayVideoRect, QRect videoRect, bool ViewportControl, QString Profile)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define MAX_VIDEO_TEXTURES
static const QString GLSL300VertexShader
static uint planes(VideoFrameType Type)
Definition: mythframe.h:567
void DeleteShaderProgram(QOpenGLShaderProgram *Program)
int ColorDepth(int Format)
Return the color depth for the given MythTV frame format.
Definition: mythframe.cpp:815
static const QString DefaultVertexShader
static QString TypeToProfile(VideoFrameType Type)
QSize GetVideoSize(void) const
MythRenderOpenGL * m_render
static void UpdateTextures(MythRenderOpenGL *Context, const VideoFrame *Frame, const vector< MythVideoTexture * > &Textures)
Update the contents of the given Textures for data held in Frame.
MythDeintType
Definition: mythframe.h:120
#define LOC
static int format_is_420(VideoFrameType Type)
Definition: mythframe.h:85
VideoFrameType m_outputType
Set by profile for software or decoder for hardware.
QString DeinterlacerName(MythDeintType Deint, bool DoubleRate, VideoFrameType Format)
Return a user friendly description of the given deinterlacer.
Definition: mythavutil.cpp:114
VideoColourSpace * m_videoColourSpace
static void DeleteTextures(MythRenderOpenGL *Context, vector< MythVideoTexture * > &Textures)
MythGLTexture * CreateFramebufferTexture(QOpenGLFramebufferObject *Framebuffer)
static int format_is_444(VideoFrameType Type)
Definition: mythframe.h:97
vector< MythVideoTexture * > m_inputTextures
Current textures with raw video data.
void OutputChanged(QSize VideoDim, QSize VideoDispDim, float)
QMatrix4x4 GetPrimaryMatrix(void)
float GetDisplayGamma(void)
virtual int IncrRef(void)
Increments reference count.
QOpenGLFunctions::OpenGLFeatures GetFeatures(void) const
~MythOpenGLVideo() override
void DeleteFramebuffer(QOpenGLFramebufferObject *Framebuffer)
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:24
static int format_is_hw(VideoFrameType Type)
Definition: mythframe.h:72
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
MythDeintType m_deinterlacer
static int format_is_yuv(VideoFrameType Type)
Definition: mythframe.h:114
QStringList GetColourMappingDefines(void)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
static const QString GLSL300YUVFragmentShader
static int format_is_422(VideoFrameType Type)
Definition: mythframe.h:91
static const QString YUVFragmentShader
VideoFrameType m_inputType
Usually YV12 for software, VDPAU etc for hardware.
void DrawBitmap(MythGLTexture *Texture, QOpenGLFramebufferObject *Target, const QRect &Source, const QRect &Destination, QOpenGLShaderProgram *Program, int Alpha=255)
void DrawRect(QOpenGLFramebufferObject *Target, const QRect &Area, const QBrush &FillBrush, const QPen &LinePen, int Alpha)
MythDeintType m_fallbackDeinterlacer
Only used if there are insufficient texture units (for kernel)
static const QString RGBFragmentShader
unsigned int uint
Definition: compat.h:140
void ResetFrameFormat(void)
static int format_is_nv12(VideoFrameType Type)
Definition: mythframe.h:104
void ResetTextures(void)
Clear reference frames after a seek as they will contain old images.
void Updated(bool PrimariesChanged)
QSize m_videoDim
Total video frame size e.g. 1920x1088.
QOpenGLFramebufferObject * m_frameBuffer
long long m_discontinuityCounter
Check when to release reference frames after a skip.
QString GetProfile() const
int m_extraFeatures
OR'd list of extra, Myth specific features.
void CleanupDeinterlacers(void)
void ProcessFrame(VideoFrame *Frame, FrameScanType Scan=kScan_Progressive)
Update the current input texture using the data from the given video frame.
void DeleteTexture(MythGLTexture *Texture)
void SetViewPort(const QRect &rect, bool viewportonly=false)
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
bool AddDeinterlacer(const VideoFrame *Frame, FrameScanType Scan, MythDeintType Filter=DEINT_SHADER, bool CreateReferences=true)
StereoscopicMode
bool GetBoolSetting(const QString &key, bool defaultval=false)
vector< MythVideoTexture * > m_prevTextures
Previous textures with raw video data.
void SetViewportRect(const QRect &DisplayVisibleRect)
bool SetupFrameFormat(VideoFrameType InputType, VideoFrameType OutputType, QSize Size, GLenum TextureTarget)
void BindTextures(bool Deinterlacing, vector< MythVideoTexture * > &Current, MythGLTexture **Textures, uint &TextureCount)
static int format_is_hwframes(VideoFrameType Type)
Definition: mythframe.h:80
GLenum m_textureTarget
Some interops require custom texture targets.
bool CreateVideoShader(VideoShaderType Type, MythDeintType Deint=DEINT_NONE)
Create the appropriate shader for the operation Type.
int m_shaderCost[ShaderCount]
void SetProfile(const QString &Profile)
const char * format_description(VideoFrameType Type)
Definition: mythframe.cpp:33
int m_lastRotation
Track rotation for pause frame.
QRect m_videoRect
Sub-rect of video_disp_dim to display (after zoom adjustments etc)
bool is_interlaced(FrameScanType Scan)
int GetMaxTextureUnits(void) const
void SetMasterViewport(QSize Size)
MythDeintType GetSingleRateOption(const VideoFrame *Frame, MythDeintType Type, MythDeintType Override)
Definition: mythframe.cpp:841
bool UpdateColourSpace(const VideoFrame *Frame)
Set the current colourspace to use.
static QString VideoResizeToString(VideoResizing Resize)
void UpdateColourSpace(bool PrimariesChanged)
QRect m_displayVideoRect
Sub-rect of display_visible_rect for video.
VideoResizing m_resizing
static vector< MythVideoTexture * > Retrieve(MythRenderOpenGL *Context, VideoColourSpace *ColourSpace, VideoFrame *Frame, FrameScanType Scan)
void PrepareFrame(VideoFrame *Frame, bool TopFieldFirst, FrameScanType Scan, StereoscopicMode Stereo, bool DrawBorder=false)