Unsolved Crash when deleting QMediaPlayer
-
I have a custom
QAbstractVideoSurface
attached to usingQMediaPlayer
usingQMediaPlayer::setVideoOutput
. I setup the media player like this:m_MediaPlayer = new QMediaPlayer(this, QMediaPlayer::VideoSurface); m_MediaPlayer->setMedia(url); m_MediaPlayer->setVideoOutput(m_VideoSurface);
When my class containing these objects is deleted, the video surface gets deleted with it (since the video surface is a child QObject). This causes a crash in
dsengined.dll!EVRCustomPresenter::flush() Line 1146 at c:\users\qt\work\qt\qtmultimedia\src\plugins\common\evr\evrcustompresenter.cpp(1146)
if (m_renderState == RenderStopped && m_surface->isActive()) {
since
m_surface
is junk at that point.To work around this, I have to unparent the video surface from my parent object and then in the destructor do a
m_videoSurface->deleteLater()
Feels like the dsengined.dll!EVRCustomPresenter::flush() should check to make sure
m_surface
is not null before dereferencing it. -
Hi and welcome to devnet,
How are you building that object ?
What version of Qt are you using ?
On what version of Windows ? -
Hi and welcome to devnet,
Thanks :)
How are you building that object ?
class VideoFileVideoSource : public AbstractVideoSource { friend class VideoFileVideoSurface; Q_OBJECT public: VideoFileVideoSource(const QUrl &url, QObject *parent); ~VideoFileVideoSource(); // Overrides virtual bool CanEditSettings() const; virtual void EditSettings(QWidget *parent); signals: void FrameAvailable(const QVideoFrame &frame); private slots: void MediaPlayerError(QMediaPlayer::Error error); void MediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus status); void MediaPlayerStateChanged(QMediaPlayer::State newState); private: // AbstractVideoSource overrides void DoStart(); void DoStop(); // Called by VideoFileVideoSurface when a new video frame is available void VideoSurfaceFrameAvailable(const QVideoFrame &frame); void LoadSettings(); void SaveSettings(); QMediaPlayer *m_MediaPlayer; VideoFileVideoSurface *m_VideoSurface; }; class VideoFileVideoSurface : public QAbstractVideoSurface { VideoFileVideoSource *m_Parent; public: VideoFileVideoSurface(VideoFileVideoSource *parent) : QAbstractVideoSurface(nullptr) // Don't pass parent in here since we want to control when things get deleted , m_Parent(parent) { } QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const { // Return the formats you will support Q_UNUSED(handleType); return AppUtils::GetSupportedPixelFormats(); } bool present(const QVideoFrame &frame) { // Handle the frame and do your processing Q_UNUSED(frame); // qDebug() << "VideoSurface::present pixelFormat: " << frame.pixelFormat(); m_Parent->VideoSurfaceFrameAvailable(frame); return true; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// VideoFileVideoSource::VideoFileVideoSource(const QUrl &url, QObject *parent) : AbstractVideoSource(parent) , m_MediaPlayer(nullptr) , m_VideoSurface(nullptr) { m_VideoSurface = new VideoFileVideoSurface(this); m_MediaPlayer = new QMediaPlayer(this, QMediaPlayer::VideoSurface); m_MediaPlayer->setMedia(url); m_MediaPlayer->setVideoOutput(m_VideoSurface); // This will set the playback rate directly onto the media player object LoadSettings(); connect(m_MediaPlayer, SIGNAL(error(QMediaPlayer::Error)), SLOT(MediaPlayerError(QMediaPlayer::Error))); connect(m_MediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), SLOT(MediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus))); connect(m_MediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), SLOT(MediaPlayerStateChanged(QMediaPlayer::State))); } VideoFileVideoSource::~VideoFileVideoSource() { // Can't delete it now since there is a crash deep in Qt's directshow relatd code that assumes // that the surface still exists (only on windows). m_VideoSurface->deleteLater(); m_VideoSurface = nullptr; } bool VideoFileVideoSource::CanEditSettings() const { return m_MediaPlayer->playbackRate() > 0.0; } void VideoFileVideoSource::EditSettings(QWidget *parent) { double playbackRate = m_MediaPlayer->playbackRate(); VideoFilesSettingsDialog dlg(playbackRate, parent); int result = dlg.exec(); if (result == QDialog::Accepted) { double playbackRate = dlg.GetPlaybackRate(); m_MediaPlayer->setPlaybackRate(playbackRate); SaveSettings(); } } void VideoFileVideoSource::MediaPlayerError(QMediaPlayer::Error error) { qCritical() << "MediaPlayerError: " << error; } void VideoFileVideoSource::MediaPlayerMediaStatusChanged(QMediaPlayer::MediaStatus status) { qDebug() << "MediaPlayerMediaStatusChanged: " << status; } void VideoFileVideoSource::MediaPlayerStateChanged(QMediaPlayer::State newState) { qDebug() << "MediaPlayerStateChanged: " << newState; // Loop if (newState == QMediaPlayer::StoppedState) { m_MediaPlayer->setPosition(0); m_MediaPlayer->play(); } } void VideoFileVideoSource::DoStart() { m_MediaPlayer->play(); } void VideoFileVideoSource::DoStop() { m_MediaPlayer->pause(); } void VideoFileVideoSource::VideoSurfaceFrameAvailable(const QVideoFrame &frame) { // So, present on the QAbstractVideoSurface gets called even when we call pause and stop // so let's only forward the frame when the players state is actually playing if (m_MediaPlayer->state() == QMediaPlayer::PlayingState) { emit FrameAvailable(frame); } } void VideoFileVideoSource::LoadSettings() { QSettings settings(AppUtils::GetSettingsPath(), QSettings::IniFormat); settings.beginGroup("VideoFileVideoSource"); double playbackRate = settings.value("PlaybackRate", m_MediaPlayer->playbackRate()).toDouble(); if (playbackRate > 0.001) { m_MediaPlayer->setPlaybackRate(playbackRate); } settings.endGroup(); } void VideoFileVideoSource::SaveSettings() { QSettings settings(AppUtils::GetSettingsPath(), QSettings::IniFormat); settings.beginGroup("VideoFileVideoSource"); settings.setValue("PlaybackRate", m_MediaPlayer->playbackRate()); settings.endGroup(); }
What version of Qt are you using ?
5.13.1
On what version of Windows ?
Windows 10 Pro Version: 1903
-
Since you seem to be properly parenting your object, why are you trying to delete them in the destructor ?
-
@sgaist This is the code with the workaround. The original code didn't do anything in the destructor and was relying on the QObject child lifecycle management for the object to be destroyed.
The original code (which I guess I should have posted) passed in the
VideoFileVideoSource
as the parent to theQAbstractVideoSurface
constructor:VideoFileVideoSurface(VideoFileVideoSource *parent) : QAbstractVideoSurface(parent) // Code above passes in nullptr
And the destructor of VideoFileVideoSource was just this:
VideoFileVideoSource::~VideoFileVideoSource() { }
One tick after that destructor executed was the crash, as the surface was automatically deleted by QObject (but the direct show code, which was running on some system callback or something, still assumed that the surface was alive). If I set the video surface to nullptr in the destructor
m_MediaPlayer->setVideoOutput((QAbstractVideoSurface *)nullptr);
, the crash would be immediate (i.e. right in the destructor call, as all over the directshow code, the surface is assumed to be non-null and still alive. -
Do you have the full stack trace ?
-
@sgaist ![Here's the Visual Studio call stack]( image url)
That's when I pass theVideoFileVideoSource
parent to theQAbstractVideoSurface
constructor and empty the constructor, as in the last code snippet I sent.The line numbers all correspond to source line numbers of Qt 5.13.1
-
Do you get the same crash with Qt 5.12 ?
-
@sgaist I haven't tried any other versions of Qt. First time working with Qt in about 8 years and this is a brand new project
-
Then I recommend testing the latest Qt 5.12 to see if you found a new issue or if there's something specific to your machine.