cid#1607171 Data race condition
[LibreOffice.git] / avmedia / source / viewer / mediawindow_impl.cxx
blob823152e3b029908ddd53d2f4d95f44bc7b2e88a1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <iostream>
21 #include "mediawindow_impl.hxx"
22 #include "mediaevent_impl.hxx"
23 #include <mediamisc.hxx>
24 #include <bitmaps.hlst>
25 #include <helpids.h>
27 #include <algorithm>
28 #include <string_view>
30 #include <sal/log.hxx>
31 #include <comphelper/processfactory.hxx>
32 #include <comphelper/diagnose_ex.hxx>
33 #include <comphelper/DirectoryHelper.hxx>
34 #include <comphelper/scopeguard.hxx>
35 #include <tools/urlobj.hxx>
36 #include <unotools/securityoptions.hxx>
37 #include <vcl/bitmapex.hxx>
38 #include <vcl/sysdata.hxx>
39 #include <vcl/commandevent.hxx>
40 #include <vcl/event.hxx>
41 #include <vcl/ptrstyle.hxx>
42 #include <vcl/svapp.hxx>
44 #include <com/sun/star/awt/SystemPointer.hpp>
45 #include <com/sun/star/lang/XComponent.hpp>
46 #include <com/sun/star/media/XManager.hpp>
47 #include <com/sun/star/uno/XComponentContext.hpp>
48 using namespace ::com::sun::star;
50 namespace avmedia::priv {
52 MediaWindowControl::MediaWindowControl(vcl::Window* pParent)
53 : MediaControl(pParent, MediaControlStyle::MultiLine)
57 void MediaWindowControl::update()
59 MediaItem aItem;
61 static_cast< MediaWindowImpl* >( GetParent() )->updateMediaItem( aItem );
62 setState(aItem);
65 void MediaWindowControl::execute(const MediaItem& rItem)
67 static_cast<MediaWindowImpl*>(GetParent())->executeMediaItem(rItem);
70 MediaChildWindow::MediaChildWindow(vcl::Window* pParent)
71 : SystemChildWindow(pParent, WB_CLIPCHILDREN)
75 void MediaChildWindow::MouseMove( const MouseEvent& rMEvt )
77 const MouseEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
78 rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
80 SystemChildWindow::MouseMove( rMEvt );
81 GetParent()->MouseMove( aTransformedEvent );
84 void MediaChildWindow::MouseButtonDown( const MouseEvent& rMEvt )
86 const MouseEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
87 rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
89 SystemChildWindow::MouseButtonDown( rMEvt );
90 GetParent()->MouseButtonDown( aTransformedEvent );
93 void MediaChildWindow::MouseButtonUp( const MouseEvent& rMEvt )
95 const MouseEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
96 rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
98 SystemChildWindow::MouseButtonUp( rMEvt );
99 GetParent()->MouseButtonUp( aTransformedEvent );
102 void MediaChildWindow::KeyInput( const KeyEvent& rKEvt )
104 SystemChildWindow::KeyInput( rKEvt );
105 GetParent()->KeyInput( rKEvt );
108 void MediaChildWindow::KeyUp( const KeyEvent& rKEvt )
110 SystemChildWindow::KeyUp( rKEvt );
111 GetParent()->KeyUp( rKEvt );
114 void MediaChildWindow::Command( const CommandEvent& rCEvt )
116 const CommandEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rCEvt.GetMousePosPixel() ) ),
117 rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData() );
119 SystemChildWindow::Command( rCEvt );
120 GetParent()->Command( aTransformedEvent );
123 MediaWindowImpl::MediaWindowImpl(vcl::Window* pParent, MediaWindow* pMediaWindow, bool bInternalMediaControl)
124 : Control(pParent)
125 , DropTargetHelper(this)
126 , DragSourceHelper(this)
127 , mpMediaWindow(pMediaWindow)
128 , mpMediaWindowControl(bInternalMediaControl ? VclPtr<MediaWindowControl>::Create(this) : nullptr)
130 if (mpMediaWindowControl)
132 mpMediaWindowControl->SetSizePixel(mpMediaWindowControl->GetOptimalSize());
133 mpMediaWindowControl->Show();
137 MediaWindowImpl::~MediaWindowImpl()
139 disposeOnce();
142 void MediaWindowImpl::dispose()
144 if (mxEvents.is())
145 mxEvents->cleanUp();
147 if (mxPlayerWindow.is())
149 mxPlayerWindow->removeKeyListener( uno::Reference< awt::XKeyListener >( mxEvents ) );
150 mxPlayerWindow->removeMouseListener( uno::Reference< awt::XMouseListener >( mxEvents ) );
151 mxPlayerWindow->removeMouseMotionListener( uno::Reference< awt::XMouseMotionListener >( mxEvents ) );
152 mxPlayerWindow->dispose();
153 mxPlayerWindow.clear();
156 uno::Reference< lang::XComponent > xComponent( mxPlayer, uno::UNO_QUERY );
157 if (xComponent.is()) // this stops the player
158 xComponent->dispose();
160 mxPlayer.clear();
162 mpMediaWindow = nullptr;
164 mpEmptyBmpEx.reset();
165 mpAudioBmpEx.reset();
166 mpMediaWindowControl.disposeAndClear();
167 mpChildWindow.disposeAndClear();
169 Control::dispose();
172 uno::Reference<media::XPlayer> MediaWindowImpl::createPlayer(const OUString& rURL, const OUString& rReferer, const OUString*)
174 if( rURL.isEmpty() )
175 return nullptr;
177 if (SvtSecurityOptions::isUntrustedReferer(rReferer))
178 return nullptr;
180 if (INetURLObject(rURL).IsExoticProtocol())
181 return nullptr;
183 uno::Reference<media::XPlayer> xPlayer;
185 // currently there isn't anything else, throw any mime type to the media players
186 //if (!pMimeType || *pMimeType == AVMEDIA_MIMETYPE_COMMON)
188 const uno::Reference<uno::XComponentContext>& xContext(::comphelper::getProcessComponentContext());
189 const OUString sToolkitName = Application::GetToolkitName();
190 if (sToolkitName == "gtk4")
191 xPlayer = createPlayer(rURL, u"com.sun.star.comp.avmedia.Manager_Gtk"_ustr, xContext);
192 else if (sToolkitName.startsWith(u"kf6") || sToolkitName.startsWith(u"qt6"))
193 xPlayer = createPlayer(rURL, u"com.sun.star.comp.avmedia.Manager_Qt"_ustr, xContext);
194 else
195 xPlayer = createPlayer(rURL, u"" AVMEDIA_MANAGER_SERVICE_NAME ""_ustr, xContext);
198 return xPlayer;
201 uno::Reference< media::XPlayer > MediaWindowImpl::createPlayer(
202 const OUString& rURL, const OUString& rManagerServName,
203 const uno::Reference< uno::XComponentContext >& xContext)
205 uno::Reference< media::XPlayer > xPlayer;
208 uno::Reference< media::XManager > xManager (
209 xContext->getServiceManager()->createInstanceWithContext(rManagerServName, xContext),
210 uno::UNO_QUERY );
211 if( xManager.is() )
212 xPlayer = xManager->createPlayer( rURL );
213 else
214 SAL_INFO( "avmedia", "failed to create media player service " << rManagerServName );
215 } catch ( const uno::Exception & )
217 TOOLS_WARN_EXCEPTION( "avmedia", "couldn't create media player " << rManagerServName);
219 return xPlayer;
222 void MediaWindowImpl::setURL( const OUString& rURL,
223 OUString const& rTempURL, OUString const& rReferer)
225 maReferer = rReferer;
226 if( rURL == getURL() )
227 return;
229 if( mxPlayer.is() )
230 mxPlayer->stop();
232 if( mxPlayerWindow.is() )
234 mxPlayerWindow->setVisible( false );
235 mxPlayerWindow.clear();
238 mxPlayer.clear();
239 mTempFileURL.clear();
241 if (!rTempURL.isEmpty())
243 maFileURL = rURL;
244 mTempFileURL = rTempURL;
246 else
248 INetURLObject aURL( rURL );
250 if (aURL.GetProtocol() != INetProtocol::NotValid)
251 maFileURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
252 else
253 maFileURL = rURL;
256 OUString mediaURL;
257 // If the file with the given URL does not exist and a fallback is specified, then use it
258 if ( rURL.startsWith("file:///")
259 && !comphelper::DirectoryHelper::fileExists(maFileURL)
260 && maFallbackFileURL.getLength() > 0 )
262 mediaURL = maFallbackFileURL;
264 else
265 mediaURL = (!mTempFileURL.isEmpty()) ? mTempFileURL : maFileURL;
267 mxPlayer = createPlayer(mediaURL, rReferer, &m_sMimeType );
269 onURLChanged();
272 const OUString& MediaWindowImpl::getURL() const
274 return maFileURL;
277 void MediaWindowImpl::setFallbackURL( const OUString& rURL )
279 maFallbackFileURL = rURL;
282 const OUString& MediaWindowImpl::getFallbackURL() const
284 return maFallbackFileURL;
287 bool MediaWindowImpl::isValid() const
289 return mxPlayer.is();
292 Size MediaWindowImpl::getPreferredSize() const
294 Size aRet(480, 360);
296 if( mxPlayer.is() )
298 awt::Size aPrefSize( mxPlayer->getPreferredPlayerWindowSize() );
300 aRet.setWidth( aPrefSize.Width );
301 aRet.setHeight( aPrefSize.Height );
304 return aRet;
307 bool MediaWindowImpl::start()
309 return mxPlayer.is() && ( mxPlayer->start(), true );
312 void MediaWindowImpl::updateMediaItem( MediaItem& rItem ) const
314 if( isPlaying() )
315 rItem.setState( MediaState::Play );
316 else
317 rItem.setState( ( getMediaTime() == 0.0 ) ? MediaState::Stop : MediaState::Pause );
319 rItem.setDuration( getDuration() );
320 rItem.setTime( getMediaTime() );
321 rItem.setLoop( mxPlayer.is() && mxPlayer->isPlaybackLoop() );
322 rItem.setMute( mxPlayer.is() && mxPlayer->isMute() );
323 rItem.setVolumeDB( mxPlayer.is() ? mxPlayer->getVolumeDB() : 0 );
324 rItem.setZoom( mxPlayerWindow.is() ? mxPlayerWindow->getZoomLevel() : media::ZoomLevel_NOT_AVAILABLE );
325 rItem.setFallbackURL( getFallbackURL() );
326 rItem.setURL( getURL(), mTempFileURL, maReferer );
329 void MediaWindowImpl::executeMediaItem( const MediaItem& rItem )
331 mpItem = &rItem;
332 comphelper::ScopeGuard g([this] { this->mpItem = nullptr; });
334 const AVMediaSetMask nMaskSet = rItem.getMaskSet();
336 // set URL first
337 if (nMaskSet & AVMediaSetMask::URL)
339 m_sMimeType = rItem.getMimeType();
340 setFallbackURL(rItem.getFallbackURL());
341 setURL(rItem.getURL(), rItem.getTempURL(), rItem.getReferer());
344 // set different states next
345 if (nMaskSet & AVMediaSetMask::TIME)
346 setMediaTime(std::min(rItem.getTime(), getDuration()));
348 if (nMaskSet & AVMediaSetMask::LOOP && mxPlayer.is() )
349 mxPlayer->setPlaybackLoop( rItem.isLoop() );
351 if (nMaskSet & AVMediaSetMask::MUTE && mxPlayer.is() )
352 mxPlayer->setMute( rItem.isMute() );
354 if (nMaskSet & AVMediaSetMask::VOLUMEDB && mxPlayer.is() )
355 mxPlayer->setVolumeDB( rItem.getVolumeDB() );
357 if (nMaskSet & AVMediaSetMask::ZOOM && mxPlayerWindow.is() )
358 mxPlayerWindow->setZoomLevel( rItem.getZoom() );
360 // set play state at last
361 if (!(nMaskSet & AVMediaSetMask::STATE))
362 return;
364 switch (rItem.getState())
366 case MediaState::Play:
368 if (!isPlaying())
369 start();
371 break;
373 case MediaState::Pause:
375 if (isPlaying())
376 stop();
378 break;
380 case MediaState::Stop:
382 if (isPlaying())
384 setMediaTime( 0.0 );
385 stop();
386 setMediaTime( 0.0 );
389 break;
393 void MediaWindowImpl::stop()
395 if( mxPlayer.is() )
396 mxPlayer->stop();
399 bool MediaWindowImpl::isPlaying() const
401 return( mxPlayer.is() && mxPlayer->isPlaying() );
404 double MediaWindowImpl::getDuration() const
406 return( mxPlayer.is() ? mxPlayer->getDuration() : 0.0 );
409 void MediaWindowImpl::setMediaTime( double fTime )
411 if( mxPlayer.is() )
412 mxPlayer->setMediaTime( fTime );
415 double MediaWindowImpl::getMediaTime() const
417 return( mxPlayer.is() ? mxPlayer->getMediaTime() : 0.0 );
420 void MediaWindowImpl::stopPlayingInternal(bool bStop)
422 if (isPlaying())
424 bStop ? mxPlayer->stop() : mxPlayer->start();
428 void MediaWindowImpl::onURLChanged()
430 //if (m_sMimeType == AVMEDIA_MIMETYPE_COMMON)
432 mpChildWindow.disposeAndClear();
433 mpChildWindow.reset(VclPtr<MediaChildWindow>::Create(this));
435 if (!mpChildWindow)
436 return;
437 mpChildWindow->SetHelpId(HID_AVMEDIA_PLAYERWINDOW);
438 mxEvents = new MediaEventListenersImpl(*mpChildWindow);
440 if (mxPlayer.is())
442 Resize();
443 uno::Reference<media::XPlayerWindow> xPlayerWindow;
444 const Point aPoint;
445 const Size aSize(mpChildWindow->GetSizePixel());
447 sal_IntPtr nParentWindowHandle(0);
448 const SystemEnvData* pEnvData = mpChildWindow->GetSystemData();
449 // tdf#139609 gtk doesn't need the handle, and fetching it is undesirable
450 if (!pEnvData || pEnvData->toolkit != SystemEnvData::Toolkit::Gtk)
451 nParentWindowHandle = mpChildWindow->GetParentWindowHandle();
452 uno::Sequence<uno::Any> aArgs{
453 uno::Any(nParentWindowHandle),
454 uno::Any(awt::Rectangle(aPoint.X(), aPoint.Y(), aSize.Width(), aSize.Height())),
455 uno::Any(reinterpret_cast<sal_IntPtr>(mpChildWindow.get())),
456 // Media item contains media properties, e.g. cropping.
457 uno::Any(reinterpret_cast<sal_IntPtr>(mpItem))
462 xPlayerWindow = mxPlayer->createPlayerWindow( aArgs );
464 catch( const uno::RuntimeException& )
466 // happens eg, on MacOSX where Java frames cannot be created from X11 window handles
469 mxPlayerWindow = xPlayerWindow;
471 if( xPlayerWindow.is() )
473 xPlayerWindow->addKeyListener( uno::Reference< awt::XKeyListener >( mxEvents ) );
474 xPlayerWindow->addMouseListener( uno::Reference< awt::XMouseListener >( mxEvents ) );
475 xPlayerWindow->addMouseMotionListener( uno::Reference< awt::XMouseMotionListener >( mxEvents ) );
476 xPlayerWindow->addFocusListener( uno::Reference< awt::XFocusListener >( mxEvents ) );
479 else
480 mxPlayerWindow.clear();
482 if( mxPlayerWindow.is() )
483 mpChildWindow->Show();
484 else
485 mpChildWindow->Hide();
487 if( mpMediaWindowControl )
489 MediaItem aItem;
491 updateMediaItem( aItem );
492 mpMediaWindowControl->setState( aItem );
496 void MediaWindowImpl::setPosSize(const tools::Rectangle& rRect)
498 SetPosSizePixel(rRect.TopLeft(), rRect.GetSize());
501 void MediaWindowImpl::setPointer(PointerStyle aPointer)
503 SetPointer(aPointer);
505 if (mpChildWindow)
506 mpChildWindow->SetPointer(aPointer);
508 if (!mxPlayerWindow.is())
509 return;
511 sal_Int32 nPointer;
513 switch (aPointer)
515 case PointerStyle::Cross:
516 nPointer = awt::SystemPointer::CROSS;
517 break;
518 case PointerStyle::Hand:
519 nPointer = awt::SystemPointer::HAND;
520 break;
521 case PointerStyle::Move:
522 nPointer = awt::SystemPointer::MOVE;
523 break;
524 case PointerStyle::Wait:
525 nPointer = awt::SystemPointer::WAIT;
526 break;
527 default:
528 nPointer = awt::SystemPointer::ARROW;
529 break;
532 mxPlayerWindow->setPointerType(nPointer);
535 void MediaWindowImpl::Resize()
537 const Size aCurSize(GetOutputSizePixel());
538 const sal_Int32 nOffset(mpMediaWindowControl ? AVMEDIA_CONTROLOFFSET : 0);
540 Size aPlayerWindowSize(aCurSize.Width() - (nOffset << 1),
541 aCurSize.Height() - (nOffset << 1));
543 if (mpMediaWindowControl)
545 const sal_Int32 nControlHeight = mpMediaWindowControl->GetSizePixel().Height();
546 const sal_Int32 nControlY = std::max(aCurSize.Height() - nControlHeight - nOffset, tools::Long(0));
548 aPlayerWindowSize.setHeight( nControlY - (nOffset << 1) );
549 mpMediaWindowControl->SetPosSizePixel(Point(nOffset, nControlY ), Size(aCurSize.Width() - (nOffset << 1), nControlHeight));
551 if (mpChildWindow)
552 mpChildWindow->SetPosSizePixel(Point(0, 0), aPlayerWindowSize);
554 if (mxPlayerWindow.is())
555 mxPlayerWindow->setPosSize(0, 0, aPlayerWindowSize.Width(), aPlayerWindowSize.Height(), 0);
558 void MediaWindowImpl::StateChanged(StateChangedType eType)
560 if (!mxPlayerWindow.is())
561 return;
563 // stop playing when going disabled or hidden
564 switch (eType)
566 case StateChangedType::Visible:
568 stopPlayingInternal(!IsVisible());
569 mxPlayerWindow->setVisible(IsVisible());
571 break;
573 case StateChangedType::Enable:
575 stopPlayingInternal(!IsEnabled());
576 mxPlayerWindow->setEnable(IsEnabled());
578 break;
580 default:
581 break;
585 void MediaWindowImpl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
587 if (mxPlayerWindow.is())
588 mxPlayerWindow->update();
590 BitmapEx* pLogo = nullptr;
592 if (!mxPlayer.is())
594 if (!mpEmptyBmpEx)
595 mpEmptyBmpEx.reset(new BitmapEx(AVMEDIA_BMP_EMPTYLOGO));
597 pLogo = mpEmptyBmpEx.get();
599 else if (!mxPlayerWindow.is())
601 if (!mpAudioBmpEx)
602 mpAudioBmpEx.reset(new BitmapEx(AVMEDIA_BMP_AUDIOLOGO));
604 pLogo = mpAudioBmpEx.get();
607 if (!mpChildWindow)
608 return;
610 const Point aBasePos(mpChildWindow->GetPosPixel());
611 const tools::Rectangle aVideoRect(aBasePos, mpChildWindow->GetSizePixel());
613 if (!pLogo || pLogo->IsEmpty() || aVideoRect.IsEmpty())
614 return;
616 Size aLogoSize(pLogo->GetSizePixel());
617 const Color aBackgroundColor(67, 67, 67);
619 rRenderContext.SetLineColor(aBackgroundColor);
620 rRenderContext.SetFillColor(aBackgroundColor);
621 rRenderContext.DrawRect(aVideoRect);
623 if ((aLogoSize.Width() > aVideoRect.GetWidth() || aLogoSize.Height() > aVideoRect.GetHeight() ) &&
624 (aLogoSize.Height() > 0))
626 const double fLogoWH = double(aLogoSize.Width()) / aLogoSize.Height();
628 if (fLogoWH < (double(aVideoRect.GetWidth()) / aVideoRect.GetHeight()))
630 aLogoSize.setWidth( tools::Long(aVideoRect.GetHeight() * fLogoWH) );
631 aLogoSize.setHeight( aVideoRect.GetHeight() );
633 else
635 aLogoSize.setWidth( aVideoRect.GetWidth() );
636 aLogoSize.setHeight( tools::Long(aVideoRect.GetWidth() / fLogoWH) );
640 Point aPoint(aBasePos.X() + ((aVideoRect.GetWidth() - aLogoSize.Width()) >> 1),
641 aBasePos.Y() + ((aVideoRect.GetHeight() - aLogoSize.Height()) >> 1));
643 rRenderContext.DrawBitmapEx(aPoint, aLogoSize, *pLogo);
646 void MediaWindowImpl::GetFocus()
650 void MediaWindowImpl::MouseMove(const MouseEvent& rMEvt)
652 if (mpMediaWindow)
653 mpMediaWindow->MouseMove(rMEvt);
656 void MediaWindowImpl::MouseButtonDown(const MouseEvent& rMEvt)
658 if (mpMediaWindow)
659 mpMediaWindow->MouseButtonDown(rMEvt);
662 void MediaWindowImpl::MouseButtonUp(const MouseEvent& rMEvt)
664 if (mpMediaWindow)
665 mpMediaWindow->MouseButtonUp(rMEvt);
668 void MediaWindowImpl::KeyInput(const KeyEvent& rKEvt)
670 if (mpMediaWindow)
671 mpMediaWindow->KeyInput(rKEvt);
674 void MediaWindowImpl::KeyUp(const KeyEvent& rKEvt)
676 if (mpMediaWindow)
677 mpMediaWindow->KeyUp(rKEvt);
680 void MediaWindowImpl::Command(const CommandEvent& rCEvt)
682 if (mpMediaWindow)
683 mpMediaWindow->Command(rCEvt);
686 sal_Int8 MediaWindowImpl::AcceptDrop(const AcceptDropEvent& rEvt)
688 return (mpMediaWindow ? mpMediaWindow->AcceptDrop(rEvt) : 0);
691 sal_Int8 MediaWindowImpl::ExecuteDrop(const ExecuteDropEvent& rEvt)
693 return (mpMediaWindow ? mpMediaWindow->ExecuteDrop(rEvt) : 0);
696 void MediaWindowImpl::StartDrag(sal_Int8 nAction, const Point& rPosPixel)
698 if (mpMediaWindow)
699 mpMediaWindow->StartDrag(nAction, rPosPixel);
702 } // namespace
704 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */