Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  videosurface.cpp   Sprache: C

 

/*****************************************************************************
 * Copyright (C) 2019 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#include "videosurface.hpp"

#include <QSGRectangleNode>
#include <QThreadPool>
#include <vlc_window.h>

#include <QQuickRenderControl>
#ifdef QT_DECLARATIVE_PRIVATE
#  include <QtGui/qpa/qplatformwindow.h>
#endif

#include "maininterface/mainctx.hpp"

VideoSurfaceProvider::VideoSurfaceProvider(bool threadedSurfaceUpdates, QObject* parent)
    : QObject(parent)
    , m_threadedSurfaceUpdates(threadedSurfaceUpdates)
{
}

bool VideoSurfaceProvider::isEnabled()
{
    return m_voutWindow != nullptr;
}

bool VideoSurfaceProvider::hasVideoEmbed() const
{
    return m_videoEmbed;
}

void VideoSurfaceProvider::enable(vlc_window_t* voutWindow)
{
    assert(voutWindow);
    m_voutWindow = voutWindow;
    emit videoEnabledChanged(true);
}

void VideoSurfaceProvider::disable()
{
    setVideoEmbed(false);
    m_voutWindow = nullptr;
    emit videoEnabledChanged(false);
}

void VideoSurfaceProvider::setVideoEmbed(bool embed)
{
    m_videoEmbed = embed;
    emit hasVideoEmbedChanged(embed);
}

void VideoSurfaceProvider::onWindowClosed()
{
    if (m_voutWindow)
        vlc_window_ReportClose(m_voutWindow);
}

void VideoSurfaceProvider::onMousePressed(int vlcButton)
{
    if (m_voutWindow)
        vlc_window_ReportMousePressed(m_voutWindow, vlcButton);
}

void VideoSurfaceProvider::onMouseReleased(int vlcButton)
{
    if (m_voutWindow)
        vlc_window_ReportMouseReleased(m_voutWindow, vlcButton);
}

void VideoSurfaceProvider::onMouseDoubleClick(int vlcButton)
{
    if (m_voutWindow)
        vlc_window_ReportMouseDoubleClick(m_voutWindow, vlcButton);
}

void VideoSurfaceProvider::onMouseMoved(float x, float y)
{
    if (m_voutWindow)
        vlc_window_ReportMouseMoved(m_voutWindow, x, y);
}

void VideoSurfaceProvider::onMouseWheeled(int vlcButton)
{
    if (m_voutWindow)
        vlc_window_ReportKeyPress(m_voutWindow, vlcButton);
}

void VideoSurfaceProvider::onKeyPressed(int key, Qt::KeyboardModifiers modifiers)
{
    QKeyEvent event(QEvent::KeyPress, key, modifiers);
    int vlckey = qtEventToVLCKey(&event);
    if (m_voutWindow)
        vlc_window_ReportKeyPress(m_voutWindow, vlckey);
}

void VideoSurfaceProvider::onSurfacePropertiesChanged(const std::optional<QSizeF>&&nbsp;size,
                                                      const std::optional<QPointF>& position,
                                                      const std::optional<qreal>& scale)
{
    emit surfacePropertiesChanged(size, position, scale);
    if (m_voutWindow && size)
        vlc_window_ReportSize(m_voutWindow, std::ceil(size->width()), std::ceil(size->height()));
}


VideoSurface::VideoSurface(QQuickItem* parent)
    : ViewBlockingRectangle(parent)
{
    setAcceptHoverEvents(true);
    setAcceptedMouseButtons(Qt::AllButtons);
    setFlag(ItemAcceptsInputMethod, true);

    {
        // This is for initialization, they are adjusted in `::updatePaintNode()`:
        setUpdateRenderPosition(true);
        setFlag(ItemHasContents, false); // `setUpdateRenderPosition(true)` sets `ItemHasContents`
    }
}

int VideoSurface::qtMouseButton2VLC( Qt::MouseButton qtButton )
{
    switch( qtButton )
    {
        case Qt::LeftButton:
            return 0;
        case Qt::RightButton:
            return 2;
        case Qt::MiddleButton:
            return 1;
        default:
            return -1;
    }
}

void VideoSurface::mousePressEvent(QMouseEvent* event)
{
    int vlc_button = qtMouseButton2VLC( event->button() );
    if( vlc_button >= 0 )
    {
        emit mousePressed(vlc_button);
        event->accept();
    }
    else
        event->ignore();
}

void VideoSurface::mouseReleaseEvent(QMouseEvent* event)
{
    int vlc_button = qtMouseButton2VLC( event->button() );
    if( vlc_button >= 0 )
    {
        emit mouseReleased(vlc_button);
        event->accept();
    }
    else
        event->ignore();
}

void VideoSurface::mouseMoveEvent(QMouseEvent* event)
{
    QPointF current_pos = event->position();
    QQuickWindow* window = this->window();
    if (!window)
        return;
    qreal dpr = window->effectiveDevicePixelRatio();
    emit mouseMoved(current_pos.x() * dpr, current_pos.y() * dpr);
    event->accept();
}

void VideoSurface::hoverMoveEvent(QHoverEvent* event)
{
    QPointF current_pos = event->position();
    if (current_pos != m_oldHoverPos)
    {
        QQuickWindow* window = this->window();
        if (!window)
            return;
        qreal dpr = window->effectiveDevicePixelRatio();
        emit mouseMoved(current_pos.x() * dpr, current_pos.y()  * dpr);
        m_oldHoverPos = current_pos;
    }
    event->accept();
}

void VideoSurface::mouseDoubleClickEvent(QMouseEvent* event)
{
    int vlc_button = qtMouseButton2VLC( event->button() );
    if( vlc_button >= 0 )
    {
        emit mouseDblClicked(vlc_button);
        event->accept();
    }
    else
        event->ignore();
}

void VideoSurface::keyPressEvent(QKeyEvent* event)
{
    emit keyPressed(event->key(), event->modifiers());
    event->ignore();
}

#if QT_CONFIG(wheelevent)
void VideoSurface::wheelEvent(QWheelEvent *event)
{
    m_wheelEventConverter.wheelEvent(event);
    event->accept();
}
#endif

Qt::CursorShape VideoSurface::getCursorShape() const
{
    return cursor().shape();
}

void VideoSurface::setCursorShape(Qt::CursorShape shape)
{
    setCursor(shape);
}

void VideoSurface::synchronize()
{
    // This may be called from the rendering thread, not necessarily
    // during synchronization (GUI thread is not blocked). Try to
    // be very careful.

    QSizeF size;
    QPointF position;

    static const bool isObjectThread = QThread::currentThread() == thread();
    if (isObjectThread)
    {
        assert(QThread::currentThread() == thread());
        // Item's thread (GUI thread):
        size = this->size();
        position = this->mapToScene(QPointF(0,0));
    }
    else
    {
        assert(QThread::currentThread() != thread());
        // Render thread:
        size = renderSize();
        position = renderPosition();
    }

    std::optional<QSizeF> newSize;
    std::optional<QPointF> newPosition;
    std::optional<qreal> newScale;

    if (m_allDirty || m_dprDirty || m_oldRenderSize != size)
    {
        if (!size.isEmpty())
        {
            newSize = size * m_dpr;
            m_oldRenderSize = size;
        }
    }

    if (m_allDirty || m_dprDirty || m_oldRenderPosition != position)
    {
        if (position.x() >= 0.0 && position.y() >= 0.0)
        {
            newPosition = position * m_dpr; // render position is relative to scene/viewport
            m_oldRenderPosition = position;
        }
    }

    if (m_allDirty || m_dprDirty)
    {
        newScale = m_dpr;
        m_dprDirty = false;
    }

    if (newPosition || newSize || newScale)
        emit surfacePropertiesChanged(newSize, newPosition, newScale);

    m_allDirty = false;
}

void VideoSurface::itemChange(ItemChange change, const ItemChangeData &value)
{
    switch (change)
    {
        case ItemSceneChange:
        {
            // It is intentional that window connection is made in `::updatePaintNode()`, and not here, because we don't
            // want to explicitly connect whenever `ItemHasContents`, `isVisible()`, `window()` are all satisfied, which
            // is implicitly the case with `::updatePaintNode()` (it is only called when all these are satisfied). This
            // is strictly for maintenance reasons.

            disconnect(m_synchConnection);

            // if window changed but is valid, we can signal dpr change just to be sure, It is not clear if Qt signals
            // ItemDevicePixelRatioHasChanged when item's window/scene changes to a new window that has different DPR:
            if (value.window)
                [[fallthrough]];
            else
                break;
        }
        case ItemDevicePixelRatioHasChanged:
        {
            m_dprChanged = true;
            // Request update, so that `updatePaintNode()` gets called which updates the DPR for `::synchronize()`:
            if (flags().testFlag(ItemHasContents)) // "Only items which specify QQuickItem::ItemHasContents are allowed to call QQuickItem::update()."
                update();
            break;
        }
        case ItemVisibleHasChanged:
        {
            if (!value.boolValue)
            {
                // Connection is made in `::updatePaintNode()` (which is called when both the item is visible and `ItemHasContents` is set).
                disconnect(m_synchConnection);
            }
            break;
        }
        default: break;
    }

    QQuickItem::itemChange(change, value);
}

void VideoSurface::setVideoSurfaceProvider(VideoSurfaceProvider *newVideoSurfaceProvider)
{
    if (m_provider == newVideoSurfaceProvider)
        return;

    if (m_provider)
    {
        disconnect(this, nullptr, m_provider, nullptr);
        disconnect(&m_wheelEventConverter, nullptr, m_provider, nullptr);
        disconnect(m_provider, nullptr, this, nullptr);

        assert(m_provider->videoSurface() == this);
        m_provider->setVideoSurface({});
    }

    m_provider = newVideoSurfaceProvider;

    if (m_provider)
    {
        if (const auto current = m_provider->videoSurface())
            current->setVideoSurfaceProvider(nullptr); // it is probably not a good idea to break the QML binding here

        connect(this, &VideoSurface::mouseMoved, m_provider, &VideoSurfaceProvider::onMouseMoved);
        connect(this, &VideoSurface::mousePressed, m_provider, &VideoSurfaceProvider::onMousePressed);
        connect(this, &VideoSurface::mouseDblClicked, m_provider, &VideoSurfaceProvider::onMouseDoubleClick);
        connect(this, &VideoSurface::mouseReleased, m_provider, &VideoSurfaceProvider::onMouseReleased);
        connect(this, &VideoSurface::keyPressed, m_provider, &VideoSurfaceProvider::onKeyPressed);
        connect(this, &VideoSurface::surfacePropertiesChanged, m_provider, &VideoSurfaceProvider::onSurfacePropertiesChanged, Qt::DirectConnection);

        // With auto connection, this should be queued if the signal was emitted from vout thread,
        // so that the slot is executed in item's thread:
        connect(m_provider, &VideoSurfaceProvider::videoEnabledChanged, this, [this](bool enabled) {
            if (enabled)
            {
                m_videoEnabledChanged = true;
                if (flags().testFlag(ItemHasContents)) // "Only items which specify QQuickItem::ItemHasContents are allowed to call QQuickItem::update()."
                    update();
            }
        });

        connect(&m_wheelEventConverter, &WheelToVLCConverter::vlcWheelKey, m_provider, &VideoSurfaceProvider::onMouseWheeled);

        assert(!m_provider->videoSurface());
        m_provider->setVideoSurface(this);

        setFlag(ItemHasContents, true);
        update(); // this should not be necessary right after setting `ItemHasContents`, but just in case
    }
    else
    {
        setFlag(ItemHasContents, false);
        // Connection is made in `::updatePaintNode()` (which is called when both the item is visible and `ItemHasContents` is set).
        disconnect(m_synchConnection);
    }

    emit videoSurfaceProviderChanged();
}

QSGNode *VideoSurface::updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
{
    // This is called from the render thread, but during synchronization.
    // So the GUI thread is blocked here. This makes it safer to access the window
    // to get the effective DPR, rather than doing it outside the synchronization
    // stage.

    const auto w = window();
    assert (w);

    if (Q_UNLIKELY(!m_provider))
        return node;

    // WARNING: For some reason `::updatePaintNode()` is called initially when the item is invisible.
    if (!m_synchConnection && isVisible())
    {
        // Disconnection is made in `::itemChange()`'s `ItemSceneChange` handler.

        // This is constant:
        if (m_provider->supportsThreadedSurfaceUpdates())
        {
            // Synchronize just before swapping the frame for better synchronization:
            m_synchConnection = connect(w, &QQuickWindow::afterRendering, this, &VideoSurface::synchronize, Qt::DirectConnection);
        }
        else
        {
            m_synchConnection = connect(w, &QQuickWindow::afterAnimating, this, &VideoSurface::synchronize);
        }
    }

    if (m_dprChanged)
    {
        m_dpr = w->effectiveDevicePixelRatio();
        m_dprDirty = true;
        m_dprChanged = false;
    }

    if (m_videoEnabledChanged)
    {
        m_allDirty = true;
        m_videoEnabledChanged = false;
    }

    if (QThread::currentThread() == thread())
    {
        // As per `::synchronize()` we do not need `ViewBlockingRectangle` to provide the render position in this case:
        setUpdateRenderPosition(false);
        // Override `ViewBlockingRectangle`'s `ItemHasContents`, if it is unset (when both `renderEnabled` and
        // `updateRenderPosition()` are false):
        setFlag(ItemHasContents, true);
    }
    else
    {
        setUpdateRenderPosition(true);
    }

    return ViewBlockingRectangle::updatePaintNode(node, data);
}

Messung
CHZG
831009592

¤ Dauer der Verarbeitung: 0.3 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104857
#Domains=418045