tdf#145735 qt avmedia: Don't deadlock with QGstreamerMediaPlayer

While opending a slide with a video worked fine for
me with the qt6 VCL plugin and a local Qt development
build on Debian testing (qtbase as of
8915ae3a75c4a356d94962dd9b31e1458f2a506f,
qtwayland as of deae8b9ce9f551b29ef98d0bb827a8543af2797e,
qtmultimedia as of 235ba5f273fbb7dfed8ba3736e4444a85aee5770),
this resulted in a freeze when using Debian's system-provided
Qt packages instead (libqt6multimedia6:amd64 6.4.2-11+b2).

While the self-compiled Qt dev is using `QFFmpegMediaPlayer`
which asynchronously loads media, the system QtMultimedia
is using `QGstreamerMediaPlayer` (s. frame 37 in below backtrace)
that apparently doesn't use multiple threads.

Therefore, using `Qt::BlockingQueuedConnection` is problematic,
as its documentation [1] says:

> Same as Qt::QueuedConnection, except that the signalling thread blocks
> until the slot returns. This connection must not be used if the receiver
> lives in the signalling thread, or else the application will deadlock.

Use `Qt::AutoConnection` (= 0, the default) instead and specify the
`Qt::SingleShotConnection` flag in addition to ensure the slot
gets called only once:

> This is a flag that can be combined with any one of the above connection
> types, using a bitwise OR. When Qt::SingleShotConnection is set, the
> slot is going to be called only once; the connection will be
> automatically broken when the signal is emitted. This flag was
> introduced in Qt 6.0.

Drop the now no longer needed manual disconnect.

This makes the scenario work with both, the custom-compiled
Qt dev using `QFFmpegMediaPlayer` and the system-provided
Qt 6.4.2 using `QGstreamerMediaPlayer`.

Side note: Unrelated to the issue addressed here, using the
system-provided Qt with `QGstreamerMediaPlayer` results
in a crash when started via the soffice shell script wrapper
with a LibreOffice debug build or when using soffice.bin directly
and manually setting `MALLOC_PERTURB_=153`, which indicates
some memory issue. That could be within Qt, though, haven't
analyzed that further.

