1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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/.
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
;
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())
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
);
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();
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
;
204 sal_IntPtr pIntPtr
= 0;
205 rArguments
[2] >>= pIntPtr
;
206 SystemChildWindow
* pParentWindow
= reinterpret_cast<SystemChildWindow
*>(pIntPtr
);
210 const SystemEnvData
* pParentEnvData
= pParentWindow
->GetSystemData();
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
);
226 createMediaPlayerWidget();
229 uno::Reference
<::media::XPlayerWindow
> xRet
= new ::avmedia::gstreamer::Window
;
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
;
242 QtPlayer::addPlayerListener(const css::uno::Reference
<css::media::XPlayerListener
>& rListener
)
244 m_lListener
.addInterface(cppu::UnoType
<css::media::XPlayerListener
>::get(), rListener
);
247 css::lang::EventObject aEvent
;
248 aEvent
.Source
= getXWeak();
249 rListener
->preferredPlayerWindowSizeAvailable(aEvent
);
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
);
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
)
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
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
;
346 QPixmap aAudioPixmap
= loadQPixmapIcon(u
"avmedia/res/avaudiologo.png"_ustr
);
348 = aAudioPixmap
.scaled(QSize(m_aPlayerWidgetRect
.Width
, m_aPlayerWidgetRect
.Height
));
350 QLabel
* pLabel
= new QLabel
;
351 pLabel
->setPixmap(aAudioPixmap
);
356 pWidget
->setSizePolicy(QSizePolicy::Expanding
, QSizePolicy::Expanding
);
358 // retrieve the layout (which is set in the QtObjectWidget ctor)
359 QLayout
* pLayout
= m_pMediaWidgetParent
->layout();
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());
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
);
386 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */