Sfoglia il codice sorgente

PlayerComponent: add API for setting video target rectangle

For now, this is implemented "manually" by creating a FBO and letting
mpv render into it. Direct output maybe later. This still does direct
output in normal fullscreen mode, which I hope web-client will continue
to use in the normal case. (Otherwise we could have this simpler by
making the video renderer a real QQuickItem.)
Vincent Lang 8 anni fa
parent
commit
21dd6aa9f4

+ 13 - 1
src/player/PlayerComponent.cpp

@@ -42,7 +42,8 @@ PlayerComponent::PlayerComponent(QObject* parent)
   m_lastPositionUpdate(0.0), m_playbackAudioDelay(0),
   m_playbackStartSent(false), m_window(nullptr), m_mediaFrameRate(0),
   m_restoreDisplayTimer(this), m_reloadAudioTimer(this),
-  m_streamSwitchImminent(false), m_doAc3Transcoding(false)
+  m_streamSwitchImminent(false), m_doAc3Transcoding(false),
+  m_videoRectangle(-1, -1, -1, -1)
 {
   qmlRegisterType<PlayerQuickItem>("Konvergo", 1, 0, "MpvVideo"); // deprecated name
   qmlRegisterType<PlayerQuickItem>("Konvergo", 1, 0, "KonvergoVideo");
@@ -181,6 +182,17 @@ bool PlayerComponent::componentInitialize()
   return true;
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+void PlayerComponent::setVideoRectangle(int x, int y, int w, int h)
+{
+  QRect rc(x, y, w, h);
+  if (rc != m_videoRectangle)
+  {
+    m_videoRectangle = rc;
+    emit onVideoRecangleChanged();
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 void PlayerComponent::setQtQuickWindow(QQuickWindow* window)
 {

+ 13 - 0
src/player/PlayerComponent.h

@@ -110,6 +110,16 @@ public:
 
   Q_INVOKABLE void userCommand(QString command);
 
+  // Set the region in which video should be rendered. This uses Qt pixel
+  // coordinates (x=0,y=0 is the top/left corner).
+  // If the aspect ratio mismatches, the video will be letterboxed or pillarboxed.
+  // The lower/right pixel border is always excluded.
+  // setVideoRectangle(-1, -1, -1 , -1) will revert to the default and
+  // automatically use the whole window. (Same if the rectangle is 0-sized.)
+  Q_INVOKABLE void setVideoRectangle(int x, int y, int w, int h);
+
+  QRect videoRectangle() { return m_videoRectangle; }
+
   const mpv::qt::Handle getMpvHandle() const { return m_mpv; }
 
   virtual void setWindow(QQuickWindow* window);
@@ -164,6 +174,8 @@ Q_SIGNALS:
   // when position updates
   void positionUpdate(quint64);
 
+  void onVideoRecangleChanged();
+
   void onMpvEvents();
   
 private:
@@ -220,6 +232,7 @@ private:
   QVariantMap m_serverMediaInfo;
   QString m_currentSubtitleStream;
   QString m_currentAudioStream;
+  QRect m_videoRectangle;
 };
 
 #endif // PLAYERCOMPONENT_H

+ 42 - 5
src/player/PlayerQuickItem.cpp

@@ -169,7 +169,7 @@ private:
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 PlayerRenderer::PlayerRenderer(mpv::qt::Handle mpv, QQuickWindow* window)
-: m_mpv(mpv), m_mpvGL(nullptr), m_window(window), m_size(), m_hAvrtHandle(nullptr)
+: m_mpv(mpv), m_mpvGL(nullptr), m_window(window), m_size(), m_hAvrtHandle(nullptr), m_videoRectangle(-1, -1, -1, -1), m_fbo(0)
 {
   m_mpvGL = (mpv_opengl_cb_context *)mpv_get_sub_api(m_mpv, MPV_SUB_API_OPENGL_CB);
 }
@@ -195,6 +195,7 @@ PlayerRenderer::~PlayerRenderer()
   // Keep in mind that the m_mpv handle must be held until this is done.
   if (m_mpvGL)
     mpv_opengl_cb_uninit_gl(m_mpvGL);
+  delete m_fbo;
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -204,19 +205,51 @@ void PlayerRenderer::render()
 
   GLint fbo = 0;
   context->functions()->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
-
-  m_window->resetOpenGLState();
-
   bool flip = true;
 #if HAVE_OPTIMALORIENTATION
   flip = !(context->format().orientationFlags() & QSurfaceFormat::MirrorVertically);
 #endif
+  bool screenFlip = flip;
+  QSize fboSize = m_size;
+  QOpenGLFramebufferObject *blitFbo = 0;
+
+  m_window->resetOpenGLState();
+
+  QRect fullWindow(0, 0, m_size.width(), m_size.height());
+  if (m_videoRectangle.width() > 0 && m_videoRectangle.height() > 0 && m_videoRectangle != fullWindow && QOpenGLFramebufferObject::hasOpenGLFramebufferBlit() && QOpenGLFramebufferObject::hasOpenGLFramebufferObjects())
+  {
+    if (!m_fbo || !m_fbo->isValid() || m_fbo->size() != m_videoRectangle.size())
+    {
+      delete m_fbo;
+      m_fbo = new QOpenGLFramebufferObject(m_videoRectangle.size());
+    }
+    if (m_fbo && m_fbo->isValid())
+    {
+      blitFbo = m_fbo;
+      fboSize = m_fbo->size();
+      fbo = m_fbo->handle();
+      flip = false;
+
+      // Need to clear the background manually, since nothing else knows it has to be done.
+      context->functions()->glClearColor(0, 0, 0, 0);
+      context->functions()->glClear(GL_COLOR_BUFFER_BIT);
+    }
+  }
 
   // The negative height signals to mpv that the video should be flipped
   // (according to the flipped OpenGL coordinate system).
-  mpv_opengl_cb_draw(m_mpvGL, fbo, m_size.width(), (flip ? -1 : 1) * m_size.height());
+  mpv_opengl_cb_draw(m_mpvGL, fbo, fboSize.width(), (flip ? -1 : 1) * fboSize.height());
 
   m_window->resetOpenGLState();
+
+  if (blitFbo)
+  {
+    QRect dstRect = m_videoRectangle;
+    if (screenFlip)
+      dstRect = QRect(dstRect.x(), m_size.height() - dstRect.y(), dstRect.width(), dstRect.top() - dstRect.bottom());
+
+    QOpenGLFramebufferObject::blitFramebuffer(0, dstRect, blitFbo, QRect(QPoint(0, 0), blitFbo->size()));
+  }
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -304,6 +337,7 @@ void PlayerQuickItem::onSynchronize()
     connect(window(), &QQuickWindow::beforeRendering, m_renderer, &PlayerRenderer::render, Qt::DirectConnection);
     connect(window(), &QQuickWindow::frameSwapped, m_renderer, &PlayerRenderer::swap, Qt::DirectConnection);
     connect(&PlayerComponent::Get(), &PlayerComponent::videoPlaybackActive, m_renderer, &PlayerRenderer::onVideoPlaybackActive, Qt::QueuedConnection);
+    connect(&PlayerComponent::Get(), &PlayerComponent::onVideoRecangleChanged, window(), &QQuickWindow::update, Qt::QueuedConnection);
     window()->setPersistentOpenGLContext(true);
     window()->setPersistentSceneGraph(true);
     window()->setClearBeforeRendering(false);
@@ -323,7 +357,10 @@ void PlayerQuickItem::onSynchronize()
     }
   }
   if (m_renderer)
+  {
     m_renderer->m_size = window()->size() * window()->devicePixelRatio();
+    m_renderer->m_videoRectangle = PlayerComponent::Get().videoRectangle();
+  }
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////

+ 3 - 0
src/player/PlayerQuickItem.h

@@ -3,6 +3,7 @@
 
 #include <Qt>
 #include <QtQuick/QQuickItem>
+#include <QOpenGLFramebufferObject>
 
 #include <mpv/client.h>
 #include <mpv/opengl_cb.h>
@@ -37,6 +38,8 @@ private:
   QQuickWindow* m_window;
   QSize m_size;
   HANDLE m_hAvrtHandle;
+  QRect m_videoRectangle;
+  QOpenGLFramebufferObject* m_fbo;
 };
 
 class PlayerQuickItem : public QQuickItem