summaryrefslogtreecommitdiffstats
path: root/avmedia/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /avmedia/source
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--avmedia/source/framework/MediaControlBase.cxx267
-rw-r--r--avmedia/source/framework/mediacontrol.cxx225
-rw-r--r--avmedia/source/framework/mediaitem.cxx568
-rw-r--r--avmedia/source/framework/mediaplayer.cxx145
-rw-r--r--avmedia/source/framework/mediatoolbox.cxx134
-rw-r--r--avmedia/source/framework/soundhandler.cxx323
-rw-r--r--avmedia/source/framework/soundhandler.hxx120
-rw-r--r--avmedia/source/gstreamer/avmediagstreamer.component16
-rw-r--r--avmedia/source/gstreamer/gstcommon.hxx43
-rw-r--r--avmedia/source/gstreamer/gstframegrabber.cxx175
-rw-r--r--avmedia/source/gstreamer/gstframegrabber.hxx65
-rw-r--r--avmedia/source/gstreamer/gstmanager.cxx75
-rw-r--r--avmedia/source/gstreamer/gstmanager.hxx47
-rw-r--r--avmedia/source/gstreamer/gstplayer.cxx946
-rw-r--r--avmedia/source/gstreamer/gstplayer.hxx110
-rw-r--r--avmedia/source/gstreamer/gstwindow.cxx192
-rw-r--r--avmedia/source/gstreamer/gstwindow.hxx82
-rw-r--r--avmedia/source/gtk/avmediagtk.component16
-rw-r--r--avmedia/source/gtk/gstwindow.cxx12
-rw-r--r--avmedia/source/gtk/gtkmanager.cxx61
-rw-r--r--avmedia/source/gtk/gtkmanager.hxx34
-rw-r--r--avmedia/source/gtk/gtkplayer.cxx461
-rw-r--r--avmedia/source/gtk/gtkplayer.hxx89
-rw-r--r--avmedia/source/inc/mediamisc.hxx42
-rw-r--r--avmedia/source/macavf/avmediaMacAVF.component26
-rw-r--r--avmedia/source/macavf/framegrabber.hxx54
-rw-r--r--avmedia/source/macavf/framegrabber.mm108
-rw-r--r--avmedia/source/macavf/macavfcommon.hxx86
-rw-r--r--avmedia/source/macavf/manager.hxx49
-rw-r--r--avmedia/source/macavf/manager.mm77
-rw-r--r--avmedia/source/macavf/player.hxx83
-rw-r--r--avmedia/source/macavf/player.mm359
-rw-r--r--avmedia/source/macavf/window.hxx108
-rw-r--r--avmedia/source/macavf/window.mm260
-rw-r--r--avmedia/source/viewer/mediaevent_impl.cxx169
-rw-r--r--avmedia/source/viewer/mediaevent_impl.hxx78
-rw-r--r--avmedia/source/viewer/mediawindow.cxx512
-rw-r--r--avmedia/source/viewer/mediawindow_impl.cxx674
-rw-r--r--avmedia/source/viewer/mediawindow_impl.hxx158
-rw-r--r--avmedia/source/win/avmediawin.component26
-rw-r--r--avmedia/source/win/framegrabber.cxx207
-rw-r--r--avmedia/source/win/framegrabber.hxx57
-rw-r--r--avmedia/source/win/interface.hxx120
-rw-r--r--avmedia/source/win/manager.cxx79
-rw-r--r--avmedia/source/win/manager.hxx48
-rw-r--r--avmedia/source/win/player.cxx418
-rw-r--r--avmedia/source/win/player.hxx114
-rw-r--r--avmedia/source/win/wincommon.hxx41
-rw-r--r--avmedia/source/win/window.cxx494
-rw-r--r--avmedia/source/win/window.hxx111
50 files changed, 8764 insertions, 0 deletions
diff --git a/avmedia/source/framework/MediaControlBase.cxx b/avmedia/source/framework/MediaControlBase.cxx
new file mode 100644
index 000000000..534c27c2c
--- /dev/null
+++ b/avmedia/source/framework/MediaControlBase.cxx
@@ -0,0 +1,267 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <avmedia/MediaControlBase.hxx>
+#include <avmedia/mediaplayer.hxx>
+#include <avmedia/mediaitem.hxx>
+#include <tools/time.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <strings.hrc>
+#include <helpids.h>
+#include <mediamisc.hxx>
+
+constexpr sal_Int32 AVMEDIA_DB_RANGE = -40;
+constexpr double AVMEDIA_LINEINCREMENT = 1.0;
+constexpr double AVMEDIA_PAGEINCREMENT = 10.0;
+
+namespace avmedia {
+
+MediaControlBase::MediaControlBase()
+ : mbCurrentlySettingZoom(false)
+{
+}
+
+void MediaControlBase::UpdateTimeField( MediaItem const & aMediaItem, double fTime )
+{
+ if( aMediaItem.getURL().isEmpty())
+ return;
+
+ OUString aTimeString;
+
+ SvtSysLocale aSysLocale;
+ const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData();
+
+ aTimeString += rLocaleData.getDuration( tools::Time( 0, 0, static_cast< sal_uInt32 >( floor( fTime ) ) ) ) +
+ " / " +
+ rLocaleData.getDuration( tools::Time( 0, 0, static_cast< sal_uInt32 >( floor( aMediaItem.getDuration() ) )) );
+
+ if( mxTimeEdit->get_text() != aTimeString )
+ mxTimeEdit->set_text( aTimeString );
+}
+
+void MediaControlBase::UpdateVolumeSlider( MediaItem const & aMediaItem )
+{
+ if( aMediaItem.getURL().isEmpty() )
+ mxVolumeSlider->set_sensitive(false);
+ else
+ {
+ mxVolumeSlider->set_sensitive(true);
+ const sal_Int32 nVolumeDB = aMediaItem.getVolumeDB();
+ mxVolumeSlider->set_value( std::clamp( nVolumeDB, AVMEDIA_DB_RANGE, sal_Int32(0)) );
+ }
+}
+
+void MediaControlBase::UpdateTimeSlider( MediaItem const & aMediaItem )
+{
+ if( aMediaItem.getURL().isEmpty() )
+ mxTimeSlider->set_sensitive(false);
+ else
+ {
+ mxTimeSlider->set_sensitive(true);
+
+ const double fDuration = aMediaItem.getDuration();
+
+ if( fDuration > 0.0 )
+ {
+ const double fTime = std::min( aMediaItem.getTime(), fDuration );
+
+ bool bChanged(false);
+ int nStep(0), nPage(0);
+ mxTimeSlider->get_increments(nStep, nPage);
+ if (!nStep)
+ {
+ nStep = AVMEDIA_TIME_RANGE * AVMEDIA_LINEINCREMENT / fDuration;
+ bChanged = true;
+ }
+ if (!nPage)
+ {
+ nPage = AVMEDIA_TIME_RANGE * AVMEDIA_PAGEINCREMENT / fDuration;
+ bChanged = true;
+ }
+ if (bChanged)
+ mxTimeSlider->set_increments(nStep, nPage);
+
+ mxTimeSlider->set_value(fTime / fDuration * AVMEDIA_TIME_RANGE);
+ }
+ }
+}
+
+void MediaControlBase::InitializeWidgets()
+{
+ mxPlayToolBox->set_item_help_id("play", HID_AVMEDIA_TOOLBOXITEM_PLAY);
+ mxPlayToolBox->set_item_help_id("pause", HID_AVMEDIA_TOOLBOXITEM_PAUSE);
+ mxPlayToolBox->set_item_help_id("stop", HID_AVMEDIA_TOOLBOXITEM_STOP);
+ mxPlayToolBox->set_item_help_id("loop", HID_AVMEDIA_TOOLBOXITEM_LOOP);
+ mxMuteToolBox->set_item_help_id("mute", HID_AVMEDIA_TOOLBOXITEM_MUTE);
+
+ mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_50), AvmResId( AVMEDIA_STR_ZOOM_50 ));
+ mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_100), AvmResId( AVMEDIA_STR_ZOOM_100 ));
+ mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_200), AvmResId( AVMEDIA_STR_ZOOM_200 ));
+ mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_FIT), AvmResId( AVMEDIA_STR_ZOOM_FIT ));
+ mxZoomListBox->set_help_id( HID_AVMEDIA_ZOOMLISTBOX );
+ mxZoomListBox->set_tooltip_text(AvmResId( AVMEDIA_STR_ZOOM_TOOLTIP ));
+
+ mxTimeEdit->set_text( " 00:00:00/00:00:00 " );
+ mxTimeEdit->set_help_id( HID_AVMEDIA_TIMEEDIT );
+ mxTimeEdit->set_sensitive(false);
+
+ mxVolumeSlider->set_range(AVMEDIA_DB_RANGE, 0);
+ mxVolumeSlider->set_tooltip_text( AvmResId( AVMEDIA_STR_VOLUME ));
+ mxVolumeSlider->set_help_id( HID_AVMEDIA_VOLUMESLIDER );
+
+ mxTimeSlider->set_range( 0, AVMEDIA_TIME_RANGE );
+ mxTimeSlider->set_tooltip_text( AvmResId( AVMEDIA_STR_POSITION ));
+}
+
+void MediaControlBase::UpdatePlayState(const MediaItem& rMediaItem)
+{
+ if (rMediaItem.getState() == MediaState::Play)
+ {
+ mxPlayToolBox->set_item_active("play", true);
+ mxPlayToolBox->set_item_active("pause", false);
+ mxPlayToolBox->set_item_active("stop", false);
+ }
+ else if( rMediaItem.getState() == MediaState::Pause )
+ {
+ mxPlayToolBox->set_item_active("play", false);
+ mxPlayToolBox->set_item_active("pause", true);
+ mxPlayToolBox->set_item_active("stop", false);
+ }
+ else
+ {
+ mxPlayToolBox->set_item_active("play", false);
+ mxPlayToolBox->set_item_active("pause", false);
+ mxPlayToolBox->set_item_active("stop", true);
+ }
+}
+
+void MediaControlBase::UpdateToolBoxes(const MediaItem& rMediaItem)
+{
+ const bool bValidURL = !rMediaItem.getURL().isEmpty();
+ mxPlayToolBox->set_item_sensitive("play", bValidURL);
+ mxPlayToolBox->set_item_sensitive("pause", bValidURL);
+ mxPlayToolBox->set_item_sensitive("stop", bValidURL);
+ mxPlayToolBox->set_item_sensitive("loop", bValidURL);
+ mxMuteToolBox->set_item_sensitive("mute", bValidURL);
+ if( !bValidURL )
+ {
+ mxZoomListBox->set_sensitive(false);
+ mxMuteToolBox->set_sensitive(false);
+ }
+ else
+ {
+ mxPlayToolBox->set_sensitive(true);
+ mxMuteToolBox->set_sensitive(true);
+ UpdatePlayState(rMediaItem);
+ mxPlayToolBox->set_item_active("loop", rMediaItem.isLoop());
+ mxMuteToolBox->set_item_active("mute", rMediaItem.isMute());
+ if (!mbCurrentlySettingZoom)
+ {
+ sal_uInt16 nSelectEntryPos ;
+
+ switch( rMediaItem.getZoom() )
+ {
+ case css::media::ZoomLevel_ZOOM_1_TO_2:
+ nSelectEntryPos = AVMEDIA_ZOOMLEVEL_50;
+ break;
+ case css::media::ZoomLevel_ORIGINAL:
+ nSelectEntryPos = AVMEDIA_ZOOMLEVEL_100;
+ break;
+ case css::media::ZoomLevel_ZOOM_2_TO_1:
+ nSelectEntryPos = AVMEDIA_ZOOMLEVEL_200;
+ break;
+ case css::media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT:
+ nSelectEntryPos = AVMEDIA_ZOOMLEVEL_FIT;
+ break;
+ case css::media::ZoomLevel_FIT_TO_WINDOW:
+ nSelectEntryPos = AVMEDIA_ZOOMLEVEL_SCALED;
+ break;
+
+ default:
+ nSelectEntryPos = AVMEDIA_ZOOMLEVEL_INVALID;
+ break;
+ }
+
+ if( nSelectEntryPos != AVMEDIA_ZOOMLEVEL_INVALID )
+ {
+ mxZoomListBox->show();
+ mxZoomListBox->set_sensitive(true);
+ mxZoomListBox->set_active(nSelectEntryPos);
+ }
+ else
+ mxZoomListBox->set_sensitive(false);
+ }
+ }
+}
+
+void MediaControlBase::SelectPlayToolBoxItem( MediaItem& aExecItem, MediaItem const & aItem, std::string_view rId)
+{
+ if (rId == "apply")
+ {
+ MediaFloater* pFloater = avmedia::getMediaFloater();
+
+ if( pFloater )
+ pFloater->dispatchCurrentURL();
+ }
+ else if (rId == "play")
+ {
+ aExecItem.setState( MediaState::Play );
+
+ if( aItem.getTime() == aItem.getDuration() )
+ aExecItem.setTime( 0.0 );
+ else
+ aExecItem.setTime( aItem.getTime() );
+
+ UpdatePlayState(aExecItem);
+ }
+ else if (rId == "pause")
+ {
+ aExecItem.setState( MediaState::Pause );
+
+ UpdatePlayState(aExecItem);
+ }
+ else if (rId == "stop")
+ {
+ aExecItem.setState( MediaState::Stop );
+ aExecItem.setTime( 0.0 );
+
+ UpdatePlayState(aExecItem);
+ }
+ else if (rId == "mute")
+ {
+ aExecItem.setMute( mxMuteToolBox->get_item_active("mute") );
+ }
+ else if (rId == "loop")
+ {
+ aExecItem.setLoop( mxPlayToolBox->get_item_active("loop") );
+ }
+}
+
+void MediaControlBase::disposeWidgets()
+{
+ mxZoomListBox.reset();
+ mxTimeEdit.reset();
+ mxVolumeSlider.reset();
+ mxMuteToolBox.reset();
+ mxTimeSlider.reset();
+ mxPlayToolBox.reset();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/framework/mediacontrol.cxx b/avmedia/source/framework/mediacontrol.cxx
new file mode 100644
index 000000000..23d700595
--- /dev/null
+++ b/avmedia/source/framework/mediacontrol.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <mediacontrol.hxx>
+#include <strings.hrc>
+#include <mediamisc.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <helpids.h>
+#include <vcl/weld.hxx>
+#include <avmedia/MediaControlBase.hxx>
+
+namespace avmedia
+{
+
+MediaControl::MediaControl( vcl::Window* pParent, MediaControlStyle eControlStyle ) :
+ // MediaControlStyle::MultiLine is the normal docking windows of tools->media player
+ // MediaControlStyle::SingleLine is the toolbar of view->toolbar->media playback
+ InterimItemWindow(pParent, eControlStyle == MediaControlStyle::MultiLine ?
+ OUString("svx/ui/mediawindow.ui") :
+ OUString("svx/ui/medialine.ui"),
+ "MediaWindow"),
+ maIdle( "avmedia MediaControl Idle" ),
+ maChangeTimeIdle( "avmedia MediaControl Change Time Idle" ),
+ maItem( 0, AVMediaSetMask::ALL ),
+ mbLocked( false ),
+ meControlStyle( eControlStyle )
+{
+ mxPlayToolBox = m_xBuilder->weld_toolbar("playtoolbox");
+ mxTimeSlider = m_xBuilder->weld_scale("timeslider");
+ mxMuteToolBox = m_xBuilder->weld_toolbar("mutetoolbox");
+ mxVolumeSlider = m_xBuilder->weld_scale("volumeslider");
+ mxZoomListBox = m_xBuilder->weld_combo_box("zoombox");
+ mxTimeEdit = m_xBuilder->weld_entry("timeedit");
+ mxMediaPath = m_xBuilder->weld_label("url");
+
+ InitializeWidgets();
+
+ mxPlayToolBox->connect_clicked( LINK( this, MediaControl, implSelectHdl ) );
+
+ mxTimeSlider->connect_value_changed( LINK( this, MediaControl, implTimeHdl ) );
+ // when changing the time, use this to do the time change after active scrolling
+ // has stopped for a little which
+ maChangeTimeIdle.SetPriority( TaskPriority::LOWEST );
+ maChangeTimeIdle.SetInvokeHandler( LINK( this, MediaControl, implTimeEndHdl ) );
+
+ mxTimeEdit->set_text(" 00:00:00/00:00:00 ");
+ Size aTextSize = mxTimeEdit->get_preferred_size();
+ mxTimeEdit->set_size_request(aTextSize.Width(), aTextSize.Height());
+ mxTimeEdit->set_text(OUString());
+
+ mxMuteToolBox->connect_clicked( LINK( this, MediaControl, implSelectHdl ) );
+ mxVolumeSlider->connect_value_changed( LINK( this, MediaControl, implVolumeHdl ) );
+
+ mxZoomListBox->connect_changed( LINK( this, MediaControl, implZoomSelectHdl ) );
+ mxZoomListBox->set_help_id(HID_AVMEDIA_ZOOMLISTBOX);
+
+ const OUString aMediaPath( AvmResId( AVMEDIA_MEDIA_PATH_DEFAULT ) );
+ mxMediaPath->set_label(aMediaPath);
+ if (meControlStyle == MediaControlStyle::SingleLine)
+ mxMediaPath->set_size_request(mxMediaPath->get_preferred_size().Width() + 400, -1); // maybe extend the no. 400 to span the screen width
+
+ // we want time field + progress slider to update as the media plays
+ // give this task a lower prio than REPAINT so that UI updates are not starved
+ maIdle.SetPriority( TaskPriority::POST_PAINT );
+ maIdle.SetInvokeHandler( LINK( this, MediaControl, implTimeoutHdl ) );
+}
+
+void MediaControl::InitializeWidgets()
+{
+ if( meControlStyle != MediaControlStyle::SingleLine )
+ {
+ mxPlayToolBox->set_item_help_id("open", HID_AVMEDIA_TOOLBOXITEM_OPEN);
+ mxPlayToolBox->set_item_help_id("apply", HID_AVMEDIA_TOOLBOXITEM_INSERT);
+ }
+ avmedia::MediaControlBase::InitializeWidgets();
+}
+
+MediaControl::~MediaControl()
+{
+ disposeOnce();
+}
+
+void MediaControl::dispose()
+{
+ disposeWidgets();
+ mxMediaPath.reset();
+ InterimItemWindow::dispose();
+}
+
+void MediaControl::UpdateURLField(MediaItem const & tempItem)
+{
+ const OUString aURL( AvmResId(AVMEDIA_MEDIA_PATH) + ": " + tempItem.getURL() ) ;
+ mxMediaPath->set_label(aURL);
+}
+
+void MediaControl::setState( const MediaItem& rItem )
+{
+ if (mbLocked)
+ return;
+ bool bChanged = maItem.merge(rItem);
+ if (bChanged)
+ {
+ if( rItem.getURL().isEmpty() && meControlStyle == MediaControlStyle::SingleLine )
+ mxPlayToolBox->set_sensitive(false);
+ UpdateToolBoxes( maItem );
+ UpdateTimeSlider( maItem );
+ UpdateVolumeSlider( maItem );
+ UpdateTimeField( maItem, maItem.getTime() );
+ UpdateURLField(maItem);
+ }
+}
+
+IMPL_LINK( MediaControl, implTimeHdl, weld::Scale&, rSlider, void )
+{
+ mbLocked = true;
+ maIdle.Stop();
+ UpdateTimeField(maItem, rSlider.get_value() * maItem.getDuration() / AVMEDIA_TIME_RANGE);
+ maChangeTimeIdle.Start();
+}
+
+IMPL_LINK_NOARG(MediaControl, implTimeEndHdl, Timer*, void)
+{
+ MediaItem aExecItem;
+
+ aExecItem.setTime( mxTimeSlider->get_value() * maItem.getDuration() / AVMEDIA_TIME_RANGE );
+ // keep state (if the media was playing, keep it playing)
+ aExecItem.setState(maItem.getState());
+ execute( aExecItem );
+ update();
+ maIdle.Start();
+ mbLocked = false;
+}
+
+IMPL_LINK( MediaControl, implVolumeHdl, weld::Scale&, rSlider, void )
+{
+ MediaItem aExecItem;
+
+ aExecItem.setVolumeDB(rSlider.get_value());
+ execute( aExecItem );
+ update();
+}
+
+IMPL_LINK( MediaControl, implSelectHdl, const OString&, rIdent, void )
+{
+ MediaItem aExecItem;
+ if (rIdent == "open")
+ {
+ OUString aURL;
+ if (MediaWindow::executeMediaURLDialog(GetFrameWeld(), aURL, nullptr))
+ {
+ if( !MediaWindow::isMediaURL( aURL, ""/*TODO?*/, true ) )
+ MediaWindow::executeFormatErrorBox(GetFrameWeld());
+ else
+ {
+ aExecItem.setURL( aURL, "", ""/*TODO?*/ );
+ aExecItem.setState( MediaState::Play );
+ }
+ }
+ }
+ else
+ SelectPlayToolBoxItem( aExecItem, maItem, rIdent );
+
+ if (aExecItem.getState() == MediaState::Play)
+ maIdle.Start();
+ else if (aExecItem.getState() == MediaState::Pause ||
+ aExecItem.getState() == MediaState::Stop)
+ maIdle.Stop();
+
+ if( aExecItem.getMaskSet() != AVMediaSetMask::NONE )
+ execute( aExecItem );
+
+ update();
+}
+
+IMPL_LINK( MediaControl, implZoomSelectHdl, weld::ComboBox&, rBox, void )
+{
+ bool bCurrentlySettingZoom = mbCurrentlySettingZoom;
+ mbCurrentlySettingZoom = true;
+
+ MediaItem aExecItem;
+ css::media::ZoomLevel eLevel;
+
+ switch (rBox.get_active())
+ {
+ case AVMEDIA_ZOOMLEVEL_50: eLevel = css::media::ZoomLevel_ZOOM_1_TO_2; break;
+ case AVMEDIA_ZOOMLEVEL_100: eLevel = css::media::ZoomLevel_ORIGINAL; break;
+ case AVMEDIA_ZOOMLEVEL_200: eLevel = css::media::ZoomLevel_ZOOM_2_TO_1; break;
+ case AVMEDIA_ZOOMLEVEL_FIT: eLevel = css::media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT; break;
+ case AVMEDIA_ZOOMLEVEL_SCALED: eLevel = css::media::ZoomLevel_FIT_TO_WINDOW; break;
+
+ default: eLevel = css::media::ZoomLevel_NOT_AVAILABLE; break;
+ }
+
+ aExecItem.setZoom( eLevel );
+ execute( aExecItem );
+ update();
+
+ mbCurrentlySettingZoom = bCurrentlySettingZoom;
+}
+
+IMPL_LINK_NOARG(MediaControl, implTimeoutHdl, Timer *, void)
+{
+ update();
+ maIdle.Start();
+}
+
+} // namespace avmedia
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/framework/mediaitem.cxx b/avmedia/source/framework/mediaitem.cxx
new file mode 100644
index 000000000..ac8c09b79
--- /dev/null
+++ b/avmedia/source/framework/mediaitem.cxx
@@ -0,0 +1,568 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <avmedia/mediaitem.hxx>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/embed/XTransactedObject.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/document/XStorageBasedDocument.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/uri/XUriReferenceFactory.hpp>
+#include <com/sun/star/text/GraphicCrop.hpp>
+
+#include <sal/log.hxx>
+
+#include <ucbhelper/content.hxx>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <mediamisc.hxx>
+#include <osl/file.hxx>
+#include <tools/diagnose_ex.h>
+#include <vcl/graph.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia
+{
+
+SfxPoolItem* MediaItem::CreateDefault() { return new MediaItem; }
+
+struct MediaItem::Impl
+{
+ OUString m_URL;
+ OUString m_TempFileURL;
+ OUString m_Referer;
+ OUString m_sMimeType;
+ AVMediaSetMask m_nMaskSet;
+ MediaState m_eState;
+ double m_fTime;
+ double m_fDuration;
+ sal_Int16 m_nVolumeDB;
+ bool m_bLoop;
+ bool m_bMute;
+ css::media::ZoomLevel m_eZoom;
+ Graphic m_aGraphic;
+ text::GraphicCrop m_aCrop;
+
+ explicit Impl(AVMediaSetMask nMaskSet)
+ : m_nMaskSet( nMaskSet )
+ , m_eState( MediaState::Stop )
+ , m_fTime( 0.0 )
+ , m_fDuration( 0.0 )
+ , m_nVolumeDB( 0 )
+ , m_bLoop( false )
+ , m_bMute( false )
+ , m_eZoom( css::media::ZoomLevel_NOT_AVAILABLE )
+ {
+ }
+};
+
+
+MediaItem::MediaItem( sal_uInt16 i_nWhich, AVMediaSetMask nMaskSet )
+ : SfxPoolItem( i_nWhich )
+ , m_pImpl( new Impl(nMaskSet) )
+{
+}
+
+
+MediaItem::MediaItem( const MediaItem& rItem )
+ : SfxPoolItem( rItem )
+ , m_pImpl( new Impl(*rItem.m_pImpl) )
+{
+}
+
+
+MediaItem::~MediaItem()
+{
+}
+
+
+bool MediaItem::operator==( const SfxPoolItem& rItem ) const
+{
+ assert( SfxPoolItem::operator==(rItem));
+ MediaItem const& rOther(static_cast< const MediaItem& >(rItem));
+ return m_pImpl->m_nMaskSet == rOther.m_pImpl->m_nMaskSet
+ && m_pImpl->m_URL == rOther.m_pImpl->m_URL
+ && m_pImpl->m_Referer == rOther.m_pImpl->m_Referer
+ && m_pImpl->m_sMimeType == rOther.m_pImpl->m_sMimeType
+ && m_pImpl->m_aGraphic == rOther.m_pImpl->m_aGraphic
+ && m_pImpl->m_aCrop == rOther.m_pImpl->m_aCrop
+ && m_pImpl->m_eState == rOther.m_pImpl->m_eState
+ && m_pImpl->m_fDuration == rOther.m_pImpl->m_fDuration
+ && m_pImpl->m_fTime == rOther.m_pImpl->m_fTime
+ && m_pImpl->m_nVolumeDB == rOther.m_pImpl->m_nVolumeDB
+ && m_pImpl->m_bLoop == rOther.m_pImpl->m_bLoop
+ && m_pImpl->m_bMute == rOther.m_pImpl->m_bMute
+ && m_pImpl->m_eZoom == rOther.m_pImpl->m_eZoom;
+}
+
+MediaItem* MediaItem::Clone( SfxItemPool* ) const
+{
+ return new MediaItem( *this );
+}
+
+bool MediaItem::GetPresentation( SfxItemPresentation,
+ MapUnit,
+ MapUnit,
+ OUString& rText,
+ const IntlWrapper& ) const
+{
+ rText.clear();
+ return false;
+}
+
+bool MediaItem::QueryValue( css::uno::Any& rVal, sal_uInt8 ) const
+{
+ uno::Sequence< uno::Any > aSeq{ uno::Any(m_pImpl->m_URL),
+ uno::Any(static_cast<sal_uInt32>(m_pImpl->m_nMaskSet)),
+ uno::Any(static_cast< sal_Int32 >( m_pImpl->m_eState )),
+ uno::Any(m_pImpl->m_fTime),
+ uno::Any(m_pImpl->m_fDuration),
+ uno::Any(m_pImpl->m_nVolumeDB),
+ uno::Any(m_pImpl->m_bLoop),
+ uno::Any(m_pImpl->m_bMute),
+ uno::Any(m_pImpl->m_eZoom),
+ uno::Any(m_pImpl->m_sMimeType) };
+
+ rVal <<= aSeq;
+
+ return true;
+}
+
+
+bool MediaItem::PutValue( const css::uno::Any& rVal, sal_uInt8 )
+{
+ uno::Sequence< uno::Any > aSeq;
+ bool bRet = false;
+
+ if( ( rVal >>= aSeq ) && ( aSeq.getLength() == 10 ) )
+ {
+ sal_Int32 nInt32 = 0;
+
+ aSeq[ 0 ] >>= m_pImpl->m_URL;
+ aSeq[ 1 ] >>= nInt32;
+ m_pImpl->m_nMaskSet = static_cast<AVMediaSetMask>(nInt32);
+ aSeq[ 2 ] >>= nInt32;
+ m_pImpl->m_eState = static_cast< MediaState >( nInt32 );
+ aSeq[ 3 ] >>= m_pImpl->m_fTime;
+ aSeq[ 4 ] >>= m_pImpl->m_fDuration;
+ aSeq[ 5 ] >>= m_pImpl->m_nVolumeDB;
+ aSeq[ 6 ] >>= m_pImpl->m_bLoop;
+ aSeq[ 7 ] >>= m_pImpl->m_bMute;
+ aSeq[ 8 ] >>= m_pImpl->m_eZoom;
+ aSeq[ 9 ] >>= m_pImpl->m_sMimeType;
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+bool MediaItem::merge(const MediaItem& rMediaItem)
+{
+ bool bChanged = false;
+
+ const AVMediaSetMask nMaskSet = rMediaItem.getMaskSet();
+
+ if( AVMediaSetMask::URL & nMaskSet )
+ bChanged |= setURL(rMediaItem.getURL(), rMediaItem.getTempURL(), rMediaItem.getReferer());
+
+ if( AVMediaSetMask::MIME_TYPE & nMaskSet )
+ bChanged |= setMimeType(rMediaItem.getMimeType());
+
+ if (nMaskSet & AVMediaSetMask::GRAPHIC)
+ bChanged |= setGraphic(rMediaItem.getGraphic());
+
+ if (nMaskSet & AVMediaSetMask::CROP)
+ bChanged |= setCrop(rMediaItem.getCrop());
+
+ if( AVMediaSetMask::STATE & nMaskSet )
+ bChanged |= setState( rMediaItem.getState() );
+
+ if( AVMediaSetMask::DURATION & nMaskSet )
+ bChanged |= setDuration(rMediaItem.getDuration());
+
+ if( AVMediaSetMask::TIME & nMaskSet )
+ bChanged |= setTime(rMediaItem.getTime());
+
+ if( AVMediaSetMask::LOOP & nMaskSet )
+ bChanged |= setLoop(rMediaItem.isLoop());
+
+ if( AVMediaSetMask::MUTE & nMaskSet )
+ bChanged |= setMute(rMediaItem.isMute());
+
+ if( AVMediaSetMask::VOLUMEDB & nMaskSet )
+ bChanged |= setVolumeDB(rMediaItem.getVolumeDB());
+
+ if( AVMediaSetMask::ZOOM & nMaskSet )
+ bChanged |= setZoom(rMediaItem.getZoom());
+
+ return bChanged;
+}
+
+AVMediaSetMask MediaItem::getMaskSet() const
+{
+ return m_pImpl->m_nMaskSet;
+}
+
+bool MediaItem::setURL(const OUString& rURL, const OUString& rTempURL, const OUString& rReferer)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::URL;
+ bool bChanged = rURL != m_pImpl->m_URL || rTempURL != m_pImpl->m_TempFileURL || rReferer != m_pImpl->m_Referer;
+ if (bChanged)
+ {
+ m_pImpl->m_URL = rURL;
+ m_pImpl->m_TempFileURL = rTempURL;
+ m_pImpl->m_Referer = rReferer;
+ }
+ return bChanged;
+}
+
+const OUString& MediaItem::getURL() const
+{
+ return m_pImpl->m_URL;
+}
+
+const OUString& MediaItem::getTempURL() const
+{
+ return m_pImpl->m_TempFileURL;
+}
+
+const OUString& MediaItem::getReferer() const
+{
+ return m_pImpl->m_Referer;
+}
+
+bool MediaItem::setMimeType(const OUString& rMimeType)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::MIME_TYPE;
+ bool bChanged = rMimeType != m_pImpl->m_sMimeType;
+ if (bChanged)
+ m_pImpl->m_sMimeType = rMimeType;
+ return bChanged;
+}
+
+OUString MediaItem::getMimeType() const
+{
+ return !m_pImpl->m_sMimeType.isEmpty() ? m_pImpl->m_sMimeType : AVMEDIA_MIMETYPE_COMMON;
+}
+
+bool MediaItem::setGraphic(const Graphic& rGraphic)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::GRAPHIC;
+ bool bChanged = rGraphic != m_pImpl->m_aGraphic;
+ if (bChanged)
+ m_pImpl->m_aGraphic = rGraphic;
+ return bChanged;
+}
+
+const Graphic & MediaItem::getGraphic() const { return m_pImpl->m_aGraphic; }
+
+bool MediaItem::setCrop(const text::GraphicCrop& rCrop)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::CROP;
+ bool bChanged = rCrop != m_pImpl->m_aCrop;
+ if (bChanged)
+ m_pImpl->m_aCrop = rCrop;
+ return bChanged;
+}
+
+const text::GraphicCrop& MediaItem::getCrop() const { return m_pImpl->m_aCrop; }
+
+bool MediaItem::setState(MediaState eState)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::STATE;
+ bool bChanged = eState != m_pImpl->m_eState;
+ if (bChanged)
+ m_pImpl->m_eState = eState;
+ return bChanged;
+}
+
+MediaState MediaItem::getState() const
+{
+ return m_pImpl->m_eState;
+}
+
+bool MediaItem::setDuration(double fDuration)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::DURATION;
+ bool bChanged = fDuration != m_pImpl->m_fDuration;
+ if (bChanged)
+ m_pImpl->m_fDuration = fDuration;
+ return bChanged;
+}
+
+double MediaItem::getDuration() const
+{
+ return m_pImpl->m_fDuration;
+}
+
+bool MediaItem::setTime(double fTime)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::TIME;
+ bool bChanged = fTime != m_pImpl->m_fTime;
+ if (bChanged)
+ m_pImpl->m_fTime = fTime;
+ return bChanged;
+}
+
+double MediaItem::getTime() const
+{
+ return m_pImpl->m_fTime;
+}
+
+bool MediaItem::setLoop(bool bLoop)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::LOOP;
+ bool bChanged = bLoop != m_pImpl->m_bLoop;
+ if (bChanged)
+ m_pImpl->m_bLoop = bLoop;
+ return bChanged;
+}
+
+bool MediaItem::isLoop() const
+{
+ return m_pImpl->m_bLoop;
+}
+
+bool MediaItem::setMute(bool bMute)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::MUTE;
+ bool bChanged = bMute != m_pImpl->m_bMute;
+ if (bChanged)
+ m_pImpl->m_bMute = bMute;
+ return bChanged;
+}
+
+bool MediaItem::isMute() const
+{
+ return m_pImpl->m_bMute;
+}
+
+bool MediaItem::setVolumeDB(sal_Int16 nDB)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::VOLUMEDB;
+ bool bChanged = nDB != m_pImpl->m_nVolumeDB;
+ if (bChanged)
+ m_pImpl->m_nVolumeDB = nDB;
+ return bChanged;
+}
+
+sal_Int16 MediaItem::getVolumeDB() const
+{
+ return m_pImpl->m_nVolumeDB;
+}
+
+bool MediaItem::setZoom(css::media::ZoomLevel eZoom)
+{
+ m_pImpl->m_nMaskSet |= AVMediaSetMask::ZOOM;
+ bool bChanged = eZoom != m_pImpl->m_eZoom;
+ if (bChanged)
+ m_pImpl->m_eZoom = eZoom;
+ return bChanged;
+}
+
+css::media::ZoomLevel MediaItem::getZoom() const
+{
+ return m_pImpl->m_eZoom;
+}
+
+OUString GetFilename(OUString const& rSourceURL)
+{
+ uno::Reference<uri::XUriReferenceFactory> const xUriFactory(
+ uri::UriReferenceFactory::create(
+ comphelper::getProcessComponentContext()));
+ uno::Reference<uri::XUriReference> const xSourceURI(
+ xUriFactory->parse(rSourceURL), uno::UNO_SET_THROW);
+
+ OUString filename;
+ {
+ sal_Int32 const nSegments(xSourceURI->getPathSegmentCount());
+ if (0 < nSegments)
+ {
+ filename = xSourceURI->getPathSegment(nSegments - 1);
+ }
+ }
+ if (!::comphelper::OStorageHelper::IsValidZipEntryFileName(
+ filename, false) || !filename.getLength())
+ {
+ filename = "media";
+ }
+ return filename;
+}
+
+
+uno::Reference<io::XStream>
+CreateStream(uno::Reference<embed::XStorage> const& xStorage,
+ OUString const& rFilename)
+{
+ OUString filename(rFilename);
+
+ if (xStorage->hasByName(filename))
+ {
+ OUString basename;
+ OUString suffix;
+ sal_Int32 const nIndex(rFilename.lastIndexOf('.'));
+ if (0 < nIndex)
+ {
+ basename = rFilename.copy(0, nIndex);
+ suffix = rFilename.copy(nIndex);
+ }
+ sal_Int32 count(0); // sigh... try to generate non-existent name
+ do
+ {
+ ++count;
+ filename = basename + OUString::number(count) + suffix;
+ }
+ while (xStorage->hasByName(filename));
+ }
+
+ uno::Reference<io::XStream> const xStream(
+ xStorage->openStreamElement(filename,
+ embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE),
+ uno::UNO_SET_THROW);
+ uno::Reference< beans::XPropertySet > const xStreamProps(xStream,
+ uno::UNO_QUERY);
+ if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage
+ xStreamProps->setPropertyValue("MediaType", uno::Any(OUString(
+ //FIXME how to detect real media type?
+ //but currently xmloff has this one hardcoded anyway...
+ "application/vnd.sun.star.media")));
+ xStreamProps->setPropertyValue( // turn off compression
+ "Compressed", uno::Any(false));
+ }
+ return xStream;
+}
+
+
+bool EmbedMedia(uno::Reference<frame::XModel> const& xModel,
+ OUString const& rSourceURL, OUString & o_rEmbeddedURL, uno::Reference<io::XInputStream> const& xInputStream)
+{
+ try
+ {
+ uno::Reference<document::XStorageBasedDocument> const xSBD(xModel,
+ uno::UNO_QUERY_THROW);
+ uno::Reference<embed::XStorage> const xStorage(
+ xSBD->getDocumentStorage(), uno::UNO_SET_THROW);
+
+ OUString const media("Media");
+ uno::Reference<embed::XStorage> const xSubStorage(
+ xStorage->openStorageElement(media, embed::ElementModes::WRITE));
+
+ OUString filename(GetFilename(rSourceURL));
+
+ uno::Reference<io::XStream> const xStream(
+ CreateStream(xSubStorage, filename), uno::UNO_SET_THROW);
+ uno::Reference<io::XOutputStream> const xOutStream(
+ xStream->getOutputStream(), uno::UNO_SET_THROW);
+
+ if (xInputStream.is())
+ {
+ // Throw Exception if failed.
+ ::comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream);
+ }
+ else
+ {
+ ::ucbhelper::Content sourceContent(rSourceURL,
+ uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+
+ if (!sourceContent.openStream(xOutStream)) // copy file to storage
+ {
+ SAL_INFO("avmedia", "openStream to storage failed");
+ return false;
+ }
+ }
+
+ uno::Reference<embed::XTransactedObject> const xSubTransaction(
+ xSubStorage, uno::UNO_QUERY);
+ if (xSubTransaction.is()) {
+ xSubTransaction->commit();
+ }
+ uno::Reference<embed::XTransactedObject> const xTransaction(
+ xStorage, uno::UNO_QUERY);
+ if (xTransaction.is()) {
+ xTransaction->commit();
+ }
+
+ o_rEmbeddedURL = "vnd.sun.star.Package:" + media + "/" + filename;
+ return true;
+ }
+ catch (uno::Exception const&)
+ {
+ SAL_WARN("avmedia",
+ "Exception while trying to embed media");
+ }
+ return false;
+}
+
+bool CreateMediaTempFile(uno::Reference<io::XInputStream> const& xInStream,
+ OUString& o_rTempFileURL, std::u16string_view rDesiredExtension)
+{
+ OUString tempFileURL;
+ ::osl::FileBase::RC const err =
+ ::osl::FileBase::createTempFile(nullptr, nullptr, & tempFileURL);
+ if (::osl::FileBase::E_None != err)
+ {
+ SAL_WARN("avmedia", "cannot create temp file");
+ return false;
+ }
+
+ if (!rDesiredExtension.empty())
+ {
+ OUString newTempFileURL = tempFileURL + rDesiredExtension;
+ if (osl::File::move(tempFileURL, newTempFileURL) != osl::FileBase::E_None)
+ {
+ SAL_WARN("avmedia", "Could not rename file '" << tempFileURL << "' to '" << newTempFileURL << "'");
+ return false;
+ }
+ tempFileURL = newTempFileURL;
+ }
+
+ try
+ {
+ ::ucbhelper::Content tempContent(tempFileURL,
+ uno::Reference<ucb::XCommandEnvironment>(),
+ comphelper::getProcessComponentContext());
+ tempContent.writeStream(xInStream, true); // copy stream to file
+ }
+ catch (uno::Exception const&)
+ {
+ TOOLS_WARN_EXCEPTION("avmedia", "");
+ return false;
+ }
+ o_rTempFileURL = tempFileURL;
+ return true;
+}
+
+MediaTempFile::~MediaTempFile()
+{
+ ::osl::File::remove(m_TempFileURL);
+}
+
+} // namespace avmedia
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/framework/mediaplayer.cxx b/avmedia/source/framework/mediaplayer.cxx
new file mode 100644
index 000000000..258f23ff4
--- /dev/null
+++ b/avmedia/source/framework/mediaplayer.cxx
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <avmedia/mediaplayer.hxx>
+#include <avmedia/mediawindow.hxx>
+#include <avmedia/mediaitem.hxx>
+#include <mediamisc.hxx>
+#include <strings.hrc>
+#include <helpids.h>
+
+#include <svl/stritem.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/bindings.hxx>
+#include <sfx2/dispatch.hxx>
+
+namespace avmedia
+{
+
+MediaPlayer::MediaPlayer( vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* _pBindings, SfxChildWinInfo* pInfo ) :
+ SfxChildWindow( _pParent, nId )
+{
+ SetWindow( VclPtr<MediaFloater>::Create( _pBindings, this, _pParent ) );
+ static_cast< MediaFloater* >( GetWindow() )->Initialize( pInfo );
+};
+
+
+MediaPlayer::~MediaPlayer()
+{
+}
+
+
+SFX_IMPL_DOCKINGWINDOW_WITHID( MediaPlayer, SID_AVMEDIA_PLAYER )
+
+
+MediaFloater::MediaFloater( SfxBindings* _pBindings, SfxChildWindow* pCW, vcl::Window* pParent ) :
+ SfxDockingWindow( _pBindings, pCW, pParent, WB_CLOSEABLE | WB_MOVEABLE | WB_SIZEABLE | WB_DOCKABLE ),
+ mpMediaWindow( new MediaWindow( this, true ) )
+{
+ const Size aSize( mpMediaWindow->getPreferredSize() );
+
+ SetPosSizePixel( Point( 0, 0 ), aSize );
+ SetMinOutputSizePixel( aSize );
+ SetText( AvmResId( AVMEDIA_STR_MEDIAPLAYER ) );
+ mpMediaWindow->show();
+}
+
+
+MediaFloater::~MediaFloater()
+{
+ disposeOnce();
+}
+
+void MediaFloater::dispose()
+{
+ if (IsFloatingMode())
+ {
+ Show(false, ShowFlags::NoFocusChange);
+ SetFloatingMode(false);
+ }
+ mpMediaWindow.reset();
+ SfxDockingWindow::dispose();
+}
+
+void MediaFloater::Resize()
+{
+ SfxDockingWindow::Resize();
+
+ if( mpMediaWindow )
+ mpMediaWindow->setPosSize( tools::Rectangle( Point(), GetOutputSizePixel() ) );
+}
+
+void MediaFloater::ToggleFloatingMode()
+{
+ ::avmedia::MediaItem aRestoreItem;
+
+ if (mpMediaWindow)
+ mpMediaWindow->updateMediaItem( aRestoreItem );
+ mpMediaWindow.reset();
+
+ SfxDockingWindow::ToggleFloatingMode();
+
+ if (isDisposed())
+ return;
+
+ mpMediaWindow.reset( new MediaWindow( this, true ) );
+
+ mpMediaWindow->setPosSize( tools::Rectangle( Point(), GetOutputSizePixel() ) );
+ mpMediaWindow->executeMediaItem( aRestoreItem );
+
+ vcl::Window* pWindow = mpMediaWindow->getWindow();
+
+ if( pWindow )
+ pWindow->SetHelpId( HID_AVMEDIA_PLAYERWINDOW );
+
+ mpMediaWindow->show();
+}
+
+
+void MediaFloater::setURL( const OUString& rURL, const OUString& rReferer, bool bPlayImmediately )
+{
+ if( mpMediaWindow )
+ {
+ mpMediaWindow->setURL( rURL, rReferer );
+
+ if( mpMediaWindow->isValid() && bPlayImmediately )
+ mpMediaWindow->start();
+ }
+}
+
+
+void MediaFloater::dispatchCurrentURL()
+{
+ SfxDispatcher* pDispatcher = GetBindings().GetDispatcher();
+
+ if( pDispatcher )
+ {
+ OUString url;
+ if (mpMediaWindow != nullptr) {
+ url = mpMediaWindow->getURL();
+ }
+ const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, url );
+ pDispatcher->ExecuteList(SID_INSERT_AVMEDIA, SfxCallMode::RECORD,
+ { &aMediaURLItem });
+ }
+}
+
+} // namespace avmedia
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/framework/mediatoolbox.cxx b/avmedia/source/framework/mediatoolbox.cxx
new file mode 100644
index 000000000..55a19ac66
--- /dev/null
+++ b/avmedia/source/framework/mediatoolbox.cxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <avmedia/mediatoolbox.hxx>
+#include <avmedia/mediaitem.hxx>
+#include <mediacontrol.hxx>
+
+#include <tools/debug.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <vcl/toolbox.hxx>
+
+#include <comphelper/propertysequence.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia
+{
+
+class MediaToolBoxControl_Impl : public MediaControl
+{
+public:
+
+ MediaToolBoxControl_Impl( vcl::Window& rParent, MediaToolBoxControl& rControl );
+
+ void update() override;
+ void execute( const MediaItem& rItem ) override;
+
+private:
+
+ MediaToolBoxControl* mpToolBoxControl;
+};
+
+MediaToolBoxControl_Impl::MediaToolBoxControl_Impl( vcl::Window& rParent, MediaToolBoxControl& rControl ) :
+ MediaControl( &rParent, MediaControlStyle::SingleLine ),
+ mpToolBoxControl( &rControl )
+{
+ SetSizePixel(m_xContainer->get_preferred_size());
+}
+
+void MediaToolBoxControl_Impl::update()
+{
+ mpToolBoxControl->implUpdateMediaControl();
+}
+
+
+void MediaToolBoxControl_Impl::execute( const MediaItem& rItem )
+{
+ mpToolBoxControl->implExecuteMediaControl( rItem );
+}
+
+
+SFX_IMPL_TOOLBOX_CONTROL( MediaToolBoxControl, ::avmedia::MediaItem );
+
+
+MediaToolBoxControl::MediaToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) :
+ SfxToolBoxControl( nSlotId, nId, rTbx )
+{
+ rTbx.Invalidate();
+}
+
+
+MediaToolBoxControl::~MediaToolBoxControl()
+{
+}
+
+
+void MediaToolBoxControl::StateChangedAtToolBoxControl( sal_uInt16, SfxItemState eState, const SfxPoolItem* pState )
+{
+ MediaToolBoxControl_Impl* pCtrl = static_cast< MediaToolBoxControl_Impl* >( GetToolBox().GetItemWindow( GetId() ) );
+
+ DBG_ASSERT( pCtrl, "MediaToolBoxControl::StateChanged: media control not found" );
+
+ if( eState == SfxItemState::DISABLED )
+ {
+ pCtrl->Enable( false, false );
+ pCtrl->SetText( OUString() );
+
+ const MediaItem aEmptyMediaItem( 0, AVMediaSetMask::ALL );
+ pCtrl->setState( aEmptyMediaItem );
+ }
+ else
+ {
+ pCtrl->Enable( true, false );
+
+ const MediaItem* pMediaItem = dynamic_cast<const MediaItem*>( pState );
+
+ if( pMediaItem && ( eState == SfxItemState::DEFAULT ) )
+ pCtrl->setState( *pMediaItem );
+ }
+}
+
+VclPtr<InterimItemWindow> MediaToolBoxControl::CreateItemWindow( vcl::Window *pParent )
+{
+ return ( pParent ? VclPtr<MediaToolBoxControl_Impl>::Create( *pParent, *this ) : nullptr );
+}
+
+void MediaToolBoxControl::implUpdateMediaControl()
+{
+ updateStatus( ".uno:AVMediaToolBox" );
+}
+
+void MediaToolBoxControl::implExecuteMediaControl( const MediaItem& rItem )
+{
+ MediaItem aExecItem( SID_AVMEDIA_TOOLBOX );
+ uno::Any aAny;
+
+ aExecItem.merge( rItem );
+ aExecItem.QueryValue( aAny );
+ auto aArgs(::comphelper::InitPropertySequence({
+ { "AVMediaToolBox", aAny }
+ }));
+
+ Dispatch( ".uno:AVMediaToolBox" , aArgs );
+}
+
+} // namespace avmedia
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/framework/soundhandler.cxx b/avmedia/source/framework/soundhandler.cxx
new file mode 100644
index 000000000..2392c3dd1
--- /dev/null
+++ b/avmedia/source/framework/soundhandler.cxx
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "soundhandler.hxx"
+
+#include <unotools/mediadescriptor.hxx>
+
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/frame/DispatchResultState.hpp>
+
+#include <avmedia/mediawindow.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+namespace avmedia{
+
+
+// XInterface, XTypeProvider, XServiceInfo
+
+
+void SAL_CALL SoundHandler::acquire() noexcept
+{
+ /* Don't use mutex in methods of XInterface! */
+ OWeakObject::acquire();
+}
+
+void SAL_CALL SoundHandler::release() noexcept
+{
+ /* Don't use mutex in methods of XInterface! */
+ OWeakObject::release();
+}
+
+css::uno::Any SAL_CALL SoundHandler::queryInterface( const css::uno::Type& aType )
+{
+ /* Attention: Don't use mutex or guard in this method!!! Is a method of XInterface. */
+ /* Ask for my own supported interfaces ...*/
+ css::uno::Any aReturn( ::cppu::queryInterface( aType,
+ static_cast< css::lang::XTypeProvider* >(this),
+ static_cast< css::lang::XServiceInfo* >(this),
+ static_cast< css::frame::XNotifyingDispatch* >(this),
+ static_cast< css::frame::XDispatch* >(this),
+ static_cast< css::document::XExtendedFilterDetection* >(this)));
+ /* If searched interface not supported by this class ... */
+ if ( !aReturn.hasValue() )
+ {
+ /* ... ask baseclass for interfaces! */
+ aReturn = OWeakObject::queryInterface( aType );
+ }
+ /* Return result of this search. */
+ return aReturn;
+}
+
+css::uno::Sequence< sal_Int8 > SAL_CALL SoundHandler::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+css::uno::Sequence< css::uno::Type > SAL_CALL SoundHandler::getTypes()
+{
+ static ::cppu::OTypeCollection aTypeCollection(
+ cppu::UnoType<css::lang::XTypeProvider>::get(),
+ cppu::UnoType<css::lang::XServiceInfo>::get(),
+ cppu::UnoType<css::frame::XNotifyingDispatch>::get(),
+ cppu::UnoType<css::frame::XDispatch>::get(),
+ cppu::UnoType<css::document::XExtendedFilterDetection>::get());
+
+ return aTypeCollection.getTypes();
+}
+
+constexpr OUStringLiteral IMPLEMENTATIONNAME_SOUNDHANDLER = u"com.sun.star.comp.framework.SoundHandler";
+
+/*===========================================================================================================*/
+/* XServiceInfo */
+/*===========================================================================================================*/
+OUString SAL_CALL SoundHandler::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_SOUNDHANDLER;
+}
+
+// XServiceInfo
+sal_Bool SAL_CALL SoundHandler::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+// XServiceInfo
+css::uno::Sequence< OUString > SAL_CALL SoundHandler::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ContentHandler" };
+}
+
+/*-************************************************************************************************************
+ @short standard ctor
+ @descr These initialize a new instance of this class with needed information for work.
+
+ @seealso using at owner
+
+ @param "xFactory", reference to service manager for creation of new services
+ @onerror Show an assertion and do nothing else.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+SoundHandler::SoundHandler()
+ // Init member
+ : m_bError ( false )
+ , m_aUpdateIdle ( "avmedia SoundHandler Update" )
+{
+ m_aUpdateIdle.SetInvokeHandler(LINK(this, SoundHandler, implts_PlayerNotify));
+}
+
+/*-************************************************************************************************************
+ @short standard dtor
+*//*-*************************************************************************************************************/
+SoundHandler::~SoundHandler()
+{
+ if (m_xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+ m_xListener->dispatchFinished(aEvent);
+ m_xListener.clear();
+ }
+}
+
+/*-************************************************************************************************************
+ @interface css::frame::XDispatch
+
+ @short try to load audio file
+ @descr This method try to load given audio file by URL and play it. We use vcl/Sound class to do that.
+ Playing of sound is asynchronous every time.
+
+ @attention We must hold us alive by ourself ... because we use async. vcl sound player ... but playing is started
+ in async interface call "dispatch()" too. And caller forget us immediately. But then our uno ref count
+ will decreased to 0 and will die. The only solution is to use own reference to our implementation.
+ But we do it for really started jobs only and release it during call back of vcl.
+
+ @seealso class vcl/Sound
+ @seealso method implts_PlayerNotify()
+
+ @param "aURL" , URL to dispatch.
+ @param "lArguments", list of optional arguments.
+ @onerror We do nothing.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+void SAL_CALL SoundHandler::dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lDescriptor,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener )
+{
+ // SAFE {
+ const ::osl::MutexGuard aLock( m_aMutex );
+
+ utl::MediaDescriptor aDescriptor(lDescriptor);
+
+ {
+ //close streams otherwise on windows we can't reopen the file in the
+ //media player when we pass the url to directx as it'll already be open
+ css::uno::Reference< css::io::XInputStream > xInputStream =
+ aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_INPUTSTREAM,
+ css::uno::Reference< css::io::XInputStream >());
+ if (xInputStream.is()) xInputStream->closeInput();
+ }
+
+ // If player currently used for other dispatch() requests ...
+ // cancel it by calling stop()!
+ m_aUpdateIdle.Stop();
+ if (m_xPlayer.is())
+ {
+ if (m_xPlayer->isPlaying())
+ m_xPlayer->stop();
+ m_xPlayer.clear();
+ }
+
+ // Try to initialize player.
+ m_xListener = xListener;
+ try
+ {
+ m_bError = false;
+ m_xPlayer.set( avmedia::MediaWindow::createPlayer( aURL.Complete, aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REFERRER, OUString()) ), css::uno::UNO_SET_THROW );
+ // OK- we can start async playing ...
+ // Count this request and initialize self-holder against dying by uno ref count ...
+ m_xSelfHold.set(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
+ m_xPlayer->start();
+ m_aUpdateIdle.SetPriority( TaskPriority::HIGH_IDLE );
+ m_aUpdateIdle.Start();
+ }
+ catch( css::uno::Exception& )
+ {
+ m_bError = true;
+ m_xPlayer.clear();
+ }
+
+ // } SAFE
+}
+
+void SAL_CALL SoundHandler::dispatch( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
+{
+ dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
+}
+
+/*-************************************************************************************************************
+ @interface css::document::XExtendedFilterDetection
+
+ @short try to detect file (given as argument included in "lDescriptor")
+ @descr We try to detect, if given file could be handled by this class and is a well known one.
+ If it is - we return right internal type name - otherwise we return nothing!
+ So call can search for another detect service and ask him too.
+
+ @attention a) We don't need any mutex here ... because we don't use any member!
+ b) Don't use internal player instance "m_pPlayer" to detect given sound file!
+ It's not necessary to do that ... and we can use temp. variable to do the same.
+ This way is easy - we don't must synchronize it with currently played sounds!
+ Another reason to do so ... We are a listener on our internal ma_Player object.
+ If you would call "IsSoundFile()" on this instance, he would call us back and
+ we make some unnecessary things ...
+ @param "lDescriptor", description of file to detect
+ @return Internal type name which match this file ... or nothing if it is unknown.
+
+ @onerror We return nothing.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+OUString SAL_CALL SoundHandler::detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor )
+{
+ // Our default is "nothing". So we can return it, if detection failed or file type is really unknown.
+ OUString sTypeName;
+
+ // Analyze given descriptor to find filename or input stream or ...
+ utl::MediaDescriptor aDescriptor(lDescriptor);
+ OUString sURL = aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_URL, OUString());
+ OUString sReferer = aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REFERRER, OUString());
+
+ if (
+ !sURL.isEmpty() &&
+ (avmedia::MediaWindow::isMediaURL(sURL, sReferer))
+ )
+ {
+ // If the file type is supported depends on the OS, so...
+ // I think we can the following ones:
+ // a) look for given extension of url to map our type decision HARD CODED!!!
+ // b) return preferred type every time... it's easy :-)
+ sTypeName = "wav_Wave_Audio_File";
+ aDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sTypeName;
+ aDescriptor >> lDescriptor;
+ }
+
+ // Return our decision.
+ return sTypeName;
+}
+
+/*-************************************************************************************************************
+ @short call back of sound player
+ @descr Our player call us back to give us some information.
+ We use this information to callback our might existing listener.
+
+ @seealso method dispatchWithNotification()
+ @return 0 every time... it doesn't matter for us.
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+IMPL_LINK_NOARG(SoundHandler, implts_PlayerNotify, Timer *, void)
+{
+ // SAFE {
+ ::osl::ClearableMutexGuard aLock( m_aMutex );
+
+ if (m_xPlayer.is() && m_xPlayer->isPlaying() && m_xPlayer->getMediaTime() < m_xPlayer->getDuration())
+ {
+ m_aUpdateIdle.Start();
+ return;
+ }
+ m_xPlayer.clear();
+
+ // We use m_xSelfHold to let us die ... but we must live till real finishing of this method too!!!
+ // So we SHOULD use another "self-holder" temp. to provide that ...
+ css::uno::Reference< css::uno::XInterface > xOperationHold = m_xSelfHold;
+ m_xSelfHold.clear();
+
+ // notify might existing listener
+ // And forget this listener!
+ // Because the corresponding dispatch was finished.
+ if (m_xListener.is())
+ {
+ css::frame::DispatchResultEvent aEvent;
+ if (!m_bError)
+ aEvent.State = css::frame::DispatchResultState::SUCCESS;
+ else
+ aEvent.State = css::frame::DispatchResultState::FAILURE;
+ m_xListener->dispatchFinished(aEvent);
+ m_xListener.clear();
+ }
+
+ // } SAFE
+ //release aLock before end of method at which point xOperationHold goes out of scope and pThis dies
+ aLock.clear();
+}
+
+} // namespace framework
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_framework_SoundHandler_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const &)
+{
+ return cppu::acquire(new avmedia::SoundHandler);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/framework/soundhandler.hxx b/avmedia/source/framework/soundhandler.hxx
new file mode 100644
index 000000000..648cbe59d
--- /dev/null
+++ b/avmedia/source/framework/soundhandler.hxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XTypeProvider.hpp>
+#include <com/sun/star/frame/XNotifyingDispatch.hpp>
+#include <com/sun/star/frame/XStatusListener.hpp>
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <com/sun/star/media/XPlayer.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/util/URL.hpp>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+#include <cppuhelper/basemutex.hxx>
+#include <cppuhelper/weak.hxx>
+
+#include <vcl/timer.hxx>
+#include <vcl/idle.hxx>
+#include <tools/link.hxx>
+
+namespace avmedia{
+
+/*-************************************************************************************************************
+ @short handler to detect and play sounds ("wav" and "au" only!)
+ @descr Register this implementation as a content handler to detect and/or play wav- and au-sounds.
+ It doesn't depend from the target platform. But one instance of this class
+ can play one sound at the same time only. Means every new dispatch request will stop the
+ might still running one. So we support one operation/one URL/one listener at the same time
+ only.
+
+ @devstatus ready
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+class SoundHandler : // interfaces
+ public css::lang::XTypeProvider
+ , public css::lang::XServiceInfo
+ , public css::frame::XNotifyingDispatch // => XDispatch
+ , public css::document::XExtendedFilterDetection
+ // baseclasses
+ // Order is necessary for right initialization!
+ , private cppu::BaseMutex
+ , public ::cppu::OWeakObject
+{
+ // public methods
+ public:
+
+ // constructor / destructor
+ SoundHandler();
+ virtual ~SoundHandler( ) override;
+
+ // XInterface, XTypeProvider, XServiceInfo
+ virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override;
+ virtual void SAL_CALL acquire() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes () override;
+ virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override;
+
+ /* interface XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName ( ) override;
+ virtual sal_Bool SAL_CALL supportsService ( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames ( ) override;
+
+ // XNotifyingDispatch
+ virtual void SAL_CALL dispatchWithNotification(const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
+ const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) override;
+
+ // XDispatch
+ virtual void SAL_CALL dispatch ( const css::util::URL& aURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override;
+ // not supported !
+ virtual void SAL_CALL addStatusListener ( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ ) override {};
+ virtual void SAL_CALL removeStatusListener ( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ ,
+ const css::util::URL& /*aURL*/ ) override {};
+
+ // XExtendedFilterDetection
+ virtual OUString SAL_CALL detect ( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override;
+
+ // protected methods
+ protected:
+
+ // private methods
+ private:
+ DECL_LINK( implts_PlayerNotify, Timer*, void );
+
+ // variables
+ // (should be private everyway!)
+ private:
+
+ bool m_bError;
+ css::uno::Reference< css::uno::XInterface > m_xSelfHold ; // we must protect us against dying during async(!) dispatch() call!
+ css::uno::Reference< css::media::XPlayer > m_xPlayer ; // uses avmedia player to play sounds...
+
+ css::uno::Reference< css::frame::XDispatchResultListener > m_xListener ;
+ Idle m_aUpdateIdle;
+
+}; // class SoundHandler
+
+} // namespace avmedia
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/avmediagstreamer.component b/avmedia/source/gstreamer/avmediagstreamer.component
new file mode 100644
index 000000000..cdc308d8b
--- /dev/null
+++ b/avmedia/source/gstreamer/avmediagstreamer.component
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+-->
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.media.Manager_GStreamer"
+ constructor="com_sun_star_comp_media_Manager_GStreamer_get_implementation">
+ <service name="com.sun.star.comp.avmedia.Manager_GStreamer"/>
+ </implementation>
+</component>
diff --git a/avmedia/source/gstreamer/gstcommon.hxx b/avmedia/source/gstreamer/gstcommon.hxx
new file mode 100644
index 000000000..0e27907fb
--- /dev/null
+++ b/avmedia/source/gstreamer/gstcommon.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+
+#include <osl/mutex.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/factory.hxx>
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/media/XManager.hpp>
+
+#define WM_GRAPHNOTIFY (WM_USER + 567)
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstframegrabber.cxx b/avmedia/source/gstreamer/gstframegrabber.cxx
new file mode 100644
index 000000000..ece799d87
--- /dev/null
+++ b/avmedia/source/gstreamer/gstframegrabber.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "gstframegrabber.hxx"
+#include "gstplayer.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include <gst/gstbuffer.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideosink.h>
+#include <o3tl/safeint.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+
+#include <string>
+
+constexpr OUStringLiteral AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.FrameGrabber_GStreamer";
+constexpr OUStringLiteral AVMEDIA_GST_FRAMEGRABBER_SERVICENAME = u"com.sun.star.media.FrameGrabber_GStreamer";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+void FrameGrabber::disposePipeline()
+{
+ if( mpPipeline != nullptr )
+ {
+ gst_element_set_state( mpPipeline, GST_STATE_NULL );
+ g_object_unref( G_OBJECT( mpPipeline ) );
+ mpPipeline = nullptr;
+ }
+}
+
+FrameGrabber::FrameGrabber( std::u16string_view rURL )
+{
+ gchar *pPipelineStr;
+ pPipelineStr = g_strdup_printf(
+ "uridecodebin uri=%s ! videoconvert ! videoscale ! appsink "
+ "name=sink caps=\"video/x-raw,format=RGB,pixel-aspect-ratio=1/1\"",
+ OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ GError *pError = nullptr;
+ mpPipeline = gst_parse_launch( pPipelineStr, &pError );
+ if( pError != nullptr) {
+ g_warning( "Failed to construct frame-grabber pipeline '%s'\n", pError->message );
+ g_error_free( pError );
+ disposePipeline();
+ }
+
+ if( mpPipeline ) {
+ // pre-roll
+ switch( gst_element_set_state( mpPipeline, GST_STATE_PAUSED ) ) {
+ case GST_STATE_CHANGE_FAILURE:
+ case GST_STATE_CHANGE_NO_PREROLL:
+ g_warning( "failure pre-rolling media" );
+ disposePipeline();
+ break;
+ default:
+ break;
+ }
+ }
+ if( mpPipeline &&
+ gst_element_get_state( mpPipeline, nullptr, nullptr, 5 * GST_SECOND ) == GST_STATE_CHANGE_FAILURE )
+ disposePipeline();
+}
+
+FrameGrabber::~FrameGrabber()
+{
+ disposePipeline();
+}
+
+rtl::Reference<FrameGrabber> FrameGrabber::create( std::u16string_view rURL )
+{
+ return new FrameGrabber( rURL );
+}
+
+uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime )
+{
+ uno::Reference< graphic::XGraphic > xRet;
+
+ if( !mpPipeline )
+ return xRet;
+
+ gint64 gst_position = llround( fMediaTime * GST_SECOND );
+ gst_element_seek_simple(
+ mpPipeline, GST_FORMAT_TIME,
+ GstSeekFlags(GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH),
+ gst_position );
+
+ GstElement *pSink = gst_bin_get_by_name( GST_BIN( mpPipeline ), "sink" );
+ if( !pSink )
+ return xRet;
+
+ GstBuffer *pBuf = nullptr;
+ GstCaps *pCaps = nullptr;
+
+ // synchronously fetch the frame
+ GstSample *pSample = nullptr;
+ g_signal_emit_by_name( pSink, "pull-preroll", &pSample, nullptr );
+
+ if( pSample )
+ {
+ pBuf = gst_sample_get_buffer( pSample );
+ pCaps = gst_sample_get_caps( pSample );
+ }
+
+ // get geometry
+ int nWidth = 0, nHeight = 0;
+ if( !pCaps )
+ g_warning( "could not get snapshot format\n" );
+ else
+ {
+ GstStructure *pStruct = gst_caps_get_structure( pCaps, 0 );
+
+ /* we need to get the final caps on the buffer to get the size */
+ if( !gst_structure_get_int( pStruct, "width", &nWidth ) ||
+ !gst_structure_get_int( pStruct, "height", &nHeight ) )
+ nWidth = nHeight = 0;
+ }
+
+ if( pBuf && nWidth > 0 && nHeight > 0 &&
+ // sanity check the size
+ gst_buffer_get_size( pBuf ) >= o3tl::make_unsigned( nWidth * nHeight * 3 )
+ )
+ {
+ sal_uInt8 *pData = nullptr;
+ GstMapInfo aMapInfo;
+ gst_buffer_map( pBuf, &aMapInfo, GST_MAP_READ );
+ pData = aMapInfo.data;
+
+ int nStride = GST_ROUND_UP_4( nWidth * 3 );
+ BitmapEx aBmp = vcl::bitmap::CreateFromData(pData, nWidth, nHeight, nStride, vcl::PixelFormat::N24_BPP);
+
+ gst_buffer_unmap( pBuf, &aMapInfo );
+ xRet = Graphic( aBmp ).GetXGraphic();
+ }
+
+ return xRet;
+}
+
+OUString SAL_CALL FrameGrabber::getImplementationName( )
+{
+ return AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME;
+}
+
+sal_Bool SAL_CALL FrameGrabber::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL FrameGrabber::getSupportedServiceNames()
+{
+ return { AVMEDIA_GST_FRAMEGRABBER_SERVICENAME };
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstframegrabber.hxx b/avmedia/source/gstreamer/gstframegrabber.hxx
new file mode 100644
index 000000000..c706192ef
--- /dev/null
+++ b/avmedia/source/gstreamer/gstframegrabber.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "gstplayer.hxx"
+#include <com/sun/star/media/XFrameGrabber.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+namespace avmedia::gstreamer {
+
+typedef ::cppu::WeakImplHelper< css::media::XFrameGrabber,
+ css::lang::XServiceInfo > FrameGrabber_BASE;
+
+
+class FrameGrabber : public FrameGrabber_BASE
+{
+ GstElement *mpPipeline;
+ void disposePipeline();
+public:
+ // noncopyable
+ FrameGrabber(const FrameGrabber&) = delete;
+ const FrameGrabber& operator=(const FrameGrabber&) =delete;
+
+ // static create method instead of public Ctor
+ static rtl::Reference<FrameGrabber> create( std::u16string_view rURL );
+
+ virtual ~FrameGrabber() override;
+
+ // XFrameGrabber
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL grabFrame( double fMediaTime ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+ explicit FrameGrabber( std::u16string_view aURL );
+};
+
+} // avmedia::gst
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstmanager.cxx b/avmedia/source/gstreamer/gstmanager.cxx
new file mode 100644
index 000000000..1803b5880
--- /dev/null
+++ b/avmedia/source/gstreamer/gstmanager.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include "gstmanager.hxx"
+#include "gstplayer.hxx"
+
+#include <tools/urlobj.hxx>
+#include <rtl/ref.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+Manager::Manager()
+{
+}
+
+Manager::~Manager()
+{
+}
+
+uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL )
+{
+ rtl::Reference<Player> pPlayer( new Player );
+ const INetURLObject aURL( rURL );
+
+ if( !pPlayer->create( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ) )
+ pPlayer.clear();
+
+ return pPlayer;
+}
+
+OUString SAL_CALL Manager::getImplementationName( )
+{
+ return "com.sun.star.comp.avmedia.Manager_GStreamer";
+}
+
+sal_Bool SAL_CALL Manager::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames( )
+{
+ return { "com.sun.star.media.Manager" };
+}
+
+} // namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_media_Manager_GStreamer_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new avmedia::gstreamer::Manager());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstmanager.hxx b/avmedia/source/gstreamer/gstmanager.hxx
new file mode 100644
index 000000000..21a5245dd
--- /dev/null
+++ b/avmedia/source/gstreamer/gstmanager.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "gstcommon.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/media/XManager.hpp>
+
+namespace avmedia::gstreamer {
+
+class Manager : public ::cppu::WeakImplHelper< css::media::XManager,
+ css::lang::XServiceInfo >
+{
+public:
+
+ explicit Manager();
+ virtual ~Manager() override;
+
+ // XManager
+ virtual css::uno::Reference< css::media::XPlayer > SAL_CALL createPlayer( const OUString& aURL ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+} // namespace avmedia::gstreamer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstplayer.cxx b/avmedia/source/gstreamer/gstplayer.cxx
new file mode 100644
index 000000000..6884654b6
--- /dev/null
+++ b/avmedia/source/gstreamer/gstplayer.cxx
@@ -0,0 +1,946 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <cstring>
+#include <map>
+#include <set>
+#include <vector>
+#include <math.h>
+
+#include <com/sun/star/text/GraphicCrop.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <rtl/string.hxx>
+#include <salhelper/thread.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syschild.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/graph.hxx>
+#include <avmedia/mediaitem.hxx>
+
+#include "gstplayer.hxx"
+#include "gstframegrabber.hxx"
+#include "gstwindow.hxx"
+
+#include <gst/video/videooverlay.h>
+#include <gst/pbutils/missing-plugins.h>
+#include <gst/pbutils/pbutils.h>
+
+constexpr OUStringLiteral AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.Player_GStreamer";
+constexpr OUStringLiteral AVMEDIA_GST_PLAYER_SERVICENAME = u"com.sun.star.media.Player_GStreamer";
+#define AVVERSION "gst 1.0: "
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+namespace {
+
+class FlagGuard {
+public:
+ explicit FlagGuard(bool & flag): flag_(flag) { flag_ = true; }
+
+ ~FlagGuard() { flag_ = false; }
+
+private:
+ bool & flag_;
+};
+
+class MissingPluginInstallerThread: public salhelper::Thread {
+public:
+ MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
+
+private:
+ void execute() override;
+};
+
+
+class MissingPluginInstaller {
+ friend class MissingPluginInstallerThread;
+
+public:
+ MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}
+
+ ~MissingPluginInstaller();
+
+ void report(rtl::Reference<Player> const & source, GstMessage * message);
+
+ // Player::~Player calls Player::disposing calls
+ // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
+ // here (which would bump its refcount back from 0 to 1):
+ void detach(Player const * source);
+
+private:
+ void processQueue();
+
+ DECL_STATIC_LINK(MissingPluginInstaller, launchUi, void*, void);
+
+ osl::Mutex mutex_;
+ std::set<OString> reported_;
+ std::map<OString, std::set<rtl::Reference<Player>>> queued_;
+ rtl::Reference<MissingPluginInstallerThread> currentThread_;
+ std::vector<OString> currentDetails_;
+ std::set<rtl::Reference<Player>> currentSources_;
+ bool launchNewThread_;
+ bool inCleanUp_;
+};
+
+
+MissingPluginInstaller::~MissingPluginInstaller() {
+ osl::MutexGuard g(mutex_);
+ SAL_WARN_IF(currentThread_.is(), "avmedia.gstreamer", "unjoined thread");
+ inCleanUp_ = true;
+}
+
+
+void MissingPluginInstaller::report(
+ rtl::Reference<Player> const & source, GstMessage * message)
+{
+ // assert(gst_is_missing_plugin_message(message));
+ gchar * det = gst_missing_plugin_message_get_installer_detail(message);
+ if (det == nullptr) {
+ SAL_WARN(
+ "avmedia.gstreamer",
+ "gst_missing_plugin_message_get_installer_detail failed");
+ return;
+ }
+ std::size_t len = std::strlen(det);
+ if (len > SAL_MAX_INT32) {
+ SAL_WARN("avmedia.gstreamer", "detail string too long");
+ g_free(det);
+ return;
+ }
+ OString detStr(det, len);
+ g_free(det);
+ rtl::Reference<MissingPluginInstallerThread> join;
+ rtl::Reference<MissingPluginInstallerThread> launch;
+ {
+ osl::MutexGuard g(mutex_);
+ if (reported_.find(detStr) != reported_.end()) {
+ return;
+ }
+ auto & i = queued_[detStr];
+ bool fresh = i.empty();
+ i.insert(source);
+ if (!(fresh && launchNewThread_)) {
+ return;
+ }
+ join = currentThread_;
+ currentThread_ = new MissingPluginInstallerThread;
+ {
+ FlagGuard f(inCleanUp_);
+ currentSources_.clear();
+ }
+ processQueue();
+ launchNewThread_ = false;
+ launch = currentThread_;
+ }
+ if (join.is()) {
+ join->join();
+ }
+ launch->acquire();
+ Application::PostUserEvent(
+ LINK(this, MissingPluginInstaller, launchUi), launch.get());
+}
+
+
+void eraseSource(std::set<rtl::Reference<Player>> & set, Player const * source)
+{
+ auto i = std::find_if(
+ set.begin(), set.end(),
+ [source](rtl::Reference<Player> const & el) {
+ return el.get() == source;
+ });
+ if (i != set.end()) {
+ set.erase(i);
+ }
+}
+
+
+void MissingPluginInstaller::detach(Player const * source) {
+ rtl::Reference<MissingPluginInstallerThread> join;
+ {
+ osl::MutexGuard g(mutex_);
+ if (inCleanUp_) {
+ // Guard against ~MissingPluginInstaller with erroneously un-joined
+ // currentThread_ (thus non-empty currentSources_) calling
+ // destructor of currentSources_, calling ~Player, calling here,
+ // which would use currentSources_ while it is already being
+ // destroyed:
+ return;
+ }
+ for (auto i = queued_.begin(); i != queued_.end();) {
+ eraseSource(i->second, source);
+ if (i->second.empty()) {
+ i = queued_.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ if (currentThread_.is()) {
+ assert(!currentSources_.empty());
+ eraseSource(currentSources_, source);
+ if (currentSources_.empty()) {
+ join = currentThread_;
+ currentThread_.clear();
+ launchNewThread_ = true;
+ }
+ }
+ }
+ if (join.is()) {
+ // missing cancellability of gst_install_plugins_sync
+ join->join();
+ }
+}
+
+
+void MissingPluginInstaller::processQueue() {
+ assert(!queued_.empty());
+ assert(currentDetails_.empty());
+ for (const auto& rEntry : queued_) {
+ reported_.insert(rEntry.first);
+ currentDetails_.push_back(rEntry.first);
+ currentSources_.insert(rEntry.second.begin(), rEntry.second.end());
+ }
+ queued_.clear();
+}
+
+
+IMPL_STATIC_LINK(MissingPluginInstaller, launchUi, void *, p, void)
+{
+ MissingPluginInstallerThread* thread = static_cast<MissingPluginInstallerThread*>(p);
+ rtl::Reference<MissingPluginInstallerThread> ref(thread, SAL_NO_ACQUIRE);
+ gst_pb_utils_init();
+ // not thread safe; hopefully fine to consistently call from our event
+ // loop (which is the only reason to have this
+ // Application::PostUserEvent diversion, in case
+ // MissingPluginInstaller::report might be called from outside our event
+ // loop), and hopefully fine to call gst_is_missing_plugin_message and
+ // gst_missing_plugin_message_get_installer_detail before calling
+ // gst_pb_utils_init
+ ref->launch();
+}
+
+
+struct TheMissingPluginInstaller:
+ public rtl::Static<MissingPluginInstaller, TheMissingPluginInstaller>
+{};
+
+
+void MissingPluginInstallerThread::execute() {
+ MissingPluginInstaller & inst = TheMissingPluginInstaller::get();
+ for (;;) {
+ std::vector<OString> details;
+ {
+ osl::MutexGuard g(inst.mutex_);
+ assert(!inst.currentDetails_.empty());
+ details.swap(inst.currentDetails_);
+ }
+ std::vector<char *> args;
+ args.reserve(details.size());
+ for (auto const& i : details)
+ {
+ args.push_back(const_cast<char *>(i.getStr()));
+ }
+ args.push_back(nullptr);
+ gst_install_plugins_sync(args.data(), nullptr);
+ {
+ osl::MutexGuard g(inst.mutex_);
+ if (inst.queued_.empty() || inst.launchNewThread_) {
+ inst.launchNewThread_ = true;
+ break;
+ }
+ inst.processQueue();
+ }
+ }
+}
+
+} // end anonymous namespace
+
+
+Player::Player() :
+ GstPlayer_BASE( m_aMutex ),
+ mpPlaybin( nullptr ),
+ mpVolumeControl( nullptr ),
+ mbUseGtkSink( false ),
+ mbFakeVideo (false ),
+ mnUnmutedVolume( 0 ),
+ mbMuted( false ),
+ mbLooping( false ),
+ mbInitialized( false ),
+ mpDisplay( nullptr ),
+ mnWindowID( 0 ),
+ mpXOverlay( nullptr ),
+ mnDuration( 0 ),
+ mnWidth( 0 ),
+ mnHeight( 0 ),
+ mnWatchID( 0 ),
+ mbWatchID( false )
+{
+ // Initialize GStreamer library
+ int argc = 1;
+ char name[] = "libreoffice";
+ char *arguments[] = { name };
+ char** argv = arguments;
+ GError* pError = nullptr;
+
+ mbInitialized = gst_init_check( &argc, &argv, &pError );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player" );
+
+ if (pError != nullptr)
+ {
+ // TODO: throw an exception?
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player error '" << pError->message << "'" );
+ g_error_free (pError);
+ }
+}
+
+
+Player::~Player()
+{
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::~Player" );
+ if( mbInitialized )
+ disposing();
+}
+
+
+void SAL_CALL Player::disposing()
+{
+ TheMissingPluginInstaller::get().detach(this);
+
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ stop();
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::disposing" );
+
+ // Release the elements and pipeline
+ if( mbInitialized )
+ {
+ if( mpPlaybin )
+ {
+ gst_element_set_state( mpPlaybin, GST_STATE_NULL );
+ g_object_unref( G_OBJECT( mpPlaybin ) );
+
+ mpPlaybin = nullptr;
+ mpVolumeControl = nullptr;
+ }
+
+ if( mpXOverlay ) {
+ g_object_unref( G_OBJECT ( mpXOverlay ) );
+ mpXOverlay = nullptr;
+ }
+
+ }
+ if (mbWatchID)
+ {
+ g_source_remove(mnWatchID);
+ mbWatchID = false;
+ }
+}
+
+
+static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
+{
+ Player* pPlayer = static_cast<Player*>(data);
+
+ pPlayer->processMessage( message );
+
+ return true;
+}
+
+
+static GstBusSyncReply pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data )
+{
+ Player* pPlayer = static_cast<Player*>(data);
+
+ return pPlayer->processSyncMessage( message );
+}
+
+
+void Player::processMessage( GstMessage *message )
+{
+ switch( GST_MESSAGE_TYPE( message ) ) {
+ case GST_MESSAGE_EOS:
+ gst_element_set_state( mpPlaybin, GST_STATE_READY );
+ if (mbLooping)
+ start();
+ break;
+ case GST_MESSAGE_STATE_CHANGED:
+ if (message->src == GST_OBJECT(mpPlaybin))
+ {
+ GstState newstate, pendingstate;
+
+ gst_message_parse_state_changed (message, nullptr, &newstate, &pendingstate);
+
+ if (!mbUseGtkSink && newstate == GST_STATE_PAUSED &&
+ pendingstate == GST_STATE_VOID_PENDING && mpXOverlay)
+ {
+ gst_video_overlay_expose(mpXOverlay);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+#define LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE "GstWaylandDisplayHandleContextType"
+
+static bool lcl_is_wayland_display_handle_need_context_message(GstMessage* msg)
+{
+ g_return_val_if_fail(GST_IS_MESSAGE(msg), false);
+
+ if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_NEED_CONTEXT)
+ return false;
+ const gchar *type = nullptr;
+ if (!gst_message_parse_context_type(msg, &type))
+ return false;
+ return !g_strcmp0(type, LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
+}
+
+static GstContext* lcl_wayland_display_handle_context_new(void* display)
+{
+ GstContext *context = gst_context_new(LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE, true);
+ gst_structure_set (gst_context_writable_structure (context),
+ "handle", G_TYPE_POINTER, display, nullptr);
+ return context;
+}
+
+GstBusSyncReply Player::processSyncMessage( GstMessage *message )
+{
+#if OSL_DEBUG_LEVEL > 0
+ if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR )
+ {
+ GError* error;
+ gchar* error_debug;
+
+ gst_message_parse_error( message, &error, &error_debug );
+ SAL_WARN(
+ "avmedia.gstreamer",
+ "error: '" << error->message << "' debug: '"
+ << error_debug << "'");
+ }
+#endif
+
+ if (!mbUseGtkSink)
+ {
+ if (gst_is_video_overlay_prepare_window_handle_message (message) )
+ {
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " processSyncMessage prepare window id: " <<
+ GST_MESSAGE_TYPE_NAME( message ) << " " << static_cast<int>(mnWindowID) );
+ if( mpXOverlay )
+ g_object_unref( G_OBJECT ( mpXOverlay ) );
+ g_object_set( GST_MESSAGE_SRC( message ), "force-aspect-ratio", FALSE, nullptr );
+ mpXOverlay = GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message ) );
+ g_object_ref( G_OBJECT ( mpXOverlay ) );
+ if ( mnWindowID != 0 )
+ {
+ gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
+ gst_video_overlay_handle_events(mpXOverlay, 0); // Let the parent window handle events.
+ if (maArea.Width > 0 && maArea.Height > 0)
+ gst_video_overlay_set_render_rectangle(mpXOverlay, maArea.X, maArea.Y, maArea.Width, maArea.Height);
+ }
+
+ return GST_BUS_DROP;
+ }
+ else if (lcl_is_wayland_display_handle_need_context_message(message))
+ {
+ GstContext *context = lcl_wayland_display_handle_context_new(mpDisplay);
+ gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context);
+
+ return GST_BUS_DROP;
+ }
+ }
+
+ if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ASYNC_DONE ) {
+ if( mnDuration == 0) {
+ gint64 gst_duration = 0;
+ if( gst_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
+ mnDuration = gst_duration;
+ }
+ if( mnWidth == 0 ) {
+ GstPad *pad = nullptr;
+
+ g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );
+
+ if( pad ) {
+ int w = 0, h = 0;
+
+ GstCaps *caps = gst_pad_get_current_caps( pad );
+
+ if( gst_structure_get( gst_caps_get_structure( caps, 0 ),
+ "width", G_TYPE_INT, &w,
+ "height", G_TYPE_INT, &h,
+ nullptr ) ) {
+ mnWidth = w;
+ mnHeight = h;
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "queried size: " << mnWidth << "x" << mnHeight );
+
+ }
+ gst_caps_unref( caps );
+ g_object_unref( pad );
+ }
+
+ maSizeCondition.set();
+ }
+ } else if (gst_is_missing_plugin_message(message)) {
+ TheMissingPluginInstaller::get().report(this, message);
+ if( mnWidth == 0 ) {
+ // an error occurred, set condition so that OOo thread doesn't wait for us
+ maSizeCondition.set();
+ }
+ } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) {
+ if( mnWidth == 0 ) {
+ // an error occurred, set condition so that OOo thread doesn't wait for us
+ maSizeCondition.set();
+ }
+ }
+
+ return GST_BUS_PASS;
+}
+
+void Player::preparePlaybin( std::u16string_view rURL, GstElement *pSink )
+{
+ if (mpPlaybin != nullptr)
+ {
+ gst_element_set_state( mpPlaybin, GST_STATE_NULL );
+ g_object_unref( mpPlaybin );
+ }
+
+ mpPlaybin = gst_element_factory_make( "playbin", nullptr );
+
+ //tdf#96989 on systems with flat-volumes setting the volume directly on the
+ //playbin to 100% results in setting the global volume to 100% of the
+ //maximum. We expect to set as % of the current volume.
+ mpVolumeControl = gst_element_factory_make( "volume", nullptr );
+ GstElement *pAudioSink = gst_element_factory_make( "autoaudiosink", nullptr );
+ GstElement* pAudioOutput = gst_bin_new("audio-output-bin");
+ assert(pAudioOutput);
+ if (pAudioSink)
+ gst_bin_add(GST_BIN(pAudioOutput), pAudioSink);
+ if (mpVolumeControl)
+ {
+ gst_bin_add(GST_BIN(pAudioOutput), mpVolumeControl);
+ if (pAudioSink)
+ gst_element_link(mpVolumeControl, pAudioSink);
+ GstPad *pPad = gst_element_get_static_pad(mpVolumeControl, "sink");
+ gst_element_add_pad(GST_ELEMENT(pAudioOutput), gst_ghost_pad_new("sink", pPad));
+ gst_object_unref(GST_OBJECT(pPad));
+ }
+ g_object_set(G_OBJECT(mpPlaybin), "audio-sink", pAudioOutput, nullptr);
+
+ if( pSink != nullptr ) // used for getting preferred size etc.
+ {
+ g_object_set( G_OBJECT( mpPlaybin ), "video-sink", pSink, nullptr );
+ mbFakeVideo = true;
+ }
+ else
+ mbFakeVideo = false;
+
+ OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 );
+ g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , nullptr );
+
+ GstBus *pBus = gst_element_get_bus( mpPlaybin );
+ if (mbWatchID)
+ {
+ g_source_remove(mnWatchID);
+ mbWatchID = false;
+ }
+ mnWatchID = gst_bus_add_watch( pBus, pipeline_bus_callback, this );
+ mbWatchID = true;
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " set sync handler" );
+ gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, nullptr );
+ g_object_unref( pBus );
+}
+
+
+bool Player::create( const OUString& rURL )
+{
+ bool bRet = false;
+
+ // create all the elements and link them
+
+ SAL_INFO( "avmedia.gstreamer", "create player, URL: '" << rURL << "'" );
+
+ if( mbInitialized && !rURL.isEmpty() )
+ {
+ // fakesink for pre-roll & sizing ...
+ preparePlaybin( rURL, gst_element_factory_make( "fakesink", nullptr ) );
+
+ gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
+
+ bRet = true;
+ }
+
+ if( bRet )
+ maURL = rURL;
+ else
+ maURL.clear();
+
+ return bRet;
+}
+
+void SAL_CALL Player::start()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // set the pipeline state to READY and run the loop
+ if( mbInitialized && mpPlaybin != nullptr )
+ {
+ gst_element_set_state( mpPlaybin, GST_STATE_PLAYING );
+ }
+}
+
+void SAL_CALL Player::stop()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // set the pipeline in PAUSED STATE
+ if( mpPlaybin )
+ gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "stop " << mpPlaybin );
+}
+
+sal_Bool SAL_CALL Player::isPlaying()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ bool bRet = false;
+
+ // return whether the pipeline target is PLAYING STATE or not
+ if (mbInitialized && mpPlaybin)
+ {
+ bRet = GST_STATE_TARGET(mpPlaybin) == GST_STATE_PLAYING;
+ }
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "isPlaying " << bRet );
+
+ return bRet;
+}
+
+double SAL_CALL Player::getDuration()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // slideshow checks for non-zero duration, so cheat here
+ double duration = 0.3;
+
+ if( mpPlaybin && mnDuration > 0 ) {
+ duration = mnDuration / GST_SECOND;
+ }
+
+ return duration;
+}
+
+
+void SAL_CALL Player::setMediaTime( double fTime )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ if( !mpPlaybin )
+ return;
+
+ gint64 gst_position = llround (fTime * GST_SECOND);
+
+ gst_element_seek( mpPlaybin, 1.0,
+ GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, gst_position,
+ GST_SEEK_TYPE_NONE, 0 );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "seek to: " << gst_position << " ns original: " << fTime << " s" );
+}
+
+
+double SAL_CALL Player::getMediaTime()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ double position = 0.0;
+
+ if( mpPlaybin ) {
+ // get current position in the stream
+ gint64 gst_position;
+ if( gst_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
+ position = gst_position / GST_SECOND;
+ }
+
+ return position;
+}
+
+
+void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ // TODO check how to do with GST
+ mbLooping = bSet;
+}
+
+
+sal_Bool SAL_CALL Player::isPlaybackLoop()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ // TODO check how to do with GST
+ return mbLooping;
+}
+
+
+void SAL_CALL Player::setMute( sal_Bool bSet )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "set mute: " << bSet << " muted: " << mbMuted << " unmuted volume: " << mnUnmutedVolume );
+
+ // change the volume to 0 or the unmuted volume
+ if (mpVolumeControl && mbMuted != bool(bSet))
+ {
+ double nVolume = mnUnmutedVolume;
+ if( bSet )
+ {
+ nVolume = 0.0;
+ }
+
+ g_object_set( G_OBJECT( mpVolumeControl ), "volume", nVolume, nullptr );
+
+ mbMuted = bSet;
+ }
+}
+
+
+sal_Bool SAL_CALL Player::isMute()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ return mbMuted;
+}
+
+
+void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "set volume: " << nVolumeDB << " gst volume: " << mnUnmutedVolume );
+
+ // change volume
+ if (mpVolumeControl && !mbMuted)
+ {
+ g_object_set( G_OBJECT( mpVolumeControl ), "volume", mnUnmutedVolume, nullptr );
+ }
+}
+
+
+sal_Int16 SAL_CALL Player::getVolumeDB()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ sal_Int16 nVolumeDB(0);
+
+ if (mpVolumeControl)
+ {
+ double nGstVolume = 0.0;
+
+ g_object_get( G_OBJECT( mpVolumeControl ), "volume", &nGstVolume, nullptr );
+
+ nVolumeDB = static_cast<sal_Int16>( 20.0*log10 ( nGstVolume ) );
+ }
+
+ return nVolumeDB;
+}
+
+
+awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ awt::Size aSize( 0, 0 );
+
+ if( maURL.isEmpty() )
+ {
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize - empty URL => 0x0" );
+ return aSize;
+ }
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " pre-Player::getPreferredPlayerWindowSize, member " << mnWidth << "x" << mnHeight );
+
+ osl::Condition::Result aResult = maSizeCondition.wait( std::chrono::seconds(10) );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize after waitCondition " << aResult << ", member " << mnWidth << "x" << mnHeight );
+
+ if( mnWidth != 0 && mnHeight != 0 ) {
+ aSize.Width = mnWidth;
+ aSize.Height = mnHeight;
+ }
+
+ return aSize;
+}
+
+uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ uno::Reference< ::media::XPlayerWindow > xRet;
+
+ if (rArguments.getLength() > 1)
+ rArguments[1] >>= maArea;
+
+ awt::Size aSize = getPreferredPlayerWindowSize();
+
+ if( mbFakeVideo )
+ preparePlaybin( maURL, nullptr );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "Player::createPlayerWindow " << aSize.Width << "x" << aSize.Height << " length: " << rArguments.getLength() );
+
+ if( aSize.Width > 0 && aSize.Height > 0 )
+ {
+ if (rArguments.getLength() <= 2)
+ {
+ xRet = new ::avmedia::gstreamer::Window;
+ return xRet;
+ }
+
+ sal_IntPtr pIntPtr = 0;
+ rArguments[ 2 ] >>= pIntPtr;
+ SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr );
+ if (!pParentWindow)
+ return nullptr;
+
+ const SystemEnvData* pEnvData = pParentWindow->GetSystemData();
+ if (!pEnvData)
+ return nullptr;
+
+ // tdf#124027: the position of embedded window is identical w/ the position
+ // of media object in all other vclplugs (kf5, gen), in gtk3 w/o gtksink it
+ // needs to be translated
+ if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk)
+ {
+ Point aPoint = pParentWindow->GetPosPixel();
+ maArea.X = aPoint.getX();
+ maArea.Y = aPoint.getY();
+ }
+
+ mbUseGtkSink = false;
+
+ GstElement *pVideosink = static_cast<GstElement*>(pParentWindow->CreateGStreamerSink());
+ if (pVideosink)
+ {
+ if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk)
+ mbUseGtkSink = true;
+ }
+ else
+ {
+ if (pEnvData->platform == SystemEnvData::Platform::Wayland)
+ pVideosink = gst_element_factory_make("waylandsink", "video-output");
+ else
+ pVideosink = gst_element_factory_make("autovideosink", "video-output");
+ if (!pVideosink)
+ return nullptr;
+ }
+
+ xRet = new ::avmedia::gstreamer::Window;
+
+ g_object_set(G_OBJECT(mpPlaybin), "video-sink", pVideosink, nullptr);
+ g_object_set(G_OBJECT(mpPlaybin), "force-aspect-ratio", FALSE, nullptr);
+
+ if ((rArguments.getLength() >= 4) && (rArguments[3] >>= pIntPtr) && pIntPtr)
+ {
+ auto pItem = reinterpret_cast<const avmedia::MediaItem*>(pIntPtr);
+ Graphic aGraphic = pItem->getGraphic();
+ const text::GraphicCrop& rCrop = pItem->getCrop();
+ if (!aGraphic.IsNone() && (rCrop.Bottom > 0 || rCrop.Left > 0 || rCrop.Right > 0 || rCrop.Top > 0))
+ {
+ // The media item has a non-empty cropping set. Try to crop the video accordingly.
+ Size aPref = aGraphic.GetPrefSize();
+ Size aPixel = aGraphic.GetSizePixel();
+ tools::Long nLeft = aPixel.getWidth() * rCrop.Left / aPref.getWidth();
+ tools::Long nTop = aPixel.getHeight() * rCrop.Top / aPref.getHeight();
+ tools::Long nRight = aPixel.getWidth() * rCrop.Right / aPref.getWidth();
+ tools::Long nBottom = aPixel.getHeight() * rCrop.Bottom / aPref.getHeight();
+ GstElement* pVideoFilter = gst_element_factory_make("videocrop", nullptr);
+ if (pVideoFilter)
+ {
+ g_object_set(G_OBJECT(pVideoFilter), "left", nLeft, nullptr);
+ g_object_set(G_OBJECT(pVideoFilter), "top", nTop, nullptr);
+ g_object_set(G_OBJECT(pVideoFilter), "right", nRight, nullptr);
+ g_object_set(G_OBJECT(pVideoFilter), "bottom", nBottom, nullptr);
+ g_object_set(G_OBJECT(mpPlaybin), "video-filter", pVideoFilter, nullptr);
+ }
+ }
+ }
+
+ if (!mbUseGtkSink)
+ {
+ mnWindowID = pEnvData->GetWindowHandle(pParentWindow->ImplGetFrame());
+ mpDisplay = pEnvData->pDisplay;
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "set window id to " << static_cast<int>(mnWindowID) << " XOverlay " << mpXOverlay);
+ }
+ gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
+ if (!mbUseGtkSink && mpXOverlay)
+ gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
+ }
+
+ return xRet;
+}
+
+uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ rtl::Reference<FrameGrabber> pFrameGrabber;
+ const awt::Size aPrefSize( getPreferredPlayerWindowSize() );
+
+ if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
+ pFrameGrabber = FrameGrabber::create( maURL );
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "created FrameGrabber " << pFrameGrabber.get() );
+
+ return pFrameGrabber;
+}
+
+
+OUString SAL_CALL Player::getImplementationName()
+{
+ return AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME;
+}
+
+
+sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames()
+{
+ return { AVMEDIA_GST_PLAYER_SERVICENAME };
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstplayer.hxx b/avmedia/source/gstreamer/gstplayer.hxx
new file mode 100644
index 000000000..2694ac00c
--- /dev/null
+++ b/avmedia/source/gstreamer/gstplayer.hxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <osl/conditn.hxx>
+#include "gstcommon.hxx"
+
+#include <com/sun/star/media/XPlayer.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+typedef struct _GstVideoOverlay GstVideoOverlay;
+
+namespace avmedia::gstreamer {
+
+
+typedef ::cppu::WeakComponentImplHelper< css::media::XPlayer,
+ css::lang::XServiceInfo > GstPlayer_BASE;
+
+class Player final : public ::cppu::BaseMutex,
+ public GstPlayer_BASE
+{
+public:
+
+ explicit Player();
+ virtual ~Player() override;
+
+ void preparePlaybin( std::u16string_view rURL, GstElement *pSink );
+ bool create( const OUString& rURL );
+ void processMessage( GstMessage *message );
+ GstBusSyncReply processSyncMessage( GstMessage *message );
+
+ // XPlayer
+ virtual void SAL_CALL start( ) override;
+ virtual void SAL_CALL stop( ) override;
+ virtual sal_Bool SAL_CALL isPlaying( ) override;
+ virtual double SAL_CALL getDuration( ) override;
+ virtual void SAL_CALL setMediaTime( double fTime ) override;
+ virtual double SAL_CALL getMediaTime( ) override;
+ virtual void SAL_CALL setPlaybackLoop( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isPlaybackLoop( ) override;
+ virtual void SAL_CALL setMute( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isMute( ) override;
+ virtual void SAL_CALL setVolumeDB( sal_Int16 nVolumeDB ) override;
+ virtual sal_Int16 SAL_CALL getVolumeDB( ) override;
+ virtual css::awt::Size SAL_CALL getPreferredPlayerWindowSize( ) override;
+ virtual css::uno::Reference< css::media::XPlayerWindow > SAL_CALL createPlayerWindow( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ virtual css::uno::Reference< css::media::XFrameGrabber > SAL_CALL createFrameGrabber( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // ::cppu::OComponentHelper
+ virtual void SAL_CALL disposing() final override;
+
+private:
+ OUString maURL;
+
+ // Add elements and pipeline here
+ GstElement* mpPlaybin; // the playbin is also a pipeline
+ GstElement* mpVolumeControl; // the playbin is also a pipeline
+ bool mbUseGtkSink;
+ bool mbFakeVideo;
+
+ gdouble mnUnmutedVolume;
+ bool mbMuted;
+ bool mbLooping;
+ bool mbInitialized;
+
+ void* mpDisplay;
+ tools::Long mnWindowID;
+ GstVideoOverlay* mpXOverlay;
+ gint64 mnDuration;
+ int mnWidth;
+ int mnHeight;
+
+ css::awt::Rectangle maArea; // Area of the player window.
+
+ guint mnWatchID;
+ bool mbWatchID;
+
+ osl::Condition maSizeCondition;
+};
+
+} // namespace avmedia::gstreamer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstwindow.cxx b/avmedia/source/gstreamer/gstwindow.cxx
new file mode 100644
index 000000000..5862443da
--- /dev/null
+++ b/avmedia/source/gstreamer/gstwindow.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/awt/SystemPointer.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include "gstwindow.hxx"
+
+constexpr OUStringLiteral AVMEDIA_GST_WINDOW_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.Window_GStreamer";
+constexpr OUStringLiteral AVMEDIA_GST_WINDOW_SERVICENAME = u"com.sun.star.media.Window_GStreamer";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+// Window
+
+
+Window::Window() :
+ meZoomLevel( media::ZoomLevel_NOT_AVAILABLE )
+{
+}
+
+Window::~Window()
+{
+}
+
+// XPlayerWindow
+
+
+void SAL_CALL Window::update( )
+{
+}
+
+sal_Bool SAL_CALL Window::setZoomLevel( media::ZoomLevel eZoomLevel )
+{
+ bool bRet = false;
+
+ if( meZoomLevel != media::ZoomLevel_NOT_AVAILABLE &&
+ eZoomLevel != media::ZoomLevel_NOT_AVAILABLE )
+ {
+ if( eZoomLevel != meZoomLevel )
+ {
+ meZoomLevel = eZoomLevel;
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+media::ZoomLevel SAL_CALL Window::getZoomLevel( )
+{
+ return meZoomLevel;
+}
+
+void SAL_CALL Window::setPointerType( sal_Int32 /*nPointerType*/ )
+{
+}
+
+// XWindow
+
+
+void SAL_CALL Window::setPosSize( sal_Int32 /*X*/, sal_Int32 /*Y*/, sal_Int32 /*Width*/, sal_Int32 /*Height*/, sal_Int16 /*Flags*/ )
+{
+}
+
+awt::Rectangle SAL_CALL Window::getPosSize()
+{
+ awt::Rectangle aRet;
+
+ aRet.X = aRet.Y = 0;
+ aRet.Width = 320;
+ aRet.Height = 240;
+
+ return aRet;
+}
+
+void SAL_CALL Window::setVisible( sal_Bool /*bVisible*/ )
+{
+}
+
+void SAL_CALL Window::setEnable( sal_Bool /*bEnable*/ )
+{
+}
+
+void SAL_CALL Window::setFocus( )
+{
+}
+
+void SAL_CALL Window::addWindowListener( const uno::Reference< awt::XWindowListener >& )
+{
+}
+
+void SAL_CALL Window::removeWindowListener( const uno::Reference< awt::XWindowListener >& )
+{
+}
+
+void SAL_CALL Window::addFocusListener( const uno::Reference< awt::XFocusListener >& )
+{
+}
+
+void SAL_CALL Window::removeFocusListener( const uno::Reference< awt::XFocusListener >& )
+{
+}
+
+void SAL_CALL Window::addKeyListener( const uno::Reference< awt::XKeyListener >& )
+{
+}
+
+void SAL_CALL Window::removeKeyListener( const uno::Reference< awt::XKeyListener >& )
+{
+}
+
+void SAL_CALL Window::addMouseListener( const uno::Reference< awt::XMouseListener >& )
+{
+}
+
+void SAL_CALL Window::removeMouseListener( const uno::Reference< awt::XMouseListener >& )
+{
+}
+
+void SAL_CALL Window::addMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& )
+{
+}
+
+void SAL_CALL Window::removeMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& )
+{
+}
+
+void SAL_CALL Window::addPaintListener( const uno::Reference< awt::XPaintListener >& )
+{
+}
+
+void SAL_CALL Window::removePaintListener( const uno::Reference< awt::XPaintListener >& )
+{
+}
+
+// XComponent
+
+
+void SAL_CALL Window::dispose( )
+{
+}
+
+void SAL_CALL Window::addEventListener( const uno::Reference< lang::XEventListener >& )
+{
+}
+
+void SAL_CALL Window::removeEventListener( const uno::Reference< lang::XEventListener >& )
+{
+}
+
+// XServiceInfo
+
+
+OUString SAL_CALL Window::getImplementationName( )
+{
+ return AVMEDIA_GST_WINDOW_IMPLEMENTATIONNAME;
+}
+
+sal_Bool SAL_CALL Window::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL Window::getSupportedServiceNames( )
+{
+ return { AVMEDIA_GST_WINDOW_SERVICENAME };
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstwindow.hxx b/avmedia/source/gstreamer/gstwindow.hxx
new file mode 100644
index 000000000..ff8a7cc91
--- /dev/null
+++ b/avmedia/source/gstreamer/gstwindow.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/interfacecontainer.h>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/media/XPlayerWindow.hpp>
+
+namespace avmedia::gstreamer {
+
+class Player;
+
+class Window : public ::cppu::WeakImplHelper< css::media::XPlayerWindow,
+ css::lang::XServiceInfo >
+{
+public:
+
+ explicit Window();
+ virtual ~Window() override;
+
+ // XPlayerWindow
+ virtual void SAL_CALL update( ) override;
+ virtual sal_Bool SAL_CALL setZoomLevel( css::media::ZoomLevel ZoomLevel ) override;
+ virtual css::media::ZoomLevel SAL_CALL getZoomLevel( ) override;
+ virtual void SAL_CALL setPointerType( sal_Int32 nPointerType ) override;
+
+ // XWindow
+ virtual void SAL_CALL setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) override;
+ virtual css::awt::Rectangle SAL_CALL getPosSize( ) override;
+ virtual void SAL_CALL setVisible( sal_Bool Visible ) override;
+ virtual void SAL_CALL setEnable( sal_Bool Enable ) override;
+ virtual void SAL_CALL setFocus( ) override;
+ virtual void SAL_CALL addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+ virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+
+ css::media::ZoomLevel meZoomLevel;
+};
+
+} // namespace avmedia::gstreamer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gtk/avmediagtk.component b/avmedia/source/gtk/avmediagtk.component
new file mode 100644
index 000000000..e3930dc9e
--- /dev/null
+++ b/avmedia/source/gtk/avmediagtk.component
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+-->
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.media.Manager_Gtk"
+ constructor="com_sun_star_comp_media_Manager_Gtk_get_implementation">
+ <service name="com.sun.star.comp.avmedia.Manager_Gtk"/>
+ </implementation>
+</component>
diff --git a/avmedia/source/gtk/gstwindow.cxx b/avmedia/source/gtk/gstwindow.cxx
new file mode 100644
index 000000000..48c70df98
--- /dev/null
+++ b/avmedia/source/gtk/gstwindow.cxx
@@ -0,0 +1,12 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "../gstreamer/gstwindow.cxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gtk/gtkmanager.cxx b/avmedia/source/gtk/gtkmanager.cxx
new file mode 100644
index 000000000..5beff87e1
--- /dev/null
+++ b/avmedia/source/gtk/gtkmanager.cxx
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include "gtkmanager.hxx"
+#include "gtkplayer.hxx"
+
+#include <tools/urlobj.hxx>
+#include <rtl/ref.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gtk
+{
+Manager::Manager() {}
+
+Manager::~Manager() {}
+
+uno::Reference<media::XPlayer> SAL_CALL Manager::createPlayer(const OUString& rURL)
+{
+ const INetURLObject aURL(rURL);
+ OUString sMainURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
+
+ rtl::Reference<GtkPlayer> xPlayer(new GtkPlayer);
+ if (!xPlayer->create(sMainURL))
+ xPlayer.clear();
+ return xPlayer;
+}
+
+OUString SAL_CALL Manager::getImplementationName()
+{
+ return "com.sun.star.comp.avmedia.ManagerGtk";
+}
+
+sal_Bool SAL_CALL Manager::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL Manager::getSupportedServiceNames()
+{
+ return { "com.sun.star.media.Manager" };
+}
+
+} // namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_media_Manager_Gtk_get_implementation(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new avmedia::gtk::Manager());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/avmedia/source/gtk/gtkmanager.hxx b/avmedia/source/gtk/gtkmanager.hxx
new file mode 100644
index 000000000..9cf6d93f5
--- /dev/null
+++ b/avmedia/source/gtk/gtkmanager.hxx
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/media/XManager.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+namespace avmedia::gtk
+{
+class Manager : public cppu::WeakImplHelper<css::media::XManager, css::lang::XServiceInfo>
+{
+public:
+ explicit Manager();
+ virtual ~Manager() override;
+
+ virtual css::uno::Reference<css::media::XPlayer>
+ SAL_CALL createPlayer(const OUString& aURL) override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/avmedia/source/gtk/gtkplayer.cxx b/avmedia/source/gtk/gtkplayer.cxx
new file mode 100644
index 000000000..b988e7f19
--- /dev/null
+++ b/avmedia/source/gtk/gtkplayer.cxx
@@ -0,0 +1,461 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/config.h>
+
+#include <mutex>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <rtl/string.hxx>
+#include <tools/link.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syschild.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/timer.hxx>
+
+#include <gstwindow.hxx>
+#include "gtkplayer.hxx"
+
+#include <gtk/gtk.h>
+
+constexpr OUStringLiteral AVMEDIA_GTK_PLAYER_IMPLEMENTATIONNAME
+ = u"com.sun.star.comp.avmedia.Player_Gtk";
+constexpr OUStringLiteral AVMEDIA_GTK_PLAYER_SERVICENAME = u"com.sun.star.media.Player_Gtk";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gtk
+{
+GtkPlayer::GtkPlayer()
+ : GtkPlayer_BASE(m_aMutex)
+ , m_lListener(m_aMutex)
+ , m_pStream(nullptr)
+ , m_pVideo(nullptr)
+ , m_nNotifySignalId(0)
+ , m_nInvalidateSizeSignalId(0)
+ , m_nTimeoutId(0)
+ , m_nUnmutedVolume(0)
+{
+}
+
+GtkPlayer::~GtkPlayer() { disposing(); }
+
+static gboolean gtk_media_stream_unref(gpointer user_data)
+{
+ g_object_unref(user_data);
+ return FALSE;
+}
+
+void GtkPlayer::cleanup()
+{
+ if (m_pVideo)
+ {
+ gtk_widget_unparent(m_pVideo);
+ m_pVideo = nullptr;
+ }
+
+ if (m_pStream)
+ {
+ uninstallNotify();
+
+ // shouldn't have to attempt this unref on idle, but with gtk4-4.4.1 I get
+ // intermittent "instance of invalid non-instantiatable type '(null)'"
+ // on some mysterious gst dbus callback
+ if (g_main_context_default())
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, gtk_media_stream_unref, m_pStream, nullptr);
+ else
+ g_object_unref(m_pStream);
+ m_pStream = nullptr;
+ }
+}
+
+void SAL_CALL GtkPlayer::disposing()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ stop();
+
+ cleanup();
+}
+
+static void do_notify(GtkPlayer* pThis)
+{
+ rtl::Reference<GtkPlayer> xThis(pThis);
+ xThis->notifyListeners();
+ xThis->uninstallNotify();
+}
+
+static void invalidate_size_cb(GdkPaintable* /*pPaintable*/, GtkPlayer* pThis) { do_notify(pThis); }
+
+static void notify_cb(GtkMediaStream* /*pStream*/, GParamSpec* pspec, GtkPlayer* pThis)
+{
+ if (g_str_equal(pspec->name, "prepared") || g_str_equal(pspec->name, "error"))
+ do_notify(pThis);
+}
+
+static bool timeout_cb(GtkPlayer* pThis)
+{
+ do_notify(pThis);
+ return false;
+}
+
+void GtkPlayer::installNotify()
+{
+ if (m_nNotifySignalId)
+ return;
+ m_nNotifySignalId = g_signal_connect(m_pStream, "notify", G_CALLBACK(notify_cb), this);
+ // notify should be enough, but there is an upstream bug so also try "invalidate-size" and add a timeout for
+ // audio-only case where that won't happen, see: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4513
+ m_nInvalidateSizeSignalId
+ = g_signal_connect(m_pStream, "invalidate-size", G_CALLBACK(invalidate_size_cb), this);
+ m_nTimeoutId = g_timeout_add_seconds(10, G_SOURCE_FUNC(timeout_cb), this);
+}
+
+void GtkPlayer::uninstallNotify()
+{
+ if (!m_nNotifySignalId)
+ return;
+ g_signal_handler_disconnect(m_pStream, m_nNotifySignalId);
+ m_nNotifySignalId = 0;
+ g_signal_handler_disconnect(m_pStream, m_nInvalidateSizeSignalId);
+ m_nInvalidateSizeSignalId = 0;
+ g_source_remove(m_nTimeoutId);
+ m_nTimeoutId = 0;
+}
+
+bool GtkPlayer::create(const OUString& rURL)
+{
+ bool bRet = false;
+
+ cleanup();
+
+ if (!rURL.isEmpty())
+ {
+ GFile* pFile = g_file_new_for_uri(OUStringToOString(rURL, RTL_TEXTENCODING_UTF8).getStr());
+ m_pStream = gtk_media_file_new_for_file(pFile);
+ g_object_unref(pFile);
+
+ bRet = gtk_media_stream_get_error(m_pStream) == nullptr;
+ }
+
+ if (bRet)
+ m_aURL = rURL;
+ else
+ m_aURL.clear();
+
+ return bRet;
+}
+
+void GtkPlayer::notifyListeners()
+{
+ comphelper::OInterfaceContainerHelper2* pContainer
+ = m_lListener.getContainer(cppu::UnoType<css::media::XPlayerListener>::get());
+ if (!pContainer)
+ return;
+
+ css::lang::EventObject aEvent;
+ aEvent.Source = static_cast<cppu::OWeakObject*>(this);
+
+ comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer);
+ while (pIterator.hasMoreElements())
+ {
+ css::uno::Reference<css::media::XPlayerListener> xListener(
+ static_cast<css::media::XPlayerListener*>(pIterator.next()));
+ xListener->preferredPlayerWindowSizeAvailable(aEvent);
+ }
+}
+
+void SAL_CALL GtkPlayer::start()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ if (m_pStream)
+ gtk_media_stream_play(m_pStream);
+}
+
+void SAL_CALL GtkPlayer::stop()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ if (m_pStream)
+ gtk_media_stream_pause(m_pStream);
+}
+
+sal_Bool SAL_CALL GtkPlayer::isPlaying()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ bool bRet = false;
+
+ if (m_pStream)
+ bRet = gtk_media_stream_get_playing(m_pStream);
+
+ return bRet;
+}
+
+double SAL_CALL GtkPlayer::getDuration()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ double duration = 0.0;
+
+ if (m_pStream)
+ duration = gtk_media_stream_get_duration(m_pStream) / 1000000.0;
+
+ return duration;
+}
+
+void SAL_CALL GtkPlayer::setMediaTime(double fTime)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ if (!m_pStream)
+ return;
+
+ gint64 gst_position = llround(fTime * 1000000);
+
+ gtk_media_stream_seek(m_pStream, gst_position);
+
+ // on resetting back to zero the reported timestamp doesn't seem to get
+ // updated in a reasonable time, so on zero just force an update of
+ // timestamp to 0
+ if (gst_position == 0 && gtk_media_stream_is_prepared(m_pStream))
+ gtk_media_stream_update(m_pStream, gst_position);
+}
+
+double SAL_CALL GtkPlayer::getMediaTime()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ double position = 0.0;
+
+ if (m_pStream)
+ position = gtk_media_stream_get_timestamp(m_pStream) / 1000000.0;
+
+ return position;
+}
+
+void SAL_CALL GtkPlayer::setPlaybackLoop(sal_Bool bSet)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ gtk_media_stream_set_loop(m_pStream, bSet);
+}
+
+sal_Bool SAL_CALL GtkPlayer::isPlaybackLoop()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ return gtk_media_stream_get_loop(m_pStream);
+}
+
+// gtk4-4.4.1 docs state: "Muting a stream will cause no audio to be played, but
+// it does not modify the volume. This means that muting and then unmuting the
+// stream will restore the volume settings." but that doesn't seem to be my
+// experience at all
+void SAL_CALL GtkPlayer::setMute(sal_Bool bSet)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ bool bMuted = gtk_media_stream_get_muted(m_pStream);
+ if (bMuted == static_cast<bool>(bSet))
+ return;
+ gtk_media_stream_set_muted(m_pStream, bSet);
+ if (!bSet)
+ setVolumeDB(m_nUnmutedVolume);
+}
+
+sal_Bool SAL_CALL GtkPlayer::isMute()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ return gtk_media_stream_get_muted(m_pStream);
+}
+
+void SAL_CALL GtkPlayer::setVolumeDB(sal_Int16 nVolumeDB)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ // range is -40 for silence to 0 for full volume
+ m_nUnmutedVolume = std::clamp<sal_Int16>(nVolumeDB, -40, 0);
+ double fValue = (m_nUnmutedVolume + 40) / 40.0;
+ gtk_media_stream_set_volume(m_pStream, fValue);
+}
+
+sal_Int16 SAL_CALL GtkPlayer::getVolumeDB()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ if (gtk_media_stream_get_muted(m_pStream))
+ return m_nUnmutedVolume;
+
+ double fVolume = gtk_media_stream_get_volume(m_pStream);
+
+ m_nUnmutedVolume = (fVolume * 40) - 40;
+
+ return m_nUnmutedVolume;
+}
+
+awt::Size SAL_CALL GtkPlayer::getPreferredPlayerWindowSize()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ awt::Size aSize(0, 0);
+
+ if (m_pStream)
+ {
+ aSize.Width = gdk_paintable_get_intrinsic_width(GDK_PAINTABLE(m_pStream));
+ aSize.Height = gdk_paintable_get_intrinsic_height(GDK_PAINTABLE(m_pStream));
+ }
+
+ return aSize;
+}
+
+uno::Reference<::media::XPlayerWindow>
+ SAL_CALL GtkPlayer::createPlayerWindow(const uno::Sequence<uno::Any>& rArguments)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ uno::Reference<::media::XPlayerWindow> xRet;
+
+ if (rArguments.getLength() > 1)
+ rArguments[1] >>= m_aArea;
+
+ if (rArguments.getLength() <= 2)
+ {
+ xRet = new ::avmedia::gstreamer::Window;
+ return xRet;
+ }
+
+ sal_IntPtr pIntPtr = 0;
+ rArguments[2] >>= pIntPtr;
+ SystemChildWindow* pParentWindow = reinterpret_cast<SystemChildWindow*>(pIntPtr);
+ if (!pParentWindow)
+ return nullptr;
+
+ const SystemEnvData* pEnvData = pParentWindow->GetSystemData();
+ if (!pEnvData)
+ return nullptr;
+
+ m_pVideo = gtk_picture_new_for_paintable(GDK_PAINTABLE(m_pStream));
+ gtk_picture_set_keep_aspect_ratio(GTK_PICTURE(m_pVideo), false);
+ gtk_widget_set_can_target(m_pVideo, false);
+ gtk_widget_set_vexpand(m_pVideo, true);
+ gtk_widget_set_hexpand(m_pVideo, true);
+
+ GtkWidget* pParent = static_cast<GtkWidget*>(pEnvData->pWidget);
+ gtk_widget_set_can_target(pParent, false);
+ gtk_grid_attach(GTK_GRID(pParent), m_pVideo, 0, 0, 1, 1);
+ gtk_widget_show(m_pVideo);
+ gtk_widget_show(pParent);
+
+ xRet = new ::avmedia::gstreamer::Window;
+
+ return xRet;
+}
+
+void SAL_CALL
+GtkPlayer::addPlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener)
+{
+ m_lListener.addInterface(cppu::UnoType<css::media::XPlayerListener>::get(), rListener);
+ if (gtk_media_stream_is_prepared(m_pStream))
+ {
+ css::lang::EventObject aEvent;
+ aEvent.Source = static_cast<cppu::OWeakObject*>(this);
+ rListener->preferredPlayerWindowSizeAvailable(aEvent);
+ }
+ else
+ installNotify();
+}
+
+void SAL_CALL
+GtkPlayer::removePlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener)
+{
+ m_lListener.removeInterface(cppu::UnoType<css::media::XPlayerListener>::get(), rListener);
+}
+
+namespace
+{
+class GtkFrameGrabber : public ::cppu::WeakImplHelper<css::media::XFrameGrabber>
+{
+private:
+ awt::Size m_aSize;
+ GtkMediaStream* m_pStream;
+
+public:
+ GtkFrameGrabber(GtkMediaStream* pStream, const awt::Size& rSize)
+ : m_aSize(rSize)
+ , m_pStream(pStream)
+ {
+ g_object_ref(m_pStream);
+ }
+
+ virtual ~GtkFrameGrabber() override { g_object_unref(m_pStream); }
+
+ // XFrameGrabber
+ virtual css::uno::Reference<css::graphic::XGraphic>
+ SAL_CALL grabFrame(double fMediaTime) override
+ {
+ gint64 gst_position = llround(fMediaTime * 1000000);
+ gtk_media_stream_seek(m_pStream, gst_position);
+
+ cairo_surface_t* surface
+ = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, m_aSize.Width, m_aSize.Height);
+
+ GtkSnapshot* snapshot = gtk_snapshot_new();
+ gdk_paintable_snapshot(GDK_PAINTABLE(m_pStream), snapshot, m_aSize.Width, m_aSize.Height);
+ GskRenderNode* node = gtk_snapshot_free_to_node(snapshot);
+
+ cairo_t* cr = cairo_create(surface);
+ gsk_render_node_draw(node, cr);
+ cairo_destroy(cr);
+
+ gsk_render_node_unref(node);
+
+ std::unique_ptr<BitmapEx> xBitmap(
+ vcl::bitmap::CreateFromCairoSurface(Size(m_aSize.Width, m_aSize.Height), surface));
+
+ cairo_surface_destroy(surface);
+
+ return Graphic(*xBitmap).GetXGraphic();
+ }
+};
+}
+
+uno::Reference<media::XFrameGrabber> SAL_CALL GtkPlayer::createFrameGrabber()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ rtl::Reference<GtkFrameGrabber> xFrameGrabber;
+
+ const awt::Size aPrefSize(getPreferredPlayerWindowSize());
+
+ if (aPrefSize.Width > 0 && aPrefSize.Height > 0)
+ xFrameGrabber.set(new GtkFrameGrabber(m_pStream, aPrefSize));
+
+ return xFrameGrabber;
+}
+
+OUString SAL_CALL GtkPlayer::getImplementationName()
+{
+ return AVMEDIA_GTK_PLAYER_IMPLEMENTATIONNAME;
+}
+
+sal_Bool SAL_CALL GtkPlayer::supportsService(const OUString& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence<OUString> SAL_CALL GtkPlayer::getSupportedServiceNames()
+{
+ return { AVMEDIA_GTK_PLAYER_SERVICENAME };
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/avmedia/source/gtk/gtkplayer.hxx b/avmedia/source/gtk/gtkplayer.hxx
new file mode 100644
index 000000000..46e416e79
--- /dev/null
+++ b/avmedia/source/gtk/gtkplayer.hxx
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/media/XPlayer.hpp>
+#include <com/sun/star/media/XPlayerNotifier.hpp>
+#include <comphelper/multicontainer2.hxx>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+typedef struct _GtkMediaStream GtkMediaStream;
+typedef struct _GtkWidget GtkWidget;
+
+namespace avmedia::gtk
+{
+typedef cppu::WeakComponentImplHelper<css::media::XPlayer, css::media::XPlayerNotifier,
+ css::lang::XServiceInfo>
+ GtkPlayer_BASE;
+
+class GtkPlayer final : public cppu::BaseMutex, public GtkPlayer_BASE
+{
+public:
+ explicit GtkPlayer();
+ virtual ~GtkPlayer() override;
+
+ bool create(const OUString& rURL);
+
+ virtual void SAL_CALL start() override;
+ virtual void SAL_CALL stop() override;
+ virtual sal_Bool SAL_CALL isPlaying() override;
+ virtual double SAL_CALL getDuration() override;
+ virtual void SAL_CALL setMediaTime(double fTime) override;
+ virtual double SAL_CALL getMediaTime() override;
+ virtual void SAL_CALL setPlaybackLoop(sal_Bool bSet) override;
+ virtual sal_Bool SAL_CALL isPlaybackLoop() override;
+ virtual void SAL_CALL setMute(sal_Bool bSet) override;
+ virtual sal_Bool SAL_CALL isMute() override;
+ virtual void SAL_CALL setVolumeDB(sal_Int16 nVolumeDB) override;
+ virtual sal_Int16 SAL_CALL getVolumeDB() override;
+ virtual css::awt::Size SAL_CALL getPreferredPlayerWindowSize() override;
+ virtual css::uno::Reference<css::media::XPlayerWindow>
+ SAL_CALL createPlayerWindow(const css::uno::Sequence<css::uno::Any>& rArgs) override;
+ virtual css::uno::Reference<css::media::XFrameGrabber> SAL_CALL createFrameGrabber() override;
+
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+
+ virtual void SAL_CALL
+ addPlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener) override;
+ virtual void SAL_CALL removePlayerListener(
+ const css::uno::Reference<css::media::XPlayerListener>& rListener) override;
+
+ virtual void SAL_CALL disposing() final override;
+
+ void notifyListeners();
+ void installNotify();
+ void uninstallNotify();
+
+private:
+ void cleanup();
+
+ comphelper::OMultiTypeInterfaceContainerHelper2 m_lListener;
+
+ OUString m_aURL;
+ css::awt::Rectangle m_aArea; // Area of the player window.
+ GtkMediaStream* m_pStream;
+ GtkWidget* m_pVideo;
+ unsigned long m_nNotifySignalId;
+ unsigned long m_nInvalidateSizeSignalId;
+ unsigned long m_nTimeoutId;
+ sal_Int16 m_nUnmutedVolume;
+};
+
+} // namespace avmedia::gtk
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/avmedia/source/inc/mediamisc.hxx b/avmedia/source/inc/mediamisc.hxx
new file mode 100644
index 000000000..061a7177f
--- /dev/null
+++ b/avmedia/source/inc/mediamisc.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <unotools/resmgr.hxx>
+
+#ifdef _WIN32
+#define AVMEDIA_MANAGER_SERVICE_NAME "com.sun.star.comp.avmedia.Manager_DirectX"
+#else
+#ifdef MACOSX
+#define AVMEDIA_MANAGER_SERVICE_NAME "com.sun.star.comp.avmedia.Manager_MacAVF"
+#else
+#define AVMEDIA_MANAGER_SERVICE_NAME "com.sun.star.comp.avmedia.Manager_GStreamer"
+#endif
+#endif
+
+// Mime types
+inline constexpr OUStringLiteral AVMEDIA_MIMETYPE_COMMON = u"application/vnd.sun.star.media";
+
+inline OUString AvmResId(TranslateId aId)
+{
+ return Translate::get(aId, Translate::Create("avmedia"));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/avmediaMacAVF.component b/avmedia/source/macavf/avmediaMacAVF.component
new file mode 100644
index 000000000..3cb9966c1
--- /dev/null
+++ b/avmedia/source/macavf/avmediaMacAVF.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.avmedia.Manager_MacAVF"
+ constructor="com_sun_star_comp_avmedia_Manager_MacAVF_get_implementation">
+ <service name="com.sun.star.media.Manager_MacAVF"/>
+ </implementation>
+</component>
diff --git a/avmedia/source/macavf/framegrabber.hxx b/avmedia/source/macavf/framegrabber.hxx
new file mode 100644
index 000000000..5e0c749d9
--- /dev/null
+++ b/avmedia/source/macavf/framegrabber.hxx
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "macavfcommon.hxx"
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/media/XFrameGrabber.hpp>
+
+namespace avmedia::macavf {
+
+class FrameGrabber : public ::cppu::WeakImplHelper< css::media::XFrameGrabber,
+ css::lang::XServiceInfo >
+{
+public:
+
+ explicit FrameGrabber();
+ virtual ~FrameGrabber() override;
+
+ bool create( AVAsset* pMovie );
+
+ // XFrameGrabber
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL grabFrame( double fMediaTime ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+
+ AVAssetImageGenerator* mpImageGen;
+};
+
+} // namespace avmedia::macavf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/framegrabber.mm b/avmedia/source/macavf/framegrabber.mm
new file mode 100644
index 000000000..e0b8bad61
--- /dev/null
+++ b/avmedia/source/macavf/framegrabber.mm
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "framegrabber.hxx"
+#include "player.hxx"
+
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <unotools/localfilehelper.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia::macavf {
+
+FrameGrabber::FrameGrabber()
+: mpImageGen( nullptr )
+{}
+
+
+FrameGrabber::~FrameGrabber()
+{
+ if( mpImageGen )
+ CFRelease( mpImageGen );
+}
+
+
+bool FrameGrabber::create( AVAsset* pMovie )
+{
+ if( [[pMovie tracksWithMediaType:AVMediaTypeVideo] count] == 0)
+ {
+ SAL_WARN("avmedia", "AVGrabber::create() found no video content!" );
+ return false;
+ }
+
+ mpImageGen = [AVAssetImageGenerator assetImageGeneratorWithAsset:pMovie];
+ CFRetain( mpImageGen );
+ return true;
+}
+
+
+uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime )
+{
+ uno::Reference< graphic::XGraphic > xRet;
+ if( !mpImageGen )
+ return xRet;
+
+ // get the requested image from the movie
+ CGImage* pCGImage = [mpImageGen copyCGImageAtTime:CMTimeMakeWithSeconds(fMediaTime,1000) actualTime:nullptr error:nullptr];
+
+ // convert the image to a TIFF-formatted byte-array
+ CFMutableDataRef pCFData = CFDataCreateMutable( kCFAllocatorDefault, 0 );
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH // kUTTypeTIFF (12.0)
+ CGImageDestination* pCGImgDest = CGImageDestinationCreateWithData( pCFData, kUTTypeTIFF, 1, nullptr );
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ CGImageDestinationAddImage( pCGImgDest, pCGImage, nullptr );
+ CGImageDestinationFinalize( pCGImgDest );
+ CFRelease( pCGImgDest );
+ const CFIndex nBitmapLen = CFDataGetLength( pCFData );
+ UInt8 * pBitmapBytes = const_cast<UInt8 *>(CFDataGetBytePtr( pCFData ));
+
+ // convert the image into the return-value type which is a graphic::XGraphic
+ SvMemoryStream aMemStm( pBitmapBytes, nBitmapLen, StreamMode::READ | StreamMode::WRITE );
+ Graphic aGraphic;
+ if( GraphicConverter::Import( aMemStm, aGraphic, ConvertDataFormat::TIF ) == ERRCODE_NONE )
+ xRet = aGraphic.GetXGraphic();
+
+ // clean up resources
+ CFRelease( pCFData );
+ return xRet;
+}
+
+
+OUString SAL_CALL FrameGrabber::getImplementationName( )
+{
+ return AVMEDIA_MACAVF_FRAMEGRABBER_IMPLEMENTATIONNAME;
+}
+
+sal_Bool SAL_CALL FrameGrabber::supportsService( const OUString& ServiceName )
+{
+ return ServiceName == AVMEDIA_MACAVF_FRAMEGRABBER_SERVICENAME;
+}
+
+uno::Sequence< OUString > SAL_CALL FrameGrabber::getSupportedServiceNames( )
+{
+ return { AVMEDIA_MACAVF_FRAMEGRABBER_SERVICENAME };
+}
+
+} // namespace avmedia::macavf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/macavfcommon.hxx b/avmedia/source/macavf/macavfcommon.hxx
new file mode 100644
index 000000000..b031f1ecc
--- /dev/null
+++ b/avmedia/source/macavf/macavfcommon.hxx
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <premac.h>
+#import <Cocoa/Cocoa.h>
+#import <AVFoundation/AVFoundation.h>
+#include <postmac.h>
+
+#include <unordered_map>
+
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/factory.hxx>
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/media/XManager.hpp>
+
+
+#define AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_MacAVF"
+#define AVMEDIA_MACAVF_PLAYER_SERVICENAME "com.sun.star.media.Player_MacAVF"
+
+#define AVMEDIA_MACAVF_WINDOW_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Window_MacAVF"
+#define AVMEDIA_MACAVF_WINDOW_SERVICENAME "com.sun.star.media.Window_MacAVF"
+
+#define AVMEDIA_MACAVF_FRAMEGRABBER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.FrameGrabber_MacAVF"
+#define AVMEDIA_MACAVF_FRAMEGRABBER_SERVICENAME "com.sun.star.media.FrameGrabber_MacAVF"
+
+
+// MacAVObserver handles the notifications used in the AVFoundation framework
+
+namespace avmedia::macavf { class MacAVObserverHandler; }
+
+typedef std::unordered_map<NSObject*,avmedia::macavf::MacAVObserverHandler*> HandlersForObject;
+
+@interface MacAVObserverObject : NSObject
+{
+ HandlersForObject maHandlersForObject;
+}
+- (void)observeValueForKeyPath:(NSString*)pKeyPath ofObject:(id)pObject change:(NSDictionary*)pChangeDict context:(void*)pContext;
+- (void)onNotification:(NSNotification*)pNotification;
+@end
+
+namespace avmedia::macavf {
+
+class MacAVObserverHandler
+{
+private:
+ static MacAVObserverObject* mpMacAVObserverObject;
+public:
+ virtual ~MacAVObserverHandler() {}
+ static MacAVObserverObject* getObserver();
+ virtual bool handleObservation( NSString* pKeyPath ) = 0;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/manager.hxx b/avmedia/source/macavf/manager.hxx
new file mode 100644
index 000000000..9072b3800
--- /dev/null
+++ b/avmedia/source/macavf/manager.hxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "macavfcommon.hxx"
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/media/XManager.hpp>
+
+
+namespace avmedia::macavf {
+
+class Manager : public ::cppu::WeakImplHelper< css::media::XManager,
+ css::lang::XServiceInfo >
+{
+public:
+
+ Manager();
+ virtual ~Manager() override;
+
+ // XManager
+ virtual css::uno::Reference< css::media::XPlayer > SAL_CALL createPlayer( const OUString& aURL ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+} // namespace avmedia::macavf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/manager.mm b/avmedia/source/macavf/manager.mm
new file mode 100644
index 000000000..d240bb27a
--- /dev/null
+++ b/avmedia/source/macavf/manager.mm
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "manager.hxx"
+#include "player.hxx"
+#include <tools/urlobj.hxx>
+#include <osl/diagnose.h>
+#include <rtl/ref.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia::macavf {
+
+Manager::Manager()
+{
+}
+
+
+Manager::~Manager()
+{}
+
+
+uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL )
+{
+ rtl::Reference<Player> xPlayer( new Player() );
+ INetURLObject aURL( rURL );
+
+ if( !xPlayer->create( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ) )
+ return {};
+
+ return uno::Reference<media::XPlayer>(xPlayer);
+}
+
+
+OUString SAL_CALL Manager::getImplementationName( )
+{
+ return "com.sun.star.comp.avmedia.Manager_MacAVF";
+}
+
+
+sal_Bool SAL_CALL Manager::supportsService( const OUString& ServiceName )
+{
+ return ServiceName == "com.sun.star.media.Manager_MacAVF";
+}
+
+
+uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames( )
+{
+ return { "com.sun.star.media.Manager_MacAVF" };
+}
+
+} // namespace avmedia::macavf
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_avmedia_Manager_MacAVF_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new ::avmedia::macavf::Manager());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/player.hxx b/avmedia/source/macavf/player.hxx
new file mode 100644
index 000000000..3dc41b347
--- /dev/null
+++ b/avmedia/source/macavf/player.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/conditn.h>
+#include "macavfcommon.hxx"
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/media/XPlayer.hpp>
+
+namespace avmedia::macavf {
+
+class Player
+: public MacAVObserverHandler
+, public ::cppu::WeakImplHelper< css::media::XPlayer,
+ css::lang::XServiceInfo >
+{
+public:
+ explicit Player();
+ virtual ~Player() override;
+
+ bool create( const OUString& rURL );
+ bool create( AVAsset* );
+
+ // XPlayer
+ virtual void SAL_CALL start() override;
+ virtual void SAL_CALL stop() override;
+ virtual sal_Bool SAL_CALL isPlaying() override;
+ virtual double SAL_CALL getDuration() override;
+ virtual void SAL_CALL setMediaTime( double fTime ) override;
+ virtual double SAL_CALL getMediaTime() override;
+ /// @throws css::uno::RuntimeException
+ virtual void setStopTime( double fTime );
+ /// @throws css::uno::RuntimeException
+ virtual double getStopTime();
+ virtual void SAL_CALL setPlaybackLoop( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isPlaybackLoop() override;
+ virtual void SAL_CALL setMute( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isMute() override;
+ virtual void SAL_CALL setVolumeDB( sal_Int16 nVolumeDB ) override;
+ virtual sal_Int16 SAL_CALL getVolumeDB() override;
+ virtual css::awt::Size SAL_CALL getPreferredPlayerWindowSize( ) override;
+ virtual css::uno::Reference< css::media::XPlayerWindow > SAL_CALL createPlayerWindow( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ virtual css::uno::Reference< css::media::XFrameGrabber > SAL_CALL createFrameGrabber( ) override;
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ AVPlayer* getAVPlayer() const { return mpPlayer; }
+ virtual bool handleObservation( NSString* pKeyPath ) override;
+
+private:
+
+ AVPlayer* mpPlayer;
+
+ float mfUnmutedVolume;
+ double mfStopTime;
+
+ bool mbMuted;
+ bool mbLooping;
+};
+
+} // namespace avmedia::macavf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/player.mm b/avmedia/source/macavf/player.mm
new file mode 100644
index 000000000..401cba74c
--- /dev/null
+++ b/avmedia/source/macavf/player.mm
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "player.hxx"
+#include "framegrabber.hxx"
+#include "window.hxx"
+#include <rtl/ref.hxx>
+
+#include <cmath> // for log10()
+
+using namespace ::com::sun::star;
+
+@implementation MacAVObserverObject
+
+- (void)observeValueForKeyPath:(NSString*)pKeyPath ofObject:(id)pObject change:(NSDictionary*)pChangeDict context:(void*)pContext
+{
+ (void) pObject;
+ (void) pChangeDict;
+ avmedia::macavf::MacAVObserverHandler* pHandler = static_cast<avmedia::macavf::MacAVObserverHandler*>(pContext);
+ pHandler->handleObservation( pKeyPath );
+}
+
+- (void)onNotification:(NSNotification*)pNotification
+{
+ NSString* pNoteName = [pNotification name];
+ HandlersForObject::iterator it = maHandlersForObject.find( [pNotification object]);
+ if( it != maHandlersForObject.end() )
+ (*it).second->handleObservation( pNoteName );
+}
+
+- (void)setHandlerForObject:(NSObject*)pObject handler:(avmedia::macavf::MacAVObserverHandler*)pHandler
+{
+ maHandlersForObject[ pObject] = pHandler;
+}
+
+- (void)removeHandlerForObject:(NSObject*)pObject
+{
+ maHandlersForObject.erase( pObject);
+}
+
+@end
+
+
+namespace avmedia::macavf {
+
+MacAVObserverObject* MacAVObserverHandler::mpMacAVObserverObject = nullptr;
+
+MacAVObserverObject* MacAVObserverHandler::getObserver()
+{
+ if( !mpMacAVObserverObject)
+ {
+ mpMacAVObserverObject = [MacAVObserverObject alloc];
+ [mpMacAVObserverObject retain];
+ }
+ return mpMacAVObserverObject;
+}
+
+
+Player::Player()
+: mpPlayer( nullptr )
+, mfUnmutedVolume( 0 )
+, mfStopTime( DBL_MAX )
+, mbMuted( false )
+, mbLooping( false )
+{}
+
+
+Player::~Player()
+{
+ if( !mpPlayer )
+ return;
+ // remove the observers
+ [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
+ AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
+ [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:pOldPlayerItem];
+ [getObserver() removeHandlerForObject:pOldPlayerItem];
+ // release the AVPlayer
+ CFRelease( mpPlayer );
+}
+
+
+bool Player::handleObservation( NSString* pKeyPath )
+{
+ if( [pKeyPath isEqualToString:AVPlayerItemDidPlayToEndTimeNotification])
+ {
+ if( mbLooping )
+ setMediaTime( 0.0);
+ }
+ return true;
+}
+
+
+bool Player::create( const OUString& rURL )
+{
+ // get the media asset
+ NSString* aNSStr = [NSString stringWithCharacters:reinterpret_cast<unichar const *>(rURL.getStr()) length:rURL.getLength()];
+ SAL_WNODEPRECATED_DECLARATIONS_PUSH
+ //TODO: 10.11 stringByAddingPercentEscapesUsingEncoding
+ NSURL* aNSURL = [NSURL URLWithString: [aNSStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
+ SAL_WNODEPRECATED_DECLARATIONS_POP
+ // get the matching AVPlayerItem
+ AVPlayerItem* pPlayerItem = [AVPlayerItem playerItemWithURL:aNSURL];
+
+ // create or update the AVPlayer with the new AVPlayerItem
+ if( !mpPlayer )
+ {
+ mpPlayer = [AVPlayer playerWithPlayerItem:pPlayerItem];
+ CFRetain( mpPlayer );
+ [mpPlayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
+ }
+ else
+ {
+ // remove the obsoleted observers
+ AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
+ [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
+ [getObserver() removeHandlerForObject:pOldPlayerItem];
+ [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:pOldPlayerItem];
+ // replace the playeritem
+ [mpPlayer replaceCurrentItemWithPlayerItem:pPlayerItem];
+ }
+
+ // observe the status of the current player item
+ [mpPlayer addObserver:getObserver() forKeyPath:@"currentItem.status" options:0 context:this];
+
+ // observe playback-end needed for playback looping
+ [[NSNotificationCenter defaultCenter] addObserver:getObserver()
+ selector:@selector(onNotification:)
+ name:AVPlayerItemDidPlayToEndTimeNotification
+ object:pPlayerItem];
+ [getObserver() setHandlerForObject:pPlayerItem handler:this];
+
+ return true;
+}
+
+
+void SAL_CALL Player::start()
+{
+ if( !mpPlayer )
+ return;
+
+ [mpPlayer play];
+ // else // TODO: delay until it becomes ready
+}
+
+
+void SAL_CALL Player::stop()
+{
+ if( !mpPlayer )
+ return;
+ const bool bPlaying = isPlaying();
+ if( bPlaying )
+ [mpPlayer pause];
+}
+
+
+sal_Bool SAL_CALL Player::isPlaying()
+{
+ if( !mpPlayer )
+ return false;
+ const float fRate = [mpPlayer rate];
+ return (fRate != 0.0);
+}
+
+
+double SAL_CALL Player::getDuration()
+{
+ // slideshow checks for non-zero duration, so cheat here
+ double duration = 0.01;
+
+ if( mpPlayer )
+ {
+ AVPlayerItem* pItem = [mpPlayer currentItem];
+ if( [pItem status] == AVPlayerItemStatusReadyToPlay )
+ duration = CMTimeGetSeconds( [pItem duration] );
+ else // fall back to AVAsset's best guess
+ duration = CMTimeGetSeconds( [[pItem asset] duration] );
+ }
+
+ return duration;
+}
+
+
+void SAL_CALL Player::setMediaTime( double fTime )
+{
+ if( mpPlayer )
+ [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
+}
+
+
+double SAL_CALL Player::getMediaTime()
+{
+ if( !mpPlayer )
+ return 0.0;
+
+ const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
+ if( position >= mfStopTime )
+ if( isPlaying() )
+ stop();
+
+ return position;
+}
+
+
+void Player::setStopTime( double fTime )
+{
+ mfStopTime = fTime;
+}
+
+
+double Player::getStopTime()
+{
+ return mfStopTime;
+}
+
+
+void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
+{
+ mbLooping = bSet;
+}
+
+
+sal_Bool SAL_CALL Player::isPlaybackLoop()
+{
+ return mbLooping;
+}
+
+
+void SAL_CALL Player::setMute( sal_Bool bSet )
+{
+ if( !mpPlayer )
+ return;
+
+ mbMuted = bSet;
+ [mpPlayer setMuted:mbMuted];
+}
+
+
+sal_Bool SAL_CALL Player::isMute()
+{
+ return mbMuted;
+}
+
+
+void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
+{
+ // -40dB <-> AVPlayer volume 0.0
+ // 0dB <-> AVPlayer volume 1.0
+ mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
+
+ // change volume
+ if( !mbMuted && mpPlayer )
+ [mpPlayer setVolume:mfUnmutedVolume];
+}
+
+
+sal_Int16 SAL_CALL Player::getVolumeDB()
+{
+ if( !mpPlayer )
+ return 0;
+
+ // get the actual volume
+ const float fVolume = [mpPlayer volume];
+
+ // convert into Decibel value
+ // -40dB <-> AVPlayer volume 0.0
+ // 0dB <-> AVPlayer volume 1.0
+ const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
+
+ return static_cast<sal_Int16>(nVolumeDB);
+}
+
+
+awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
+{
+ awt::Size aSize( 0, 0 ); // default size
+
+ AVAsset* pMovie = [[mpPlayer currentItem] asset];
+ NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
+ if ([pVideoTracks count] > 0)
+ {
+ AVAssetTrack* pFirstVideoTrack = static_cast<AVAssetTrack*>([pVideoTracks objectAtIndex:0]);
+ const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
+ aSize = awt::Size( aPrefSize.width, aPrefSize.height );
+ }
+
+ return aSize;
+}
+
+
+uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
+{
+ // get the preferred window size
+ const awt::Size aSize( getPreferredPlayerWindowSize() );
+
+ // get the parent view
+ sal_IntPtr nNSViewPtr = 0;
+ aArguments[0] >>= nNSViewPtr;
+ NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
+
+ // check the window parameters
+ if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == nullptr) )
+ return {};
+
+ // create the window
+ return new ::avmedia::macavf::Window( *this, pParentView );
+}
+
+
+uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
+{
+ rtl::Reference<FrameGrabber> pGrabber = new FrameGrabber();
+ AVAsset* pMovie = [[mpPlayer currentItem] asset];
+ if( !pGrabber->create( pMovie ) )
+ return {};
+
+ return pGrabber;
+}
+
+
+OUString SAL_CALL Player::getImplementationName( )
+{
+ return AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME;
+}
+
+
+sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
+{
+ return ServiceName == AVMEDIA_MACAVF_PLAYER_SERVICENAME;
+}
+
+
+uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames( )
+{
+ return { AVMEDIA_MACAVF_PLAYER_SERVICENAME };
+}
+
+} // namespace avmedia::macavf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/window.hxx b/avmedia/source/macavf/window.hxx
new file mode 100644
index 000000000..14fdaf389
--- /dev/null
+++ b/avmedia/source/macavf/window.hxx
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "macavfcommon.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/multicontainer2.hxx>
+
+#include <com/sun/star/media/XPlayerWindow.hpp>
+
+
+@interface MyMediaView : NSView
+@property (nonatomic, readonly, strong) AVPlayer* player;
+@property (nonatomic, readonly, strong) AVPlayerLayer* playerLayer;
+@property (nonatomic, retain) NSURL* videoURL;
+- (void) play;
+@end
+
+namespace avmedia::macavf {
+
+class Player;
+
+class Window
+: public MacAVObserverHandler
+, public ::cppu::WeakImplHelper< css::media::XPlayerWindow,
+ css::lang::XServiceInfo >
+{
+public:
+
+ Window( Player& i_rPlayer,
+ NSView* i_pParentView
+ );
+ virtual ~Window() override;
+
+ void processGraphEvent();
+ void updatePointer();
+
+ // XPlayerWindow
+ virtual void SAL_CALL update( ) override;
+ virtual sal_Bool SAL_CALL setZoomLevel( css::media::ZoomLevel ZoomLevel ) override;
+ virtual css::media::ZoomLevel SAL_CALL getZoomLevel( ) override;
+ virtual void SAL_CALL setPointerType( sal_Int32 nPointerType ) override;
+
+ // XWindow
+ virtual void SAL_CALL setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) override;
+ virtual css::awt::Rectangle SAL_CALL getPosSize( ) override;
+ virtual void SAL_CALL setVisible( sal_Bool Visible ) override;
+ virtual void SAL_CALL setEnable( sal_Bool Enable ) override;
+ virtual void SAL_CALL setFocus( ) override;
+ virtual void SAL_CALL addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+ virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ virtual bool handleObservation( NSString* pKeyPath ) override;
+
+private:
+
+ ::osl::Mutex maMutex;
+ comphelper::OMultiTypeInterfaceContainerHelper2 maListeners;
+ css::media::ZoomLevel meZoomLevel;
+ Player& mrPlayer;
+ int mnPointerType;
+
+ NSView* mpView; // parent-view == movie-view
+ AVPlayerLayer* mpPlayerLayer;
+
+ void ImplLayoutVideoWindow();
+};
+
+} // namespace avmedia::macavf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/macavf/window.mm b/avmedia/source/macavf/window.mm
new file mode 100644
index 000000000..fdb2e5065
--- /dev/null
+++ b/avmedia/source/macavf/window.mm
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/awt/SystemPointer.hpp>
+#include <com/sun/star/awt/PosSize.hpp>
+
+#include "window.hxx"
+#include "player.hxx"
+
+using namespace ::com::sun::star;
+
+
+namespace avmedia::macavf {
+
+Window::Window( Player& i_rPlayer, NSView* i_pParentView )
+: maListeners( maMutex )
+, meZoomLevel( media::ZoomLevel_NOT_AVAILABLE )
+, mrPlayer( i_rPlayer )
+, mnPointerType( awt::SystemPointer::ARROW )
+, mpView( i_pParentView )
+, mpPlayerLayer( nullptr )
+{
+ if( !mpView ) // sanity check
+ return;
+
+ // check the media asset for video content
+ AVPlayer* pAVPlayer = mrPlayer.getAVPlayer();
+ AVAsset* pMovie = [[pAVPlayer currentItem] asset];
+ const int nVideoCount = [pMovie tracksWithMediaType:AVMediaTypeVideo].count;
+ if( nVideoCount <= 0 )
+ return;
+
+ // setup the AVPlayerLayer
+ [pAVPlayer retain];
+ [pAVPlayer pause];
+ mpPlayerLayer = [AVPlayerLayer playerLayerWithPlayer:pAVPlayer];
+ [mpPlayerLayer retain];
+ NSRect viewFrame = [mpView frame];
+ [mpPlayerLayer setFrame:CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.width, viewFrame.size.height)];
+ [mpPlayerLayer setHidden:YES];
+ [mpPlayerLayer setVideoGravity:AVLayerVideoGravityResizeAspectFill];
+ [mpPlayerLayer addObserver:getObserver() forKeyPath:@"readyForDisplay" options:0 context:this];
+
+ // setup the target view
+ [mpView setWantsLayer:YES];
+ [mpView.layer addSublayer:mpPlayerLayer];
+}
+
+
+Window::~Window()
+{
+ [mpPlayerLayer removeObserver:getObserver() forKeyPath:@"readyForDisplay"];
+ [mpPlayerLayer release];
+}
+
+
+bool Window::handleObservation( NSString* /*pKeyPath*/ )
+{
+ const bool bReadyForDisplay = [mpPlayerLayer isReadyForDisplay];
+ [mpPlayerLayer setHidden:!bReadyForDisplay];
+ return true;
+}
+
+// XPlayerWindow
+
+void SAL_CALL Window::update()
+{}
+
+
+sal_Bool SAL_CALL Window::setZoomLevel( media::ZoomLevel /* eZoomLevel */ )
+{
+ return false;
+}
+
+
+media::ZoomLevel SAL_CALL Window::getZoomLevel( )
+{
+ return meZoomLevel;
+}
+
+
+void SAL_CALL Window::setPointerType( sal_Int32 nPointerType )
+{
+ mnPointerType = nPointerType;
+}
+
+// XWindow
+
+void SAL_CALL Window::setPosSize( sal_Int32 /*X*/, sal_Int32 /*Y*/, sal_Int32 Width, sal_Int32 Height, sal_Int16 /* Flags */ )
+{
+ if( !mpView )
+ return;
+ NSRect aRect = [mpView frame];
+ // NOTE: if( (Flags & awt::PosSize::WIDTH) )
+ aRect.size.width = Width;
+ // NOTE: if( (Flags & awt::PosSize::HEIGHT) )
+ aRect.size.height = Height;
+
+ [mpView setFrameSize: aRect.size];
+ NSRect viewFrame = [mpView frame];
+ [mpPlayerLayer setFrame:CGRectMake(viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.width, viewFrame.size.height)];
+}
+
+
+awt::Rectangle SAL_CALL Window::getPosSize()
+{
+ awt::Rectangle aRet;
+
+ NSRect aRect = [mpView frame];
+ aRet.X = aRet.Y = 0;
+ aRet.Width = aRect.size.width;
+ aRet.Height = aRect.size.height;
+
+ return aRet;
+}
+
+
+void SAL_CALL Window::setVisible( sal_Bool /*bVisible*/ )
+{
+}
+
+
+void SAL_CALL Window::setEnable( sal_Bool /*bEnable*/ )
+{
+}
+
+
+void SAL_CALL Window::setFocus()
+{
+}
+
+
+void SAL_CALL Window::addWindowListener( const uno::Reference< awt::XWindowListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::removeWindowListener( const uno::Reference< awt::XWindowListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::addFocusListener( const uno::Reference< awt::XFocusListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::removeFocusListener( const uno::Reference< awt::XFocusListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::addKeyListener( const uno::Reference< awt::XKeyListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::removeKeyListener( const uno::Reference< awt::XKeyListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::addMouseListener( const uno::Reference< awt::XMouseListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::removeMouseListener( const uno::Reference< awt::XMouseListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::addMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::removeMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::addPaintListener( const uno::Reference< awt::XPaintListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::removePaintListener( const uno::Reference< awt::XPaintListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+// XComponent
+
+void SAL_CALL Window::dispose( )
+{
+}
+
+
+void SAL_CALL Window::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+
+void SAL_CALL Window::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+// XServiceInfo
+
+OUString SAL_CALL Window::getImplementationName( )
+{
+ return AVMEDIA_MACAVF_WINDOW_IMPLEMENTATIONNAME;
+}
+
+
+sal_Bool SAL_CALL Window::supportsService( const OUString& ServiceName )
+{
+ return ServiceName == AVMEDIA_MACAVF_WINDOW_SERVICENAME;
+}
+
+
+uno::Sequence< OUString > SAL_CALL Window::getSupportedServiceNames( )
+{
+ return { AVMEDIA_MACAVF_WINDOW_SERVICENAME };
+}
+
+} // namespace avmedia::macavf
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/viewer/mediaevent_impl.cxx b/avmedia/source/viewer/mediaevent_impl.cxx
new file mode 100644
index 000000000..d6a2c082c
--- /dev/null
+++ b/avmedia/source/viewer/mediaevent_impl.cxx
@@ -0,0 +1,169 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "mediaevent_impl.hxx"
+#include <osl/mutex.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/event.hxx>
+#include <vcl/window.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia::priv {
+
+MediaEventListenersImpl::MediaEventListenersImpl( vcl::Window& rEventWindow ) :
+ mpNotifyWindow( &rEventWindow )
+{
+}
+
+
+MediaEventListenersImpl::~MediaEventListenersImpl()
+{
+}
+
+
+void MediaEventListenersImpl::cleanUp()
+{
+ Application::RemoveMouseAndKeyEvents( mpNotifyWindow.get() );
+ mpNotifyWindow = nullptr;
+}
+
+
+void SAL_CALL MediaEventListenersImpl::disposing( const css::lang::EventObject& )
+{
+}
+
+
+void SAL_CALL MediaEventListenersImpl::keyPressed( const css::awt::KeyEvent& e )
+{
+ const std::unique_lock aGuard( maMutex );
+
+ if( mpNotifyWindow )
+ {
+ vcl::KeyCode aVCLKeyCode( e.KeyCode,
+ ( ( e.Modifiers & 1 ) ? KEY_SHIFT : 0 ) |
+ ( ( e.Modifiers & 2 ) ? KEY_MOD1 : 0 ) |
+ ( ( e.Modifiers & 4 ) ? KEY_MOD2 : 0 ) );
+ KeyEvent aVCLKeyEvt( e.KeyChar, aVCLKeyCode );
+
+ Application::PostKeyEvent( VclEventId::WindowKeyInput, mpNotifyWindow.get(), &aVCLKeyEvt );
+ }
+}
+
+
+void SAL_CALL MediaEventListenersImpl::keyReleased( const css::awt::KeyEvent& e )
+{
+ const std::unique_lock aGuard( maMutex );
+
+ if( mpNotifyWindow )
+ {
+ vcl::KeyCode aVCLKeyCode( e.KeyCode,
+ ( ( e.Modifiers & 1 ) ? KEY_SHIFT : 0 ) |
+ ( ( e.Modifiers & 2 ) ? KEY_MOD1 : 0 ) |
+ ( ( e.Modifiers & 4 ) ? KEY_MOD2 : 0 ) );
+ KeyEvent aVCLKeyEvt( e.KeyChar, aVCLKeyCode );
+ Application::PostKeyEvent( VclEventId::WindowKeyUp, mpNotifyWindow.get(), &aVCLKeyEvt );
+ }
+}
+
+
+void SAL_CALL MediaEventListenersImpl::mousePressed( const css::awt::MouseEvent& e )
+{
+ const std::unique_lock aGuard( maMutex );
+
+ if( mpNotifyWindow )
+ {
+ MouseEvent aVCLMouseEvt( Point( e.X, e.Y ),
+ sal::static_int_cast< sal_uInt16 >(e.ClickCount),
+ MouseEventModifiers::NONE,
+ ( ( e.Buttons & 1 ) ? MOUSE_LEFT : 0 ) |
+ ( ( e.Buttons & 2 ) ? MOUSE_RIGHT : 0 ) |
+ ( ( e.Buttons & 4 ) ? MOUSE_MIDDLE : 0 ),
+ e.Modifiers );
+ Application::PostMouseEvent( VclEventId::WindowMouseButtonDown, mpNotifyWindow.get(), &aVCLMouseEvt );
+ }
+}
+
+
+void SAL_CALL MediaEventListenersImpl::mouseReleased( const css::awt::MouseEvent& e )
+{
+ const std::unique_lock aGuard( maMutex );
+ const SolarMutexGuard aAppGuard;
+
+ if( mpNotifyWindow )
+ {
+ MouseEvent aVCLMouseEvt( Point( e.X, e.Y ),
+ sal::static_int_cast< sal_uInt16 >(e.ClickCount),
+ MouseEventModifiers::NONE,
+ ( ( e.Buttons & 1 ) ? MOUSE_LEFT : 0 ) |
+ ( ( e.Buttons & 2 ) ? MOUSE_RIGHT : 0 ) |
+ ( ( e.Buttons & 4 ) ? MOUSE_MIDDLE : 0 ),
+ e.Modifiers );
+ Application::PostMouseEvent( VclEventId::WindowMouseButtonUp, mpNotifyWindow.get(), &aVCLMouseEvt );
+ }
+}
+
+
+void SAL_CALL MediaEventListenersImpl::mouseEntered( const css::awt::MouseEvent& )
+{
+}
+
+
+void SAL_CALL MediaEventListenersImpl::mouseExited( const css::awt::MouseEvent& )
+{
+}
+
+
+void SAL_CALL MediaEventListenersImpl::mouseDragged( const css::awt::MouseEvent& e )
+{
+ const std::unique_lock aGuard( maMutex );
+
+ if( mpNotifyWindow )
+ {
+ MouseEvent aVCLMouseEvt( Point( e.X, e.Y ), 0, MouseEventModifiers::NONE, e.Buttons, e.Modifiers );
+ Application::PostMouseEvent( VclEventId::WindowMouseMove, mpNotifyWindow.get(), &aVCLMouseEvt );
+ }
+}
+
+
+void SAL_CALL MediaEventListenersImpl::mouseMoved( const css::awt::MouseEvent& e )
+{
+ const std::unique_lock aGuard( maMutex );
+
+ if( mpNotifyWindow )
+ {
+ MouseEvent aVCLMouseEvt( Point( e.X, e.Y ), 0, MouseEventModifiers::NONE, e.Buttons, e.Modifiers );
+ Application::PostMouseEvent( VclEventId::WindowMouseMove, mpNotifyWindow.get(), &aVCLMouseEvt );
+ }
+}
+
+
+void SAL_CALL MediaEventListenersImpl::focusGained( const css::awt::FocusEvent& )
+{
+}
+
+
+void SAL_CALL MediaEventListenersImpl::focusLost( const css::awt::FocusEvent& )
+{
+}
+
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/viewer/mediaevent_impl.hxx b/avmedia/source/viewer/mediaevent_impl.hxx
new file mode 100644
index 000000000..682eff1d7
--- /dev/null
+++ b/avmedia/source/viewer/mediaevent_impl.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <avmedia/mediawindow.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/awt/XKeyListener.hpp>
+#include <com/sun/star/awt/XMouseListener.hpp>
+#include <com/sun/star/awt/XMouseMotionListener.hpp>
+#include <com/sun/star/awt/XFocusListener.hpp>
+#include <vcl/vclptr.hxx>
+
+#include <mutex>
+
+namespace avmedia::priv
+ {
+
+ // - MediaEventListenersImpl -
+
+ class MediaEventListenersImpl : public ::cppu::WeakImplHelper< css::awt::XKeyListener,
+ css::awt::XMouseListener,
+ css::awt::XMouseMotionListener,
+ css::awt::XFocusListener >
+ {
+ public:
+
+ explicit MediaEventListenersImpl( vcl::Window& rNotifyWindow );
+ virtual ~MediaEventListenersImpl() override;
+
+ void cleanUp();
+
+ protected:
+
+ // XKeyListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+ virtual void SAL_CALL keyPressed( const css::awt::KeyEvent& e ) override;
+ virtual void SAL_CALL keyReleased( const css::awt::KeyEvent& e ) override;
+
+ // XMouseListener
+ virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override;
+
+ // XMouseMotionListener
+ virtual void SAL_CALL mouseDragged( const css::awt::MouseEvent& e ) override;
+ virtual void SAL_CALL mouseMoved( const css::awt::MouseEvent& e ) override;
+
+ // XFocusListener
+ virtual void SAL_CALL focusGained( const css::awt::FocusEvent& e ) override;
+ virtual void SAL_CALL focusLost( const css::awt::FocusEvent& e ) override;
+
+ private:
+
+ VclPtr<vcl::Window> mpNotifyWindow;
+ mutable std::mutex maMutex;
+ };
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/viewer/mediawindow.cxx b/avmedia/source/viewer/mediawindow.cxx
new file mode 100644
index 000000000..df8110f54
--- /dev/null
+++ b/avmedia/source/viewer/mediawindow.cxx
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <avmedia/mediawindow.hxx>
+#include "mediawindow_impl.hxx"
+#include <mediamisc.hxx>
+#include <bitmaps.hlst>
+#include <strings.hrc>
+#include <tools/urlobj.hxx>
+#include <utility>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/weld.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/frame/XDispatchHelper.hpp>
+#include <com/sun/star/media/XPlayer.hpp>
+#include <com/sun/star/media/XPlayerNotifier.hpp>
+#include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp>
+#include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
+#include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
+#include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp>
+#include <com/sun/star/util/URLTransformer.hpp>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <memory>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#define AVMEDIA_FRAMEGRABBER_DEFAULTFRAME_MEDIATIME 3.0
+
+using namespace ::com::sun::star;
+
+namespace avmedia {
+
+MediaWindow::MediaWindow( vcl::Window* parent, bool bInternalMediaControl ) :
+ mpImpl( VclPtr<priv::MediaWindowImpl>::Create( parent, this, bInternalMediaControl ) )
+{
+ mpImpl->Show();
+}
+
+
+MediaWindow::~MediaWindow()
+{
+ mpImpl.disposeAndClear();
+}
+
+
+void MediaWindow::setURL( const OUString& rURL, const OUString& rReferer )
+{
+ mpImpl->setURL( rURL, OUString(), rReferer );
+}
+
+
+const OUString& MediaWindow::getURL() const
+{
+ return mpImpl->getURL();
+}
+
+
+bool MediaWindow::isValid() const
+{
+ return mpImpl->isValid();
+}
+
+
+void MediaWindow::MouseMove( const MouseEvent& )
+{
+}
+
+
+void MediaWindow::MouseButtonDown( const MouseEvent& )
+{
+}
+
+
+void MediaWindow::MouseButtonUp( const MouseEvent& )
+{
+}
+
+
+void MediaWindow::KeyInput( const KeyEvent& )
+{
+}
+
+
+void MediaWindow::KeyUp( const KeyEvent& )
+{
+}
+
+void MediaWindow::Command( const CommandEvent& )
+{
+}
+
+
+sal_Int8 MediaWindow::AcceptDrop( const AcceptDropEvent& )
+{
+ return 0;
+}
+
+
+sal_Int8 MediaWindow::ExecuteDrop( const ExecuteDropEvent& )
+{
+ return 0;
+}
+
+
+void MediaWindow::StartDrag( sal_Int8, const Point& )
+{
+}
+
+
+Size MediaWindow::getPreferredSize() const
+{
+ return mpImpl->getPreferredSize();
+}
+
+
+void MediaWindow::setPosSize( const tools::Rectangle& rNewRect )
+{
+ mpImpl->setPosSize( rNewRect );
+}
+
+
+void MediaWindow::setPointer( PointerStyle nPointer )
+{
+ mpImpl->setPointer( nPointer );
+}
+
+
+bool MediaWindow::start()
+{
+ return mpImpl->start();
+}
+
+void MediaWindow::updateMediaItem( MediaItem& rItem ) const
+{
+ mpImpl->updateMediaItem( rItem );
+}
+
+void MediaWindow::executeMediaItem( const MediaItem& rItem )
+{
+ mpImpl->executeMediaItem( rItem );
+}
+
+void MediaWindow::show()
+{
+ mpImpl->Show();
+}
+
+void MediaWindow::hide()
+{
+ mpImpl->Hide();
+}
+
+bool MediaWindow::isVisible() const
+{
+ return mpImpl->IsVisible();
+}
+
+vcl::Window* MediaWindow::getWindow() const
+{
+ return mpImpl.get();
+}
+
+
+FilterNameVector MediaWindow::getMediaFilters()
+{
+ return {{"Advanced Audio Coding", "aac"},
+ {"AIF Audio", "aif;aiff"},
+ {"Advanced Systems Format", "asf;wma;wmv"},
+ {"AU Audio", "au"},
+ {"AC3 Audio", "ac3"},
+ {"AVI", "avi"},
+ {"CD Audio", "cda"},
+ {"Digital Video", "dv"},
+ {"FLAC Audio", "flac"},
+ {"Flash Video", "flv"},
+ {"Matroska Media", "mkv"},
+ {"MIDI Audio", "mid;midi"},
+ {"MPEG Audio", "mp2;mp3;mpa;m4a"},
+ {"MPEG Video", "mpg;mpeg;mpv;mp4;m4v"},
+ {"Ogg Audio", "ogg;oga;opus"},
+ {"Ogg Video", "ogv;ogx"},
+ {"Real Audio", "ra"},
+ {"Real Media", "rm"},
+ {"RMI MIDI Audio", "rmi"},
+ {"SND (SouND) Audio", "snd"},
+ {"Quicktime Video", "mov"},
+ {"Vivo Video", "viv"},
+ {"WAVE Audio", "wav"},
+ {"WebM Video", "webm"},
+ {"Windows Media Audio", "wma"},
+ {"Windows Media Video", "wmv"}};
+}
+
+
+bool MediaWindow::executeMediaURLDialog(weld::Window* pParent, OUString& rURL, bool *const o_pbLink)
+{
+ ::sfx2::FileDialogHelper aDlg(o_pbLink != nullptr
+ ? ui::dialogs::TemplateDescription::FILEOPEN_LINK_PREVIEW
+ : ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE,
+ FileDialogFlags::NONE, pParent);
+ static const char aWildcard[] = "*.";
+ FilterNameVector aFilters = getMediaFilters();
+ static const char aSeparator[] = ";";
+ OUStringBuffer aAllTypes;
+
+ aDlg.SetContext(sfx2::FileDialogHelper::InsertMedia);
+ aDlg.SetTitle( AvmResId( o_pbLink != nullptr
+ ? AVMEDIA_STR_INSERTMEDIA_DLG : AVMEDIA_STR_OPENMEDIA_DLG ) );
+
+ for( const auto &filter : aFilters )
+ {
+ for( sal_Int32 nIndex = 0; nIndex >= 0; )
+ {
+ if( !aAllTypes.isEmpty() )
+ aAllTypes.append(aSeparator);
+
+ aAllTypes.append(OUString::Concat(aWildcard) + o3tl::getToken(filter.second, 0, ';', nIndex ));
+ }
+ }
+
+ // add filter for all media types
+ aDlg.AddFilter( AvmResId( AVMEDIA_STR_ALL_MEDIAFILES ), aAllTypes.makeStringAndClear() );
+
+ for( const auto &filter : aFilters )
+ {
+ OUStringBuffer aTypes;
+
+ for( sal_Int32 nIndex = 0; nIndex >= 0; )
+ {
+ if( !aTypes.isEmpty() )
+ aTypes.append(aSeparator);
+
+ aTypes.append(OUString::Concat(aWildcard) + o3tl::getToken(filter.second, 0, ';', nIndex ));
+ }
+
+ // add single filters
+ aDlg.AddFilter( filter.first, aTypes.makeStringAndClear() );
+ }
+
+ // add filter for all types
+ aDlg.AddFilter( AvmResId( AVMEDIA_STR_ALL_FILES ), "*.*" );
+
+ uno::Reference<ui::dialogs::XFilePicker3> const xFP(aDlg.GetFilePicker());
+ uno::Reference<ui::dialogs::XFilePickerControlAccess> const xCtrlAcc(xFP,
+ uno::UNO_QUERY_THROW);
+ if (o_pbLink != nullptr)
+ {
+ // for video link should be the default
+ xCtrlAcc->setValue(
+ ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK, 0,
+ uno::Any(true) );
+ // disabled for now: TODO: preview?
+ xCtrlAcc->enableControl(
+ ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_PREVIEW,
+ false);
+ }
+
+ if( aDlg.Execute() == ERRCODE_NONE )
+ {
+ const INetURLObject aURL( aDlg.GetPath() );
+ rURL = aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous );
+
+ if (o_pbLink != nullptr)
+ {
+ uno::Any const any = xCtrlAcc->getValue(
+ ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK, 0);
+ if (!(any >>= *o_pbLink))
+ {
+ SAL_WARN("avmedia", "invalid link property");
+ *o_pbLink = true;
+ }
+ }
+ }
+ else if( !rURL.isEmpty() )
+ rURL.clear();
+
+ return !rURL.isEmpty();
+}
+
+void MediaWindow::executeFormatErrorBox(weld::Window* pParent)
+{
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent,
+ VclMessageType::Warning, VclButtonsType::Ok, AvmResId(AVMEDIA_STR_ERR_URL)));
+ xBox->run();
+}
+
+bool MediaWindow::isMediaURL(std::u16string_view rURL, const OUString& rReferer, bool bDeep, rtl::Reference<PlayerListener> xPreferredPixelSizeListener)
+{
+ const INetURLObject aURL( rURL );
+
+ if( aURL.GetProtocol() == INetProtocol::NotValid )
+ return false;
+
+ if (bDeep || xPreferredPixelSizeListener)
+ {
+ try
+ {
+ uno::Reference< media::XPlayer > xPlayer( priv::MediaWindowImpl::createPlayer(
+ aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ),
+ rReferer, nullptr ) );
+
+ if( xPlayer.is() )
+ {
+ if (xPreferredPixelSizeListener)
+ {
+ uno::Reference<media::XPlayerNotifier> xPlayerNotifier(xPlayer, css::uno::UNO_QUERY);
+ if (xPlayerNotifier)
+ {
+ // wait until it's possible to query this to get a sensible answer
+ xPreferredPixelSizeListener->startListening(xPlayerNotifier);
+ }
+ else
+ {
+ // assume the size is possible to query immediately
+ xPreferredPixelSizeListener->callPlayerWindowSizeAvailable(xPlayer);
+ }
+ }
+ return true;
+ }
+ }
+ catch( ... )
+ {
+ }
+ }
+ else
+ {
+ FilterNameVector aFilters = getMediaFilters();
+ const OUString aExt( aURL.getExtension() );
+
+ for( const auto &filter : aFilters )
+ {
+ for( sal_Int32 nIndex = 0; nIndex >= 0; )
+ {
+ if( aExt.equalsIgnoreAsciiCase( o3tl::getToken(filter.second, 0, ';', nIndex ) ) )
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+uno::Reference< media::XPlayer > MediaWindow::createPlayer( const OUString& rURL, const OUString& rReferer, const OUString* pMimeType )
+{
+ return priv::MediaWindowImpl::createPlayer( rURL, rReferer, pMimeType );
+}
+
+uno::Reference<graphic::XGraphic>
+MediaWindow::grabFrame(const uno::Reference<media::XPlayer>& xPlayer,
+ const uno::Reference<graphic::XGraphic>& rGraphic)
+{
+ uno::Reference< graphic::XGraphic > xRet;
+ std::unique_ptr< Graphic > xGraphic;
+
+ if( xPlayer.is() )
+ {
+ uno::Reference< media::XFrameGrabber > xGrabber( xPlayer->createFrameGrabber() );
+
+ if( xGrabber.is() )
+ {
+ double fMediaTime = AVMEDIA_FRAMEGRABBER_DEFAULTFRAME_MEDIATIME;
+
+ if( fMediaTime >= xPlayer->getDuration() )
+ fMediaTime = ( xPlayer->getDuration() * 0.5 );
+
+ xRet = xGrabber->grabFrame( fMediaTime );
+ }
+
+ if( !xRet.is() )
+ {
+ awt::Size aPrefSize( xPlayer->getPreferredPlayerWindowSize() );
+
+ if( !aPrefSize.Width && !aPrefSize.Height )
+ {
+ const BitmapEx aBmpEx(AVMEDIA_BMP_AUDIOLOGO);
+ xGraphic.reset( new Graphic( aBmpEx ) );
+ }
+ }
+ }
+
+ if (!xRet.is() && !xGraphic)
+ {
+ const BitmapEx aBmpEx(AVMEDIA_BMP_EMPTYLOGO);
+ xGraphic.reset( new Graphic( aBmpEx ) );
+ }
+
+ if (xGraphic)
+ {
+ if (rGraphic)
+ xGraphic.reset(new Graphic(rGraphic));
+ xRet = xGraphic->GetXGraphic();
+ }
+
+ return xRet;
+}
+
+uno::Reference< graphic::XGraphic > MediaWindow::grabFrame(const OUString& rURL,
+ const OUString& rReferer,
+ const OUString& sMimeType,
+ rtl::Reference<PlayerListener> xPreferredPixelSizeListener)
+{
+ uno::Reference<media::XPlayer> xPlayer(createPlayer(rURL, rReferer, &sMimeType));
+
+ if (xPreferredPixelSizeListener)
+ {
+ uno::Reference<media::XPlayerNotifier> xPlayerNotifier(xPlayer, css::uno::UNO_QUERY);
+ if (xPlayerNotifier)
+ {
+ // set a callback to call when a more sensible result is available, which
+ // might be called immediately if already available
+ xPreferredPixelSizeListener->startListening(xPlayerNotifier);
+ }
+ else
+ {
+ // assume the size is possible to query immediately
+ xPreferredPixelSizeListener->callPlayerWindowSizeAvailable(xPlayer);
+ }
+
+ return nullptr;
+ }
+
+ return grabFrame(xPlayer);
+}
+
+void MediaWindow::dispatchInsertAVMedia(const css::uno::Reference<css::frame::XDispatchProvider>& rDispatchProvider,
+ const css::awt::Size& rSize, const OUString& rURL, bool bLink)
+{
+ util::URL aDispatchURL;
+ aDispatchURL.Complete = ".uno:InsertAVMedia";
+
+ css::uno::Reference<css::util::XURLTransformer> xTrans(css::util::URLTransformer::create(::comphelper::getProcessComponentContext()));
+ xTrans->parseStrict(aDispatchURL);
+
+ css::uno::Reference<css::frame::XDispatch> xDispatch = rDispatchProvider->queryDispatch(aDispatchURL, "", 0);
+ css::uno::Sequence<css::beans::PropertyValue> aArgs(comphelper::InitPropertySequence({
+ { "URL", css::uno::Any(rURL) },
+ { "Size.Width", uno::Any(rSize.Width)},
+ { "Size.Height", uno::Any(rSize.Height)},
+ { "IsLink", css::uno::Any(bLink) },
+ }));
+ xDispatch->dispatch(aDispatchURL, aArgs);
+}
+
+PlayerListener::PlayerListener(std::function<void(const css::uno::Reference<css::media::XPlayer>&)> fn)
+ : PlayerListener_BASE(m_aMutex)
+ , m_aFn(std::move(fn))
+{
+}
+
+void PlayerListener::dispose()
+{
+ stopListening();
+ PlayerListener_BASE::dispose();
+}
+
+void PlayerListener::startListening(const css::uno::Reference<media::XPlayerNotifier>& rNotifier)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ m_xNotifier = rNotifier;
+ m_xNotifier->addPlayerListener(this);
+}
+
+void PlayerListener::stopListening()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+ if (!m_xNotifier)
+ return;
+ m_xNotifier->removePlayerListener(this);
+ m_xNotifier.clear();
+}
+
+void SAL_CALL PlayerListener::preferredPlayerWindowSizeAvailable(const css::lang::EventObject&)
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ css::uno::Reference<media::XPlayer> xPlayer(m_xNotifier, css::uno::UNO_QUERY_THROW);
+ callPlayerWindowSizeAvailable(xPlayer);
+
+ stopListening();
+}
+
+void SAL_CALL PlayerListener::disposing(const css::lang::EventObject&)
+{
+}
+
+PlayerListener::~PlayerListener()
+{
+}
+
+} // namespace avmedia
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/viewer/mediawindow_impl.cxx b/avmedia/source/viewer/mediawindow_impl.cxx
new file mode 100644
index 000000000..a12ed42d4
--- /dev/null
+++ b/avmedia/source/viewer/mediawindow_impl.cxx
@@ -0,0 +1,674 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <iostream>
+#include "mediawindow_impl.hxx"
+#include "mediaevent_impl.hxx"
+#include <mediamisc.hxx>
+#include <bitmaps.hlst>
+#include <helpids.h>
+
+#include <algorithm>
+#include <string_view>
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <tools/diagnose_ex.h>
+#include <comphelper/scopeguard.hxx>
+#include <tools/urlobj.hxx>
+#include <unotools/securityoptions.hxx>
+#include <vcl/bitmapex.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/svapp.hxx>
+
+#include <com/sun/star/awt/SystemPointer.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/media/XManager.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+using namespace ::com::sun::star;
+
+namespace avmedia::priv {
+
+MediaWindowControl::MediaWindowControl(vcl::Window* pParent)
+ : MediaControl(pParent, MediaControlStyle::MultiLine)
+{
+}
+
+void MediaWindowControl::update()
+{
+ MediaItem aItem;
+
+ static_cast< MediaWindowImpl* >( GetParent() )->updateMediaItem( aItem );
+ setState(aItem);
+}
+
+void MediaWindowControl::execute(const MediaItem& rItem)
+{
+ static_cast<MediaWindowImpl*>(GetParent())->executeMediaItem(rItem);
+}
+
+MediaChildWindow::MediaChildWindow(vcl::Window* pParent)
+ : SystemChildWindow(pParent, WB_CLIPCHILDREN)
+{
+}
+
+void MediaChildWindow::MouseMove( const MouseEvent& rMEvt )
+{
+ const MouseEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
+ rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
+
+ SystemChildWindow::MouseMove( rMEvt );
+ GetParent()->MouseMove( aTransformedEvent );
+}
+
+void MediaChildWindow::MouseButtonDown( const MouseEvent& rMEvt )
+{
+ const MouseEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
+ rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
+
+ SystemChildWindow::MouseButtonDown( rMEvt );
+ GetParent()->MouseButtonDown( aTransformedEvent );
+}
+
+void MediaChildWindow::MouseButtonUp( const MouseEvent& rMEvt )
+{
+ const MouseEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rMEvt.GetPosPixel() ) ),
+ rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), rMEvt.GetModifier() );
+
+ SystemChildWindow::MouseButtonUp( rMEvt );
+ GetParent()->MouseButtonUp( aTransformedEvent );
+}
+
+void MediaChildWindow::KeyInput( const KeyEvent& rKEvt )
+{
+ SystemChildWindow::KeyInput( rKEvt );
+ GetParent()->KeyInput( rKEvt );
+}
+
+void MediaChildWindow::KeyUp( const KeyEvent& rKEvt )
+{
+ SystemChildWindow::KeyUp( rKEvt );
+ GetParent()->KeyUp( rKEvt );
+}
+
+void MediaChildWindow::Command( const CommandEvent& rCEvt )
+{
+ const CommandEvent aTransformedEvent( GetParent()->ScreenToOutputPixel( OutputToScreenPixel( rCEvt.GetMousePosPixel() ) ),
+ rCEvt.GetCommand(), rCEvt.IsMouseEvent(), rCEvt.GetEventData() );
+
+ SystemChildWindow::Command( rCEvt );
+ GetParent()->Command( aTransformedEvent );
+}
+
+MediaWindowImpl::MediaWindowImpl(vcl::Window* pParent, MediaWindow* pMediaWindow, bool bInternalMediaControl)
+ : Control(pParent)
+ , DropTargetHelper(this)
+ , DragSourceHelper(this)
+ , mpMediaWindow(pMediaWindow)
+ , mpMediaWindowControl(bInternalMediaControl ? VclPtr<MediaWindowControl>::Create(this) : nullptr)
+{
+ if (mpMediaWindowControl)
+ {
+ mpMediaWindowControl->SetSizePixel(mpMediaWindowControl->GetOptimalSize());
+ mpMediaWindowControl->Show();
+ }
+}
+
+MediaWindowImpl::~MediaWindowImpl()
+{
+ disposeOnce();
+}
+
+void MediaWindowImpl::dispose()
+{
+ if (mxEvents.is())
+ mxEvents->cleanUp();
+
+ if (mxPlayerWindow.is())
+ {
+ mxPlayerWindow->removeKeyListener( uno::Reference< awt::XKeyListener >( mxEvents ) );
+ mxPlayerWindow->removeMouseListener( uno::Reference< awt::XMouseListener >( mxEvents ) );
+ mxPlayerWindow->removeMouseMotionListener( uno::Reference< awt::XMouseMotionListener >( mxEvents ) );
+ mxPlayerWindow->dispose();
+ mxPlayerWindow.clear();
+ }
+
+ uno::Reference< lang::XComponent > xComponent( mxPlayer, uno::UNO_QUERY );
+ if (xComponent.is()) // this stops the player
+ xComponent->dispose();
+
+ mxPlayer.clear();
+
+ mpMediaWindow = nullptr;
+
+ mpEmptyBmpEx.reset();
+ mpAudioBmpEx.reset();
+ mpMediaWindowControl.disposeAndClear();
+ mpChildWindow.disposeAndClear();
+
+ Control::dispose();
+}
+
+uno::Reference<media::XPlayer> MediaWindowImpl::createPlayer(const OUString& rURL, const OUString& rReferer, const OUString* pMimeType)
+{
+ uno::Reference<media::XPlayer> xPlayer;
+
+ if( rURL.isEmpty() )
+ return xPlayer;
+
+ if (SvtSecurityOptions::isUntrustedReferer(rReferer))
+ {
+ return xPlayer;
+ }
+
+ if (!pMimeType || *pMimeType == AVMEDIA_MIMETYPE_COMMON)
+ {
+ uno::Reference<uno::XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ if (Application::GetToolkitName() == "gtk4")
+ xPlayer = createPlayer(rURL, "com.sun.star.comp.avmedia.Manager_Gtk", xContext);
+ else
+ xPlayer = createPlayer(rURL, AVMEDIA_MANAGER_SERVICE_NAME, xContext);
+ }
+
+ return xPlayer;
+}
+
+uno::Reference< media::XPlayer > MediaWindowImpl::createPlayer(
+ const OUString& rURL, const OUString& rManagerServName,
+ const uno::Reference< uno::XComponentContext >& xContext)
+{
+ uno::Reference< media::XPlayer > xPlayer;
+ try
+ {
+ uno::Reference< media::XManager > xManager (
+ xContext->getServiceManager()->createInstanceWithContext(rManagerServName, xContext),
+ uno::UNO_QUERY );
+ if( xManager.is() )
+ xPlayer = xManager->createPlayer( rURL );
+ else
+ SAL_INFO( "avmedia", "failed to create media player service " << rManagerServName );
+ } catch ( const uno::Exception & )
+ {
+ TOOLS_WARN_EXCEPTION( "avmedia", "couldn't create media player " << rManagerServName);
+ }
+ return xPlayer;
+}
+
+void MediaWindowImpl::setURL( const OUString& rURL,
+ OUString const& rTempURL, OUString const& rReferer)
+{
+ maReferer = rReferer;
+ if( rURL == getURL() )
+ return;
+
+ if( mxPlayer.is() )
+ mxPlayer->stop();
+
+ if( mxPlayerWindow.is() )
+ {
+ mxPlayerWindow->setVisible( false );
+ mxPlayerWindow.clear();
+ }
+
+ mxPlayer.clear();
+ mTempFileURL.clear();
+
+ if (!rTempURL.isEmpty())
+ {
+ maFileURL = rURL;
+ mTempFileURL = rTempURL;
+ }
+ else
+ {
+ INetURLObject aURL( rURL );
+
+ if (aURL.GetProtocol() != INetProtocol::NotValid)
+ maFileURL = aURL.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous);
+ else
+ maFileURL = rURL;
+ }
+
+ mxPlayer = createPlayer((!mTempFileURL.isEmpty()) ? mTempFileURL : maFileURL, rReferer, &m_sMimeType );
+ onURLChanged();
+}
+
+const OUString& MediaWindowImpl::getURL() const
+{
+ return maFileURL;
+}
+
+bool MediaWindowImpl::isValid() const
+{
+ return mxPlayer.is();
+}
+
+Size MediaWindowImpl::getPreferredSize() const
+{
+ Size aRet(480, 360);
+
+ if( mxPlayer.is() )
+ {
+ awt::Size aPrefSize( mxPlayer->getPreferredPlayerWindowSize() );
+
+ aRet.setWidth( aPrefSize.Width );
+ aRet.setHeight( aPrefSize.Height );
+ }
+
+ return aRet;
+}
+
+bool MediaWindowImpl::start()
+{
+ return mxPlayer.is() && ( mxPlayer->start(), true );
+}
+
+void MediaWindowImpl::updateMediaItem( MediaItem& rItem ) const
+{
+ if( isPlaying() )
+ rItem.setState( MediaState::Play );
+ else
+ rItem.setState( ( getMediaTime() == 0.0 ) ? MediaState::Stop : MediaState::Pause );
+
+ rItem.setDuration( getDuration() );
+ rItem.setTime( getMediaTime() );
+ rItem.setLoop( mxPlayer.is() && mxPlayer->isPlaybackLoop() );
+ rItem.setMute( mxPlayer.is() && mxPlayer->isMute() );
+ rItem.setVolumeDB( mxPlayer.is() ? mxPlayer->getVolumeDB() : 0 );
+ rItem.setZoom( mxPlayerWindow.is() ? mxPlayerWindow->getZoomLevel() : media::ZoomLevel_NOT_AVAILABLE );
+ rItem.setURL( getURL(), mTempFileURL, maReferer );
+}
+
+void MediaWindowImpl::executeMediaItem( const MediaItem& rItem )
+{
+ mpItem = &rItem;
+ comphelper::ScopeGuard g([this] { this->mpItem = nullptr; });
+
+ const AVMediaSetMask nMaskSet = rItem.getMaskSet();
+
+ // set URL first
+ if (nMaskSet & AVMediaSetMask::URL)
+ {
+ m_sMimeType = rItem.getMimeType();
+ setURL(rItem.getURL(), rItem.getTempURL(), rItem.getReferer());
+ }
+
+ // set different states next
+ if (nMaskSet & AVMediaSetMask::TIME)
+ setMediaTime(std::min(rItem.getTime(), getDuration()));
+
+ if (nMaskSet & AVMediaSetMask::LOOP && mxPlayer.is() )
+ mxPlayer->setPlaybackLoop( rItem.isLoop() );
+
+ if (nMaskSet & AVMediaSetMask::MUTE && mxPlayer.is() )
+ mxPlayer->setMute( rItem.isMute() );
+
+ if (nMaskSet & AVMediaSetMask::VOLUMEDB && mxPlayer.is() )
+ mxPlayer->setVolumeDB( rItem.getVolumeDB() );
+
+ if (nMaskSet & AVMediaSetMask::ZOOM && mxPlayerWindow.is() )
+ mxPlayerWindow->setZoomLevel( rItem.getZoom() );
+
+ // set play state at last
+ if (!(nMaskSet & AVMediaSetMask::STATE))
+ return;
+
+ switch (rItem.getState())
+ {
+ case MediaState::Play:
+ {
+ if (!isPlaying())
+ start();
+ }
+ break;
+
+ case MediaState::Pause:
+ {
+ if (isPlaying())
+ stop();
+ }
+ break;
+
+ case MediaState::Stop:
+ {
+ if (isPlaying())
+ {
+ setMediaTime( 0.0 );
+ stop();
+ setMediaTime( 0.0 );
+ }
+ }
+ break;
+ }
+}
+
+void MediaWindowImpl::stop()
+{
+ if( mxPlayer.is() )
+ mxPlayer->stop();
+}
+
+bool MediaWindowImpl::isPlaying() const
+{
+ return( mxPlayer.is() && mxPlayer->isPlaying() );
+}
+
+double MediaWindowImpl::getDuration() const
+{
+ return( mxPlayer.is() ? mxPlayer->getDuration() : 0.0 );
+}
+
+void MediaWindowImpl::setMediaTime( double fTime )
+{
+ if( mxPlayer.is() )
+ mxPlayer->setMediaTime( fTime );
+}
+
+double MediaWindowImpl::getMediaTime() const
+{
+ return( mxPlayer.is() ? mxPlayer->getMediaTime() : 0.0 );
+}
+
+void MediaWindowImpl::stopPlayingInternal(bool bStop)
+{
+ if (isPlaying())
+ {
+ bStop ? mxPlayer->stop() : mxPlayer->start();
+ }
+}
+
+void MediaWindowImpl::onURLChanged()
+{
+ if (m_sMimeType == AVMEDIA_MIMETYPE_COMMON)
+ {
+ mpChildWindow.disposeAndClear();
+ mpChildWindow.reset(VclPtr<MediaChildWindow>::Create(this));
+ }
+ if (!mpChildWindow)
+ return;
+ mpChildWindow->SetHelpId(HID_AVMEDIA_PLAYERWINDOW);
+ mxEvents = new MediaEventListenersImpl(*mpChildWindow);
+
+ if (mxPlayer.is())
+ {
+ Resize();
+ uno::Reference<media::XPlayerWindow> xPlayerWindow;
+ const Point aPoint;
+ const Size aSize(mpChildWindow->GetSizePixel());
+
+ sal_IntPtr nParentWindowHandle(0);
+ const SystemEnvData* pEnvData = mpChildWindow->GetSystemData();
+ // tdf#139609 gtk doesn't need the handle, and fetching it is undesirable
+ if (!pEnvData || pEnvData->toolkit != SystemEnvData::Toolkit::Gtk)
+ nParentWindowHandle = mpChildWindow->GetParentWindowHandle();
+ uno::Sequence<uno::Any> aArgs{
+ uno::Any(nParentWindowHandle),
+ uno::Any(awt::Rectangle(aPoint.X(), aPoint.Y(), aSize.Width(), aSize.Height())),
+ uno::Any(reinterpret_cast<sal_IntPtr>(mpChildWindow.get())),
+ // Media item contains media properties, e.g. cropping.
+ uno::Any(reinterpret_cast<sal_IntPtr>(mpItem))
+ };
+
+ try
+ {
+ xPlayerWindow = mxPlayer->createPlayerWindow( aArgs );
+ }
+ catch( const uno::RuntimeException& )
+ {
+ // happens eg, on MacOSX where Java frames cannot be created from X11 window handles
+ }
+
+ mxPlayerWindow = xPlayerWindow;
+
+ if( xPlayerWindow.is() )
+ {
+ xPlayerWindow->addKeyListener( uno::Reference< awt::XKeyListener >( mxEvents ) );
+ xPlayerWindow->addMouseListener( uno::Reference< awt::XMouseListener >( mxEvents ) );
+ xPlayerWindow->addMouseMotionListener( uno::Reference< awt::XMouseMotionListener >( mxEvents ) );
+ xPlayerWindow->addFocusListener( uno::Reference< awt::XFocusListener >( mxEvents ) );
+ }
+ }
+ else
+ mxPlayerWindow.clear();
+
+ if( mxPlayerWindow.is() )
+ mpChildWindow->Show();
+ else
+ mpChildWindow->Hide();
+
+ if( mpMediaWindowControl )
+ {
+ MediaItem aItem;
+
+ updateMediaItem( aItem );
+ mpMediaWindowControl->setState( aItem );
+ }
+}
+
+void MediaWindowImpl::setPosSize(const tools::Rectangle& rRect)
+{
+ SetPosSizePixel(rRect.TopLeft(), rRect.GetSize());
+}
+
+void MediaWindowImpl::setPointer(PointerStyle aPointer)
+{
+ SetPointer(aPointer);
+
+ if (mpChildWindow)
+ mpChildWindow->SetPointer(aPointer);
+
+ if (!mxPlayerWindow.is())
+ return;
+
+ sal_Int32 nPointer;
+
+ switch (aPointer)
+ {
+ case PointerStyle::Cross:
+ nPointer = awt::SystemPointer::CROSS;
+ break;
+ case PointerStyle::Hand:
+ nPointer = awt::SystemPointer::HAND;
+ break;
+ case PointerStyle::Move:
+ nPointer = awt::SystemPointer::MOVE;
+ break;
+ case PointerStyle::Wait:
+ nPointer = awt::SystemPointer::WAIT;
+ break;
+ default:
+ nPointer = awt::SystemPointer::ARROW;
+ break;
+ }
+
+ mxPlayerWindow->setPointerType(nPointer);
+}
+
+void MediaWindowImpl::Resize()
+{
+ const Size aCurSize(GetOutputSizePixel());
+ const sal_Int32 nOffset(mpMediaWindowControl ? AVMEDIA_CONTROLOFFSET : 0);
+
+ Size aPlayerWindowSize(aCurSize.Width() - (nOffset << 1),
+ aCurSize.Height() - (nOffset << 1));
+
+ if (mpMediaWindowControl)
+ {
+ const sal_Int32 nControlHeight = mpMediaWindowControl->GetSizePixel().Height();
+ const sal_Int32 nControlY = std::max(aCurSize.Height() - nControlHeight - nOffset, tools::Long(0));
+
+ aPlayerWindowSize.setHeight( nControlY - (nOffset << 1) );
+ mpMediaWindowControl->SetPosSizePixel(Point(nOffset, nControlY ), Size(aCurSize.Width() - (nOffset << 1), nControlHeight));
+ }
+ if (mpChildWindow)
+ mpChildWindow->SetPosSizePixel(Point(0, 0), aPlayerWindowSize);
+
+ if (mxPlayerWindow.is())
+ mxPlayerWindow->setPosSize(0, 0, aPlayerWindowSize.Width(), aPlayerWindowSize.Height(), 0);
+}
+
+void MediaWindowImpl::StateChanged(StateChangedType eType)
+{
+ if (!mxPlayerWindow.is())
+ return;
+
+ // stop playing when going disabled or hidden
+ switch (eType)
+ {
+ case StateChangedType::Visible:
+ {
+ stopPlayingInternal(!IsVisible());
+ mxPlayerWindow->setVisible(IsVisible());
+ }
+ break;
+
+ case StateChangedType::Enable:
+ {
+ stopPlayingInternal(!IsEnabled());
+ mxPlayerWindow->setEnable(IsEnabled());
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void MediaWindowImpl::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ if (mxPlayerWindow.is())
+ mxPlayerWindow->update();
+
+ BitmapEx* pLogo = nullptr;
+
+ if (!mxPlayer.is())
+ {
+ if (!mpEmptyBmpEx)
+ mpEmptyBmpEx.reset(new BitmapEx(AVMEDIA_BMP_EMPTYLOGO));
+
+ pLogo = mpEmptyBmpEx.get();
+ }
+ else if (!mxPlayerWindow.is())
+ {
+ if (!mpAudioBmpEx)
+ mpAudioBmpEx.reset(new BitmapEx(AVMEDIA_BMP_AUDIOLOGO));
+
+ pLogo = mpAudioBmpEx.get();
+ }
+
+ if (!mpChildWindow)
+ return;
+
+ const Point aBasePos(mpChildWindow->GetPosPixel());
+ const tools::Rectangle aVideoRect(aBasePos, mpChildWindow->GetSizePixel());
+
+ if (!pLogo || pLogo->IsEmpty() || aVideoRect.IsEmpty())
+ return;
+
+ Size aLogoSize(pLogo->GetSizePixel());
+ const Color aBackgroundColor(67, 67, 67);
+
+ rRenderContext.SetLineColor(aBackgroundColor);
+ rRenderContext.SetFillColor(aBackgroundColor);
+ rRenderContext.DrawRect(aVideoRect);
+
+ if ((aLogoSize.Width() > aVideoRect.GetWidth() || aLogoSize.Height() > aVideoRect.GetHeight() ) &&
+ (aLogoSize.Height() > 0))
+ {
+ const double fLogoWH = double(aLogoSize.Width()) / aLogoSize.Height();
+
+ if (fLogoWH < (double(aVideoRect.GetWidth()) / aVideoRect.GetHeight()))
+ {
+ aLogoSize.setWidth( tools::Long(aVideoRect.GetHeight() * fLogoWH) );
+ aLogoSize.setHeight( aVideoRect.GetHeight() );
+ }
+ else
+ {
+ aLogoSize.setWidth( aVideoRect.GetWidth() );
+ aLogoSize.setHeight( tools::Long(aVideoRect.GetWidth() / fLogoWH) );
+ }
+ }
+
+ Point aPoint(aBasePos.X() + ((aVideoRect.GetWidth() - aLogoSize.Width()) >> 1),
+ aBasePos.Y() + ((aVideoRect.GetHeight() - aLogoSize.Height()) >> 1));
+
+ rRenderContext.DrawBitmapEx(aPoint, aLogoSize, *pLogo);
+}
+
+void MediaWindowImpl::GetFocus()
+{
+}
+
+void MediaWindowImpl::MouseMove(const MouseEvent& rMEvt)
+{
+ if (mpMediaWindow)
+ mpMediaWindow->MouseMove(rMEvt);
+}
+
+void MediaWindowImpl::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (mpMediaWindow)
+ mpMediaWindow->MouseButtonDown(rMEvt);
+}
+
+void MediaWindowImpl::MouseButtonUp(const MouseEvent& rMEvt)
+{
+ if (mpMediaWindow)
+ mpMediaWindow->MouseButtonUp(rMEvt);
+}
+
+void MediaWindowImpl::KeyInput(const KeyEvent& rKEvt)
+{
+ if (mpMediaWindow)
+ mpMediaWindow->KeyInput(rKEvt);
+}
+
+void MediaWindowImpl::KeyUp(const KeyEvent& rKEvt)
+{
+ if (mpMediaWindow)
+ mpMediaWindow->KeyUp(rKEvt);
+}
+
+void MediaWindowImpl::Command(const CommandEvent& rCEvt)
+{
+ if (mpMediaWindow)
+ mpMediaWindow->Command(rCEvt);
+}
+
+sal_Int8 MediaWindowImpl::AcceptDrop(const AcceptDropEvent& rEvt)
+{
+ return (mpMediaWindow ? mpMediaWindow->AcceptDrop(rEvt) : 0);
+}
+
+sal_Int8 MediaWindowImpl::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ return (mpMediaWindow ? mpMediaWindow->ExecuteDrop(rEvt) : 0);
+}
+
+void MediaWindowImpl::StartDrag(sal_Int8 nAction, const Point& rPosPixel)
+{
+ if (mpMediaWindow)
+ mpMediaWindow->StartDrag(nAction, rPosPixel);
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/viewer/mediawindow_impl.hxx b/avmedia/source/viewer/mediawindow_impl.hxx
new file mode 100644
index 000000000..aa95fde22
--- /dev/null
+++ b/avmedia/source/viewer/mediawindow_impl.hxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/transfer.hxx>
+#include <vcl/syschild.hxx>
+
+#include <mediacontrol.hxx>
+
+namespace com::sun::star::media {
+ class XPlayer;
+ class XPlayerWindow;
+}
+
+namespace com::sun::star::uno {
+ class XComponentContext;
+}
+
+class BitmapEx;
+
+namespace avmedia
+{
+
+class MediaWindow;
+
+namespace priv
+{
+
+class MediaWindowControl : public MediaControl
+{
+public:
+
+ explicit MediaWindowControl( vcl::Window* pParent );
+
+protected:
+
+ void update() override;
+ void execute( const MediaItem& rItem ) override;
+};
+
+class MediaChildWindow : public SystemChildWindow
+{
+public:
+
+ explicit MediaChildWindow( vcl::Window* pParent );
+
+protected:
+
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+ virtual void KeyUp( const KeyEvent& rKEvt ) override;
+ virtual void Command( const CommandEvent& rCEvt ) override;
+};
+
+class MediaEventListenersImpl;
+
+class MediaWindowImpl : public Control, public DropTargetHelper, public DragSourceHelper
+{
+public:
+ MediaWindowImpl(vcl::Window* parent, MediaWindow* pMediaWindow, bool bInternalMediaControl);
+ virtual ~MediaWindowImpl() override;
+
+ virtual void dispose() override;
+
+ static css::uno::Reference<css::media::XPlayer> createPlayer(const OUString& rURL, const OUString& rReferer, const OUString* pMimeType);
+
+ void setURL(const OUString& rURL, OUString const& rTempURL, OUString const& rReferer);
+
+ const OUString& getURL() const;
+
+ bool isValid() const;
+
+ Size getPreferredSize() const;
+
+ bool start();
+
+ void updateMediaItem( MediaItem& rItem ) const;
+ void executeMediaItem( const MediaItem& rItem );
+
+ void setPosSize( const tools::Rectangle& rRect );
+
+ void setPointer( PointerStyle nPointer );
+
+private:
+
+ // Window
+ virtual void MouseMove( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonDown( const MouseEvent& rMEvt ) override;
+ virtual void MouseButtonUp( const MouseEvent& rMEvt ) override;
+ virtual void KeyInput( const KeyEvent& rKEvt ) override;
+ virtual void KeyUp( const KeyEvent& rKEvt ) override;
+ virtual void Command( const CommandEvent& rCEvt ) override;
+ virtual void Resize() override;
+ virtual void StateChanged( StateChangedType ) override;
+ virtual void Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) override; // const
+ virtual void GetFocus() override;
+
+ // DropTargetHelper
+ virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override;
+ virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override;
+
+ // DragSourceHelper
+ virtual void StartDrag( sal_Int8 nAction, const Point& rPosPixel ) override;
+
+ void stop();
+
+ bool isPlaying() const;
+
+ double getDuration() const;
+
+ void setMediaTime( double fTime );
+ double getMediaTime() const;
+
+ void stopPlayingInternal( bool );
+
+ void onURLChanged();
+
+ static css::uno::Reference<css::media::XPlayer> createPlayer(const OUString& rURL, const OUString& rManagerServName,
+ const css::uno::Reference<css::uno::XComponentContext>& xContext);
+
+ OUString maFileURL;
+ OUString mTempFileURL;
+ OUString maReferer;
+ OUString m_sMimeType;
+ css::uno::Reference<css::media::XPlayer> mxPlayer;
+ css::uno::Reference<css::media::XPlayerWindow> mxPlayerWindow;
+ MediaWindow* mpMediaWindow;
+
+ rtl::Reference<MediaEventListenersImpl> mxEvents;
+ VclPtr<MediaChildWindow> mpChildWindow;
+ VclPtr<MediaWindowControl> mpMediaWindowControl;
+ std::unique_ptr<BitmapEx> mpEmptyBmpEx;
+ std::unique_ptr<BitmapEx> mpAudioBmpEx;
+ const MediaItem* mpItem = nullptr;
+};
+
+}} // end namespace avmedia::priv
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/avmediawin.component b/avmedia/source/win/avmediawin.component
new file mode 100644
index 000000000..4b68b1cfc
--- /dev/null
+++ b/avmedia/source/win/avmediawin.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.avmedia.Manager_DirectX"
+ constructor="avmedia_Manager_DirectX_get_implementation">
+ <service name="com.sun.star.media.Manager_DirectX"/>
+ </implementation>
+</component>
diff --git a/avmedia/source/win/framegrabber.cxx b/avmedia/source/win/framegrabber.cxx
new file mode 100644
index 000000000..547b3ffa6
--- /dev/null
+++ b/avmedia/source/win/framegrabber.cxx
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <memory>
+
+#include <prewin.h>
+#include <postwin.h>
+#include <objbase.h>
+#include <strmif.h>
+#include <Amvideo.h>
+#include "interface.hxx"
+#include <uuids.h>
+
+#include "framegrabber.hxx"
+#include "player.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/file.hxx>
+#include <tools/stream.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/dibtools.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <systools/win32/oleauto.hxx>
+
+constexpr OUStringLiteral AVMEDIA_WIN_FRAMEGRABBER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.FrameGrabber_DirectX";
+constexpr OUStringLiteral AVMEDIA_WIN_FRAMEGRABBER_SERVICENAME = u"com.sun.star.media.FrameGrabber_DirectX";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::win {
+
+
+FrameGrabber::FrameGrabber()
+ : sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED, false,
+ sal::systools::CoInitializeGuard::WhenFailed::NoThrow)
+{
+}
+
+
+FrameGrabber::~FrameGrabber() = default;
+
+namespace {
+
+sal::systools::COMReference<IMediaDet> implCreateMediaDet( const OUString& rURL )
+{
+ sal::systools::COMReference<IMediaDet> pDet;
+
+ if( SUCCEEDED(pDet.CoCreateInstance(CLSID_MediaDet, nullptr, CLSCTX_INPROC_SERVER)) )
+ {
+ OUString aLocalStr;
+
+ if( osl::FileBase::getSystemPathFromFileURL( rURL, aLocalStr )
+ == osl::FileBase::E_None )
+ {
+ if( !SUCCEEDED( pDet->put_Filename(sal::systools::BStr(aLocalStr)) ) )
+ pDet.clear();
+ }
+ }
+
+ return pDet;
+}
+
+}
+
+bool FrameGrabber::create( const OUString& rURL )
+{
+ // just check if a MediaDet interface can be created with the given URL
+ if (implCreateMediaDet(rURL))
+ maURL = rURL;
+ else
+ maURL.clear();
+
+ return !maURL.isEmpty();
+}
+
+
+uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime )
+{
+ uno::Reference< graphic::XGraphic > xRet;
+ if (sal::systools::COMReference<IMediaDet> pDet = implCreateMediaDet(maURL))
+ {
+ double fLength;
+ long nStreamCount;
+ bool bFound = false;
+
+ if( SUCCEEDED( pDet->get_OutputStreams( &nStreamCount ) ) )
+ {
+ for( long n = 0; ( n < nStreamCount ) && !bFound; ++n )
+ {
+ GUID aMajorType;
+
+ if( SUCCEEDED( pDet->put_CurrentStream( n ) ) &&
+ SUCCEEDED( pDet->get_StreamType( &aMajorType ) ) &&
+ ( aMajorType == MEDIATYPE_Video ) )
+ {
+ bFound = true;
+ }
+ }
+ }
+
+ if( bFound &&
+ ( S_OK == pDet->get_StreamLength( &fLength ) ) &&
+ ( fLength > 0.0 ) && ( fMediaTime >= 0.0 ) && ( fMediaTime <= fLength ) )
+ {
+ AM_MEDIA_TYPE aMediaType;
+ LONG nWidth = 0, nHeight = 0;
+ long nSize = 0;
+
+ if( SUCCEEDED( pDet->get_StreamMediaType( &aMediaType ) ) )
+ {
+ if( ( aMediaType.formattype == FORMAT_VideoInfo ) &&
+ ( aMediaType.cbFormat >= sizeof( VIDEOINFOHEADER ) ) )
+ {
+ VIDEOINFOHEADER* pVih = reinterpret_cast< VIDEOINFOHEADER* >( aMediaType.pbFormat );
+
+ nWidth = pVih->bmiHeader.biWidth;
+ nHeight = pVih->bmiHeader.biHeight;
+
+ if( nHeight < 0 )
+ nHeight *= -1;
+ }
+
+ if( aMediaType.cbFormat != 0 )
+ {
+ ::CoTaskMemFree( aMediaType.pbFormat );
+ aMediaType.cbFormat = 0;
+ aMediaType.pbFormat = nullptr;
+ }
+
+ if( aMediaType.pUnk != nullptr )
+ {
+ aMediaType.pUnk->Release();
+ aMediaType.pUnk = nullptr;
+ }
+ }
+
+ if( ( nWidth > 0 ) && ( nHeight > 0 ) &&
+ SUCCEEDED( pDet->GetBitmapBits( 0, &nSize, nullptr, nWidth, nHeight ) ) &&
+ ( nSize > 0 ) )
+ {
+ auto pBuffer = std::make_unique<char[]>(nSize);
+
+ try
+ {
+ if( SUCCEEDED( pDet->GetBitmapBits( fMediaTime, nullptr, pBuffer.get(), nWidth, nHeight ) ) )
+ {
+ SvMemoryStream aMemStm( pBuffer.get(), nSize, StreamMode::READ | StreamMode::WRITE );
+ Bitmap aBmp;
+
+ if( ReadDIB(aBmp, aMemStm, false ) && !aBmp.IsEmpty() )
+ {
+ BitmapEx aBitmapEx(aBmp);
+ Graphic aGraphic(aBitmapEx);
+ xRet = aGraphic.GetXGraphic();
+ }
+ }
+ }
+ catch( ... )
+ {
+ }
+ }
+ }
+ }
+
+ return xRet;
+}
+
+
+OUString SAL_CALL FrameGrabber::getImplementationName( )
+{
+ return AVMEDIA_WIN_FRAMEGRABBER_IMPLEMENTATIONNAME;
+}
+
+
+sal_Bool SAL_CALL FrameGrabber::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+uno::Sequence< OUString > SAL_CALL FrameGrabber::getSupportedServiceNames( )
+{
+ return { AVMEDIA_WIN_FRAMEGRABBER_SERVICENAME };
+}
+
+} // namespace avmedia::win
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/framegrabber.hxx b/avmedia/source/win/framegrabber.hxx
new file mode 100644
index 000000000..d1ca48e84
--- /dev/null
+++ b/avmedia/source/win/framegrabber.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "wincommon.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <systools/win32/comtools.hxx>
+
+#include <com/sun/star/media/XFrameGrabber.hpp>
+
+struct IMediaDet;
+
+namespace avmedia::win {
+
+class FrameGrabber : public ::cppu::WeakImplHelper< css::media::XFrameGrabber,
+ css::lang::XServiceInfo >,
+ public sal::systools::CoInitializeGuard
+{
+public:
+ explicit FrameGrabber();
+ ~FrameGrabber() override;
+
+ bool create( const OUString& rURL );
+
+ // XFrameGrabber
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL grabFrame( double fMediaTime ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+ OUString maURL;
+};
+
+} // namespace avmedia::win
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/interface.hxx b/avmedia/source/win/interface.hxx
new file mode 100644
index 000000000..a52c6ab1f
--- /dev/null
+++ b/avmedia/source/win/interface.hxx
@@ -0,0 +1,120 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+extern "C" const CLSID CLSID_MediaDet;
+extern "C" const IID IID_IMediaDet;
+struct ISampleGrabber;
+
+struct
+__declspec(uuid("65BD0710-24D2-4ff7-9324-ED2E5D3ABAFA")) __declspec(novtable)
+IMediaDet : public IUnknown
+{
+public:
+ virtual HRESULT __stdcall get_Filter(
+ IUnknown **pVal) = 0;
+ virtual HRESULT __stdcall put_Filter(
+ IUnknown *newVal) = 0;
+ virtual HRESULT __stdcall get_OutputStreams(
+ long *pVal) = 0;
+ virtual HRESULT __stdcall get_CurrentStream(
+ long *pVal) = 0;
+ virtual HRESULT __stdcall put_CurrentStream(
+ long newVal) = 0;
+ virtual HRESULT __stdcall get_StreamType(
+ GUID *pVal) = 0;
+ virtual HRESULT __stdcall get_StreamTypeB(
+ BSTR *pVal) = 0;
+ virtual HRESULT __stdcall get_StreamLength(
+ double *pVal) = 0;
+ virtual HRESULT __stdcall get_Filename(
+ BSTR *pVal) = 0;
+ virtual HRESULT __stdcall put_Filename(
+ BSTR newVal) = 0;
+ virtual HRESULT __stdcall GetBitmapBits(
+ double StreamTime,
+ long *pBufferSize,
+ char *pBuffer,
+ long Width,
+ long Height) = 0;
+ virtual HRESULT __stdcall WriteBitmapBits(
+ double StreamTime,
+ long Width,
+ long Height,
+ BSTR Filename) = 0;
+ virtual HRESULT __stdcall get_StreamMediaType(
+ AM_MEDIA_TYPE *pVal) = 0;
+ virtual HRESULT __stdcall GetSampleGrabber(
+ ISampleGrabber **ppVal) = 0;
+ virtual HRESULT __stdcall get_FrameRate(
+ double *pVal) = 0;
+ virtual HRESULT __stdcall EnterBitmapGrabMode(
+ double SeekTime) = 0;
+
+protected:
+ ~IMediaDet() {}
+};
+
+extern "C" const IID IID_ISampleGrabberCB;
+struct
+__declspec(uuid("0579154A-2B53-4994-B0D0-E773148EFF85")) __declspec(novtable)
+ISampleGrabberCB : public IUnknown
+{
+public:
+ virtual HRESULT __stdcall SampleCB(
+ double SampleTime,
+ IMediaSample *pSample) = 0;
+ virtual HRESULT __stdcall BufferCB(
+ double SampleTime,
+ BYTE *pBuffer,
+ long BufferLen) = 0;
+
+protected:
+ ~ISampleGrabberCB() {}
+};
+
+extern "C" const IID IID_ISampleGrabber;
+struct
+__declspec(uuid("6B652FFF-11FE-4fce-92AD-0266B5D7C78F")) __declspec(novtable)
+ISampleGrabber : public IUnknown
+{
+public:
+ virtual HRESULT __stdcall SetOneShot(
+ BOOL OneShot) = 0;
+ virtual HRESULT __stdcall SetMediaType(
+ const AM_MEDIA_TYPE *pType) = 0;
+ virtual HRESULT __stdcall GetConnectedMediaType(
+ AM_MEDIA_TYPE *pType) = 0;
+ virtual HRESULT __stdcall SetBufferSamples(
+ BOOL BufferThem) = 0;
+ virtual HRESULT __stdcall GetCurrentBuffer(
+ long *pBufferSize,
+ long *pBuffer) = 0;
+ virtual HRESULT __stdcall GetCurrentSample(
+ IMediaSample **ppSample) = 0;
+ virtual HRESULT __stdcall SetCallback(
+ ISampleGrabberCB *pCallback,
+ long WhichMethodToCallback) = 0;
+
+protected:
+ ~ISampleGrabber() {}
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/manager.cxx b/avmedia/source/win/manager.cxx
new file mode 100644
index 000000000..578ec9d2a
--- /dev/null
+++ b/avmedia/source/win/manager.cxx
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "manager.hxx"
+#include "player.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+#include <rtl/ref.hxx>
+#include <tools/urlobj.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia::win {
+
+Manager::Manager()
+{
+}
+
+
+Manager::~Manager()
+{
+}
+
+
+uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL )
+{
+ rtl::Reference<Player> pPlayer( new Player() );
+ const INetURLObject aURL( rURL );
+
+ if( !pPlayer->create( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ) )
+ pPlayer.clear();
+
+ return pPlayer;
+}
+
+
+OUString SAL_CALL Manager::getImplementationName( )
+{
+ return "com.sun.star.comp.avmedia.Manager_DirectX";
+}
+
+
+sal_Bool SAL_CALL Manager::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames( )
+{
+ return { "com.sun.star.media.Manager" };
+}
+
+} // namespace avmedia::win
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+avmedia_Manager_DirectX_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new avmedia::win::Manager());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/manager.hxx b/avmedia/source/win/manager.hxx
new file mode 100644
index 000000000..8742103c4
--- /dev/null
+++ b/avmedia/source/win/manager.hxx
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "wincommon.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/media/XManager.hpp>
+
+
+namespace avmedia::win {
+
+class Manager : public ::cppu::WeakImplHelper< css::media::XManager,
+ css::lang::XServiceInfo >
+{
+public:
+
+ explicit Manager();
+ ~Manager() override;
+
+ // XManager
+ virtual css::uno::Reference< css::media::XPlayer > SAL_CALL createPlayer( const OUString& aURL ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+} // namespace avmedia::win
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/player.cxx b/avmedia/source/win/player.cxx
new file mode 100644
index 000000000..5c05dcbc3
--- /dev/null
+++ b/avmedia/source/win/player.cxx
@@ -0,0 +1,418 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <objbase.h>
+#include <strmif.h>
+#include <control.h>
+#include <uuids.h>
+#include <evcode.h>
+
+#include "player.hxx"
+#include "framegrabber.hxx"
+#include "window.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <osl/file.hxx>
+#include <rtl/ref.hxx>
+
+constexpr OUStringLiteral AVMEDIA_WIN_PLAYER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.Player_DirectX";
+constexpr OUStringLiteral AVMEDIA_WIN_PLAYER_SERVICENAME = u"com.sun.star.media.Player_DirectX";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::win {
+
+static LRESULT CALLBACK MediaPlayerWndProc_2( HWND hWnd,UINT nMsg, WPARAM nPar1, LPARAM nPar2 )
+{
+ Player* pPlayer = reinterpret_cast<Player*>(::GetWindowLongPtrW( hWnd, 0 ));
+ bool bProcessed = true;
+
+ if( pPlayer )
+ {
+ switch( nMsg )
+ {
+ case WM_GRAPHNOTIFY:
+ pPlayer->processEvent();
+ break;
+ default:
+ bProcessed = false;
+ break;
+ }
+ }
+ else
+ bProcessed = false;
+
+ return( bProcessed ? 0 : DefWindowProcW( hWnd, nMsg, nPar1, nPar2 ) );
+}
+
+
+Player::Player() :
+ Player_BASE(m_aMutex),
+ sal::systools::CoInitializeGuard(COINIT_APARTMENTTHREADED, false,
+ sal::systools::CoInitializeGuard::WhenFailed::NoThrow),
+ mnUnmutedVolume( 0 ),
+ mnFrameWnd( nullptr ),
+ mbMuted( false ),
+ mbLooping( false ),
+ mbAddWindow( true )
+{
+}
+
+
+Player::~Player()
+{
+ if( mnFrameWnd )
+ ::DestroyWindow( mnFrameWnd );
+}
+
+
+void SAL_CALL Player::disposing()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ stop();
+ if( mpME )
+ mpME->SetNotifyWindow( 0, WM_GRAPHNOTIFY, 0);
+}
+
+
+bool Player::create( const OUString& rURL )
+{
+ bool bRet = false;
+
+ if( SUCCEEDED(mpGB.CoCreateInstance(CLSID_FilterGraph, nullptr, CLSCTX_INPROC_SERVER)) )
+ {
+ // Don't use the overlay mixer on Windows Vista
+ // It disables the desktop composition as soon as RenderFile is called
+ // also causes some other problems: video rendering is not reliable
+
+ // tdf#128057: IGraphBuilder::RenderFile seems to fail to handle file URIs properly when
+ // they contain encoded characters like "%23"; so pass system path in that case instead.
+ OUString aFile(rURL);
+ if (aFile.startsWithIgnoreAsciiCase("file:"))
+ osl::FileBase::getSystemPathFromFileURL(rURL, aFile);
+
+ if( SUCCEEDED( mpGB->RenderFile( o3tl::toW(aFile.getStr()), nullptr ) ) &&
+ mpMC.set(mpGB, sal::systools::COM_QUERY) &&
+ mpME.set(mpGB, sal::systools::COM_QUERY) &&
+ mpMP.set(mpGB, sal::systools::COM_QUERY) )
+ {
+ // Video interfaces
+ mpVW.set(mpGB, sal::systools::COM_QUERY);
+ mpBV.set(mpGB, sal::systools::COM_QUERY);
+
+ // Audio interface
+ mpBA.set(mpGB, sal::systools::COM_QUERY);
+
+ if( mpBA )
+ mpBA->put_Volume( mnUnmutedVolume );
+
+ bRet = true;
+ }
+ }
+
+ if( bRet )
+ maURL = rURL;
+ else
+ maURL.clear();
+
+ return bRet;
+}
+
+
+const IVideoWindow* Player::getVideoWindow() const
+{
+ return mpVW;
+}
+
+
+void Player::setNotifyWnd( HWND nNotifyWnd )
+{
+ mbAddWindow = false;
+ if( mpME )
+ mpME->SetNotifyWindow( reinterpret_cast<OAHWND>(nNotifyWnd), WM_GRAPHNOTIFY, reinterpret_cast< LONG_PTR>( this ) );
+}
+
+
+void Player::processEvent()
+{
+ long nCode;
+ LONG_PTR nParam1, nParam2;
+
+ while( mpME && SUCCEEDED( mpME->GetEvent( &nCode, &nParam1, &nParam2, 0 ) ) )
+ {
+ if( EC_COMPLETE == nCode )
+ {
+ if( mbLooping )
+ {
+ setMediaTime( 0.0 );
+ start();
+ }
+ else
+ {
+ setMediaTime( getDuration() );
+ stop();
+ }
+ }
+
+ mpME->FreeEventParams( nCode, nParam1, nParam2 );
+ }
+}
+
+
+void SAL_CALL Player::start( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ if( mpMC )
+ {
+ if ( mbAddWindow )
+ {
+ static WNDCLASSW* mpWndClass = nullptr;
+ if ( !mpWndClass )
+ {
+ mpWndClass = new WNDCLASSW;
+
+ memset( mpWndClass, 0, sizeof( *mpWndClass ) );
+ mpWndClass->hInstance = GetModuleHandleW( nullptr );
+ mpWndClass->cbWndExtra = sizeof( DWORD );
+ mpWndClass->lpfnWndProc = MediaPlayerWndProc_2;
+ mpWndClass->lpszClassName = L"com_sun_star_media_Sound_Player";
+ mpWndClass->hbrBackground = static_cast<HBRUSH>(::GetStockObject( BLACK_BRUSH ));
+ mpWndClass->hCursor = ::LoadCursor( nullptr, IDC_ARROW );
+
+ RegisterClassW( mpWndClass );
+ }
+ if ( !mnFrameWnd )
+ {
+ mnFrameWnd = CreateWindowW( mpWndClass->lpszClassName, nullptr,
+ 0,
+ 0, 0, 0, 0,
+ nullptr, nullptr, mpWndClass->hInstance, nullptr );
+ if ( mnFrameWnd )
+ {
+ ::ShowWindow(mnFrameWnd, SW_HIDE);
+ SetWindowLongPtrW( mnFrameWnd, 0, reinterpret_cast<LONG_PTR>(this) );
+ // mpVW->put_Owner( (OAHWND) mnFrameWnd );
+ setNotifyWnd( mnFrameWnd );
+ }
+ }
+ }
+
+ mpMC->Run();
+ }
+}
+
+
+void SAL_CALL Player::stop( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ if( mpMC )
+ mpMC->Stop();
+}
+
+
+sal_Bool SAL_CALL Player::isPlaying()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ OAFilterState eFilterState;
+ bool bRet = false;
+
+ if( mpMC && SUCCEEDED( mpMC->GetState( 10, &eFilterState ) ) )
+ bRet = ( State_Running == eFilterState );
+
+ return bRet;
+}
+
+
+double SAL_CALL Player::getDuration( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ REFTIME aRefTime( 0.0 );
+
+ if( mpMP )
+ mpMP->get_Duration( &aRefTime );
+
+ return aRefTime;
+}
+
+
+void SAL_CALL Player::setMediaTime( double fTime )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ if( mpMP )
+ {
+ const bool bPlaying = isPlaying();
+
+ mpMP->put_CurrentPosition( fTime );
+
+ if( !bPlaying && mpMC )
+ mpMC->StopWhenReady();
+ }
+}
+
+
+double SAL_CALL Player::getMediaTime( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ REFTIME aRefTime( 0.0 );
+
+ if( mpMP )
+ mpMP->get_CurrentPosition( &aRefTime );
+
+ return aRefTime;
+}
+
+
+void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ mbLooping = bSet;
+}
+
+
+sal_Bool SAL_CALL Player::isPlaybackLoop( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ return mbLooping;
+}
+
+
+void SAL_CALL Player::setMute( sal_Bool bSet )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ if (mpBA && (mbMuted != static_cast<bool>(bSet)))
+ {
+ mbMuted = bSet;
+ mpBA->put_Volume( mbMuted ? -10000 : mnUnmutedVolume );
+ }
+}
+
+
+sal_Bool SAL_CALL Player::isMute( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ return mbMuted;
+}
+
+
+void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ mnUnmutedVolume = static_cast< long >( nVolumeDB ) * 100;
+
+ if( !mbMuted && mpBA )
+ mpBA->put_Volume( mnUnmutedVolume );
+}
+
+
+sal_Int16 SAL_CALL Player::getVolumeDB( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ return static_cast< sal_Int16 >( mnUnmutedVolume / 100 );
+}
+
+
+awt::Size SAL_CALL Player::getPreferredPlayerWindowSize( )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ awt::Size aSize( 0, 0 );
+
+ if( mpBV )
+ {
+ long nWidth = 0, nHeight = 0;
+
+ mpBV->GetVideoSize( &nWidth, &nHeight );
+ aSize.Width = nWidth;
+ aSize.Height = nHeight;
+ }
+
+ return aSize;
+}
+
+
+uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ uno::Reference< ::media::XPlayerWindow > xRet;
+ awt::Size aSize( getPreferredPlayerWindowSize() );
+
+ if( mpVW && aSize.Width > 0 && aSize.Height > 0 )
+ {
+ rtl::Reference<::avmedia::win::Window> pWindow = new ::avmedia::win::Window( *this );
+
+ xRet = pWindow;
+
+ if( !pWindow->create( aArguments ) )
+ xRet.clear();
+ }
+
+ return xRet;
+}
+
+
+uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber( )
+{
+ uno::Reference< media::XFrameGrabber > xRet;
+
+ if( !maURL.isEmpty() )
+ {
+ rtl::Reference<FrameGrabber> pGrabber = new FrameGrabber();
+
+ xRet = pGrabber;
+
+ if( !pGrabber->create( maURL ) )
+ xRet.clear();
+ }
+
+ return xRet;
+}
+
+
+OUString SAL_CALL Player::getImplementationName( )
+{
+ return AVMEDIA_WIN_PLAYER_IMPLEMENTATIONNAME;
+}
+
+
+sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames( )
+{
+ return { AVMEDIA_WIN_PLAYER_SERVICENAME };
+}
+
+} // namespace avmedia::win
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/player.hxx b/avmedia/source/win/player.hxx
new file mode 100644
index 000000000..1563d549a
--- /dev/null
+++ b/avmedia/source/win/player.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <WinDef.h>
+
+#include "wincommon.hxx"
+
+#include <com/sun/star/media/XPlayer.hpp>
+
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+#include <systools/win32/comtools.hxx>
+
+struct IGraphBuilder;
+struct IBaseFilter;
+struct IMediaControl;
+struct IMediaEventEx;
+struct IMediaSeeking;
+struct IMediaPosition;
+struct IBasicAudio;
+struct IBasicVideo;
+struct IVideoWindow;
+struct IDDrawExclModeVideo;
+struct IDirectDraw;
+struct IDirectDrawSurface;
+
+namespace avmedia::win {
+
+typedef ::cppu::WeakComponentImplHelper< css::media::XPlayer,
+ css::lang::XServiceInfo > Player_BASE;
+
+
+class Player : public cppu::BaseMutex,
+ public Player_BASE,
+ public sal::systools::CoInitializeGuard
+{
+public:
+
+ explicit Player();
+ ~Player() override;
+
+ bool create( const OUString& rURL );
+
+ void setNotifyWnd( HWND nNotifyWnd );
+ void processEvent();
+
+ const IVideoWindow* getVideoWindow() const;
+
+ // XPlayer
+ virtual void SAL_CALL start( ) override;
+ virtual void SAL_CALL stop( ) override;
+ virtual sal_Bool SAL_CALL isPlaying( ) override;
+ virtual double SAL_CALL getDuration( ) override;
+ virtual void SAL_CALL setMediaTime( double fTime ) override;
+ virtual double SAL_CALL getMediaTime( ) override;
+ virtual void SAL_CALL setPlaybackLoop( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isPlaybackLoop( ) override;
+ virtual void SAL_CALL setMute( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isMute( ) override;
+ virtual void SAL_CALL setVolumeDB( sal_Int16 nVolumeDB ) override;
+ virtual sal_Int16 SAL_CALL getVolumeDB( ) override;
+ virtual css::awt::Size SAL_CALL getPreferredPlayerWindowSize( ) override;
+ virtual css::uno::Reference< css::media::XPlayerWindow > SAL_CALL createPlayerWindow( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ virtual css::uno::Reference< css::media::XFrameGrabber > SAL_CALL createFrameGrabber( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // ::cppu::OComponentHelper
+ virtual void SAL_CALL disposing() override;
+
+private:
+
+ OUString maURL;
+ sal::systools::COMReference<IGraphBuilder> mpGB;
+ sal::systools::COMReference<IMediaControl> mpMC;
+ sal::systools::COMReference<IMediaEventEx> mpME;
+ sal::systools::COMReference<IMediaPosition> mpMP;
+ sal::systools::COMReference<IBasicAudio> mpBA;
+ sal::systools::COMReference<IBasicVideo> mpBV;
+ sal::systools::COMReference<IVideoWindow> mpVW;
+ long mnUnmutedVolume;
+ HWND mnFrameWnd;
+ bool mbMuted;
+ bool mbLooping;
+ bool mbAddWindow;
+};
+
+} // namespace avmedia::win
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/wincommon.hxx b/avmedia/source/win/wincommon.hxx
new file mode 100644
index 000000000..5572f6c3e
--- /dev/null
+++ b/avmedia/source/win/wincommon.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <osl/mutex.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/stream.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/factory.hxx>
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/media/XManager.hpp>
+
+#define WM_GRAPHNOTIFY (WM_USER + 567)
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/window.cxx b/avmedia/source/win/window.cxx
new file mode 100644
index 000000000..4f4ef4745
--- /dev/null
+++ b/avmedia/source/win/window.cxx
@@ -0,0 +1,494 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <objbase.h>
+#include <strmif.h>
+#include <control.h>
+#include <dshow.h>
+
+#include <com/sun/star/awt/SystemPointer.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+#include "window.hxx"
+#include "player.hxx"
+
+constexpr OUStringLiteral AVMEDIA_WIN_WINDOW_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.Window_DirectX";
+constexpr OUStringLiteral AVMEDIA_WIN_WINDOW_SERVICENAME = u"com.sun.star.media.Window_DirectX";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::win {
+
+static LRESULT CALLBACK MediaPlayerWndProc( HWND hWnd,UINT nMsg, WPARAM nPar1, LPARAM nPar2 )
+{
+ Window* pWindow = reinterpret_cast<Window*>(GetWindowLongPtrW( hWnd, 0 ));
+ bool bProcessed = true;
+
+ if( pWindow )
+ {
+ switch( nMsg )
+ {
+ case WM_SETCURSOR:
+ pWindow->updatePointer();
+ break;
+
+ case WM_GRAPHNOTIFY:
+ pWindow->processGraphEvent();
+ break;
+
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_MBUTTONUP:
+ case WM_RBUTTONUP:
+ PostMessage(pWindow->getParentWnd(), nMsg, nPar1, nPar2);
+ break;
+
+ case WM_SETFOCUS:
+ {
+ const awt::FocusEvent aUNOEvt;
+ pWindow->fireSetFocusEvent( aUNOEvt );
+ }
+ break;
+
+ default:
+ bProcessed = false;
+ break;
+ }
+ }
+ else
+ bProcessed = false;
+
+ return( bProcessed ? 0 : DefWindowProcW( hWnd, nMsg, nPar1, nPar2 ) );
+}
+
+static WNDCLASSW* lcl_getWndClass()
+{
+ WNDCLASSW* s_pWndClass = new WNDCLASSW;
+
+ memset( s_pWndClass, 0, sizeof( *s_pWndClass ) );
+ s_pWndClass->hInstance = GetModuleHandleW( nullptr );
+ s_pWndClass->cbWndExtra = sizeof( DWORD_PTR );
+ s_pWndClass->lpfnWndProc = MediaPlayerWndProc;
+ s_pWndClass->lpszClassName = L"com_sun_star_media_PlayerWnd";
+ s_pWndClass->hbrBackground = static_cast<HBRUSH>(::GetStockObject( BLACK_BRUSH ));
+ s_pWndClass->hCursor = ::LoadCursor( nullptr, IDC_ARROW );
+
+ RegisterClassW( s_pWndClass );
+
+ return s_pWndClass;
+}
+
+Window::Window( Player& rPlayer ) :
+ maListeners( maMutex ),
+ meZoomLevel( media::ZoomLevel_NOT_AVAILABLE ),
+ mrPlayer( rPlayer ),
+ mnFrameWnd( nullptr ),
+ mnParentWnd( nullptr ),
+ mnPointerType( awt::SystemPointer::ARROW )
+{
+}
+
+Window::~Window()
+{
+ if( mnFrameWnd )
+ ::DestroyWindow( mnFrameWnd );
+}
+
+void Window::ImplLayoutVideoWindow()
+{
+ if( media::ZoomLevel_NOT_AVAILABLE != meZoomLevel )
+ {
+ awt::Size aPrefSize( mrPlayer.getPreferredPlayerWindowSize() );
+ awt::Rectangle aRect = getPosSize();
+ int nW = aRect.Width, nH = aRect.Height;
+ int nVideoW = nW, nVideoH = nH;
+ int nX = 0, nY = 0, nWidth = 0, nHeight = 0;
+ bool bDone = false, bZoom = false;
+
+ if( media::ZoomLevel_ORIGINAL == meZoomLevel )
+ {
+ bZoom = true;
+ }
+ else if( media::ZoomLevel_ZOOM_1_TO_4 == meZoomLevel )
+ {
+ aPrefSize.Width >>= 2;
+ aPrefSize.Height >>= 2;
+ bZoom = true;
+ }
+ else if( media::ZoomLevel_ZOOM_1_TO_2 == meZoomLevel )
+ {
+ aPrefSize.Width >>= 1;
+ aPrefSize.Height >>= 1;
+ bZoom = true;
+ }
+ else if( media::ZoomLevel_ZOOM_2_TO_1 == meZoomLevel )
+ {
+ aPrefSize.Width <<= 1;
+ aPrefSize.Height <<= 1;
+ bZoom = true;
+ }
+ else if( media::ZoomLevel_ZOOM_4_TO_1 == meZoomLevel )
+ {
+ aPrefSize.Width <<= 2;
+ aPrefSize.Height <<= 2;
+ bZoom = true;
+ }
+ else if( media::ZoomLevel_FIT_TO_WINDOW == meZoomLevel )
+ {
+ nWidth = nVideoW;
+ nHeight = nVideoH;
+ bDone = true;
+ }
+
+ if( bZoom )
+ {
+ if( ( aPrefSize.Width <= nVideoW ) && ( aPrefSize.Height <= nVideoH ) )
+ {
+ nX = ( nVideoW - aPrefSize.Width ) >> 1;
+ nY = ( nVideoH - aPrefSize.Height ) >> 1;
+ nWidth = aPrefSize.Width;
+ nHeight = aPrefSize.Height;
+ bDone = true;
+ }
+ }
+
+ if( !bDone )
+ {
+ if( aPrefSize.Width > 0 && aPrefSize.Height > 0 && nVideoW > 0 && nVideoH > 0 )
+ {
+ double fPrefWH = static_cast<double>(aPrefSize.Width) / aPrefSize.Height;
+
+ if( fPrefWH < ( static_cast<double>(nVideoW) / nVideoH ) )
+ nVideoW = static_cast<int>( nVideoH * fPrefWH );
+ else
+ nVideoH = static_cast<int>( nVideoW / fPrefWH );
+
+ nX = ( nW - nVideoW ) >> 1;
+ nY = ( nH - nVideoH ) >> 1;
+ nWidth = nVideoW;
+ nHeight = nVideoH;
+ }
+ else
+ nX = nY = nWidth = nHeight = 0;
+ }
+
+ IVideoWindow* pVideoWindow = const_cast< IVideoWindow* >( mrPlayer.getVideoWindow() );
+
+ if( pVideoWindow )
+ pVideoWindow->SetWindowPosition( nX, nY, nWidth, nHeight );
+ }
+}
+
+bool Window::create( const uno::Sequence< uno::Any >& rArguments )
+{
+ IVideoWindow* pVideoWindow = const_cast< IVideoWindow* >( mrPlayer.getVideoWindow() );
+ static WNDCLASSW* mpWndClass = lcl_getWndClass();
+
+ if( !mnFrameWnd && pVideoWindow && mpWndClass )
+ {
+ awt::Rectangle aRect;
+ sal_IntPtr nWnd;
+
+ rArguments[ 0 ] >>= nWnd;
+ rArguments[ 1 ] >>= aRect;
+
+ mnParentWnd = reinterpret_cast<HWND>(nWnd);
+
+ mnFrameWnd = CreateWindowW( mpWndClass->lpszClassName, nullptr,
+ WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
+ aRect.X, aRect.Y, aRect.Width, aRect.Height,
+ mnParentWnd, nullptr, mpWndClass->hInstance, nullptr );
+
+ if( mnFrameWnd )
+ {
+ SetWindowLongPtrW( mnFrameWnd, 0, reinterpret_cast<LONG_PTR>(this) );
+
+ pVideoWindow->put_Owner( reinterpret_cast<OAHWND>(mnFrameWnd) );
+ pVideoWindow->put_MessageDrain( reinterpret_cast<OAHWND>(mnFrameWnd) );
+ pVideoWindow->put_WindowStyle( WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN );
+
+ mrPlayer.setNotifyWnd( mnFrameWnd );
+
+ meZoomLevel = media::ZoomLevel_FIT_TO_WINDOW;
+ ImplLayoutVideoWindow();
+ }
+ }
+
+ return( mnFrameWnd != nullptr );
+}
+
+void Window::processGraphEvent()
+{
+ mrPlayer.processEvent();
+}
+
+void Window::updatePointer()
+{
+ LPCTSTR pCursorName;
+
+ switch( mnPointerType )
+ {
+ case awt::SystemPointer::CROSS: pCursorName = IDC_CROSS; break;
+ case awt::SystemPointer::MOVE: pCursorName = IDC_SIZEALL; break;
+ case awt::SystemPointer::WAIT: pCursorName = IDC_WAIT; break;
+
+ default:
+ pCursorName = IDC_ARROW;
+ break;
+ }
+
+ SetCursor( LoadCursor( nullptr, pCursorName ) );
+}
+
+void SAL_CALL Window::update( )
+{
+ ::RedrawWindow( mnFrameWnd, nullptr, nullptr, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE );
+}
+
+sal_Bool SAL_CALL Window::setZoomLevel( media::ZoomLevel eZoomLevel )
+{
+ bool bRet = false;
+
+ if( media::ZoomLevel_NOT_AVAILABLE != meZoomLevel &&
+ media::ZoomLevel_NOT_AVAILABLE != eZoomLevel )
+ {
+ if( eZoomLevel != meZoomLevel )
+ {
+ meZoomLevel = eZoomLevel;
+ ImplLayoutVideoWindow();
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+media::ZoomLevel SAL_CALL Window::getZoomLevel( )
+{
+ return meZoomLevel;
+}
+
+void SAL_CALL Window::setPointerType( sal_Int32 nPointerType )
+{
+ mnPointerType = nPointerType;
+}
+
+void SAL_CALL Window::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 )
+{
+ if( mnFrameWnd )
+ {
+ ::SetWindowPos( mnFrameWnd, HWND_TOP, X, Y, Width, Height, 0 );
+ ImplLayoutVideoWindow();
+ }
+}
+
+awt::Rectangle SAL_CALL Window::getPosSize()
+{
+ awt::Rectangle aRet;
+
+ if( mnFrameWnd )
+ {
+ ::RECT aWndRect;
+
+ if( ::GetClientRect( mnFrameWnd, &aWndRect ) )
+ {
+ aRet.X = aWndRect.left;
+ aRet.Y = aWndRect.top;
+ aRet.Width = aWndRect.right - aWndRect.left + 1;
+ aRet.Height = aWndRect.bottom - aWndRect.top + 1;
+ }
+ }
+
+ return aRet;
+}
+
+void SAL_CALL Window::setVisible( sal_Bool bVisible )
+{
+ if( mnFrameWnd )
+ {
+ IVideoWindow* pVideoWindow = const_cast< IVideoWindow* >( mrPlayer.getVideoWindow() );
+
+ if( pVideoWindow )
+ pVideoWindow->put_Visible( bVisible ? OATRUE : OAFALSE );
+
+ ::ShowWindow( mnFrameWnd, bVisible ? SW_SHOW : SW_HIDE );
+ }
+}
+
+void SAL_CALL Window::setEnable( sal_Bool bEnable )
+{
+ if( mnFrameWnd )
+ ::EnableWindow( mnFrameWnd, bEnable );
+}
+
+void SAL_CALL Window::setFocus( )
+{
+ if( mnFrameWnd )
+ ::SetFocus( mnFrameWnd );
+}
+
+void SAL_CALL Window::addWindowListener( const uno::Reference< awt::XWindowListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::removeWindowListener( const uno::Reference< awt::XWindowListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::addFocusListener( const uno::Reference< awt::XFocusListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::removeFocusListener( const uno::Reference< awt::XFocusListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::addKeyListener( const uno::Reference< awt::XKeyListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::removeKeyListener( const uno::Reference< awt::XKeyListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::addMouseListener( const uno::Reference< awt::XMouseListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::removeMouseListener( const uno::Reference< awt::XMouseListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::addMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::removeMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::addPaintListener( const uno::Reference< awt::XPaintListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::removePaintListener( const uno::Reference< awt::XPaintListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::dispose( )
+{
+}
+
+void SAL_CALL Window::addEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ maListeners.addInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void SAL_CALL Window::removeEventListener( const uno::Reference< lang::XEventListener >& xListener )
+{
+ maListeners.removeInterface( cppu::UnoType<decltype(xListener)>::get(), xListener );
+}
+
+void Window::fireMousePressedEvent( const css::awt::MouseEvent& rEvt )
+{
+ comphelper::OInterfaceContainerHelper2* pContainer = maListeners.getContainer( cppu::UnoType<awt::XMouseListener>::get());
+
+ if( pContainer )
+ {
+ comphelper::OInterfaceIteratorHelper2 aIter( *pContainer );
+
+ while( aIter.hasMoreElements() )
+ static_cast< awt::XMouseListener* >( aIter.next() )->mousePressed( rEvt );
+ }
+}
+
+void Window::fireMouseReleasedEvent( const css::awt::MouseEvent& rEvt )
+{
+ comphelper::OInterfaceContainerHelper2* pContainer = maListeners.getContainer( cppu::UnoType<awt::XMouseListener>::get());
+
+ if( pContainer )
+ {
+ comphelper::OInterfaceIteratorHelper2 aIter( *pContainer );
+
+ while( aIter.hasMoreElements() )
+ static_cast< awt::XMouseListener* >( aIter.next() )->mouseReleased( rEvt );
+ }
+}
+
+void Window::fireMouseMovedEvent( const css::awt::MouseEvent& rEvt )
+{
+ comphelper::OInterfaceContainerHelper2* pContainer = maListeners.getContainer( cppu::UnoType<awt::XMouseMotionListener>::get());
+
+ if( pContainer )
+ {
+ comphelper::OInterfaceIteratorHelper2 aIter( *pContainer );
+
+ while( aIter.hasMoreElements() )
+ static_cast< awt::XMouseMotionListener* >( aIter.next() )->mouseMoved( rEvt );
+ }
+}
+
+void Window::fireSetFocusEvent( const css::awt::FocusEvent& rEvt )
+{
+ comphelper::OInterfaceContainerHelper2* pContainer = maListeners.getContainer( cppu::UnoType<awt::XFocusListener>::get());
+
+ if( pContainer )
+ {
+ comphelper::OInterfaceIteratorHelper2 aIter( *pContainer );
+
+ while( aIter.hasMoreElements() )
+ static_cast< awt::XFocusListener* >( aIter.next() )->focusGained( rEvt );
+ }
+}
+
+OUString SAL_CALL Window::getImplementationName( )
+{
+ return AVMEDIA_WIN_WINDOW_IMPLEMENTATIONNAME;
+}
+
+sal_Bool SAL_CALL Window::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL Window::getSupportedServiceNames( )
+{
+ return { AVMEDIA_WIN_WINDOW_SERVICENAME };
+}
+
+} // namespace avmedia::win
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/win/window.hxx b/avmedia/source/win/window.hxx
new file mode 100644
index 000000000..39d5aec62
--- /dev/null
+++ b/avmedia/source/win/window.hxx
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <WinDef.h>
+
+#include "wincommon.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/multicontainer2.hxx>
+
+#include <com/sun/star/media/XPlayerWindow.hpp>
+
+struct IVideoWindow;
+
+namespace avmedia::win {
+
+class Player;
+
+
+class Window : public ::cppu::WeakImplHelper< css::media::XPlayerWindow,
+ css::lang::XServiceInfo >
+{
+public:
+
+ Window( Player& rPlayer );
+ ~Window() override;
+
+ bool create( const css::uno::Sequence< css::uno::Any >& aArguments );
+ void processGraphEvent();
+ void updatePointer();
+
+ // XPlayerWindow
+ virtual void SAL_CALL update( ) override;
+ virtual sal_Bool SAL_CALL setZoomLevel( css::media::ZoomLevel ZoomLevel ) override;
+ virtual css::media::ZoomLevel SAL_CALL getZoomLevel( ) override;
+ virtual void SAL_CALL setPointerType( sal_Int32 nPointerType ) override;
+
+ // XWindow
+ virtual void SAL_CALL setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) override;
+ virtual css::awt::Rectangle SAL_CALL getPosSize( ) override;
+ virtual void SAL_CALL setVisible( sal_Bool Visible ) override;
+ virtual void SAL_CALL setEnable( sal_Bool Enable ) override;
+ virtual void SAL_CALL setFocus( ) override;
+ virtual void SAL_CALL addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+ virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+public:
+
+ void fireMousePressedEvent( const css::awt::MouseEvent& rEvt );
+ void fireMouseReleasedEvent( const css::awt::MouseEvent& rEvt );
+ void fireMouseMovedEvent( const css::awt::MouseEvent& rEvt );
+ void fireKeyPressedEvent( const css::awt::KeyEvent& rEvt );
+ void fireKeyReleasedEvent( const css::awt::KeyEvent& rEvt );
+ void fireSetFocusEvent( const css::awt::FocusEvent& rEvt );
+ HWND getParentWnd() const { return mnParentWnd; }
+
+private:
+
+ ::osl::Mutex maMutex;
+ comphelper::OMultiTypeInterfaceContainerHelper2 maListeners;
+ css::media::ZoomLevel meZoomLevel;
+ Player& mrPlayer;
+ HWND mnFrameWnd;
+ HWND mnParentWnd;
+ int mnPointerType;
+
+ void ImplLayoutVideoWindow();
+};
+
+} // namespace avmedia::win
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */