defer finding dialog parent until we need it
[LibreOffice.git] / avmedia / source / qt6 / QtPlayer.cxx
blob23460c92d67535e4664eef2493595eddad5b24f4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/config.h>
12 #include <QtCore/QUrl>
13 #include <QtMultimedia/QAudioOutput>
14 #include <QtMultimedia/QMediaMetaData>
15 #include <QtMultimediaWidgets/QVideoWidget>
16 #include <QtWidgets/QLabel>
17 #include <QtWidgets/QLayout>
19 #include <cppuhelper/supportsservice.hxx>
20 #include <sal/log.hxx>
21 #include <rtl/string.hxx>
22 #include <tools/link.hxx>
23 #include <vcl/BitmapTools.hxx>
24 #include <vcl/graph.hxx>
25 #include <vcl/qt/QtUtils.hxx>
26 #include <vcl/svapp.hxx>
27 #include <vcl/syschild.hxx>
28 #include <vcl/sysdata.hxx>
29 #include <vcl/timer.hxx>
31 #include <gstwindow.hxx>
32 #include "QtFrameGrabber.hxx"
33 #include "QtPlayer.hxx"
35 #include <QtPlayer.moc>
37 using namespace ::com::sun::star;
39 namespace avmedia::qt
41 QtPlayer::QtPlayer()
42 : QtPlayer_BASE(m_aMutex)
43 , m_lListener(m_aMutex)
44 , m_pMediaWidgetParent(nullptr)
48 bool QtPlayer::create(const OUString& rURL)
50 const QUrl aQUrl(toQString(rURL));
51 if (!aQUrl.isValid() || !aQUrl.isLocalFile())
52 return false;
54 m_xMediaPlayer = std::make_unique<QMediaPlayer>();
55 m_xMediaPlayer->setSource(aQUrl);
56 QAudioOutput* pAudioOutput = new QAudioOutput;
57 pAudioOutput->setVolume(50);
58 m_xMediaPlayer->setAudioOutput(pAudioOutput);
60 return true;
63 void SAL_CALL QtPlayer::start()
65 osl::MutexGuard aGuard(m_aMutex);
67 assert(m_xMediaPlayer);
68 m_xMediaPlayer->play();
71 void SAL_CALL QtPlayer::stop()
73 osl::MutexGuard aGuard(m_aMutex);
75 assert(m_xMediaPlayer);
76 // don't use QMediaPlayer::stop because XPlayer::stop should leave the position unchanged
77 m_xMediaPlayer->pause();
80 sal_Bool SAL_CALL QtPlayer::isPlaying()
82 osl::MutexGuard aGuard(m_aMutex);
84 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
85 assert(m_xMediaPlayer);
86 return m_xMediaPlayer->isPlaying();
87 #else
88 return false;
89 #endif
92 double SAL_CALL QtPlayer::getDuration()
94 osl::MutexGuard aGuard(m_aMutex);
96 assert(m_xMediaPlayer);
97 return m_xMediaPlayer->duration() / 1000.0;
100 void SAL_CALL QtPlayer::setMediaTime(double fTime)
102 osl::MutexGuard aGuard(m_aMutex);
104 assert(m_xMediaPlayer);
105 m_xMediaPlayer->setPosition(fTime * 1000);
108 double SAL_CALL QtPlayer::getMediaTime()
110 osl::MutexGuard aGuard(m_aMutex);
112 assert(m_xMediaPlayer);
113 return m_xMediaPlayer->position() / 1000.0;
116 void SAL_CALL QtPlayer::setPlaybackLoop(sal_Bool bSet)
118 assert(m_xMediaPlayer);
119 const int nLoops = bSet ? QMediaPlayer::Infinite : QMediaPlayer::Once;
120 m_xMediaPlayer->setLoops(nLoops);
123 sal_Bool SAL_CALL QtPlayer::isPlaybackLoop()
125 assert(m_xMediaPlayer);
126 return m_xMediaPlayer->loops() == QMediaPlayer::Infinite;
129 void SAL_CALL QtPlayer::setVolumeDB(sal_Int16 nVolumeDB)
131 osl::MutexGuard aGuard(m_aMutex);
133 // range is -40 for silence to 0 for full volume
134 const sal_Int16 nVolume = std::clamp<sal_Int16>(nVolumeDB, -40, 0);
135 double fValue = (nVolume + 40) / 40.0;
136 assert(m_xMediaPlayer);
137 QAudioOutput* pAudioOutput = m_xMediaPlayer->audioOutput();
138 assert(pAudioOutput);
139 pAudioOutput->setVolume(fValue);
142 sal_Int16 SAL_CALL QtPlayer::getVolumeDB()
144 osl::MutexGuard aGuard(m_aMutex);
146 assert(m_xMediaPlayer);
147 QAudioOutput* pAudioOutput = m_xMediaPlayer->audioOutput();
148 assert(pAudioOutput);
150 double fVolume = pAudioOutput->volume();
151 return (fVolume * 40) - 40;
154 void SAL_CALL QtPlayer::setMute(sal_Bool bSet)
156 osl::MutexGuard aGuard(m_aMutex);
158 assert(m_xMediaPlayer);
159 QAudioOutput* pAudioOutput = m_xMediaPlayer->audioOutput();
160 assert(pAudioOutput);
161 pAudioOutput->setMuted(bSet);
164 sal_Bool SAL_CALL QtPlayer::isMute()
166 osl::MutexGuard aGuard(m_aMutex);
168 assert(m_xMediaPlayer);
169 QAudioOutput* pAudioOutput = m_xMediaPlayer->audioOutput();
170 assert(pAudioOutput);
171 return pAudioOutput->isMuted();
174 awt::Size SAL_CALL QtPlayer::getPreferredPlayerWindowSize()
176 osl::MutexGuard aGuard(m_aMutex);
178 assert(m_xMediaPlayer);
179 const QMediaMetaData aMetaData = m_xMediaPlayer->metaData();
180 const QVariant aResolutionVariant = aMetaData.value(QMediaMetaData::Resolution);
181 if (aResolutionVariant.canConvert<QSize>())
183 const QSize aResolution = aResolutionVariant.value<QSize>();
184 return awt::Size(aResolution.width(), aResolution.height());
187 return awt::Size(0, 0);
190 uno::Reference<::media::XPlayerWindow>
191 SAL_CALL QtPlayer::createPlayerWindow(const uno::Sequence<uno::Any>& rArguments)
193 osl::MutexGuard aGuard(m_aMutex);
195 if (rArguments.getLength() > 1)
196 rArguments[1] >>= m_aPlayerWidgetRect;
198 if (rArguments.getLength() <= 2)
200 uno::Reference<::media::XPlayerWindow> xRet = new ::avmedia::gstreamer::Window;
201 return xRet;
204 sal_IntPtr pIntPtr = 0;
205 rArguments[2] >>= pIntPtr;
206 SystemChildWindow* pParentWindow = reinterpret_cast<SystemChildWindow*>(pIntPtr);
207 if (!pParentWindow)
208 return nullptr;
210 const SystemEnvData* pParentEnvData = pParentWindow->GetSystemData();
211 if (!pParentEnvData)
212 return nullptr;
214 m_pMediaWidgetParent = static_cast<QWidget*>(pParentEnvData->pWidget);
215 assert(m_pMediaWidgetParent);
217 // while media is loading, QMediaPlayer::hasVideo doesn't yet return
218 // whether media actually has video; defer creating audio/video widget
219 if (m_xMediaPlayer->mediaStatus() == QMediaPlayer::LoadingMedia)
221 connect(m_xMediaPlayer.get(), &QMediaPlayer::mediaStatusChanged, this,
222 &QtPlayer::createMediaPlayerWidget, Qt::SingleShotConnection);
224 else
226 createMediaPlayerWidget();
229 uno::Reference<::media::XPlayerWindow> xRet = new ::avmedia::gstreamer::Window;
230 return xRet;
233 uno::Reference<media::XFrameGrabber> SAL_CALL QtPlayer::createFrameGrabber()
235 osl::MutexGuard aGuard(m_aMutex);
237 rtl::Reference<QtFrameGrabber> xFrameGrabber = new QtFrameGrabber(m_xMediaPlayer->source());
238 return xFrameGrabber;
241 void SAL_CALL
242 QtPlayer::addPlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener)
244 m_lListener.addInterface(cppu::UnoType<css::media::XPlayerListener>::get(), rListener);
245 if (isReadyToPlay())
247 css::lang::EventObject aEvent;
248 aEvent.Source = getXWeak();
249 rListener->preferredPlayerWindowSizeAvailable(aEvent);
251 else
253 installNotify();
257 void SAL_CALL
258 QtPlayer::removePlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener)
260 m_lListener.removeInterface(cppu::UnoType<css::media::XPlayerListener>::get(), rListener);
263 OUString SAL_CALL QtPlayer::getImplementationName()
265 return u"com.sun.star.comp.avmedia.Player_Qt"_ustr;
268 sal_Bool SAL_CALL QtPlayer::supportsService(const OUString& rServiceName)
270 return cppu::supportsService(this, rServiceName);
273 uno::Sequence<OUString> SAL_CALL QtPlayer::getSupportedServiceNames()
275 return { u"com.sun.star.media.Player_Qt"_ustr };
278 void SAL_CALL QtPlayer::disposing()
280 osl::MutexGuard aGuard(m_aMutex);
281 stop();
282 QtPlayer_BASE::disposing();
285 QtPlayer::~QtPlayer()
287 // ensure output objects get deleted as QMediaPlayer doesn't take ownership of them
288 std::unique_ptr<QObject> xVideoWidget(m_xMediaPlayer->videoOutput());
289 std::unique_ptr<QAudioOutput> xAudioOutput(m_xMediaPlayer->audioOutput());
290 m_xMediaPlayer.reset();
293 bool QtPlayer::isReadyToPlay()
295 assert(m_xMediaPlayer);
296 QMediaPlayer::MediaStatus eStatus = m_xMediaPlayer->mediaStatus();
297 return eStatus == QMediaPlayer::BufferingMedia || eStatus == QMediaPlayer::BufferedMedia
298 || eStatus == QMediaPlayer::LoadedMedia || eStatus == QMediaPlayer::EndOfMedia;
301 void QtPlayer::installNotify()
303 connect(m_xMediaPlayer.get(), &QMediaPlayer::mediaStatusChanged, this,
304 &QtPlayer::notifyIfReady);
307 void QtPlayer::uninstallNotify()
309 disconnect(m_xMediaPlayer.get(), &QMediaPlayer::mediaStatusChanged, this,
310 &QtPlayer::notifyIfReady);
313 void QtPlayer::notifyIfReady(QMediaPlayer::MediaStatus)
315 if (isReadyToPlay())
317 rtl::Reference<QtPlayer> xThis(this);
318 xThis->notifyListeners();
319 xThis->uninstallNotify();
323 void QtPlayer::createMediaPlayerWidget()
325 assert(m_xMediaPlayer);
326 assert(m_xMediaPlayer->mediaStatus() != QMediaPlayer::LoadingMedia
327 && "Media is still loading, detecting video availability not possible.");
329 assert(m_pMediaWidgetParent && "Parent for media widget not set");
331 // if media contains video, show the video output,
332 // otherwise show an audio icon as a placeholder
333 QWidget* pWidget;
334 if (m_xMediaPlayer->hasVideo())
336 QVideoWidget* pVideoWidget = new QVideoWidget(m_pMediaWidgetParent);
337 pVideoWidget->setAspectRatioMode(Qt::IgnoreAspectRatio);
339 assert(!m_xMediaPlayer->videoOutput() && "Video output already set.");
340 m_xMediaPlayer->setVideoOutput(pVideoWidget);
342 pWidget = pVideoWidget;
344 else
346 QPixmap aAudioPixmap = loadQPixmapIcon(u"avmedia/res/avaudiologo.png"_ustr);
347 aAudioPixmap
348 = aAudioPixmap.scaled(QSize(m_aPlayerWidgetRect.Width, m_aPlayerWidgetRect.Height));
350 QLabel* pLabel = new QLabel;
351 pLabel->setPixmap(aAudioPixmap);
352 pWidget = pLabel;
355 assert(pWidget);
356 pWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
358 // retrieve the layout (which is set in the QtObjectWidget ctor)
359 QLayout* pLayout = m_pMediaWidgetParent->layout();
360 assert(pLayout);
361 assert(pLayout->count() == 0 && "Layout already has a widget set");
362 pLayout->addWidget(pWidget);
365 void QtPlayer::notifyListeners()
367 comphelper::OInterfaceContainerHelper2* pContainer
368 = m_lListener.getContainer(cppu::UnoType<css::media::XPlayerListener>::get());
369 if (!pContainer)
370 return;
372 css::lang::EventObject aEvent;
373 aEvent.Source = getXWeak();
375 comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
376 while (pIterator.hasMoreElements())
378 css::uno::Reference<css::media::XPlayerListener> xListener(
379 static_cast<css::media::XPlayerListener*>(pIterator.next()));
380 xListener->preferredPlayerWindowSizeAvailable(aEvent);
384 } // namespace
386 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */