MythTV  master
mythvideooutopengl.cpp
Go to the documentation of this file.
1 // C/C++
2 #include <utility>
3 
4 // MythTV
5 #include "mythcontext.h"
6 #include "mythmainwindow.h"
7 #include "mythplayer.h"
8 #include "videodisplayprofile.h"
9 #include "osd.h"
10 #include "mythuihelper.h"
11 #include "opengl/mythopenglperf.h"
14 #include "mythcodeccontext.h"
15 #include "mythopenglinterop.h"
16 #include "mythvideooutopengl.h"
17 
18 #define LOC QString("VidOutGL: ")
19 
27 {
28  QStringList safe;
29  safe << "opengl" << "opengl-yv12";
30 
31  // all profiles can handle all software frames
32  (*Options.safe_renderers)["dummy"].append(safe);
33  (*Options.safe_renderers)["nuppel"].append(safe);
34  if (Options.decoders->contains("ffmpeg"))
35  (*Options.safe_renderers)["ffmpeg"].append(safe);
36  if (Options.decoders->contains("mediacodec-dec"))
37  (*Options.safe_renderers)["mediacodec-dec"].append(safe);
38  if (Options.decoders->contains("vaapi-dec"))
39  (*Options.safe_renderers)["vaapi-dec"].append(safe);
40  if (Options.decoders->contains("vdpau-dec"))
41  (*Options.safe_renderers)["vdpau-dec"].append(safe);
42  if (Options.decoders->contains("nvdec-dec"))
43  (*Options.safe_renderers)["nvdec-dec"].append(safe);
44  if (Options.decoders->contains("vtb-dec"))
45  (*Options.safe_renderers)["vtb-dec"].append(safe);
46  if (Options.decoders->contains("v4l2-dec"))
47  (*Options.safe_renderers)["v4l2-dec"].append(safe);
48  if (Options.decoders->contains("mmal-dec"))
49  (*Options.safe_renderers)["mmal-dec"].append(safe);
50 
51  // OpenGL UYVY
52  Options.renderers->append("opengl");
53  Options.priorities->insert("opengl", 65);
54 
55  // OpenGL YV12
56  Options.renderers->append("opengl-yv12");
57  Options.priorities->insert("opengl-yv12", 65);
58 
59 #if defined(USING_VAAPI) || defined (USING_VTB) || defined (USING_MEDIACODEC) || defined (USING_VDPAU) || defined (USING_NVDEC) || defined (USING_MMAL) || defined (USING_V4L2PRIME) || defined (USING_EGL)
60  Options.renderers->append("opengl-hw");
61  (*Options.safe_renderers)["dummy"].append("opengl-hw");
62  (*Options.safe_renderers)["nuppel"].append("opengl-hw");
63  Options.priorities->insert("opengl-hw", 110);
64 #endif
65 #ifdef USING_VAAPI
66  if (Options.decoders->contains("vaapi"))
67  (*Options.safe_renderers)["vaapi"].append("opengl-hw");
68 #endif
69 #ifdef USING_VTB
70  if (Options.decoders->contains("vtb"))
71  (*Options.safe_renderers)["vtb"].append("opengl-hw");
72 #endif
73 #ifdef USING_MEDIACODEC
74  if (Options.decoders->contains("mediacodec"))
75  (*Options.safe_renderers)["mediacodec"].append("opengl-hw");
76 #endif
77 #ifdef USING_VDPAU
78  if (Options.decoders->contains("vdpau"))
79  (*Options.safe_renderers)["vdpau"].append("opengl-hw");
80 #endif
81 #ifdef USING_NVDEC
82  if (Options.decoders->contains("nvdec"))
83  (*Options.safe_renderers)["nvdec"].append("opengl-hw");
84 #endif
85 #ifdef USING_MMAL
86  if (Options.decoders->contains("mmal"))
87  (*Options.safe_renderers)["mmal"].append("opengl-hw");
88 #endif
89 #ifdef USING_V4L2PRIME
90  if (Options.decoders->contains("v4l2"))
91  (*Options.safe_renderers)["v4l2"].append("opengl-hw");
92 #endif
93 #ifdef USING_EGL
94  if (Options.decoders->contains("drmprime"))
95  (*Options.safe_renderers)["drmprime"].append("opengl-hw");
96 #endif
97 }
98 
100  : m_videoProfile(std::move(Profile))
101 {
102  // Retrieve render context
104  if (!m_render)
105  {
106  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to retrieve OpenGL context");
107  return;
108  }
109 
110  // Retain and lock
111  m_render->IncrRef();
112  OpenGLLocker locker(m_render);
113 
114  // enable performance monitoring if requested
115  // will report the execution time for the key GL code blocks
116  // N.B. 'Upload' should always be zero when using hardware decoding and direct
117  // rendering. Any copy cost for direct rendering will be included within 'Render'
118  if (VERBOSE_LEVEL_CHECK(VB_GPUVIDEO, LOG_INFO))
119  {
120  m_openGLPerf = new MythOpenGLPerf("GLVidPerf: ", { "Upload:", "Clear:", "Render:", "Flush:", "Swap:" });
121  if (!m_openGLPerf->isCreated())
122  {
123  delete m_openGLPerf;
124  m_openGLPerf = nullptr;
125  }
126  }
127 
128  // Disallow unsupported video texturing on GLES2/GL1.X
131 
132  // Retrieve OpenGL painter
134  m_openGLPainter = dynamic_cast<MythOpenGLPainter*>(win->GetCurrentPainter());
135  if (!m_openGLPainter)
136  {
137  LOG(VB_GENERAL, LOG_ERR, LOC + "Failed to get painter");
138  return;
139  }
140 
141  // we need to control buffer swapping
143 
144  // Create OpenGLVideo
145  QRect dvr = GetDisplayVisibleRect();
149 
150  // Connect VideoOutWindow to OpenGLVideo
155 }
156 
158 {
160  while (!m_openGLVideoPiPs.empty())
161  {
162  delete *m_openGLVideoPiPs.begin();
163  m_openGLVideoPiPs.erase(m_openGLVideoPiPs.begin());
164  }
165  m_openGLVideoPiPsReady.clear();
166  if (m_openGLPainter)
168  delete m_openGLVideo;
169  if (m_render)
170  {
172  delete m_openGLPerf;
174  m_render->DecrRef();
175  }
176  m_render = nullptr;
177 }
178 
180 {
184  m_buffersCreated = false;
185 }
186 
187 bool MythVideoOutputOpenGL::Init(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect,
188  MythDisplay *Display, const QRect &DisplayVisibleRect, MythCodecID CodecId)
189 {
191  return false;
192 
193  if (!gCoreContext->IsUIThread())
194  {
195  LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot initialise OpenGL video from this thread");
196  return false;
197  }
198 
199  OpenGLLocker ctx_lock(m_render);
200 
201  // if we are the main video player then free up as much video memory
202  // as possible at startup
203  PIPState pip = m_window.GetPIPState();
204  if ((kCodec_NONE == m_newCodecId) && ((kPIPOff == pip) || (kPBPLeft == pip)))
206 
207  // Default initialisation - mainly VideoOutWindow
208  if (!MythVideoOutput::Init(VideoDim, VideoDispDim, Aspect, Display, DisplayVisibleRect, CodecId))
209  return false;
210 
211  // Ensure any new profile preferences are handled after a stream change
212  if (m_dbDisplayProfile)
214 
215  // Set default support for picture attributes
217 
218  // Setup display
219  QSize size = m_window.GetVideoDim();
220 
221  // Set the display mode if required
223  ResizeForVideo(size);
225 
226  // Create buffers
227  if (!CreateBuffers(CodecId, m_window.GetVideoDim()))
228  return false;
229 
230  // Adjust visible rect for embedding
231  QRect dvr = GetDisplayVisibleRect();
233  {
234  m_render->SetViewPort(QRect(QPoint(), dvr.size()));
235  return true;
236  }
237 
239  {
240  QRect tmprect = QRect(QPoint(0,0), dvr.size());
241  ResizeDisplayWindow(tmprect, true);
242  }
243 
244  // Reset OpenGLVideo
245  if (m_openGLVideo->IsValid())
247 
248  // This works around an issue with VDPAU direct rendering using legacy drivers
249  m_render->Flush();
250 
251  return true;
252 }
253 
255 {
256  if (!m_dbDisplayProfile)
257  return;
258 
259  if (qFuzzyCompare(m_dbDisplayProfile->GetOutput() + 1.0F, NewRate + 1.0F))
260  return;
261 
262  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Video frame rate changed: %1->%2")
263  .arg(static_cast<double>(m_dbDisplayProfile->GetOutput())).arg(static_cast<double>(NewRate)));
264  m_dbDisplayProfile->SetOutput(NewRate);
265  m_newFrameRate = true;
266 }
267 
268 bool MythVideoOutputOpenGL::InputChanged(const QSize &VideoDim, const QSize &VideoDispDim,
269  float Aspect, MythCodecID CodecId, bool &AspectOnly,
270  MythMultiLocker* /*Locks*/, int ReferenceFrames,
271  bool ForceChange)
272 {
273  QSize currentvideodim = m_window.GetVideoDim();
274  QSize currentvideodispdim = m_window.GetVideoDispDim();
275  MythCodecID currentcodec = m_videoCodecID;
276  float currentaspect = m_window.GetVideoAspect();
277 
278  if (m_newCodecId != kCodec_NONE)
279  {
280  // InputChanged has been called twice in quick succession without a call to ProcessFrame
281  currentvideodim = m_newVideoDim;
282  currentvideodispdim = m_newVideoDispDim;
283  currentcodec = m_newCodecId;
284  currentaspect = m_newAspect;
285  }
286 
287  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Video changed: %1x%2 (%3x%4) '%5' (Aspect %6 Refs %13)"
288  "-> %7x%8 (%9x%10) '%11' (Aspect %12 Refs %14)")
289  .arg(currentvideodispdim.width()).arg(currentvideodispdim.height())
290  .arg(currentvideodim.width()).arg(currentvideodim.height())
291  .arg(toString(currentcodec)).arg(static_cast<double>(currentaspect))
292  .arg(VideoDispDim.width()).arg(VideoDispDim.height())
293  .arg(VideoDim.width()).arg(VideoDim.height())
294  .arg(toString(CodecId)).arg(static_cast<double>(Aspect))
295  .arg(m_maxReferenceFrames).arg(ReferenceFrames));
296 
297  bool cidchanged = (CodecId != currentcodec);
298  bool reschanged = (VideoDispDim != currentvideodispdim);
299  bool refschanged = m_maxReferenceFrames != ReferenceFrames;
300 
301  // aspect ratio changes are a no-op as changes are handled at display time
302  if (!(cidchanged || reschanged || refschanged || ForceChange))
303  {
304  AspectOnly = true;
305  return true;
306  }
307 
308  // N.B. We no longer check for interop support for the new codec as it is a
309  // poor substitute for a full check of decoder capabilities etc. Better to let
310  // hardware decoding fail if necessary - which should at least fallback to
311  // software decoding rather than bailing out here.
312 
313  // delete and recreate the buffers and flag that the input has changed
314  m_maxReferenceFrames = ReferenceFrames;
316  if (!m_buffersCreated)
317  return false;
318 
319  m_newCodecId= CodecId;
320  m_newVideoDim = VideoDim;
321  m_newVideoDispDim = VideoDispDim;
322  m_newAspect = Aspect;
323  return true;
324 }
325 
327 {
328  QRect dvr = m_window.GetDisplayVisibleRect();
329 
330  MythMainWindow *mainwin = GetMythMainWindow();
331  if (!mainwin)
332  return dvr;
333  QSize size = mainwin->size();
334 
335  // may be called before m_window is initialised fully
336  if (dvr.isEmpty())
337  dvr = QRect(QPoint(0, 0), size);
338 
339  // If the Video screen mode has vertically less pixels
340  // than the GUI screen mode - OpenGL coordinate adjustments
341  // must be made to put the video at the top of the display
342  // area instead of at the bottom.
343  if (dvr.height() < size.height())
344  dvr.setTop(dvr.top() - size.height() + dvr.height());
345 
346  // If the Video screen mode has horizontally less pixels
347  // than the GUI screen mode - OpenGL width must be set
348  // as the higher GUI width so that the Program Guide
349  // invoked from playback is not cut off.
350  if (dvr.width() < size.width())
351  dvr.setWidth(size.width());
352 
353  return dvr;
354 }
355 
357 {
358  if (m_buffersCreated)
359  return true;
360 
361  if (codec_is_copyback(CodecID))
362  {
364  return m_videoBuffers.CreateBuffers(FMT_YV12, Size.width(), Size.height());
365  }
366 
367  if (codec_is_mediacodec(CodecID))
368  return m_videoBuffers.CreateBuffers(FMT_MEDIACODEC, Size, false, 1, 2, 2);
369  if (codec_is_vaapi(CodecID))
370  return m_videoBuffers.CreateBuffers(FMT_VAAPI, Size, false, 2, 1, 4, m_maxReferenceFrames);
371  if (codec_is_vtb(CodecID))
372  return m_videoBuffers.CreateBuffers(FMT_VTB, Size, false, 1, 4, 2);
373  if (codec_is_vdpau(CodecID))
374  return m_videoBuffers.CreateBuffers(FMT_VDPAU, Size, false, 2, 1, 4, m_maxReferenceFrames);
375  if (codec_is_nvdec(CodecID))
376  return m_videoBuffers.CreateBuffers(FMT_NVDEC, Size, false, 2, 1, 4);
377  if (codec_is_mmal(CodecID))
378  return m_videoBuffers.CreateBuffers(FMT_MMAL, Size, false, 2, 1, 4);
379  if (codec_is_v4l2(CodecID) || codec_is_drmprime(CodecID))
380  return m_videoBuffers.CreateBuffers(FMT_DRMPRIME, Size, false, 2, 1, 4);
381 
382  return m_videoBuffers.CreateBuffers(FMT_YV12, Size, false, 1, 8, 4, m_maxReferenceFrames);
383 }
384 
386  const PIPMap &PiPPlayers,
387  FrameScanType Scan)
388 {
389  if (!m_render)
390  return;
391 
392  OpenGLLocker ctx_lock(m_render);
393 
394  // start the first timer
395  if (m_openGLPerf)
397 
398  // Process input changes
399  if (m_newCodecId != kCodec_NONE)
400  {
401  // Ensure we don't lose embedding through program changes.
402  bool wasembedding = m_window.IsEmbedding();
403  QRect oldrect;
404  if (wasembedding)
405  {
406  oldrect = m_window.GetEmbeddingRect();
407  StopEmbedding();
408  }
409 
410  // Note - we don't call the default VideoOutput::InputChanged method as
411  // the OpenGL implementation is asynchronous.
412  // So we need to update the video display profile here. It is a little
413  // circular as we need to set the video dimensions first which are then
414  // reset in Init.
415  // All told needs a cleanup - not least because the use of codecName appears
416  // to be inconsistent.
418  AVCodecID avCodecId = myth2av_codecid(m_newCodecId);
419  AVCodec *codec = avcodec_find_decoder(avCodecId);
420  QString codecName;
421  if (codec)
422  codecName = codec->name;
423  if (m_dbDisplayProfile)
425 
429  m_newVideoDim = QSize();
430  m_newVideoDispDim = QSize();
431  m_newAspect = 0.0F;
432  m_newFrameRate = false;
433 
434  if (wasembedding && ok)
435  EmbedInWidget(oldrect);
436 
437  if (!ok)
438  return;
439  }
440  else if (m_newFrameRate)
441  {
442  // If we are switching mode purely for a refresh rate change, then there
443  // is no need to recreate buffers etc etc
444  ResizeForVideo();
445  m_newFrameRate = false;
446  }
447 
448  if (Frame)
449  m_window.SetRotation(Frame->rotation);
450 
451  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
452  m_render->logDebugMarker(LOC + "PROCESS_FRAME_START");
453 
454  bool swframe = Frame ? !format_is_hw(Frame->codec) : false;
455  bool dummy = Frame ? Frame->dummy : false;
456 
457  // software deinterlacing
458  if (!dummy && swframe)
460 
461  if (!m_window.IsEmbedding())
462  {
463  m_openGLVideoPiPActive = nullptr;
464  ShowPIPs(Frame, PiPPlayers);
465  }
466 
467  if (m_openGLVideo && swframe && !dummy)
469 
470  // time texture update
471  if (m_openGLPerf)
473 
474  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
475  m_render->logDebugMarker(LOC + "PROCESS_FRAME_END");
476 }
477 
479 {
480  if (!m_render)
481  return;
482 
483  if (m_newCodecId != kCodec_NONE)
484  return; // input changes need to be handled in ProcessFrame
485 
486  OpenGLLocker ctx_lock(m_render);
487 
488  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
489  m_render->logDebugMarker(LOC + "PREPARE_FRAME_START");
490 
491  bool dummy = false;
492  bool topfieldfirst = false;
493  if (Frame)
494  {
495  m_framesPlayed = Frame->frameNumber + 1;
496  topfieldfirst = Frame->interlaced_reversed ? !Frame->top_field_first : Frame->top_field_first;
497  dummy = Frame->dummy;
498  }
499  else
500  {
501  // see VideoOutputOpenGL::DoneDisplayingFrame
502  // we only retain pause frames for hardware formats
505  }
506 
507  // if process frame has not been called (double rate hardware deint), then
508  // we need to start the first 2 performance timers here
509  if (m_openGLPerf)
510  {
512  {
515  }
516  }
517 
518  m_render->BindFramebuffer(nullptr);
519 
520  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
521  m_render->logDebugMarker(LOC + "CLEAR_START");
522 
523  int gray = m_dbLetterboxColour == kLetterBoxColour_Gray25 ? 64 : 0;
524  bool useclear = !Frame || dummy || ((m_render->GetExtraFeatures() & kGLTiled) != 0);
525 #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0)
526  // Qt < 5.8 uses a different QRegion API. Just clear and remove this code
527  // when 5.8 is standard
528  useclear = true;
529 #endif
530 
531  if (useclear)
532  {
533  m_render->SetBackground(gray, gray, gray, 255);
535  }
536 #if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
537  // avoid clearing the framebuffer if it will be entirely overwritten by video
538  else if (!m_window.VideoIsFullScreen())
539  {
540  if (m_window.IsEmbedding())
541  {
542  // use MythRenderOpenGL rendering as it will clear to the appropriate 'black level'
543  m_render->ClearRect(nullptr, m_window.GetWindowRect(), gray);
544  }
545  else
546  {
547  // in the vast majority of cases it is significantly quicker to just
548  // clear the unused portions of the screen
549  QRegion toclear = m_window.GetBoundingRegion();
550  for (auto rect : qAsConst(toclear))
551  m_render->ClearRect(nullptr, rect, gray);
552  }
553  }
554 #endif
555 
556  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
557  m_render->logDebugMarker(LOC + "CLEAR_END");
558 
559  // time framebuffer clearing
560  if (m_openGLPerf)
562 
563  // stereoscopic views
564  QRect main = m_render->GetViewPort();
565  QRect first = main;
566  QRect second = main;
568 
570  {
571  first = QRect(main.left() / 2, main.top(), main.width() / 2, main.height());
572  second = first.translated(main.width() / 2, 0);
573  }
575  {
576  first = QRect(main.left(), main.top() / 2, main.width(), main.height() / 2);
577  second = first.translated(0, main.height() / 2);
578  }
579 
580  // main UI when embedded
581  if (m_window.IsEmbedding())
582  {
584  if (win && win->GetPaintWindow())
585  {
586  if (twopass)
587  m_render->SetViewPort(first, true);
588  win->GetPaintWindow()->clearMask();
589  win->Draw(m_openGLPainter);
590  if (twopass)
591  {
592  m_render->SetViewPort(second, true);
593  win->GetPaintWindow()->clearMask();
594  win->Draw(m_openGLPainter);
595  m_render->SetViewPort(main, true);
596  }
597  }
598  }
599 
600  // video
601  if (m_openGLVideo && !dummy)
602  {
603  m_openGLVideo->PrepareFrame(Frame, topfieldfirst, Scan, m_stereo);
604  // dummy streams need the viewport updated in case we have resized the window
605  }
606  else if (dummy)
607  {
609  }
610 
611  // PiPs/PBPs
612  if (!m_openGLVideoPiPs.empty() && !m_window.IsEmbedding())
613  {
614  for (auto it = m_openGLVideoPiPs.begin(); it != m_openGLVideoPiPs.end(); ++it)
615  {
616  if (m_openGLVideoPiPsReady[it.key()])
617  {
618  bool active = m_openGLVideoPiPActive == *it;
619  if (twopass)
620  m_render->SetViewPort(first, true);
621  (*it)->PrepareFrame(nullptr, topfieldfirst, Scan, kStereoscopicModeNone, active);
622  if (twopass)
623  {
624  m_render->SetViewPort(second, true);
625  (*it)->PrepareFrame(nullptr, topfieldfirst, Scan, kStereoscopicModeNone, active);
627  }
628  }
629  }
630  }
631 
632  // visualisation
634  {
635  if (twopass)
636  m_render->SetViewPort(first, true);
638  if (twopass)
639  {
640  m_render->SetViewPort(second, true);
643  }
644  }
645 
646  // OSD
647  if (Osd && m_openGLPainter && !m_window.IsEmbedding())
648  {
649  if (twopass)
650  m_render->SetViewPort(first, true);
651  Osd->Draw(m_openGLPainter, GetTotalOSDBounds().size(), true);
652  if (twopass)
653  {
654  m_render->SetViewPort(second, true);
655  Osd->Draw(m_openGLPainter, GetTotalOSDBounds().size(), true);
657  }
658  }
659 
660  // time rendering
661  if (m_openGLPerf)
663 
664  m_render->Flush();
665 
666  // time flush
667  if (m_openGLPerf)
669 
670  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
671  m_render->logDebugMarker(LOC + "PREPARE_FRAME_END");
672 }
673 
686 {
687  if (!Frame)
688  return;
689 
690  bool retain = format_is_hw(Frame->codec);
691  QVector<VideoFrame*> release;
692 
695  {
697  if (!retain || (retain && (frame != Frame)))
698  release.append(frame);
699  }
700 
701  if (retain)
702  {
706  }
707  else
708  {
709  release.append(Frame);
710  }
712 
713  for (auto * frame : release)
715 }
716 
723 void MythVideoOutputOpenGL::DiscardFrames(bool KeyFrame, bool Flushed)
724 {
725  if (Flushed)
726  {
727  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("(%1): %2").arg(KeyFrame).arg(m_videoBuffers.GetStatus()));
729  }
730  MythVideoOutput::DiscardFrames(KeyFrame, Flushed);
731 }
732 
734 {
735  // Complete list of formats supported for OpenGL 2.0 and higher and OpenGL ES3.X
736  static VideoFrameType s_AllFormats[] =
742  FMT_NONE };
743 
744  // OpenGL ES 2.0 and OpenGL1.X only allow luminance textures
745  static VideoFrameType s_LegacyFormats[] =
747 
748  static VideoFrameType* s_formats[2] = { s_AllFormats, s_LegacyFormats };
749  return s_formats[m_textureFormats];
750 }
751 
753 {
754  m_window.SetWindowSize(Size);
756 }
757 
759 {
760  if (m_render && !IsErrored())
761  {
763  if (VERBOSE_LEVEL_CHECK(VB_GPU, LOG_INFO))
764  m_render->logDebugMarker(LOC + "SHOW");
766  if (m_openGLPerf)
767  {
768  // time buffer swap and log
769  // Results will normally be available on the next pass - and we will typically
770  // test every other frame as a result to avoid blocking in the driver.
771  // With the default of averaging over 30 samples this should give a 30 sample
772  // average over 60 frames
775  }
777  }
778 }
779 
781 {
782  if (m_openGLVideo)
785 }
786 
796 QStringList MythVideoOutputOpenGL::GetAllowedRenderers(MythCodecID CodecId, const QSize& /*VideoDim*/)
797 {
798  QStringList allowed;
799  if (getenv("NO_OPENGL"))
800  return allowed;
801 
802  if (codec_sw_copy(CodecId))
803  {
804  allowed << "opengl" << "opengl-yv12";
805  return allowed;
806  }
807 
808  VideoFrameType format = FMT_NONE;
809  if (codec_is_vaapi(CodecId))
810  format = FMT_VAAPI;
811  else if (codec_is_vdpau(CodecId))
812  format = FMT_VDPAU;
813  else if (codec_is_nvdec(CodecId))
814  format = FMT_NVDEC;
815  else if (codec_is_vtb(CodecId))
816  format = FMT_VTB;
817  else if (codec_is_mmal(CodecId))
818  format = FMT_MMAL;
819  else if (codec_is_v4l2(CodecId) || codec_is_drmprime(CodecId))
820  format = FMT_DRMPRIME;
821  else if (codec_is_mediacodec(CodecId))
822  format = FMT_MEDIACODEC;
823 
824  if (FMT_NONE == format)
825  return allowed;
826 
827  allowed += MythOpenGLInterop::GetAllowedRenderers(format);
828  return allowed;
829 }
830 
831 void MythVideoOutputOpenGL::UpdatePauseFrame(int64_t &DisplayTimecode, FrameScanType Scan)
832 {
833  VideoFrame* release = nullptr;
836  if (used)
837  {
838  if (format_is_hw(used->codec))
839  {
841  }
842  else
843  {
845  m_deinterlacer.Filter(used, Scan, m_dbDisplayProfile, true);
846  m_openGLVideo->ProcessFrame(used, Scan);
847  }
848  DisplayTimecode = used->disp_timecode;
849  }
850  else
851  {
852  LOG(VB_PLAYBACK, LOG_WARNING, LOC + "Could not update pause frame");
853  }
855 
856  if (release)
857  DoneDisplayingFrame(release);
858 }
859 
861 {
863 }
864 
865 void MythVideoOutputOpenGL::ShowPIP(VideoFrame* /*Frame*/, MythPlayer *PiPPlayer, PIPLocation Location)
866 {
867  if (!PiPPlayer)
868  return;
869 
870  int pipw = 0;
871  int piph = 0;
872  VideoFrame *pipimage = PiPPlayer->GetCurrentFrame(pipw, piph);
873  const QSize pipvideodim = PiPPlayer->GetVideoBufferSize();
874  QRect pipvideorect = QRect(QPoint(0, 0), pipvideodim);
875 
876  if ((PiPPlayer->GetVideoAspect() <= 0.0F) || !pipimage || !pipimage->buf ||
877  (pipimage->codec != FMT_YV12) || !PiPPlayer->IsPIPVisible())
878  {
879  PiPPlayer->ReleaseCurrentFrame(pipimage);
880  return;
881  }
882 
883  QRect position = GetPIPRect(Location, PiPPlayer);
884  QRect dvr = m_window.GetDisplayVisibleRect();
885 
886  m_openGLVideoPiPsReady[PiPPlayer] = false;
887  MythOpenGLVideo *gl_pipchain = m_openGLVideoPiPs[PiPPlayer];
888  if (!gl_pipchain)
889  {
890  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Initialise PiP");
891  auto *colourspace = new VideoColourSpace(&m_videoColourSpace);
892  m_openGLVideoPiPs[PiPPlayer] = gl_pipchain = new MythOpenGLVideo(m_render, colourspace,
893  pipvideodim, pipvideodim,
894  dvr, position, pipvideorect,
895  false, m_videoProfile);
896 
897  colourspace->DecrRef();
898  if (!gl_pipchain->IsValid())
899  {
900  PiPPlayer->ReleaseCurrentFrame(pipimage);
901  return;
902  }
903  gl_pipchain->SetMasterViewport(dvr.size());
904  }
905 
906  if (gl_pipchain->GetVideoSize() != pipvideodim)
907  {
908  LOG(VB_PLAYBACK, LOG_INFO, LOC + "Re-initialise PiP.");
909  delete gl_pipchain;
910  auto *colourspace = new VideoColourSpace(&m_videoColourSpace);
911  m_openGLVideoPiPs[PiPPlayer] = gl_pipchain = new MythOpenGLVideo(m_render, colourspace,
912  pipvideodim, pipvideodim,
913  dvr, position, pipvideorect,
914  false, m_videoProfile);
915  colourspace->DecrRef();
916  if (!gl_pipchain->IsValid())
917  {
918  PiPPlayer->ReleaseCurrentFrame(pipimage);
919  return;
920  }
921  gl_pipchain->SetMasterViewport(dvr.size());
922  }
923 
924  if (gl_pipchain->IsValid())
925  {
926  gl_pipchain->SetVideoRects(position, pipvideorect);
927  gl_pipchain->ProcessFrame(pipimage);
928  }
929 
930  m_openGLVideoPiPsReady[PiPPlayer] = true;
931  if (PiPPlayer->IsPIPActive())
932  m_openGLVideoPiPActive = gl_pipchain;
933  PiPPlayer->ReleaseCurrentFrame(pipimage);
934 }
935 
937 {
938  if (m_openGLVideoPiPs.contains(PiPPlayer))
939  {
941  delete m_openGLVideoPiPs.take(PiPPlayer);
942  m_openGLVideoPiPsReady.remove(PiPPlayer);
943  m_openGLVideoPiPs.remove(PiPPlayer);
945  }
946 }
947 
949 {
950  if (m_render)
953 }
954 
956 {
957  return m_openGLPainter;
958 }
959 
961 {
963 }
964 
965 bool MythVideoOutputOpenGL::SetupVisualisation(AudioPlayer *Audio, MythRender* /*Render*/, const QString &Name)
966 {
968 }
QRegion GetBoundingRegion(void) const
Return the region of DisplayVisibleRect that lies outside of DisplayVideoRect.
int GetExtraFeatures(void) const
static MythMainWindow * getMainWindow(bool useDB=true)
Return the existing main window, or create one.
void Reset(void)
Resets the class so that Init may be called again.
#define codec_sw_copy(id)
Definition: mythcodecid.h:349
void SetVideoRects(const QRect &DisplayVideoRect, const QRect &VideoRect)
void FreeResources(void) override
QMap< MythPlayer *, PIPLocation > PIPMap
Definition: mythvideoout.h:32
VideoColourSpace contains a QMatrix4x4 that can convert YCbCr data to RGB.
AVCodecID myth2av_codecid(MythCodecID codec_id)
virtual void ResizeDisplayWindow(const QRect &Rect, bool SaveVisible)
Resize Display Window.
float GetVideoAspect(void) const
Definition: mythplayer.h:218
void SetInput(const QSize &Size, float Framerate=0, const QString &CodecName=QString(), const QStringList &DisallowedDecoders=QStringList())
MythCodecID m_videoCodecID
Definition: mythvideoout.h:164
#define codec_is_mmal(id)
Definition: mythcodecid.h:341
MythRenderOpenGL * m_render
VideoColourSpace m_videoColourSpace
Definition: mythvideoout.h:160
static QStringList GetAllowedRenderers(MythCodecID CodecId, const QSize &VideoDim)
Generate a list of supported OpenGL profiles.
unsigned char * buf
Definition: mythframe.h:139
QString GetStatus(uint Num=0) const
virtual QStringList GetVisualiserList(void)
uint Size(BufferType Type) const
QString toString(MarkTypes type)
void DoneDisplayingFrame(VideoFrame *Frame) override
Release a video frame back into the decoder pool.
MythDeinterlacer m_deinterlacer
Definition: mythvideoout.h:173
QRect GetTotalOSDBounds(void) const
void VideoRectsChanged(const QRect &DisplayVideoRect, const QRect &VideoRect)
void SetOutput(float Framerate)
MythCodecID
Definition: mythcodecid.h:10
virtual bool CanVisualise(AudioPlayer *Audio, MythRender *Render)
void SetBackground(int Red, int Green, int Blue, int Alpha)
VideoFrame * Dequeue(BufferType Type)
void SetViewPort(const QRect &Rect, bool ViewportOnly=false)
void PrepareFrame(VideoFrame *Frame, FrameScanType Scan, OSD *Osd) override
VideoDisplayProfile * m_dbDisplayProfile
Definition: mythvideoout.h:166
VideoVisual * m_visual
Definition: mythvideoout.h:170
void BindFramebuffer(QOpenGLFramebufferObject *Framebuffer)
RenderType Type(void) const
float GetOutput(void) const
bool Contains(BufferType Type, VideoFrame *Frame) const
VideoFrameType codec
Definition: mythframe.h:138
bool IsValid(void) const
QRect GetVideoRect(void) const
void WindowResized(const QSize &Size) override
#define codec_is_vdpau(id)
Definition: mythcodecid.h:298
void SetSwapControl(bool Swap)
VideoFrame * GetCurrentFrame(int &w, int &h)
QStringList GetVisualiserList(void) override
bool DiscardAndRecreate(MythCodecID CodecID, QSize VideoDim, int References)
Discard all buffers and recreate.
#define codec_is_copyback(id)
Definition: mythcodecid.h:344
void logDebugMarker(const QString &Message)
FrameScanType
Definition: videoouttypes.h:78
VideoFrameType
Definition: mythframe.h:23
void SetVideoDimensions(const QSize &VideoDim, const QSize &VideoDispDim)
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
bool CreateBuffers(MythCodecID CodecID, QSize Size)
void DoneDisplayingFrame(VideoFrame *Frame)
Removes frame from used queue and adds it to the available list.
MythVideoOutputOpenGL(QString Profile=QString())
MythOpenGLVideo * m_openGLVideo
#define LOC
QSize GetVideoSize(void) const
void Init(uint NumDecode, bool ExtraForPause, uint NeedFree, uint NeedprebufferNormal, uint NeedPrebufferSmall)
Creates buffers and sets various buffer management parameters.
static QStringList GetAllowedRenderers(VideoFrameType Format)
TextureFormats m_textureFormats
void WindowRectChanged(const QRect &WindowRect)
virtual void ClearAfterSeek(void)
Tells video output to toss decoded buffers due to a seek.
void RecordSample(void)
VideoFrameType * DirectRenderFormats(void) override
bool CanVisualise(AudioPlayer *Audio, MythRender *Render) override
QMap< QString, QStringList > * safe_renderers
virtual bool UsingVideoModes(void)
Definition: mythdisplay.h:29
#define codec_is_vaapi(id)
Definition: mythcodecid.h:311
QMap< MythPlayer *, MythOpenGLVideo * > m_openGLVideoPiPs
LetterBoxColour m_dbLetterboxColour
Definition: mythvideoout.h:163
virtual bool Init(const QSize &VideoDim, const QSize &VideoDispDim, float VideoAspect, MythDisplay *Display, const QRect &WindowRect, MythCodecID CodecID)
void OutputChanged(QSize VideoDim, QSize VideoDispDim, float)
MythOpenGLVideo * m_openGLVideoPiPActive
virtual int IncrRef(void)
Increments reference count.
VideoOutWindow m_window
Definition: mythvideoout.h:159
QStringList * renderers
int GetTimersRunning(void) const
#define VERBOSE_LEVEL_CHECK(_MASK_, _LEVEL_)
Definition: mythlogging.h:14
void DiscardFrames(bool KeyFrame, bool Flushed) override
Discard video frames.
void ReleaseCurrentFrame(VideoFrame *frame)
void Remove(BufferType Type, VideoFrame *Frame)
VideoBuffers m_videoBuffers
Definition: mythvideoout.h:167
void SetWindowSize(QSize Size)
QRect GetEmbeddingRect(void) const
virtual void EmbedInWidget(const QRect &EmbedRect)
Tells video output to embed video in an existing window.
MythDisplay * m_display
Definition: mythvideoout.h:158
QWidget * GetPaintWindow()
bool IsErrored() const
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
MythPainter * GetOSDPainter(void) override
void Show(FrameScanType Scan) override
virtual void StopEmbedding(void)
bool IsEmbedding(void) const
frame_queue_t::iterator BeginLock(BufferType Type)
Lock the video buffers.
QSize GetVideoDispDim(void) const
void Filter(VideoFrame *Frame, FrameScanType Scan, VideoDisplayProfile *Profile, bool Force=false)
Deinterlace Frame if needed.
void ResetFrameFormat(void)
bool CreateBuffers(VideoFrameType Type, QSize Size, bool ExtraForPause, uint NeedFree, uint NeedprebufferNormal, uint NeedPrebufferSmall, int MaxReferenceFrames=16)
virtual void ResizeForVideo(QSize Size=QSize())
QRect GetDisplayVideoRect(void) const
#define ALL_PICTURE_ATTRIBUTES
MythPainter * GetCurrentPainter()
#define codec_is_mediacodec(id)
Definition: mythcodecid.h:323
bool Draw(MythPainter *Painter, QSize Size, bool Repaint=false)
Definition: osd.cpp:633
void RemovePIP(MythPlayer *PiPPlayer) override
void ResetTextures(void)
Clear reference frames after a seek as they will contain old images.
static void GetRenderOptions(RenderOptions &Options)
Generate the list of available OpenGL profiles.
void ShowPIP(VideoFrame *Frame, MythPlayer *PiPPlayer, PIPLocation Location) override
virtual void DiscardFrames(bool KeyFrame, bool Flushed)
Releases all frames not being actively displayed from any queue onto the queue of frames ready for de...
bool VideoIsFullScreen(void) const
Check whether the video display rect covers the entire window/framebuffer.
QStringList * decoders
long long m_framesPlayed
Definition: mythvideoout.h:169
MythMainWindow * GetMythMainWindow(void)
PIPState GetPIPState(void) const
QMap< MythPlayer *, bool > m_openGLVideoPiPsReady
PIPLocation
Definition: videoouttypes.h:17
int64_t disp_timecode
Definition: mythframe.h:150
QRect GetViewPort(void)
void SetSupportedAttributes(PictureAttributeSupported Supported)
Enable the given set of picture attributes.
void ProcessFrame(VideoFrame *Frame, FrameScanType Scan=kScan_Progressive)
Update the current input texture using the data from the given video frame.
#define codec_is_vtb(id)
Definition: mythcodecid.h:333
QSize GetVideoBufferSize(void) const
Definition: mythplayer.h:216
virtual void ShowPIPs(VideoFrame *Frame, const PIPMap &PiPPlayers)
static uint GetNumBuffers(int PixelFormat, int MaxReferenceFrames=16, bool Decoder=false)
void ClearAfterSeek(void) override
Tells video output to toss decoded buffers due to a seek.
PIPState
Definition: videoouttypes.h:8
void SetViewportRect(const QRect &DisplayVisibleRect)
virtual QRect GetPIPRect(PIPLocation Location, MythPlayer *PiPPlayer=nullptr, bool DoPixelAdj=true) const
returns QRect of PIP based on PIPLocation
static QStringList GetVisualiserList(RenderType type)
Definition: videovisual.cpp:14
static MythRenderOpenGL * GetOpenGLRender(void)
A simple overload of QOpenGLTimeMonitor to record and log OpenGL execution intervals.
bool already_deinterlaced
temporary? TODO move scan detection/tracking into decoder
Definition: mythframe.h:171
static bool format_is_hw(VideoFrameType Type)
Definition: mythframe.h:72
bool SetupVisualisation(AudioPlayer *Audio, MythRender *Render, const QString &Name) override
void UpdatePauseFrame(int64_t &DisplayTimecode, FrameScanType Scan=kScan_Progressive) override
void VideoSizeChanged(const QSize &VideoDim, const QSize &VideoDispDim)
void SetProfile(const QString &Profile)
Definition: osd.h:131
void Draw(MythPainter *Painter=nullptr)
float GetVideoAspect(void) const
virtual void Draw(const QRect &area, MythPainter *painter, QPaintDevice *device)=0
QRect GetDisplayVisibleRect(void) const
QMap< QString, uint > * priorities
QString GetVideoRenderer(void) const
void LogSamples(void)
QRect GetWindowRect(void) const
QSize GetVideoDim(void) const
virtual bool SetupVisualisation(AudioPlayer *Audio, MythRender *Render, const QString &Name)
MythOpenGLPerf * m_openGLPerf
#define codec_is_nvdec(id)
Definition: mythcodecid.h:328
bool Init(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect, MythDisplay *Display, const QRect &DisplayVisibleRect, MythCodecID CodecId) override
void InputChanged(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect)
Tells video output to discard decoded frames and wait for new ones.
bool IsPIPVisible(void) const
Definition: mythplayer.h:267
void ClearRect(QOpenGLFramebufferObject *Target, const QRect &Area, int Color)
An optimised method to clear a QRect to the given color.
bool is_interlaced(FrameScanType Scan)
#define codec_is_drmprime(id)
Definition: mythcodecid.h:296
void SetVideoFrameRate(float NewRate) override
void SetMasterViewport(QSize Size)
VideoFrame * Head(BufferType Type)
StereoscopicMode m_stereo
Definition: mythvideoout.h:171
void Enqueue(BufferType Type, VideoFrame *Frame)
VideoFrame * Tail(BufferType Type)
void SetRotation(int Rotation)
Set the rotation in degrees.
void DeleteBuffers(void)
void InitDisplayMeasurements(void)
Initialise display measurement.
void DiscardPauseFrames(void)
#define codec_is_v4l2(id)
Definition: mythcodecid.h:338
bool InputChanged(const QSize &VideoDim, const QSize &VideoDispDim, float Aspect, MythCodecID CodecId, bool &AspectOnly, MythMultiLocker *Locks, int ReferenceFrames, bool ForceChange) override
Tells video output to discard decoded frames and wait for new ones.
void ClearFramebuffer(void)
MythOpenGLVideo is responsible for displaying video frames on screen using OpenGL.
MythOpenGLPainter * m_openGLPainter
bool IsPIPActive(void) const
Definition: mythplayer.h:266
void PrepareFrame(VideoFrame *Frame, bool TopFieldFirst, FrameScanType Scan, StereoscopicMode Stereo, bool DrawBorder=false)
void InitPictureAttributes(void) override
void ProcessFrame(VideoFrame *Frame, OSD *Osd, const PIPMap &PiPPlayers, FrameScanType Scan) override
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23