Backtrace of deadlock:

    1 syscall syscall.S 38 0x7f40a83249f9
    2 QSemaphore::acquire(int) 0x7f40948714e2
    3 ?? 0x7f409477fede
    4 QVideoSink::videoFrameChanged(QVideoFrame const&) const 0x7f4095195376
    5 ?? 0x7f405c219f5c
    6 ?? 0x7f405c21a25b
    7 QApplicationPrivate::notify_helper(QObject *, QEvent *) 0x7f4093782d62
    8 QCoreApplication::notifyInternal2(QObject *, QEvent *) 0x7f40947356d8
    9 QCoreApplicationPrivate::sendPostedEvents(QObject *, int, QThreadData *) 0x7f40947358b7
    10 ?? 0x7f4094925257
    11 ?? 0x7f409ab0de3f
    12 ?? 0x7f409ab0fec7
    13 g_main_context_iteration 0x7f409ab104e0
    14 QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) 0x7f4094922f60
    15 QtInstance::ImplYield QtInstance.cxx 455 0x7f4094e534e0
    16 QtInstance::DoYield QtInstance.cxx 464 0x7f4094e568a5
    17 ImplYield svapp.cxx 384 0x7f409f3c48cc
    18 Scheduler::ProcessEventsToIdle svapp.cxx 419 0x7f409f3c4bc8
    19 avmedia::qt::QtFrameGrabber::grabFrame QtFrameGrabber.cxx 106 0x7f405cac5524
    20 non-virtual thunk to avmedia::qt::QtFrameGrabber::grabFrame(double) 0x7f405cac564e
    21 avmedia::MediaWindow::grabFrame mediawindow.cxx 385 0x7f40a03e41ad
    22 SdrMediaObj::getSnapshot() const::$_0::operator()(com::sun::uno::Reference<com::sun::media::XPlayer> const&) const svdomedia.cxx 195 0x7f40a249e5b5
    23 std::__invoke_impl<void, SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&>(std::__invoke_other, SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&) invoke.h 61 0x7f40a249e52d
    24 std::__invoke_r<void, SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&>(SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&) invoke.h 111 0x7f40a249e4dd
    25 std::_Function_handler<void (com::sun::uno::Reference<com::sun::media::XPlayer> const&), SdrMediaObj::getSnapshot() const::$_0>::_M_invoke(std::_Any_data const&, com::sun::uno::Reference<com::sun::media::XPlayer> const&) std_function.h 290 0x7f40a249e345
    26 std::function<void (com::sun::uno::Reference<com::sun::media::XPlayer> const&)>::operator()(com::sun::uno::Reference<com::sun::media::XPlayer> const&) const std_function.h 591 0x7f40a03e96cd
    27 avmedia::PlayerListener::callPlayerWindowSizeAvailable mediawindow.hxx 76 0x7f40a03e6bf1
    28 avmedia::PlayerListener::preferredPlayerWindowSizeAvailable mediawindow.cxx 496 0x7f40a03e5055
    29 avmedia::qt::QtPlayer::notifyListeners QtPlayer.cxx 395 0x7f405cacfd53
    30 avmedia::qt::QtPlayer::notifyIfReady QtPlayer.cxx 326 0x7f405cacfb42
    31 QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QMediaPlayer::MediaStatus>, void, void (avmedia::qt::QtPlayer:: *)(QMediaPlayer::MediaStatus)>::call qobjectdefs_impl.h 135 0x7f405cad477b
    32 QtPrivate::FunctionPointer<void (avmedia::qt::QtPlayer:: *)(QMediaPlayer::MediaStatus)>::call<QtPrivate::List<QMediaPlayer::MediaStatus>, void> qobjectdefs_impl.h 172 0x7f405cad46cd
    33 QtPrivate::QSlotObject<void (avmedia::qt::QtPlayer:: *)(QMediaPlayer::MediaStatus), QtPrivate::List<QMediaPlayer::MediaStatus>, void>::impl qobjectdefs_impl.h 383 0x7f405cad4612
    34 ?? 0x7f409477fbbe
    35 QMediaPlayer::mediaStatusChanged(QMediaPlayer::MediaStatus) 0x7f4095184a05
    36 ?? 0x7f405c210740
    37 non-virtual thunk to QGstreamerMediaPlayer::processBusMessage(QGstreamerMessage const&) 0x7f405c2009f7
    38 ?? 0x7f405c21aa94
    39 QObject::event(QEvent *) 0x7f40947723e0
    40 QApplicationPrivate::notify_helper(QObject *, QEvent *) 0x7f4093782d62
    41 QCoreApplication::notifyInternal2(QObject *, QEvent *) 0x7f40947356d8
    42 QCoreApplicationPrivate::sendPostedEvents(QObject *, int, QThreadData *) 0x7f40947358b7
    43 ?? 0x7f4094925257
    44 ?? 0x7f409ab0de3f
    45 ?? 0x7f409ab0fec7
    46 g_main_context_iteration 0x7f409ab104e0
    47 QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) 0x7f4094922f60
    48 QtInstance::ImplYield QtInstance.cxx 455 0x7f4094e534e0
    49 QtInstance::DoYield QtInstance.cxx 464 0x7f4094e568a5
    50 ImplYield svapp.cxx 384 0x7f409f3c48cc
    51 Scheduler::ProcessEventsToIdle svapp.cxx 419 0x7f409f3c4bc8
    52 avmedia::qt::QtFrameGrabber::grabFrame QtFrameGrabber.cxx 106 0x7f405cac5524
    53 non-virtual thunk to avmedia::qt::QtFrameGrabber::grabFrame(double) 0x7f405cac564e
    54 avmedia::MediaWindow::grabFrame mediawindow.cxx 385 0x7f40a03e41ad
    55 SdrMediaObj::getSnapshot() const::$_0::operator()(com::sun::uno::Reference<com::sun::media::XPlayer> const&) const svdomedia.cxx 195 0x7f40a249e5b5
    56 std::__invoke_impl<void, SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&>(std::__invoke_other, SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&) invoke.h 61 0x7f40a249e52d
    57 std::__invoke_r<void, SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&>(SdrMediaObj::getSnapshot() const::$_0&, com::sun::uno::Reference<com::sun::media::XPlayer> const&) invoke.h 111 0x7f40a249e4dd
    58 std::_Function_handler<void (com::sun::uno::Reference<com::sun::media::XPlayer> const&), SdrMediaObj::getSnapshot() const::$_0>::_M_invoke(std::_Any_data const&, com::sun::uno::Reference<com::sun::media::XPlayer> const&) std_function.h 290 0x7f40a249e345
    59 std::function<void (com::sun::uno::Reference<com::sun::media::XPlayer> const&)>::operator()(com::sun::uno::Reference<com::sun::media::XPlayer> const&) const std_function.h 591 0x7f40a03e96cd
    60 avmedia::PlayerListener::callPlayerWindowSizeAvailable mediawindow.hxx 76 0x7f40a03e6bf1
    61 avmedia::PlayerListener::preferredPlayerWindowSizeAvailable mediawindow.cxx 496 0x7f40a03e5055
    62 avmedia::qt::QtPlayer::notifyListeners QtPlayer.cxx 395 0x7f405cacfd53
    63 avmedia::qt::QtPlayer::notifyIfReady QtPlayer.cxx 326 0x7f405cacfb42
    64 QtPrivate::FunctorCall<QtPrivate::IndexesList<0>, QtPrivate::List<QMediaPlayer::MediaStatus>, void, void (avmedia::qt::QtPlayer:: *)(QMediaPlayer::MediaStatus)>::call qobjectdefs_impl.h 135 0x7f405cad477b
    65 QtPrivate::FunctionPointer<void (avmedia::qt::QtPlayer:: *)(QMediaPlayer::MediaStatus)>::call<QtPrivate::List<QMediaPlayer::MediaStatus>, void> qobjectdefs_impl.h 172 0x7f405cad46cd
    66 QtPrivate::QSlotObject<void (avmedia::qt::QtPlayer:: *)(QMediaPlayer::MediaStatus), QtPrivate::List<QMediaPlayer::MediaStatus>, void>::impl qobjectdefs_impl.h 383 0x7f405cad4612

[1] https://doc.qt.io/qt-6/qt.html#ConnectionType-enum

Change-Id: Ia8bfd19b0c0c4f970a5eb200c2a0b45784ef25fd
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/169036
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
This commit is contained in:
Michael Weghorn 2024-06-17 17:37:49 +02:00
parent 12c4b7ee91
commit 4df2a30c57

View file

@ -55,7 +55,7 @@ QtFrameGrabber::QtFrameGrabber(const QUrl& rSourceUrl)
m_xMediaPlayer->setVideoSink(m_xVideoSink.get());
connect(m_xMediaPlayer.get(), &QMediaPlayer::errorOccurred, this,
&QtFrameGrabber::onErrorOccured, Qt::BlockingQueuedConnection);
&QtFrameGrabber::onErrorOccured, Qt::SingleShotConnection);
}
void QtFrameGrabber::onErrorOccured(QMediaPlayer::Error eError, const QString& rErrorString)
@ -72,9 +72,6 @@ void QtFrameGrabber::onVideoFrameChanged(const QVideoFrame& rFrame)
{
std::lock_guard aLock(m_aMutex);
disconnect(m_xVideoSink.get(), &QVideoSink::videoFrameChanged, this,
&QtFrameGrabber::onVideoFrameChanged);
const QImage aImage = rFrame.toImage();
m_xGraphic = toXGraphic(aImage);
m_bWaitingForFrame = false;
@ -90,7 +87,7 @@ css::uno::Reference<css::graphic::XGraphic> SAL_CALL QtFrameGrabber::grabFrame(d
// until the first frame has been received
m_bWaitingForFrame = true;
connect(m_xVideoSink.get(), &QVideoSink::videoFrameChanged, this,
&QtFrameGrabber::onVideoFrameChanged, Qt::BlockingQueuedConnection);
&QtFrameGrabber::onVideoFrameChanged, Qt::SingleShotConnection);
m_xMediaPlayer->play();
while (m_bWaitingForFrame)
{