From 940b4d1848e8c70ab7642901a68594e8016caffc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Apr 2024 18:51:28 +0200 Subject: Adding upstream version 1:7.0.4. Signed-off-by: Daniel Baumann --- avmedia/AllLangMoTarget_avmedia.mk | 11 + avmedia/IwyuFilter_avmedia.yaml | 9 + avmedia/Library_avmedia.mk | 76 ++ avmedia/Library_avmediaMacAVF.mk | 46 + avmedia/Library_avmediagst.mk | 46 + avmedia/Library_avmediavlc.mk | 56 ++ avmedia/Library_avmediawin.mk | 48 ++ avmedia/Makefile | 14 + avmedia/Module_avmedia.mk | 47 ++ avmedia/README | 25 + avmedia/inc/bitmaps.hlst | 18 + avmedia/inc/helpids.h | 37 + avmedia/inc/mediacontrol.hxx | 74 ++ avmedia/inc/strings.hrc | 50 ++ avmedia/source/avmediadummy.cxx | 92 ++ avmedia/source/framework/MediaControlBase.cxx | 262 ++++++ avmedia/source/framework/mediacontrol.cxx | 238 ++++++ avmedia/source/framework/mediaitem.cxx | 526 ++++++++++++ avmedia/source/framework/mediaplayer.cxx | 145 ++++ avmedia/source/framework/mediatoolbox.cxx | 134 +++ avmedia/source/framework/soundhandler.cxx | 323 +++++++ avmedia/source/framework/soundhandler.hxx | 120 +++ .../source/gstreamer/avmediagstreamer.component | 15 + avmedia/source/gstreamer/gstcommon.hxx | 43 + avmedia/source/gstreamer/gstframegrabber.cxx | 176 ++++ avmedia/source/gstreamer/gstframegrabber.hxx | 60 ++ avmedia/source/gstreamer/gstmanager.cxx | 71 ++ avmedia/source/gstreamer/gstmanager.hxx | 47 ++ avmedia/source/gstreamer/gstplayer.cxx | 929 +++++++++++++++++++++ avmedia/source/gstreamer/gstplayer.hxx | 107 +++ avmedia/source/gstreamer/gstuno.cxx | 59 ++ avmedia/source/gstreamer/gstwindow.cxx | 193 +++++ avmedia/source/gstreamer/gstwindow.hxx | 82 ++ avmedia/source/inc/mediamisc.hxx | 45 + avmedia/source/macavf/avmediaMacAVF.component | 25 + avmedia/source/macavf/framegrabber.hxx | 56 ++ avmedia/source/macavf/framegrabber.mm | 106 +++ avmedia/source/macavf/macavfcommon.hxx | 89 ++ avmedia/source/macavf/macavfuno.mm | 55 ++ avmedia/source/macavf/manager.hxx | 52 ++ avmedia/source/macavf/manager.mm | 71 ++ avmedia/source/macavf/player.hxx | 84 ++ avmedia/source/macavf/player.mm | 364 ++++++++ avmedia/source/macavf/window.hxx | 111 +++ avmedia/source/macavf/window.mm | 261 ++++++ avmedia/source/viewer/mediaevent_impl.cxx | 173 ++++ avmedia/source/viewer/mediaevent_impl.hxx | 78 ++ avmedia/source/viewer/mediawindow.cxx | 404 +++++++++ avmedia/source/viewer/mediawindow_impl.cxx | 671 +++++++++++++++ avmedia/source/viewer/mediawindow_impl.hxx | 157 ++++ avmedia/source/vlc/avmediavlc.component | 15 + avmedia/source/vlc/inc/wrapper/Common.hxx | 29 + avmedia/source/vlc/inc/wrapper/EventHandler.hxx | 42 + avmedia/source/vlc/inc/wrapper/EventManager.hxx | 55 ++ avmedia/source/vlc/inc/wrapper/Instance.hxx | 41 + avmedia/source/vlc/inc/wrapper/Media.hxx | 47 ++ avmedia/source/vlc/inc/wrapper/Player.hxx | 73 ++ avmedia/source/vlc/inc/wrapper/ThreadsafeQueue.hxx | 81 ++ avmedia/source/vlc/inc/wrapper/Wrapper.hxx | 19 + avmedia/source/vlc/vlccommon.hxx | 40 + avmedia/source/vlc/vlcframegrabber.cxx | 131 +++ avmedia/source/vlc/vlcframegrabber.hxx | 52 ++ avmedia/source/vlc/vlcmanager.cxx | 124 +++ avmedia/source/vlc/vlcmanager.hxx | 54 ++ avmedia/source/vlc/vlcplayer.cxx | 251 ++++++ avmedia/source/vlc/vlcplayer.hxx | 86 ++ avmedia/source/vlc/vlcuno.cxx | 76 ++ avmedia/source/vlc/vlcwindow.cxx | 197 +++++ avmedia/source/vlc/vlcwindow.hxx | 74 ++ avmedia/source/vlc/wrapper/Common.cxx | 45 + avmedia/source/vlc/wrapper/EventHandler.cxx | 42 + avmedia/source/vlc/wrapper/EventManager.cxx | 84 ++ avmedia/source/vlc/wrapper/Instance.cxx | 62 ++ avmedia/source/vlc/wrapper/Media.cxx | 109 +++ avmedia/source/vlc/wrapper/Player.cxx | 241 ++++++ avmedia/source/vlc/wrapper/SymbolLoader.hxx | 126 +++ avmedia/source/vlc/wrapper/Types.hxx | 58 ++ avmedia/source/win/avmediawin.component | 25 + avmedia/source/win/framegrabber.cxx | 225 +++++ avmedia/source/win/framegrabber.hxx | 57 ++ avmedia/source/win/interface.hxx | 120 +++ avmedia/source/win/manager.cxx | 77 ++ avmedia/source/win/manager.hxx | 51 ++ avmedia/source/win/player.cxx | 463 ++++++++++ avmedia/source/win/player.hxx | 119 +++ avmedia/source/win/wincommon.hxx | 44 + avmedia/source/win/window.cxx | 495 +++++++++++ avmedia/source/win/window.hxx | 114 +++ avmedia/source/win/winuno.cxx | 59 ++ avmedia/util/avmedia.component | 26 + 90 files changed, 11110 insertions(+) create mode 100644 avmedia/AllLangMoTarget_avmedia.mk create mode 100644 avmedia/IwyuFilter_avmedia.yaml create mode 100644 avmedia/Library_avmedia.mk create mode 100644 avmedia/Library_avmediaMacAVF.mk create mode 100644 avmedia/Library_avmediagst.mk create mode 100644 avmedia/Library_avmediavlc.mk create mode 100644 avmedia/Library_avmediawin.mk create mode 100644 avmedia/Makefile create mode 100644 avmedia/Module_avmedia.mk create mode 100644 avmedia/README create mode 100644 avmedia/inc/bitmaps.hlst create mode 100644 avmedia/inc/helpids.h create mode 100644 avmedia/inc/mediacontrol.hxx create mode 100644 avmedia/inc/strings.hrc create mode 100644 avmedia/source/avmediadummy.cxx create mode 100644 avmedia/source/framework/MediaControlBase.cxx create mode 100644 avmedia/source/framework/mediacontrol.cxx create mode 100644 avmedia/source/framework/mediaitem.cxx create mode 100644 avmedia/source/framework/mediaplayer.cxx create mode 100644 avmedia/source/framework/mediatoolbox.cxx create mode 100644 avmedia/source/framework/soundhandler.cxx create mode 100644 avmedia/source/framework/soundhandler.hxx create mode 100644 avmedia/source/gstreamer/avmediagstreamer.component create mode 100644 avmedia/source/gstreamer/gstcommon.hxx create mode 100644 avmedia/source/gstreamer/gstframegrabber.cxx create mode 100644 avmedia/source/gstreamer/gstframegrabber.hxx create mode 100644 avmedia/source/gstreamer/gstmanager.cxx create mode 100644 avmedia/source/gstreamer/gstmanager.hxx create mode 100644 avmedia/source/gstreamer/gstplayer.cxx create mode 100644 avmedia/source/gstreamer/gstplayer.hxx create mode 100644 avmedia/source/gstreamer/gstuno.cxx create mode 100644 avmedia/source/gstreamer/gstwindow.cxx create mode 100644 avmedia/source/gstreamer/gstwindow.hxx create mode 100644 avmedia/source/inc/mediamisc.hxx create mode 100644 avmedia/source/macavf/avmediaMacAVF.component create mode 100644 avmedia/source/macavf/framegrabber.hxx create mode 100644 avmedia/source/macavf/framegrabber.mm create mode 100644 avmedia/source/macavf/macavfcommon.hxx create mode 100644 avmedia/source/macavf/macavfuno.mm create mode 100644 avmedia/source/macavf/manager.hxx create mode 100644 avmedia/source/macavf/manager.mm create mode 100644 avmedia/source/macavf/player.hxx create mode 100644 avmedia/source/macavf/player.mm create mode 100644 avmedia/source/macavf/window.hxx create mode 100644 avmedia/source/macavf/window.mm create mode 100644 avmedia/source/viewer/mediaevent_impl.cxx create mode 100644 avmedia/source/viewer/mediaevent_impl.hxx create mode 100644 avmedia/source/viewer/mediawindow.cxx create mode 100644 avmedia/source/viewer/mediawindow_impl.cxx create mode 100644 avmedia/source/viewer/mediawindow_impl.hxx create mode 100644 avmedia/source/vlc/avmediavlc.component create mode 100644 avmedia/source/vlc/inc/wrapper/Common.hxx create mode 100644 avmedia/source/vlc/inc/wrapper/EventHandler.hxx create mode 100644 avmedia/source/vlc/inc/wrapper/EventManager.hxx create mode 100644 avmedia/source/vlc/inc/wrapper/Instance.hxx create mode 100644 avmedia/source/vlc/inc/wrapper/Media.hxx create mode 100644 avmedia/source/vlc/inc/wrapper/Player.hxx create mode 100644 avmedia/source/vlc/inc/wrapper/ThreadsafeQueue.hxx create mode 100644 avmedia/source/vlc/inc/wrapper/Wrapper.hxx create mode 100644 avmedia/source/vlc/vlccommon.hxx create mode 100644 avmedia/source/vlc/vlcframegrabber.cxx create mode 100644 avmedia/source/vlc/vlcframegrabber.hxx create mode 100644 avmedia/source/vlc/vlcmanager.cxx create mode 100644 avmedia/source/vlc/vlcmanager.hxx create mode 100644 avmedia/source/vlc/vlcplayer.cxx create mode 100644 avmedia/source/vlc/vlcplayer.hxx create mode 100644 avmedia/source/vlc/vlcuno.cxx create mode 100644 avmedia/source/vlc/vlcwindow.cxx create mode 100644 avmedia/source/vlc/vlcwindow.hxx create mode 100644 avmedia/source/vlc/wrapper/Common.cxx create mode 100644 avmedia/source/vlc/wrapper/EventHandler.cxx create mode 100644 avmedia/source/vlc/wrapper/EventManager.cxx create mode 100644 avmedia/source/vlc/wrapper/Instance.cxx create mode 100644 avmedia/source/vlc/wrapper/Media.cxx create mode 100644 avmedia/source/vlc/wrapper/Player.cxx create mode 100644 avmedia/source/vlc/wrapper/SymbolLoader.hxx create mode 100644 avmedia/source/vlc/wrapper/Types.hxx create mode 100644 avmedia/source/win/avmediawin.component create mode 100644 avmedia/source/win/framegrabber.cxx create mode 100644 avmedia/source/win/framegrabber.hxx create mode 100644 avmedia/source/win/interface.hxx create mode 100644 avmedia/source/win/manager.cxx create mode 100644 avmedia/source/win/manager.hxx create mode 100644 avmedia/source/win/player.cxx create mode 100644 avmedia/source/win/player.hxx create mode 100644 avmedia/source/win/wincommon.hxx create mode 100644 avmedia/source/win/window.cxx create mode 100644 avmedia/source/win/window.hxx create mode 100644 avmedia/source/win/winuno.cxx create mode 100644 avmedia/util/avmedia.component (limited to 'avmedia') diff --git a/avmedia/AllLangMoTarget_avmedia.mk b/avmedia/AllLangMoTarget_avmedia.mk new file mode 100644 index 000000000..564b9ab94 --- /dev/null +++ b/avmedia/AllLangMoTarget_avmedia.mk @@ -0,0 +1,11 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. + +$(eval $(call gb_AllLangMoTarget_AllLangMoTarget,avmedia)) + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/IwyuFilter_avmedia.yaml b/avmedia/IwyuFilter_avmedia.yaml new file mode 100644 index 000000000..17d0a1299 --- /dev/null +++ b/avmedia/IwyuFilter_avmedia.yaml @@ -0,0 +1,9 @@ +--- +assumeFilename: avmedia/source/gstreamer/gstplayer.cxx +blacklist: + avmedia/source/viewer/mediawindow_impl.cxx: + # Actually used + - com/sun/star/uno/XComponentContext.hpp + avmedia/source/viewer/mediawindow.cxx: + # Actually used + - com/sun/star/media/XPlayer.hpp diff --git a/avmedia/Library_avmedia.mk b/avmedia/Library_avmedia.mk new file mode 100644 index 000000000..b27b9bae8 --- /dev/null +++ b/avmedia/Library_avmedia.mk @@ -0,0 +1,76 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,avmedia)) + +$(eval $(call gb_Library_set_componentfile,avmedia,avmedia/util/avmedia)) + +$(eval $(call gb_Library_set_include,avmedia,\ + $$(INCLUDE) \ + -I$(SRCDIR)/avmedia/inc \ + -I$(SRCDIR)/avmedia/source/inc \ +)) + +$(eval $(call gb_Library_set_componentfile,avmedia,avmedia/util/avmedia)) + +$(eval $(call gb_Library_use_sdk_api,avmedia,)) + +$(eval $(call gb_Library_use_externals,avmedia,\ + boost_headers \ +)) + +ifeq ($(USE_AVMEDIA_DUMMY),TRUE) +$(eval $(call gb_Library_add_exception_objects,avmedia,\ + avmedia/source/avmediadummy \ +)) + + +else + + +$(eval $(call gb_Library_add_defs,avmedia,\ + -DAVMEDIA_DLLIMPLEMENTATION \ +)) + +ifeq ($(DISABLE_GUI),) +$(eval $(call gb_Library_use_externals,avmedia,\ + epoxy \ +)) +endif + + +$(eval $(call gb_Library_use_libraries,avmedia,\ + comphelper \ + ucbhelper \ + cppu \ + cppuhelper \ + sal \ + i18nlangtag \ + sfx \ + svl \ + svt \ + tl \ + utl \ + vcl \ +)) + +$(eval $(call gb_Library_add_exception_objects,avmedia,\ + avmedia/source/framework/mediacontrol \ + avmedia/source/framework/MediaControlBase \ + avmedia/source/framework/mediaitem \ + avmedia/source/framework/mediaplayer \ + avmedia/source/framework/mediatoolbox \ + avmedia/source/framework/soundhandler \ + avmedia/source/viewer/mediaevent_impl \ + avmedia/source/viewer/mediawindow \ + avmedia/source/viewer/mediawindow_impl \ +)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/Library_avmediaMacAVF.mk b/avmedia/Library_avmediaMacAVF.mk new file mode 100644 index 000000000..877efd97a --- /dev/null +++ b/avmedia/Library_avmediaMacAVF.mk @@ -0,0 +1,46 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,avmediaMacAVF)) + +$(eval $(call gb_Library_set_componentfile,avmediaMacAVF,avmedia/source/macavf/avmediaMacAVF)) + +$(eval $(call gb_Library_set_include,avmediaMacAVF,\ + $$(INCLUDE) \ + -I$(SRCDIR)/avmedia/source/inc \ +)) + +$(eval $(call gb_Library_use_external,avmediaMacAVF,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,avmediaMacAVF)); + +$(eval $(call gb_Library_use_libraries,avmediaMacAVF,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + tl \ + vcl \ +)) + +$(eval $(call gb_Library_use_system_darwin_frameworks,avmediaMacAVF,\ + AVFoundation \ + Cocoa \ + CoreMedia \ +)) + +$(eval $(call gb_Library_add_objcxxobjects,avmediaMacAVF,\ + avmedia/source/macavf/framegrabber \ + avmedia/source/macavf/macavfuno \ + avmedia/source/macavf/manager \ + avmedia/source/macavf/player \ + avmedia/source/macavf/window \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/Library_avmediagst.mk b/avmedia/Library_avmediagst.mk new file mode 100644 index 000000000..7ed6cc451 --- /dev/null +++ b/avmedia/Library_avmediagst.mk @@ -0,0 +1,46 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,avmediagst)) + +$(eval $(call gb_Library_set_componentfile,avmediagst,avmedia/source/gstreamer/avmediagstreamer)) + +$(eval $(call gb_Library_set_include,avmediagst,\ + $$(INCLUDE) \ + -I$(SRCDIR)/avmedia/source/inc \ + $(GSTREAMER_1_0_CFLAGS) \ +)) + +$(eval $(call gb_Library_add_libs,avmediagst,\ + $(GSTREAMER_1_0_LIBS) \ +)) + +$(eval $(call gb_Library_use_external,avmediagst,boost_headers)) + +$(eval $(call gb_Library_use_sdk_api,avmediagst)) + +$(eval $(call gb_Library_use_libraries,avmediagst,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + salhelper \ + tl \ + vcl \ +)) + +$(eval $(call gb_Library_add_exception_objects,avmediagst,\ + avmedia/source/gstreamer/gstmanager \ + avmedia/source/gstreamer/gstplayer \ + avmedia/source/gstreamer/gstuno \ + avmedia/source/gstreamer/gstwindow \ + avmedia/source/gstreamer/gstframegrabber \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/Library_avmediavlc.mk b/avmedia/Library_avmediavlc.mk new file mode 100644 index 000000000..3db43315e --- /dev/null +++ b/avmedia/Library_avmediavlc.mk @@ -0,0 +1,56 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,avmediavlc)) + +$(eval $(call gb_Library_set_componentfile,avmediavlc,avmedia/source/vlc/avmediavlc)) + +$(eval $(call gb_Library_set_include,avmediavlc,\ + $$(INCLUDE) \ + -I$(SRCDIR)/avmedia/source/inc \ + -I$(SRCDIR)/avmedia/source/vlc/inc \ +)) + +$(eval $(call gb_Library_use_custom_headers,avmediavlc,\ + officecfg/registry \ +)) + +$(eval $(call gb_Library_use_sdk_api,avmediavlc)) + +$(eval $(call gb_Library_use_externals,avmediavlc,\ + boost_headers \ + boost_system \ +)) + +$(eval $(call gb_Library_use_libraries,avmediavlc,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + tl \ + vcl \ + utl \ + salhelper \ +)) + +$(eval $(call gb_Library_add_exception_objects,avmediavlc,\ + avmedia/source/vlc/vlcmanager \ + avmedia/source/vlc/vlcplayer \ + avmedia/source/vlc/vlcuno \ + avmedia/source/vlc/vlcwindow \ + avmedia/source/vlc/vlcframegrabber \ + avmedia/source/vlc/wrapper/Instance \ + avmedia/source/vlc/wrapper/Media \ + avmedia/source/vlc/wrapper/Player \ + avmedia/source/vlc/wrapper/EventManager \ + avmedia/source/vlc/wrapper/EventHandler \ + avmedia/source/vlc/wrapper/Common \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/Library_avmediawin.mk b/avmedia/Library_avmediawin.mk new file mode 100644 index 000000000..09453d6c7 --- /dev/null +++ b/avmedia/Library_avmediawin.mk @@ -0,0 +1,48 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Library_Library,avmediawin)) + +$(eval $(call gb_Library_set_componentfile,avmediawin,avmedia/source/win/avmediawin)) + +$(eval $(call gb_Library_use_external,avmediawin,boost_headers)) + +$(eval $(call gb_Library_set_include,avmediawin,\ + $$(INCLUDE) \ + -I$(SRCDIR)/avmedia/source/inc \ +)) + +$(eval $(call gb_Library_use_sdk_api,avmediawin)) + +$(eval $(call gb_Library_use_libraries,avmediawin,\ + comphelper \ + cppu \ + cppuhelper \ + sal \ + tl \ + utl \ + vcl \ +)) + +$(eval $(call gb_Library_use_system_win32_libs,avmediawin,\ + gdi32 \ + ole32 \ + oleaut32 \ + strmiids \ +)) + +$(eval $(call gb_Library_add_exception_objects,avmediawin,\ + avmedia/source/win/framegrabber \ + avmedia/source/win/manager \ + avmedia/source/win/player \ + avmedia/source/win/window \ + avmedia/source/win/winuno \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/Makefile b/avmedia/Makefile new file mode 100644 index 000000000..0997e6284 --- /dev/null +++ b/avmedia/Makefile @@ -0,0 +1,14 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/Module_avmedia.mk b/avmedia/Module_avmedia.mk new file mode 100644 index 000000000..9d8895a8e --- /dev/null +++ b/avmedia/Module_avmedia.mk @@ -0,0 +1,47 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# 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/. +# + +$(eval $(call gb_Module_Module,avmedia)) + +$(eval $(call gb_Module_add_targets,avmedia,\ + Library_avmedia \ +)) + +ifneq ($(USE_AVMEDIA_DUMMY),TRUE) +$(eval $(call gb_Module_add_l10n_targets,avmedia,\ + AllLangMoTarget_avmedia \ +)) + +ifeq ($(ENABLE_GSTREAMER_1_0),TRUE) +$(eval $(call gb_Module_add_targets,avmedia,\ + Library_avmediagst \ +)) +endif + +ifeq ($(ENABLE_VLC),TRUE) +$(eval $(call gb_Module_add_targets,avmedia,\ + Library_avmediavlc \ +)) +endif + +ifeq ($(OS),MACOSX) +$(eval $(call gb_Module_add_targets,avmedia,\ + Library_avmediaMacAVF \ +)) +endif + +ifeq ($(OS),WNT) +$(eval $(call gb_Module_add_targets,avmedia,\ + Library_avmediawin \ +)) +endif + +endif + +# vim: set noet sw=4 ts=4: diff --git a/avmedia/README b/avmedia/README new file mode 100644 index 000000000..32155b2ac --- /dev/null +++ b/avmedia/README @@ -0,0 +1,25 @@ +Audio/Video media implementation. + +Provides per-platform implementations of multimedia functionality. +Currently no stream API is provided, only a URI based one, so +streaming has to be wrapped around it via temp files. + +Also provides (in source/framework/mediacontrol.cxx) an implementation +of the graphical media playback control that appears in the toolbar / +mediaobject bar when media is selected under the .uno:AVMediaToolBox +item. + +== avmedia/gstreamer == + +The avmedia component is implementation of manager service defined in +offapi/com/sun/star/media/. Radek has added implementation based on +gstreamer so that we can add audio and video files into impress +presentation on Linux with gstreamer. + +The implementation is pretty straightforward, sometimes it has +problems when gstreamer installation is incomplete. + +In the beginning the media files were not embedded, Thorsten added +support for that later. + +FUTURE work: it might be worthwhile to revamp the avmedia UI diff --git a/avmedia/inc/bitmaps.hlst b/avmedia/inc/bitmaps.hlst new file mode 100644 index 000000000..682d090fe --- /dev/null +++ b/avmedia/inc/bitmaps.hlst @@ -0,0 +1,18 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_AVMEDIA_INC_BITMAPS_HRC +#define INCLUDED_AVMEDIA_INC_BITMAPS_HRC + +#define AVMEDIA_BMP_AUDIOLOGO "avmedia/res/avaudiologo.png" +#define AVMEDIA_BMP_EMPTYLOGO "avmedia/res/avemptylogo.png" + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/avmedia/inc/helpids.h b/avmedia/inc/helpids.h new file mode 100644 index 000000000..3d9a8c782 --- /dev/null +++ b/avmedia/inc/helpids.h @@ -0,0 +1,37 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_AVMEDIA_INC_HELPIDS_H +#define INCLUDED_AVMEDIA_INC_HELPIDS_H + +#define HID_AVMEDIA_TOOLBOXITEM_PLAY "AVMEDIA_HID_AVMEDIA_TOOLBOXITEM_PLAY" +#define HID_AVMEDIA_TOOLBOXITEM_PAUSE "AVMEDIA_HID_AVMEDIA_TOOLBOXITEM_PAUSE" +#define HID_AVMEDIA_TOOLBOXITEM_STOP "AVMEDIA_HID_AVMEDIA_TOOLBOXITEM_STOP" +#define HID_AVMEDIA_TOOLBOXITEM_MUTE "AVMEDIA_HID_AVMEDIA_TOOLBOXITEM_MUTE" +#define HID_AVMEDIA_TOOLBOXITEM_LOOP "AVMEDIA_HID_AVMEDIA_TOOLBOXITEM_LOOP" +#define HID_AVMEDIA_TOOLBOXITEM_OPEN "AVMEDIA_HID_AVMEDIA_TOOLBOXITEM_OPEN" +#define HID_AVMEDIA_TOOLBOXITEM_INSERT "AVMEDIA_HID_AVMEDIA_TOOLBOXITEM_INSERT" +#define HID_AVMEDIA_ZOOMLISTBOX "AVMEDIA_HID_AVMEDIA_ZOOMLISTBOX" +#define HID_AVMEDIA_TIMEEDIT "AVMEDIA_HID_AVMEDIA_TIMEEDIT" +#define HID_AVMEDIA_VOLUMESLIDER "AVMEDIA_HID_AVMEDIA_VOLUMESLIDER" +#define HID_AVMEDIA_PLAYERWINDOW "AVMEDIA_HID_AVMEDIA_PLAYERWINDOW" + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/inc/mediacontrol.hxx b/avmedia/inc/mediacontrol.hxx new file mode 100644 index 000000000..33c063fc9 --- /dev/null +++ b/avmedia/inc/mediacontrol.hxx @@ -0,0 +1,74 @@ +/* -*- 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 + +#include +#include +#include +#include +#include + +#define AVMEDIA_CONTROLOFFSET 6 + +namespace avmedia +{ + +class MediaItem; + +class MediaControl : public InterimItemWindow, public MediaControlBase +{ +public: + MediaControl( vcl::Window* pParent, MediaControlStyle eControlStyle ); + virtual ~MediaControl() override; + virtual void dispose() override; + + void setState( const MediaItem& rItem ); + void UpdateURLField( MediaItem const & maItem ); + +protected: + + virtual void update() = 0; + virtual void execute( const MediaItem& rItem ) = 0; + + virtual void InitializeWidgets() override; + std::unique_ptr mxMediaPath; + +private: + + DECL_LINK(implTimeHdl, weld::Scale&, void); + DECL_LINK(implTimeEndHdl, Timer*, void); + DECL_LINK(implVolumeHdl, weld::Scale&, void); + DECL_LINK(implSelectHdl, const OString&, void); + DECL_LINK(implZoomSelectHdl, weld::ComboBox&, void); + DECL_LINK(implTimeoutHdl, Timer*, void); + + Idle maIdle; + Idle maChangeTimeIdle; + MediaItem maItem; + bool mbLocked; + MediaControlStyle meControlStyle; + double mfTime; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/inc/strings.hrc b/avmedia/inc/strings.hrc new file mode 100644 index 000000000..2b1b23412 --- /dev/null +++ b/avmedia/inc/strings.hrc @@ -0,0 +1,50 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_AVMEDIA_INC_STRINGS_HRC +#define INCLUDED_AVMEDIA_INC_STRINGS_HRC + +#define NC_(Context, String) reinterpret_cast(Context "\004" u8##String) + +#define AVMEDIA_STR_OPEN NC_("AVMEDIA_STR_OPEN", "Open") +#define AVMEDIA_STR_INSERT NC_("AVMEDIA_STR_INSERT", "Apply") +#define AVMEDIA_STR_PLAY NC_("AVMEDIA_STR_PLAY", "Play") +#define AVMEDIA_STR_PAUSE NC_("AVMEDIA_STR_PAUSE", "Pause") +#define AVMEDIA_STR_STOP NC_("AVMEDIA_STR_STOP", "Stop") +#define AVMEDIA_STR_LOOP NC_("AVMEDIA_STR_LOOP", "Repeat") +#define AVMEDIA_STR_MUTE NC_("AVMEDIA_STR_MUTE", "Mute") +#define AVMEDIA_STR_ZOOM_50 NC_("AVMEDIA_STR_ZOOM_50", "50%") +#define AVMEDIA_STR_ZOOM_100 NC_("AVMEDIA_STR_ZOOM_100", "100%") +#define AVMEDIA_STR_ZOOM_200 NC_("AVMEDIA_STR_ZOOM_200", "200%") +#define AVMEDIA_STR_ZOOM_FIT NC_("AVMEDIA_STR_ZOOM_FIT", "Scaled") +#define AVMEDIA_STR_VOLUME NC_("AVMEDIA_STR_VOLUME", "Volume") +#define AVMEDIA_STR_POSITION NC_("AVMEDIA_STR_POSITION", "Position") +#define AVMEDIA_STR_MEDIAPLAYER NC_("AVMEDIA_STR_MEDIAPLAYER", "Media Player") +#define AVMEDIA_MEDIA_PATH NC_("AVMEDIA_MEDIA_PATH", "Media Path") +#define AVMEDIA_MEDIA_PATH_DEFAULT NC_("AVMEDIA_MEDIA_PATH_DEFAULT", "No Media Selected") +#define AVMEDIA_STR_INSERTMEDIA_DLG NC_("AVMEDIA_STR_INSERTMEDIA_DLG", "Insert Audio or Video") +#define AVMEDIA_STR_OPENMEDIA_DLG NC_("AVMEDIA_STR_OPENMEDIA_DLG", "Open Audio or Video") +#define AVMEDIA_STR_ALL_MEDIAFILES NC_("AVMEDIA_STR_ALL_MEDIAFILES", "All audio and video files") +#define AVMEDIA_STR_ALL_FILES NC_("AVMEDIA_STR_ALL_FILES", "All files") +#define AVMEDIA_STR_ERR_URL NC_("AVMEDIA_STR_ERR_URL", "The format of the selected file is not supported.") +#define AVMEDIA_STR_ZOOM_TOOLTIP NC_("AVMEDIA_STR_ZOOM_TOOLTIP", "View") + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/avmediadummy.cxx b/avmedia/source/avmediadummy.cxx new file mode 100644 index 000000000..6c6ada016 --- /dev/null +++ b/avmedia/source/avmediadummy.cxx @@ -0,0 +1,92 @@ +/* -*- 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 + +#include +#include + +using namespace ::com::sun::star; + +namespace avmedia +{ +MediaItem::MediaItem( sal_uInt16 i_nWhich, AVMediaSetMask ) + : SfxPoolItem( i_nWhich ) +{ +} + +MediaItem::MediaItem( const MediaItem& rItem ) + : SfxPoolItem( rItem ) +{ +} + +MediaItem::~MediaItem() +{ +} + +struct MediaItem::Impl +{ +}; + +bool MediaItem::QueryValue( css::uno::Any&, sal_uInt8 ) const +{ + return false; +} + +bool MediaItem::GetPresentation( SfxItemPresentation, MapUnit, MapUnit, OUString&, const IntlWrapper& ) const +{ + return false; +} + +bool MediaItem::PutValue( const css::uno::Any&, sal_uInt8 ) +{ + return false; +} + +AVMediaSetMask MediaItem::getMaskSet() const +{ + return AVMediaSetMask::NONE; +} + +SfxPoolItem* MediaItem::CreateDefault() +{ + return new MediaItem; +} + + +MediaItem* MediaItem::Clone( SfxItemPool*) const +{ + return nullptr; +} + +bool MediaItem::operator==( const SfxPoolItem& rItem ) const +{ + assert(SfxPoolItem::operator==(rItem)); (void)rItem; + return false; +} + +} // namespace avmedia + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* SAL_CALL +com_sun_star_comp_framework_SoundHandler_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return NULL; +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/framework/MediaControlBase.cxx b/avmedia/source/framework/MediaControlBase.cxx new file mode 100644 index 000000000..e84586d76 --- /dev/null +++ b/avmedia/source/framework/MediaControlBase.cxx @@ -0,0 +1,262 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::rtl::OUString; + +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::min( std::max( nVolumeDB, static_cast< sal_Int32 >( AVMEDIA_DB_RANGE ) ), + static_cast< 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); + 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_label("play", AvmResId(AVMEDIA_STR_PLAY)); + mxPlayToolBox->set_item_help_id("pause", HID_AVMEDIA_TOOLBOXITEM_PAUSE); + mxPlayToolBox->set_item_label("pause", AvmResId(AVMEDIA_STR_PAUSE)); + mxPlayToolBox->set_item_help_id("stop", HID_AVMEDIA_TOOLBOXITEM_STOP); + mxPlayToolBox->set_item_label("stop", AvmResId(AVMEDIA_STR_STOP)); + mxPlayToolBox->set_item_help_id("loop", HID_AVMEDIA_TOOLBOXITEM_LOOP); + mxPlayToolBox->set_item_label("loop", AvmResId(AVMEDIA_STR_LOOP)); + mxMuteToolBox->set_item_help_id("mute", HID_AVMEDIA_TOOLBOXITEM_MUTE); + mxMuteToolBox->set_item_label("mute", AvmResId(AVMEDIA_STR_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 )); + + const OUString aTimeText( " 00:00:00/00:00:00 " ); + mxTimeEdit->set_text( aTimeText ); + 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::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); + 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); + } + 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, const OString& 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() ); + } + else if (rId == "pause") + { + aExecItem.setState( MediaState::Pause ); + } + else if (rId == "stop") + { + aExecItem.setState( MediaState::Stop ); + aExecItem.setTime( 0.0 ); + } + 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..66818fc07 --- /dev/null +++ b/avmedia/source/framework/mediacontrol.cxx @@ -0,0 +1,238 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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"), + MediaControlBase(), + maIdle( "avmedia MediaControl Idle" ), + maChangeTimeIdle( "avmedia MediaControl Change Time Idle" ), + maItem( 0, AVMediaSetMask::ALL ), + mbLocked( false ), + meControlStyle( eControlStyle ), + mfTime(0.0) +{ + 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"); + + // TODO SetParentClipMode( ParentClipMode::NoClip ); + + 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 ) ); + + const OUString aTimeText( " 00:00:00/00:00:00 " ); + mxTimeEdit->set_text(aTimeText); + 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_label("open", AvmResId(AVMEDIA_STR_OPEN)); + mxPlayToolBox->set_item_help_id("apply", HID_AVMEDIA_TOOLBOXITEM_INSERT); + mxPlayToolBox->set_item_label("apply", AvmResId(AVMEDIA_STR_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 ) +{ + double fTime = rItem.getTime(); + if( !mbLocked && fTime != mfTime) + { + mfTime = fTime; + maItem.merge( rItem ); + 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..3b83f3853 --- /dev/null +++ b/avmedia/source/framework/mediaitem.cxx @@ -0,0 +1,526 @@ +/* -*- 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 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +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; + + 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_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( 10 ); + + aSeq[ 0 ] <<= m_pImpl->m_URL; + aSeq[ 1 ] <<= static_cast(m_pImpl->m_nMaskSet); + aSeq[ 2 ] <<= static_cast< sal_Int32 >( m_pImpl->m_eState ); + 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; + + 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(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; +} + + +void MediaItem::merge( const MediaItem& rMediaItem ) +{ + const AVMediaSetMask nMaskSet = rMediaItem.getMaskSet(); + + if( AVMediaSetMask::URL & nMaskSet ) + setURL( rMediaItem.getURL(), rMediaItem.getTempURL(), rMediaItem.getReferer() ); + + if( AVMediaSetMask::MIME_TYPE & nMaskSet ) + setMimeType( rMediaItem.getMimeType() ); + + if( AVMediaSetMask::STATE & nMaskSet ) + setState( rMediaItem.getState() ); + + if( AVMediaSetMask::DURATION & nMaskSet ) + setDuration( rMediaItem.getDuration() ); + + if( AVMediaSetMask::TIME & nMaskSet ) + setTime( rMediaItem.getTime() ); + + if( AVMediaSetMask::LOOP & nMaskSet ) + setLoop( rMediaItem.isLoop() ); + + if( AVMediaSetMask::MUTE & nMaskSet ) + setMute( rMediaItem.isMute() ); + + if( AVMediaSetMask::VOLUMEDB & nMaskSet ) + setVolumeDB( rMediaItem.getVolumeDB() ); + + if( AVMediaSetMask::ZOOM & nMaskSet ) + setZoom( rMediaItem.getZoom() ); +} + + +AVMediaSetMask MediaItem::getMaskSet() const +{ + return m_pImpl->m_nMaskSet; +} + + +void MediaItem::setURL( const OUString& rURL, const OUString& rTempURL, const OUString& rReferer ) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::URL; + m_pImpl->m_URL = rURL; + m_pImpl->m_TempFileURL = rTempURL; + m_pImpl->m_Referer = rReferer; +} + + +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; +} + + +void MediaItem::setMimeType( const OUString& rMimeType ) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::MIME_TYPE; + m_pImpl->m_sMimeType = rMimeType; +} + + +OUString MediaItem::getMimeType() const +{ + return !m_pImpl->m_sMimeType.isEmpty() ? m_pImpl->m_sMimeType : AVMEDIA_MIMETYPE_COMMON; +} + + +void MediaItem::setState( MediaState eState ) +{ + m_pImpl->m_eState = eState; + m_pImpl->m_nMaskSet |= AVMediaSetMask::STATE; +} + + +MediaState MediaItem::getState() const +{ + return m_pImpl->m_eState; +} + + +void MediaItem::setDuration( double fDuration ) +{ + m_pImpl->m_fDuration = fDuration; + m_pImpl->m_nMaskSet |= AVMediaSetMask::DURATION; +} + + +double MediaItem::getDuration() const +{ + return m_pImpl->m_fDuration; +} + + +void MediaItem::setTime( double fTime ) +{ + m_pImpl->m_fTime = fTime; + m_pImpl->m_nMaskSet |= AVMediaSetMask::TIME; +} + + +double MediaItem::getTime() const +{ + return m_pImpl->m_fTime; +} + + +void MediaItem::setLoop( bool bLoop ) +{ + m_pImpl->m_bLoop = bLoop; + m_pImpl->m_nMaskSet |= AVMediaSetMask::LOOP; +} + + +bool MediaItem::isLoop() const +{ + return m_pImpl->m_bLoop; +} + + +void MediaItem::setMute( bool bMute ) +{ + m_pImpl->m_bMute = bMute; + m_pImpl->m_nMaskSet |= AVMediaSetMask::MUTE; +} + + +bool MediaItem::isMute() const +{ + return m_pImpl->m_bMute; +} + + +void MediaItem::setVolumeDB( sal_Int16 nDB ) +{ + m_pImpl->m_nVolumeDB = nDB; + m_pImpl->m_nMaskSet |= AVMediaSetMask::VOLUMEDB; +} + + +sal_Int16 MediaItem::getVolumeDB() const +{ + return m_pImpl->m_nVolumeDB; +} + + +void MediaItem::setZoom( css::media::ZoomLevel eZoom ) +{ + m_pImpl->m_eZoom = eZoom; + m_pImpl->m_nMaskSet |= AVMediaSetMask::ZOOM; +} + + +css::media::ZoomLevel MediaItem::getZoom() const +{ + return m_pImpl->m_eZoom; +} + + +OUString GetFilename(OUString const& rSourceURL) +{ + uno::Reference const xUriFactory( + uri::UriReferenceFactory::create( + comphelper::getProcessComponentContext())); + uno::Reference 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 +CreateStream(uno::Reference 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 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 const& xModel, + OUString const& rSourceURL, OUString & o_rEmbeddedURL, uno::Reference const& xInputStream) +{ + try + { + uno::Reference const xSBD(xModel, + uno::UNO_QUERY_THROW); + uno::Reference const xStorage( + xSBD->getDocumentStorage(), uno::UNO_SET_THROW); + + OUString const media("Media"); + uno::Reference const xSubStorage( + xStorage->openStorageElement(media, embed::ElementModes::WRITE)); + + OUString filename(GetFilename(rSourceURL)); + + uno::Reference const xStream( + CreateStream(xSubStorage, filename), uno::UNO_SET_THROW); + uno::Reference 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(), + comphelper::getProcessComponentContext()); + + if (!sourceContent.openStream(xOutStream)) // copy file to storage + { + SAL_INFO("avmedia", "openStream to storage failed"); + return false; + } + } + + uno::Reference const xSubTransaction( + xSubStorage, uno::UNO_QUERY); + if (xSubTransaction.is()) { + xSubTransaction->commit(); + } + uno::Reference 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 const& xInStream, + OUString& o_rTempFileURL, const OUString& 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.isEmpty()) + { + 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(), + 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace avmedia +{ + +MediaPlayer::MediaPlayer( vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* _pBindings, SfxChildWinInfo* pInfo ) : + SfxChildWindow( _pParent, nId ) +{ + SetWindow( VclPtr::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..0eac2eff0 --- /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 +#include +#include + +#include +#include +#include + +#include + +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( ::avmedia::MediaToolBoxControl, ::avmedia::MediaItem ); + + +MediaToolBoxControl::MediaToolBoxControl( sal_uInt16 nSlotId, sal_uInt16 nId, ToolBox& rTbx ) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ + rTbx.Invalidate(); +} + + +MediaToolBoxControl::~MediaToolBoxControl() +{ +} + + +void MediaToolBoxControl::StateChanged( 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( pState ); + + if( pMediaItem && ( eState == SfxItemState::DEFAULT ) ) + pCtrl->setState( *pMediaItem ); + } +} + +VclPtr MediaToolBoxControl::CreateItemWindow( vcl::Window *pParent ) +{ + return ( pParent ? VclPtr::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..cf341f722 --- /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 + +#include +#include + +#include +#include +#include +#include +#include + +namespace avmedia{ + + +// XInterface, XTypeProvider, XServiceInfo + + +void SAL_CALL SoundHandler::acquire() throw() +{ + /* Don't use mutex in methods of XInterface! */ + OWeakObject::acquire(); +} + +void SAL_CALL SoundHandler::release() throw() +{ + /* 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(); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL SoundHandler::getTypes() +{ + static ::cppu::OTypeCollection aTypeCollection( + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + return aTypeCollection.getTypes(); +} + +#define IMPLEMENTATIONNAME_SOUNDHANDLER "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 asynchron 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( GetMutex() ); + + 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( GetMutex() ); + + 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 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..8a1dfde2e --- /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 +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +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 MutexHelper + , 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() throw() override; + virtual void SAL_CALL release() throw() 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..252ac8990 --- /dev/null +++ b/avmedia/source/gstreamer/avmediagstreamer.component @@ -0,0 +1,15 @@ + + + + + + + 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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..7da1917a0 --- /dev/null +++ b/avmedia/source/gstreamer/gstframegrabber.cxx @@ -0,0 +1,176 @@ +/* -*- 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 + +#include +#include +#include +#include +#include +#include + +#include + +#define AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.FrameGrabber_GStreamer" +#define AVMEDIA_GST_FRAMEGRABBER_SERVICENAME "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( const OUString &rURL ) : + FrameGrabber_BASE() +{ + 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(); +} + +FrameGrabber* FrameGrabber::create( const OUString &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, 24 ); + + 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..03c4af65f --- /dev/null +++ b/avmedia/source/gstreamer/gstframegrabber.hxx @@ -0,0 +1,60 @@ +/* -*- 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 "gstplayer.hxx" +#include +#include + +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 FrameGrabber* create( const OUString &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( const OUString &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..2b107e5c2 --- /dev/null +++ b/avmedia/source/gstreamer/gstmanager.cxx @@ -0,0 +1,71 @@ +/* -*- 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 + +#include "gstmanager.hxx" +#include "gstplayer.hxx" + +#include + +#define AVMEDIA_GST_MANAGER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Manager_GStreamer" +#define AVMEDIA_GST_MANAGER_SERVICENAME "com.sun.star.media.Manager" + +using namespace ::com::sun::star; + +namespace avmedia::gstreamer { + +Manager::Manager() +{ +} + +Manager::~Manager() +{ +} + +uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL ) +{ + Player* pPlayer( new Player ); + uno::Reference< media::XPlayer > xRet( pPlayer ); + const INetURLObject aURL( rURL ); + + if( !pPlayer->create( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ) ) + xRet.clear(); + + return xRet; +} + +OUString SAL_CALL Manager::getImplementationName( ) +{ + return AVMEDIA_GST_MANAGER_IMPLEMENTATIONNAME; +} + +sal_Bool SAL_CALL Manager::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames( ) +{ + return { AVMEDIA_GST_MANAGER_SERVICENAME }; +} + +} // namespace + +/* 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 +#include + +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..9511be54b --- /dev/null +++ b/avmedia/source/gstreamer/gstplayer.cxx @@ -0,0 +1,929 @@ +/* -*- 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "gstplayer.hxx" +#include "gstframegrabber.hxx" +#include "gstwindow.hxx" + +#include +#define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer" +#define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer" + +#include +#include + +#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 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 reported_; + std::map>> queued_; + rtl::Reference currentThread_; + std::vector currentDetails_; + std::set> 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 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 join; + rtl::Reference 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> & set, Player const * source) +{ + auto i = std::find_if( + set.begin(), set.end(), + [source](rtl::Reference const & el) { + return el.get() == source; + }); + if (i != set.end()) { + set.erase(i); + } +} + + +void MissingPluginInstaller::detach(Player const * source) { + rtl::Reference 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(p); + rtl::Reference 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 +{}; + + +void MissingPluginInstallerThread::execute() { + MissingPluginInstaller & inst = TheMissingPluginInstaller::get(); + for (;;) { + std::vector details; + { + osl::MutexGuard g(inst.mutex_); + assert(!inst.currentDetails_.empty()); + details.swap(inst.currentDetails_); + } + std::vector args; + args.reserve(details.size()); + for (auto const& i : details) + { + args.push_back(const_cast(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 ), + mbPlayPending ( false ), + 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(data); + + pPlayer->processMessage( message ); + + return true; +} + + +static GstBusSyncReply pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data ) +{ + Player* pPlayer = static_cast(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 ); + mbPlayPending = false; + 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); + } + + if (mbPlayPending) + mbPlayPending = ((newstate == GST_STATE_READY) || (newstate == GST_STATE_PAUSED)); + } + 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(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( const OUString& rURL, GstElement *pSink ) +{ + if (mpPlaybin != nullptr) + { + gst_element_set_state( mpPlaybin, GST_STATE_NULL ); + mbPlayPending = false; + 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 ); + mbPlayPending = false; + + 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 ); + mbPlayPending = true; + } +} + + +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 ); + + mbPlayPending = false; + SAL_INFO( "avmedia.gstreamer", AVVERSION "stop " << mpPlaybin ); +} + + +sal_Bool SAL_CALL Player::isPlaying() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + bool bRet = mbPlayPending; + + // return whether the pipeline is in PLAYING STATE or not + if( !mbPlayPending && mbInitialized && mpPlaybin ) + { + bRet = GST_STATE( 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 ); + if( !isPlaying() ) + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); + + 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( 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::Gtk3) + { + Point aPoint = pParentWindow->GetPosPixel(); + maArea.X = aPoint.getX(); + maArea.Y = aPoint.getY(); + } + + mbUseGtkSink = false; + + GstElement *pVideosink = static_cast(pParentWindow->CreateGStreamerSink()); + if (pVideosink) + { + if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk3) + 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); + + mnWindowID = pEnvData->aWindow; + mpDisplay = pEnvData->pDisplay; + SAL_INFO( "avmedia.gstreamer", AVVERSION "set window id to " << static_cast(mnWindowID) << " XOverlay " << mpXOverlay); + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); + if ( mpXOverlay != nullptr ) + gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID ); + } + + return xRet; +} + +uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber() +{ + ::osl::MutexGuard aGuard(m_aMutex); + FrameGrabber* pFrameGrabber = nullptr; + const awt::Size aPrefSize( getPreferredPlayerWindowSize() ); + + if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) ) + pFrameGrabber = FrameGrabber::create( maURL ); + SAL_INFO( "avmedia.gstreamer", AVVERSION "created FrameGrabber " << pFrameGrabber ); + + 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..1b7c7f23b --- /dev/null +++ b/avmedia/source/gstreamer/gstplayer.hxx @@ -0,0 +1,107 @@ +/* -*- 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 +#include "gstcommon.hxx" + +#include +#include +#include + +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( const OUString& 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 mbPlayPending; + bool mbMuted; + bool mbLooping; + bool mbInitialized; + + void* mpDisplay; + 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/gstuno.cxx b/avmedia/source/gstreamer/gstuno.cxx new file mode 100644 index 000000000..2ca447d38 --- /dev/null +++ b/avmedia/source/gstreamer/gstuno.cxx @@ -0,0 +1,59 @@ +/* -*- 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 +#include +#include "gstmanager.hxx" + +using namespace ::com::sun::star; + + +// - factory methods - + + +#define IMPL_NAME "com.sun.star.comp.media.Manager_GStreamer" +#define SERVICE_NAME AVMEDIA_MANAGER_SERVICE_NAME // "com.sun.star.comp.avmedia.Manager_GStreamer" + +static uno::Reference< uno::XInterface > create_MediaPlayer( const uno::Reference< lang::XMultiServiceFactory >& ) +{ + return uno::Reference< uno::XInterface >( *new ::avmedia::gstreamer::Manager ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* avmediagst_component_getFactory( const char* pImplName, void* pServiceManager, void* /*pRegistryKey*/ ) +{ + uno::Reference< lang::XSingleServiceFactory > xFactory; + void* pRet = nullptr; + + if( rtl_str_compare( pImplName, IMPL_NAME ) == 0 ) + { + xFactory.set( ::cppu::createSingleFactory( + static_cast< lang::XMultiServiceFactory* >( pServiceManager ), + IMPL_NAME, create_MediaPlayer, uno::Sequence< OUString > { SERVICE_NAME } ) ); + } + + if( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + + return pRet; +} + +/* 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..61f1b6a4c --- /dev/null +++ b/avmedia/source/gstreamer/gstwindow.cxx @@ -0,0 +1,193 @@ +/* -*- 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 + +#include + +#include "gstwindow.hxx" +#include "gstplayer.hxx" + +#define AVMEDIA_GST_WINDOW_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Window_GStreamer" +#define AVMEDIA_GST_WINDOW_SERVICENAME "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..c9d633d54 --- /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 "gstcommon.hxx" +#include +#include + +#include + +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/inc/mediamisc.hxx b/avmedia/source/inc/mediamisc.hxx new file mode 100644 index 000000000..504a136aa --- /dev/null +++ b/avmedia/source/inc/mediamisc.hxx @@ -0,0 +1,45 @@ +/* -*- 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 + +#define AVMEDIA_MANAGER_SERVICE_PREFERRED "com.sun.star.comp.avmedia.Manager_VLC" +#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 + +#define AVMEDIA_OPENGL_MANAGER_SERVICE_NAME "com.sun.star.media.Manager_OpenGL" + +// Mime types +#define AVMEDIA_MIMETYPE_COMMON "application/vnd.sun.star.media" + +inline OUString AvmResId(const char* pId) +{ + return Translate::get(pId, 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..376576ff5 --- /dev/null +++ b/avmedia/source/macavf/avmediaMacAVF.component @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/avmedia/source/macavf/framegrabber.hxx b/avmedia/source/macavf/framegrabber.hxx new file mode 100644 index 000000000..0b199991e --- /dev/null +++ b/avmedia/source/macavf/framegrabber.hxx @@ -0,0 +1,56 @@ +/* -*- 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 + +#include + +namespace avmedia::macavf { + +class FrameGrabber : public ::cppu::WeakImplHelper< css::media::XFrameGrabber, + css::lang::XServiceInfo > +{ +public: + + explicit FrameGrabber( const css::uno::Reference< css::lang::XMultiServiceFactory >& ); + 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: + + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; + + 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..711800103 --- /dev/null +++ b/avmedia/source/macavf/framegrabber.mm @@ -0,0 +1,106 @@ +/* -*- 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 +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace avmedia::macavf { + +FrameGrabber::FrameGrabber( const uno::Reference< lang::XMultiServiceFactory >& /*rxMgr*/ ) +: 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 ); + CGImageDestination* pCGImgDest = CGImageDestinationCreateWithData( pCFData, kUTTypeTIFF, 1, nullptr ); + CGImageDestinationAddImage( pCGImgDest, pCGImage, nullptr ); + CGImageDestinationFinalize( pCGImgDest ); + CFRelease( pCGImgDest ); + const long nBitmapLen = CFDataGetLength( pCFData ); + UInt8 * pBitmapBytes = const_cast(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..699f495a0 --- /dev/null +++ b/avmedia/source/macavf/macavfcommon.hxx @@ -0,0 +1,89 @@ +/* -*- 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 +#import +#import +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define AVMEDIA_MACAVF_MANAGER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Manager_MacAVF" +#define AVMEDIA_MACAVF_MANAGER_SERVICENAME "com.sun.star.media.Manager_MacAVF" + +#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 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/macavfuno.mm b/avmedia/source/macavf/macavfuno.mm new file mode 100644 index 000000000..dbee2e8ce --- /dev/null +++ b/avmedia/source/macavf/macavfuno.mm @@ -0,0 +1,55 @@ +/* -*- 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 "macavfcommon.hxx" +#include "manager.hxx" +#include + +using namespace ::com::sun::star; + +static uno::Reference< uno::XInterface > create_MediaPlayer( const uno::Reference< lang::XMultiServiceFactory >& rxFact ) +{ + return uno::Reference< uno::XInterface >( *new ::avmedia::macavf::Manager( rxFact ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* avmediaMacAVF_component_getFactory( const char* pImplName, void* pServiceManager, void* /* pRegistryKey */ ) +{ + uno::Reference< lang::XSingleServiceFactory > xFactory; + void* pRet = nullptr; + + if( rtl_str_compare( pImplName, AVMEDIA_MACAVF_MANAGER_IMPLEMENTATIONNAME ) == 0 ) + { + const OUString aServiceName( AVMEDIA_MACAVF_MANAGER_SERVICENAME ); + + xFactory.set( ::cppu::createSingleFactory( + static_cast< lang::XMultiServiceFactory* >( pServiceManager ), + AVMEDIA_MACAVF_MANAGER_IMPLEMENTATIONNAME, + create_MediaPlayer, uno::Sequence< OUString >( &aServiceName, 1 ) ) ); + } + + if( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + + return pRet; +} + +/* 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..fbdcb61b3 --- /dev/null +++ b/avmedia/source/macavf/manager.hxx @@ -0,0 +1,52 @@ +/* -*- 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 + +#include + + +namespace avmedia::macavf { + +class Manager : public ::cppu::WeakImplHelper< css::media::XManager, + css::lang::XServiceInfo > +{ +public: + + Manager( const css::uno::Reference< css::lang::XMultiServiceFactory >& rxMgr ); + 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; +private: + + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; +}; + +} // 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..7f3036c86 --- /dev/null +++ b/avmedia/source/macavf/manager.mm @@ -0,0 +1,71 @@ +/* -*- 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 +#include + +using namespace ::com::sun::star; + +namespace avmedia::macavf { + +Manager::Manager( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) : + mxMgr( rxMgr ) +{ +} + + +Manager::~Manager() +{} + + +uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL ) +{ + Player* pPlayer( new Player( mxMgr ) ); + uno::Reference< media::XPlayer > xRet( pPlayer ); + INetURLObject aURL( rURL ); + + if( !pPlayer->create( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ) ) + xRet.clear(); + + return xRet; +} + + +OUString SAL_CALL Manager::getImplementationName( ) +{ + return AVMEDIA_MACAVF_MANAGER_IMPLEMENTATIONNAME; +} + + +sal_Bool SAL_CALL Manager::supportsService( const OUString& ServiceName ) +{ + return ServiceName == AVMEDIA_MACAVF_MANAGER_SERVICENAME; +} + + +uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames( ) +{ + return { AVMEDIA_MACAVF_MANAGER_SERVICENAME }; +} + +} // namespace avmedia::macavf + +/* 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..b9b49cc8c --- /dev/null +++ b/avmedia/source/macavf/player.hxx @@ -0,0 +1,84 @@ +/* -*- 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 +#include "macavfcommon.hxx" +#include + +#include + +namespace avmedia::macavf { + +class Player +: public MacAVObserverHandler +, public ::cppu::WeakImplHelper< css::media::XPlayer, + css::lang::XServiceInfo > +{ +public: + explicit Player( const css::uno::Reference< css::lang::XMultiServiceFactory >& ); + 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: + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; + + 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..dfe558524 --- /dev/null +++ b/avmedia/source/macavf/player.mm @@ -0,0 +1,364 @@ +/* -*- 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 // 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(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( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) +: mxMgr( rxMgr ) +, 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(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(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([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(nNSViewPtr); + + // check the window parameters + uno::Reference< ::media::XPlayerWindow > xRet; + if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == nullptr) ) + return xRet; + + // create the window + ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxMgr, *this, pParentView ); + xRet = pWindow; + return xRet; +} + + +uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber() +{ + uno::Reference< media::XFrameGrabber > xRet; + + FrameGrabber* pGrabber = new FrameGrabber( mxMgr ); + AVAsset* pMovie = [[mpPlayer currentItem] asset]; + if( pGrabber->create( pMovie ) ) + xRet = pGrabber; + + return xRet; +} + + +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..c494b6def --- /dev/null +++ b/avmedia/source/macavf/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 "macavfcommon.hxx" +#include +#include + +#include + + +@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( const css::uno::Reference< css::lang::XMultiServiceFactory >& i_rxMgr, + 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: + + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; + + ::osl::Mutex maMutex; + ::cppu::OMultiTypeInterfaceContainerHelper 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..3c39a0725 --- /dev/null +++ b/avmedia/source/macavf/window.mm @@ -0,0 +1,261 @@ +/* -*- 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 +#include + +#include "window.hxx" +#include "player.hxx" + +using namespace ::com::sun::star; + + +namespace avmedia::macavf { + +Window::Window( const uno::Reference< lang::XMultiServiceFactory >& i_rxMgr, Player& i_rPlayer, NSView* i_pParentView ) +: mxMgr( i_rxMgr ) +, 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::get(), xListener ); +} + + +void SAL_CALL Window::removeWindowListener( const uno::Reference< awt::XWindowListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::addFocusListener( const uno::Reference< awt::XFocusListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::removeFocusListener( const uno::Reference< awt::XFocusListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::addKeyListener( const uno::Reference< awt::XKeyListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::removeKeyListener( const uno::Reference< awt::XKeyListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::addMouseListener( const uno::Reference< awt::XMouseListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::removeMouseListener( const uno::Reference< awt::XMouseListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::addMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::removeMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::addPaintListener( const uno::Reference< awt::XPaintListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::removePaintListener( const uno::Reference< awt::XPaintListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + + +// XComponent + +void SAL_CALL Window::dispose( ) +{ +} + + +void SAL_CALL Window::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + + +void SAL_CALL Window::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::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..9fbaceea1 --- /dev/null +++ b/avmedia/source/viewer/mediaevent_impl.cxx @@ -0,0 +1,173 @@ +/* -*- 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 +#include +#include + +using namespace ::com::sun::star; + +namespace avmedia::priv { + +MediaEventListenersImpl::MediaEventListenersImpl( vcl::Window& rEventWindow ) : + mpNotifyWindow( &rEventWindow ) +{ +} + + +MediaEventListenersImpl::~MediaEventListenersImpl() +{ +} + + +void MediaEventListenersImpl::cleanUp() +{ + Application::RemoveMouseAndKeyEvents( reinterpret_cast< vcl::Window* >( mpNotifyWindow.get() ) ); + mpNotifyWindow = nullptr; +} + + +void SAL_CALL MediaEventListenersImpl::disposing( const css::lang::EventObject& ) +{ +} + + +void SAL_CALL MediaEventListenersImpl::keyPressed( const css::awt::KeyEvent& e ) +{ + const ::osl::MutexGuard aGuard( maMutex ); + const SolarMutexGuard aAppGuard; + + 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, reinterpret_cast< vcl::Window* >( mpNotifyWindow.get() ), &aVCLKeyEvt ); + } +} + + +void SAL_CALL MediaEventListenersImpl::keyReleased( const css::awt::KeyEvent& e ) +{ + const ::osl::MutexGuard aGuard( maMutex ); + const SolarMutexGuard aAppGuard; + + 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, reinterpret_cast< vcl::Window* >( mpNotifyWindow.get() ), &aVCLKeyEvt ); + } +} + + +void SAL_CALL MediaEventListenersImpl::mousePressed( const css::awt::MouseEvent& e ) +{ + const ::osl::MutexGuard 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::WindowMouseButtonDown, reinterpret_cast< vcl::Window* >( mpNotifyWindow.get() ), &aVCLMouseEvt ); + } +} + + +void SAL_CALL MediaEventListenersImpl::mouseReleased( const css::awt::MouseEvent& e ) +{ + const ::osl::MutexGuard 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, reinterpret_cast< vcl::Window* >( 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 ::osl::MutexGuard aGuard( maMutex ); + const SolarMutexGuard aAppGuard; + + if( mpNotifyWindow ) + { + MouseEvent aVCLMouseEvt( Point( e.X, e.Y ), 0, MouseEventModifiers::NONE, e.Buttons, e.Modifiers ); + Application::PostMouseEvent( VclEventId::WindowMouseMove, reinterpret_cast< vcl::Window* >( mpNotifyWindow.get() ), &aVCLMouseEvt ); + } +} + + +void SAL_CALL MediaEventListenersImpl::mouseMoved( const css::awt::MouseEvent& e ) +{ + const ::osl::MutexGuard aGuard( maMutex ); + const SolarMutexGuard aAppGuard; + + if( mpNotifyWindow ) + { + MouseEvent aVCLMouseEvt( Point( e.X, e.Y ), 0, MouseEventModifiers::NONE, e.Buttons, e.Modifiers ); + Application::PostMouseEvent( VclEventId::WindowMouseMove, reinterpret_cast< vcl::Window* >( 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..9c864ecff --- /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 +#include +#include +#include +#include +#include +#include + +namespace avmedia +{ + namespace 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 mpNotifyWindow; + mutable ::osl::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..e823c2e8d --- /dev/null +++ b/avmedia/source/viewer/mediawindow.cxx @@ -0,0 +1,404 @@ +/* -*- 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 +#include "mediawindow_impl.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AVMEDIA_FRAMEGRABBER_DEFAULTFRAME_MEDIATIME 3.0 + +using namespace ::com::sun::star; + +namespace avmedia { + +MediaWindow::MediaWindow( vcl::Window* parent, bool bInternalMediaControl ) : + mpImpl( VclPtr::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(); +} + + +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.SetTitle( AvmResId( o_pbLink != nullptr + ? AVMEDIA_STR_INSERTMEDIA_DLG : AVMEDIA_STR_OPENMEDIA_DLG ) ); + + for( FilterNameVector::size_type i = 0; i < aFilters.size(); ++i ) + { + for( sal_Int32 nIndex = 0; nIndex >= 0; ) + { + if( !aAllTypes.isEmpty() ) + aAllTypes.append(aSeparator); + + aAllTypes.append(aWildcard).append(aFilters[ i ].second.getToken( 0, ';', nIndex )); + } + } + + // add filter for all media types + aDlg.AddFilter( AvmResId( AVMEDIA_STR_ALL_MEDIAFILES ), aAllTypes.makeStringAndClear() ); + + for( FilterNameVector::size_type i = 0; i < aFilters.size(); ++i ) + { + OUStringBuffer aTypes; + + for( sal_Int32 nIndex = 0; nIndex >= 0; ) + { + if( !aTypes.isEmpty() ) + aTypes.append(aSeparator); + + aTypes.append(aWildcard).append(aFilters[ i ].second.getToken( 0, ';', nIndex )); + } + + // add single filters + aDlg.AddFilter( aFilters[ i ].first, aTypes.makeStringAndClear() ); + } + + // add filter for all types + aDlg.AddFilter( AvmResId( AVMEDIA_STR_ALL_FILES ), "*.*" ); + + uno::Reference const xFP(aDlg.GetFilePicker()); + uno::Reference 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 xBox(Application::CreateMessageDialog(pParent, + VclMessageType::Warning, VclButtonsType::Ok, AvmResId(AVMEDIA_STR_ERR_URL))); + xBox->run(); +} + +bool MediaWindow::isMediaURL( const OUString& rURL, const OUString& rReferer, bool bDeep, Size* pPreferredSizePixel ) +{ + const INetURLObject aURL( rURL ); + + if( aURL.GetProtocol() != INetProtocol::NotValid ) + { + if( bDeep || pPreferredSizePixel ) + { + try + { + uno::Reference< media::XPlayer > xPlayer( priv::MediaWindowImpl::createPlayer( + aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ), + rReferer, nullptr ) ); + + if( xPlayer.is() ) + { + if( pPreferredSizePixel ) + { + const awt::Size aAwtSize( xPlayer->getPreferredPlayerWindowSize() ); + + pPreferredSizePixel->setWidth( aAwtSize.Width ); + pPreferredSizePixel->setHeight( aAwtSize.Height ); + } + + return true; + } + } + catch( ... ) + { + } + } + else + { + FilterNameVector aFilters = getMediaFilters(); + const OUString aExt( aURL.getExtension() ); + + for( FilterNameVector::size_type i = 0; i < aFilters.size(); ++i ) + { + for( sal_Int32 nIndex = 0; nIndex >= 0; ) + { + if( aExt.equalsIgnoreAsciiCase( aFilters[ i ].second.getToken( 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 OUString& rURL, + const OUString& rReferer, + const OUString& sMimeType ) +{ + uno::Reference< media::XPlayer > xPlayer( createPlayer( rURL, rReferer, &sMimeType ) ); + 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) + xRet = xGraphic->GetXGraphic(); + + return xRet; +} + + +} // 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..98f867e4e --- /dev/null +++ b/avmedia/source/viewer/mediawindow_impl.cxx @@ -0,0 +1,671 @@ +/* -*- 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 +#include "mediawindow_impl.hxx" +#include "mediaevent_impl.hxx" +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +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(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::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()) + { + auto pEventsIf = static_cast(mxEvents.get()); + mxPlayerWindow->removeKeyListener( uno::Reference< awt::XKeyListener >( pEventsIf, uno::UNO_QUERY ) ); + mxPlayerWindow->removeMouseListener( uno::Reference< awt::XMouseListener >( pEventsIf, uno::UNO_QUERY ) ); + mxPlayerWindow->removeMouseMotionListener( uno::Reference< awt::XMouseMotionListener >( pEventsIf, uno::UNO_QUERY ) ); + 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 MediaWindowImpl::createPlayer(const OUString& rURL, const OUString& rReferer, const OUString* pMimeType) +{ + uno::Reference xPlayer; + + if( rURL.isEmpty() ) + return xPlayer; + + if (SvtSecurityOptions().isUntrustedReferer(rReferer)) + { + return xPlayer; + } + + if (!pMimeType || *pMimeType == AVMEDIA_MIMETYPE_COMMON) + { + uno::Reference xContext(::comphelper::getProcessComponentContext()); + + static OUStringLiteral aServiceManagers[] = + { + AVMEDIA_MANAGER_SERVICE_PREFERRED, + AVMEDIA_MANAGER_SERVICE_NAME, + }; + + for (const auto& rServiceName : aServiceManagers) + { + xPlayer = createPlayer(rURL, rServiceName, xContext); + if (xPlayer) + break; + } + } + + 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 ) +{ + 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::Create(this)); + } + if (!mpChildWindow) + return; + mpChildWindow->SetHelpId(HID_AVMEDIA_PLAYERWINDOW); + mxEvents = new MediaEventListenersImpl(*mpChildWindow); + + if (mxPlayer.is()) + { + Resize(); + uno::Sequence aArgs( 3 ); + uno::Reference xPlayerWindow; + const Point aPoint; + const Size aSize(mpChildWindow->GetSizePixel()); + + aArgs[0] <<= mpChildWindow->GetParentWindowHandle(); + aArgs[1] <<= awt::Rectangle(aPoint.X(), aPoint.Y(), aSize.Width(), aSize.Height()); + aArgs[2] <<= reinterpret_cast(mpChildWindow.get()); + + 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() ) + { + auto pEventsIf = static_cast(mxEvents.get()); + xPlayerWindow->addKeyListener( uno::Reference< awt::XKeyListener >( pEventsIf, uno::UNO_QUERY ) ); + xPlayerWindow->addMouseListener( uno::Reference< awt::XMouseListener >( pEventsIf, uno::UNO_QUERY ) ); + xPlayerWindow->addMouseMotionListener( uno::Reference< awt::XMouseMotionListener >( pEventsIf, uno::UNO_QUERY ) ); + xPlayerWindow->addFocusListener( uno::Reference< awt::XFocusListener >( pEventsIf, uno::UNO_QUERY ) ); + } + } + 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; + + long 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, 0L); + + 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( long(aVideoRect.GetHeight() * fLogoWH) ); + aLogoSize.setHeight( aVideoRect.GetHeight() ); + } + else + { + aLogoSize.setWidth( aVideoRect.GetWidth() ); + aLogoSize.setHeight( 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..8bceebb08 --- /dev/null +++ b/avmedia/source/viewer/mediawindow_impl.hxx @@ -0,0 +1,157 @@ +/* -*- 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 +#include + +#include + +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 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 createPlayer(const OUString& rURL, const OUString& rManagerServName, + const css::uno::Reference& xContext); + + OUString maFileURL; + OUString mTempFileURL; + OUString maReferer; + OUString m_sMimeType; + css::uno::Reference mxPlayer; + css::uno::Reference mxPlayerWindow; + MediaWindow* mpMediaWindow; + + rtl::Reference mxEvents; + VclPtr mpChildWindow; + VclPtr mpMediaWindowControl; + std::unique_ptr mpEmptyBmpEx; + std::unique_ptr mpAudioBmpEx; +}; + +}} // end namespace avmedia::priv + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/avmediavlc.component b/avmedia/source/vlc/avmediavlc.component new file mode 100644 index 000000000..cbdba1d03 --- /dev/null +++ b/avmedia/source/vlc/avmediavlc.component @@ -0,0 +1,15 @@ + + + + + + + diff --git a/avmedia/source/vlc/inc/wrapper/Common.hxx b/avmedia/source/vlc/inc/wrapper/Common.hxx new file mode 100644 index 000000000..c9b7f9682 --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/Common.hxx @@ -0,0 +1,29 @@ +/* -*- 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/. + */ + +#pragma once + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ + class Common + { + public: + static bool LoadSymbols(); + static const char* Version(); + static const char* LastErrorMessage(); + }; +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/inc/wrapper/EventHandler.hxx b/avmedia/source/vlc/inc/wrapper/EventHandler.hxx new file mode 100644 index 000000000..955c83478 --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/EventHandler.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/. + */ + +#pragma once + +#include +#include +#include + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ + class EventHandler : public ::osl::Thread + { + public: + EventHandler(const EventHandler&) = delete; + const EventHandler& operator=(const EventHandler&) = delete; + + EventHandler(); + void stop(); + + protected: + virtual void SAL_CALL run() override; + + public: + typedef std::function< void() > TCallback; + ThreadsafeQueue< TCallback > mCallbackQueue; + }; +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/inc/wrapper/EventManager.hxx b/avmedia/source/vlc/inc/wrapper/EventManager.hxx new file mode 100644 index 000000000..9a8515483 --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/EventManager.hxx @@ -0,0 +1,55 @@ +/* -*- 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/. + */ + +#pragma once + +#include +#include + +struct libvlc_event_manager_t; +struct libvlc_event_t; + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ + class EventHandler; + class EventManager + { + + public: + EventManager(const EventManager&) = delete; + const EventManager& operator=(const EventManager&) = delete; + + static bool LoadSymbols(); + typedef std::function Callback; + + EventManager( Player& player, EventHandler& eh ); + + void onPaused( const Callback& callback = Callback() ); + void onEndReached( const Callback& callback = Callback() ); + + private: + EventHandler& mEventHandler; + typedef std::function< void() > TCallback; + libvlc_event_manager_t *mManager; + TCallback mOnPaused; + TCallback mOnEndReached; + + void registerSignal( int signal, const Callback& callback ); + + static void Handler( const libvlc_event_t *event, void *pData ); + }; +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/inc/wrapper/Instance.hxx b/avmedia/source/vlc/inc/wrapper/Instance.hxx new file mode 100644 index 000000000..7e4ba7741 --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/Instance.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/. + */ + +#pragma once + +struct libvlc_instance_t; + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ + class Instance + { + public: + static bool LoadSymbols(); + Instance( int argc, const char * const argv[] ); + Instance( const Instance& other ); + Instance& operator=( const Instance& other ); + ~Instance(); + + operator libvlc_instance_t*() + { + return mInstance; + } + + private: + libvlc_instance_t *mInstance; + }; +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/inc/wrapper/Media.hxx b/avmedia/source/vlc/inc/wrapper/Media.hxx new file mode 100644 index 000000000..288bb0d43 --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/Media.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/. + */ + +#pragma once + +struct libvlc_media_t; + +namespace rtl { class OUString; } + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ + class Instance; + class Media + { + public: + static bool LoadSymbols(); + Media( const rtl::OUString& url, Instance& instance ); + Media( const Media& other ); + Media& operator=( const Media& other ); + + int getDuration() const; + + ~Media(); + + operator libvlc_media_t*() + { + return mMedia; + } + + private: + libvlc_media_t *mMedia; + }; +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/inc/wrapper/Player.hxx b/avmedia/source/vlc/inc/wrapper/Player.hxx new file mode 100644 index 000000000..a41e01b10 --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/Player.hxx @@ -0,0 +1,73 @@ +/* -*- 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/. + */ + +#pragma once +#if defined UNX +# include +#endif + +struct libvlc_media_player_t; + +namespace rtl +{ + class OUString; +} + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ + class Media; + class Player + { + public: + static bool LoadSymbols(); + explicit Player( Media& media ); + Player( const Player& other ); + Player& operator=( const Player& other ); + ~Player(); + + bool play(); + void pause(); + void stop(); + void setTime( int time ); + int getTime() const; + bool isPlaying() const; + + void setVolume( int volume ); + int getVolume() const; + + void setMute( bool mute); + bool getMute() const; + + void setWindow( intptr_t id ); + + void takeSnapshot(const rtl::OUString& file); + + bool hasVout() const; + + void setScale( float factor ); + void setVideoSize( unsigned width, unsigned height ); + + operator libvlc_media_player_t*() + { + return mPlayer; + } + + void setMouseHandling(bool flag); + private: + libvlc_media_player_t *mPlayer; + }; +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/inc/wrapper/ThreadsafeQueue.hxx b/avmedia/source/vlc/inc/wrapper/ThreadsafeQueue.hxx new file mode 100644 index 000000000..f8eb480dd --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/ThreadsafeQueue.hxx @@ -0,0 +1,81 @@ +/* -*- 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 +#include +#include +#include + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ +template +class ThreadsafeQueue +{ +public: + ThreadsafeQueue(const ThreadsafeQueue&) = delete; + const ThreadsafeQueue& operator=(const ThreadsafeQueue&) = delete; + + ThreadsafeQueue(); + + void push( const T& data ); + void pop( T& data ); + +private: + std::queue< T > mQueue; + mutable ::osl::Mutex mMutex; + ::osl::Condition mCondition; +}; + +template +ThreadsafeQueue::ThreadsafeQueue() +{ +} + +template +void ThreadsafeQueue::push( const T& data ) +{ + ::osl::MutexGuard guard( mMutex ); + mQueue.push( data ); + mMutex.release(); + mCondition.set(); +} + +template +void ThreadsafeQueue::pop( T& data ) +{ + mCondition.wait(); + ::osl::MutexGuard guard( mMutex ); + while ( mQueue.empty() ) + { + mMutex.release(); + mCondition.wait(); + mMutex.acquire(); + } + data = mQueue.front(); + mQueue.pop(); +} +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/inc/wrapper/Wrapper.hxx b/avmedia/source/vlc/inc/wrapper/Wrapper.hxx new file mode 100644 index 000000000..c381ea8db --- /dev/null +++ b/avmedia/source/vlc/inc/wrapper/Wrapper.hxx @@ -0,0 +1,19 @@ +/* -*- 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/. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlccommon.hxx b/avmedia/source/vlc/vlccommon.hxx new file mode 100644 index 000000000..13719516b --- /dev/null +++ b/avmedia/source/vlc/vlccommon.hxx @@ -0,0 +1,40 @@ +/* -*- 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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcframegrabber.cxx b/avmedia/source/vlc/vlcframegrabber.cxx new file mode 100644 index 000000000..034de4511 --- /dev/null +++ b/avmedia/source/vlc/vlcframegrabber.cxx @@ -0,0 +1,131 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vlcframegrabber.hxx" +#include "vlcplayer.hxx" +#include +#include + +using namespace ::com::sun::star; + +namespace avmedia::vlc { + +namespace +{ + const OUString AVMEDIA_VLC_GRABBER_IMPLEMENTATIONNAME = "com.sun.star.comp.avmedia.VLCFrameGrabber_VLC"; + const OUString AVMEDIA_VLC_GRABBER_SERVICENAME = "com.sun.star.media.VLCFrameGrabber_VLC"; + const int MSEC_IN_SEC = 1000; + + const char * const VLC_ARGS[] = { + "-Vdummy", + "--demux", + "ffmpeg", + "--snapshot-format=png", + "--ffmpeg-threads", /* Is deprecated in 2.1.0 */ + "--verbose=-1", + "--no-audio" + }; +} + +VLCFrameGrabber::VLCFrameGrabber( wrapper::EventHandler& eh, const OUString& url ) + : FrameGrabber_BASE() + , mInstance( SAL_N_ELEMENTS(VLC_ARGS), VLC_ARGS ) + , mMedia( url, mInstance ) + , mPlayer( mMedia ) + , mEventHandler( eh ) +{ +} + +::uno::Reference< css::graphic::XGraphic > SAL_CALL VLCFrameGrabber::grabFrame( double fMediaTime ) +{ + osl::Condition condition; + + const OUString& fileName = utl::TempFile::CreateTempName(); + { + wrapper::EventManager manager( mPlayer, mEventHandler ); + manager.onPaused([&condition](){ condition.set(); }); + + if ( !mPlayer.play() ) + { + SAL_WARN("avmedia", "Couldn't play when trying to grab frame"); + return ::uno::Reference< css::graphic::XGraphic >(); + } + + mPlayer.setTime( std::max(fMediaTime, 0.0) * MSEC_IN_SEC ); + mPlayer.pause(); + + condition.wait(std::chrono::seconds(2)); + + if ( !mPlayer.hasVout() ) + { + SAL_WARN("avmedia", "Couldn't grab frame"); + manager.onPaused(); + return ::uno::Reference< css::graphic::XGraphic >(); + } + + mPlayer.takeSnapshot( fileName ); + mPlayer.stop(); + + manager.onPaused(); + } + + OUString url; + osl::FileBase::getFileURLFromSystemPath( fileName, url ); + std::unique_ptr stream( utl::UcbStreamHelper::CreateStream( url, + StreamMode::STD_READ ) ); + + vcl::PNGReader reader( *stream ); + + const BitmapEx& bitmap = reader.Read(); + + return Graphic( bitmap ).GetXGraphic(); +} + +OUString SAL_CALL VLCFrameGrabber::getImplementationName() +{ + return AVMEDIA_VLC_GRABBER_IMPLEMENTATIONNAME; +} + +sal_Bool SAL_CALL VLCFrameGrabber::supportsService( const OUString& serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +::uno::Sequence< OUString > SAL_CALL VLCFrameGrabber::getSupportedServiceNames() +{ + return { AVMEDIA_VLC_GRABBER_SERVICENAME }; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcframegrabber.hxx b/avmedia/source/vlc/vlcframegrabber.hxx new file mode 100644 index 000000000..77684d6b9 --- /dev/null +++ b/avmedia/source/vlc/vlcframegrabber.hxx @@ -0,0 +1,52 @@ +/* -*- 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 +#include +#include "vlccommon.hxx" +#include + +namespace avmedia { +namespace vlc { + +typedef ::cppu::WeakImplHelper< css::media::XFrameGrabber, + css::lang::XServiceInfo > FrameGrabber_BASE; + +class VLCFrameGrabber : public FrameGrabber_BASE +{ + wrapper::Instance mInstance; + wrapper::Media mMedia; + wrapper::Player mPlayer; + wrapper::EventHandler& mEventHandler; +public: + VLCFrameGrabber( wrapper::EventHandler& eh, const OUString& url ); + + css::uno::Reference< css::graphic::XGraphic > SAL_CALL grabFrame( double fMediaTime ) override; + + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& serviceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcmanager.cxx b/avmedia/source/vlc/vlcmanager.cxx new file mode 100644 index 000000000..60c02b259 --- /dev/null +++ b/avmedia/source/vlc/vlcmanager.cxx @@ -0,0 +1,124 @@ +/* -*- 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 +#include +#include +#include +#include +#include "vlcmanager.hxx" +#include "vlcplayer.hxx" +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace avmedia::vlc { + +namespace +{ + const OUString VLC_IMPLEMENTATION_NAME = "com.sun.star.comp.avmedia.Manager_VLC"; + const OUString VLC_SERVICENAME = "com.sun.star.media.Manager_VLC"; + + const char * const VLC_ARGS[] = { + "--demux", + "ffmpeg", + "--no-mouse-events", + "--verbose=-1" + }; +} + +Manager::Manager() + : mEventHandler() +{ + using namespace wrapper; + static bool success = Instance::LoadSymbols() && EventManager::LoadSymbols() + && Media::LoadSymbols() && Player::LoadSymbols() + && Common::LoadSymbols(); + + m_is_vlc_found = success; + if (m_is_vlc_found) + { + mInstance.reset(new Instance( SAL_N_ELEMENTS(VLC_ARGS), VLC_ARGS )); + //Check VLC version + std::vector verComponents; + const std::string str(Common::Version()); + + boost::split(verComponents, + str, + boost::is_any_of(". ")); + if (verComponents.size() < 3 + || boost::lexical_cast(verComponents[0]) < 2 + || (boost::lexical_cast(verComponents[1]) == 0 + && boost::lexical_cast(verComponents[2]) < 8)) + { + SAL_WARN("avmedia", "VLC version '" << str << "' is too old"); + m_is_vlc_found = false; + } + else + SAL_INFO("avmedia", "VLC version '" << str << "' is acceptable"); + } + else + SAL_WARN("avmedia", "Cannot load symbols"); + + if (m_is_vlc_found) + { + mEventHandler.create(); + } +} + +Manager::~Manager() +{ + mEventHandler.stop(); +} + +uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL ) +{ + if ( !m_is_vlc_found ) + throw uno::RuntimeException("VLC not found", nullptr); + + if ( !rURL.isEmpty() ) + { + if (mURL == rURL) + return mPlayer; + + mURL = rURL; + } + else + return mPlayer; + + VLCPlayer* pPlayer( new VLCPlayer( mURL, + *mInstance, + mEventHandler /*, mxMgr */ ) ); + mPlayer.set( pPlayer ); + + return mPlayer; +} + +OUString SAL_CALL Manager::getImplementationName() +{ + return VLC_IMPLEMENTATION_NAME; +} + +sal_Bool SAL_CALL Manager::supportsService( const OUString& serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames() +{ + return { VLC_SERVICENAME }; +} + +} // end namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcmanager.hxx b/avmedia/source/vlc/vlcmanager.hxx new file mode 100644 index 000000000..3615ab17d --- /dev/null +++ b/avmedia/source/vlc/vlcmanager.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 +#include "vlccommon.hxx" +#include +#include +#include + +namespace avmedia { +namespace vlc { + +class Manager : public ::cppu::WeakImplHelper< css::media::XManager, + css::lang::XServiceInfo > +{ + std::unique_ptr mInstance; + wrapper::EventHandler mEventHandler; +public: + explicit Manager(); + virtual ~Manager() override; + + css::uno::Reference< css::media::XPlayer > SAL_CALL createPlayer( const OUString& aURL ) override; + + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& serviceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + css::uno::Reference< css::media::XPlayer > mPlayer; + OUString mURL; + bool m_is_vlc_found; +}; + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcplayer.cxx b/avmedia/source/vlc/vlcplayer.cxx new file mode 100644 index 000000000..bd2cc92ba --- /dev/null +++ b/avmedia/source/vlc/vlcplayer.cxx @@ -0,0 +1,251 @@ +/* -*- 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 +#include +#include + +#include "vlcplayer.hxx" +#include "vlcwindow.hxx" +#include "vlcframegrabber.hxx" +#include + +using namespace ::com::sun::star; + +namespace avmedia::vlc { + +namespace +{ + const OUString AVMEDIA_VLC_PLAYER_IMPLEMENTATIONNAME = "com.sun.star.comp.avmedia.Player_VLC"; + const OUString AVMEDIA_VLC_PLAYER_SERVICENAME = "com.sun.star.media.Player_VLC"; + + const int MS_IN_SEC = 1000; // Millisec in sec +} + +VLCPlayer::VLCPlayer( const OUString& url, + wrapper::Instance& instance, + wrapper::EventHandler& eh ) + : VLC_Base( m_aMutex ) + , mEventHandler( eh ) + , mMedia( url, instance ) + , mPlayer( mMedia ) + , mEventManager( mPlayer, mEventHandler ) + , mUrl( url ) + , mPlaybackLoop( false ) + , mPrevWinID( 0 ) +{ + mPlayer.setMouseHandling( false ); +} + +void SAL_CALL VLCPlayer::start() +{ + ::osl::MutexGuard aGuard(m_aMutex); + if (!mPlayer.play()) + { + // TODO: Error + } +} + +void SAL_CALL VLCPlayer::stop() +{ + ::osl::MutexGuard aGuard(m_aMutex); + mPlayer.pause(); +} + +sal_Bool SAL_CALL VLCPlayer::isPlaying() +{ + ::osl::MutexGuard aGuard(m_aMutex); + return mPlayer.isPlaying(); +} + +double SAL_CALL VLCPlayer::getDuration() +{ + ::osl::MutexGuard aGuard(m_aMutex); + return static_cast( mMedia.getDuration() ) / MS_IN_SEC; +} + +void SAL_CALL VLCPlayer::setMediaTime( double fTime ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + if ( fTime < 0.00000001 && !mPlayer.isPlaying() ) + { + mPlayer.stop(); + } + + mPlayer.setTime( fTime * MS_IN_SEC ); +} + +double SAL_CALL VLCPlayer::getMediaTime() +{ + ::osl::MutexGuard aGuard(m_aMutex); + return static_cast( mPlayer.getTime() ) / MS_IN_SEC; +} + +void VLCPlayer::replay() +{ + setPlaybackLoop( false ); + stop(); + setMediaTime( 0 ); + start(); +} + +void SAL_CALL VLCPlayer::setPlaybackLoop( sal_Bool bSet ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + mPlaybackLoop = bSet; + + if ( bSet ) + mEventManager.onEndReached([this](){ this->replay(); }); + else + mEventManager.onEndReached(); +} + +sal_Bool SAL_CALL VLCPlayer::isPlaybackLoop() +{ + ::osl::MutexGuard aGuard(m_aMutex); + return mPlaybackLoop; +} + +void SAL_CALL VLCPlayer::setVolumeDB( ::sal_Int16 nDB ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + mPlayer.setVolume( static_cast( ( nDB + 40 ) * 10.0 / 4 ) ); +} + +::sal_Int16 SAL_CALL VLCPlayer::getVolumeDB() +{ + ::osl::MutexGuard aGuard(m_aMutex); + return static_cast( mPlayer.getVolume() / 10.0 * 4 - 40 ); +} + +void SAL_CALL VLCPlayer::setMute( sal_Bool bSet ) +{ + ::osl::MutexGuard aGuard(m_aMutex); + mPlayer.setMute( bSet ); +} + +sal_Bool SAL_CALL VLCPlayer::isMute() +{ + ::osl::MutexGuard aGuard(m_aMutex); + return mPlayer.getMute(); +} + +css::awt::Size SAL_CALL VLCPlayer::getPreferredPlayerWindowSize() +{ + return css::awt::Size( 480, 360 ); +} + +namespace +{ + // TODO: Move this function to the common space for avoiding duplication with + // gstreamer/gstwindow::createPlayerWindow functionality + intptr_t GetWindowID( const uno::Sequence< uno::Any >& arguments ) + { + if (arguments.getLength() <= 2) + return -1; + + sal_IntPtr pIntPtr = 0; + + arguments[ 2 ] >>= pIntPtr; + + SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr ); + + const SystemEnvData* pEnvData = pParentWindow ? pParentWindow->GetSystemData() : nullptr; + + if (pEnvData == nullptr) + return -1; + +#if defined MACOSX + const intptr_t id = reinterpret_cast( pEnvData->mpNSView ); +#elif defined _WIN32 + const intptr_t id = reinterpret_cast( pEnvData->hWnd ); +#else + const intptr_t id = static_cast( pEnvData->aWindow ); +#endif + + return id; + } +} + +void VLCPlayer::setWindowID( const intptr_t windowID ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + mPlayer.stop(); + mPlayer.setWindow( windowID ); +} + +void VLCPlayer::setVideoSize( unsigned width, unsigned height ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + mPlayer.setVideoSize( width, height ); +} + +uno::Reference< css::media::XPlayerWindow > SAL_CALL VLCPlayer::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + const intptr_t winID = GetWindowID( aArguments ); + VLCWindow * window; + if ( mPrevWinID == 0 ) + { + mPrevWinID = winID; + window = new VLCWindow( *this, 0 ); + } + else + window = new VLCWindow( *this, mPrevWinID ); + + if ( winID != -1 ) + { + setWindowID( winID ); + } + + return css::uno::Reference< css::media::XPlayerWindow >( window ); +} + +uno::Reference< css::media::XFrameGrabber > SAL_CALL VLCPlayer::createFrameGrabber() +{ + ::osl::MutexGuard aGuard(m_aMutex); + + if ( !mrFrameGrabber.is() ) + { + VLCFrameGrabber *frameGrabber = new VLCFrameGrabber( mEventHandler, mUrl ); + mrFrameGrabber.set( frameGrabber ); + } + + return mrFrameGrabber; +} + +OUString SAL_CALL VLCPlayer::getImplementationName() +{ + return AVMEDIA_VLC_PLAYER_IMPLEMENTATIONNAME; +} + +sal_Bool SAL_CALL VLCPlayer::supportsService( const OUString& serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +::uno::Sequence< OUString > SAL_CALL VLCPlayer::getSupportedServiceNames() +{ + return { AVMEDIA_VLC_PLAYER_SERVICENAME }; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcplayer.hxx b/avmedia/source/vlc/vlcplayer.hxx new file mode 100644 index 000000000..4c3c03d0b --- /dev/null +++ b/avmedia/source/vlc/vlcplayer.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 "vlccommon.hxx" +#include +#include +#include + +#include +#include +#include +#include + +namespace avmedia { +namespace vlc { + +typedef ::cppu::WeakComponentImplHelper< css::media::XPlayer, + css::lang::XServiceInfo > VLC_Base; + +class VLCPlayer : public ::cppu::BaseMutex, + public VLC_Base +{ + wrapper::EventHandler& mEventHandler; + + wrapper::Media mMedia; + wrapper::Player mPlayer; + wrapper::EventManager mEventManager; + const OUString mUrl; + bool mPlaybackLoop; + css::uno::Reference< css::media::XFrameGrabber > mrFrameGrabber; + intptr_t mPrevWinID; +public: + VLCPlayer( const OUString& url, + wrapper::Instance& instance, + wrapper::EventHandler& eh ); + + void setVideoSize( unsigned width, unsigned height ); + + void setWindowID( const intptr_t windowID ); + + void SAL_CALL start() override; + void SAL_CALL stop() override; + sal_Bool SAL_CALL isPlaying() override; + double SAL_CALL getDuration() override; + void SAL_CALL setMediaTime( double fTime ) override; + double SAL_CALL getMediaTime() override; + void SAL_CALL setPlaybackLoop( sal_Bool bSet ) override; + sal_Bool SAL_CALL isPlaybackLoop() override; + void SAL_CALL setVolumeDB( ::sal_Int16 nDB ) override; + ::sal_Int16 SAL_CALL getVolumeDB() override; + void SAL_CALL setMute( sal_Bool bSet ) override; + sal_Bool SAL_CALL isMute() override; + css::awt::Size SAL_CALL getPreferredPlayerWindowSize() override; + css::uno::Reference< css::media::XPlayerWindow > SAL_CALL createPlayerWindow( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + css::uno::Reference< css::media::XFrameGrabber > SAL_CALL createFrameGrabber() override; + + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& serviceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + +private: + void replay(); +}; + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcuno.cxx b/avmedia/source/vlc/vlcuno.cxx new file mode 100644 index 000000000..dba992eb9 --- /dev/null +++ b/avmedia/source/vlc/vlcuno.cxx @@ -0,0 +1,76 @@ +/* -*- 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 + +#include +#include +#include +#include + +#include "vlccommon.hxx" +#include "vlcmanager.hxx" + +using namespace ::com::sun::star; + +#define IMPL_NAME "com.sun.star.comp.media.Manager_VLC" +#define SERVICE_NAME "com.sun.star.comp.avmedia.Manager_VLC" + +static uno::Reference< uno::XInterface > create_MediaPlayer( const uno::Reference< lang::XMultiServiceFactory >& /*rxFact*/ ) +{ + SAL_INFO("avmedia", "create VLC Media player !"); + + // Experimental for now - code is neither elegant nor well tested. + uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + if (!xContext.is() || !officecfg::Office::Common::Misc::ExperimentalMode::get(xContext)) + return nullptr; + + static uno::Reference< uno::XInterface > manager( *new ::avmedia::vlc::Manager ); + return manager; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* avmediavlc_component_getFactory( const char* pImplName, void* pServiceManager, void* /*pRegistryKey*/ ) +{ + uno::Reference< lang::XSingleServiceFactory > xFactory; + void* pRet = nullptr; + + // Experimental for now - code is neither elegant nor well tested. + uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext(); + if (!xContext.is() || !officecfg::Office::Common::Misc::ExperimentalMode::get(xContext)) + return nullptr; + + SAL_INFO("avmedia", "Create VLC Media component: " << pImplName); + if( rtl_str_compare( pImplName, IMPL_NAME ) == 0 ) + { + const OUString aServiceName( SERVICE_NAME ); + xFactory.set( ::cppu::createSingleFactory( + static_cast< lang::XMultiServiceFactory* >( pServiceManager ), + IMPL_NAME, create_MediaPlayer, uno::Sequence< OUString >( &aServiceName, 1 ) ) ); + } + + if( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcwindow.cxx b/avmedia/source/vlc/vlcwindow.cxx new file mode 100644 index 000000000..1cd0e1306 --- /dev/null +++ b/avmedia/source/vlc/vlcwindow.cxx @@ -0,0 +1,197 @@ +/* -*- 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 +#include +#include "vlcwindow.hxx" +#include "vlcplayer.hxx" + +using namespace ::com::sun::star; + +namespace avmedia::vlc { + +namespace +{ + const OUString AVMEDIA_VLC_WINDOW_IMPLEMENTATIONNAME = "com.sun.star.comp.avmedia.Window_VLC"; + const OUString AVMEDIA_VLC_WINDOW_SERVICENAME = "com.sun.star.media.Window_VLC"; +} + +VLCWindow::VLCWindow( VLCPlayer& player, const intptr_t prevWinID ) + : mPlayer( player ) + , mPrevWinID( prevWinID ) + , meZoomLevel( media::ZoomLevel_ORIGINAL ) +{ +} + +VLCWindow::~VLCWindow() +{ + if ( mPrevWinID != 0 ) + mPlayer.setWindowID( mPrevWinID ); +} + +void SAL_CALL VLCWindow::update() +{ +} + +sal_Bool SAL_CALL VLCWindow::setZoomLevel( css::media::ZoomLevel eZoomLevel ) +{ + bool bRet = false; + + if( media::ZoomLevel_NOT_AVAILABLE != meZoomLevel && + media::ZoomLevel_NOT_AVAILABLE != eZoomLevel ) + { + if( eZoomLevel != meZoomLevel ) + { + meZoomLevel = eZoomLevel; + } + + switch ( eZoomLevel ) + { + case media::ZoomLevel_ORIGINAL: + case media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT: + mPlayer.setVideoSize( mSize.Width, mSize.Height ); + break; + case media::ZoomLevel_ZOOM_1_TO_2: + mPlayer.setVideoSize( mSize.Width / 2, mSize.Height / 2 ); + break; + case media::ZoomLevel_ZOOM_2_TO_1: + mPlayer.setVideoSize( mSize.Width * 2, mSize.Height * 2 ); + break; + default: + break; + } + + bRet = true; + } + + return bRet; +} + +css::media::ZoomLevel SAL_CALL VLCWindow::getZoomLevel() +{ + return meZoomLevel; +} + +void SAL_CALL VLCWindow::setPointerType( ::sal_Int32 ) +{ +} + +OUString SAL_CALL VLCWindow::getImplementationName() +{ + return AVMEDIA_VLC_WINDOW_IMPLEMENTATIONNAME; +} + +sal_Bool SAL_CALL VLCWindow::supportsService( const OUString& serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +uno::Sequence< OUString > SAL_CALL VLCWindow::getSupportedServiceNames() +{ + return { AVMEDIA_VLC_WINDOW_SERVICENAME }; +} + +void SAL_CALL VLCWindow::dispose() +{ +} + +void SAL_CALL VLCWindow::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ +} + +void SAL_CALL VLCWindow::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ +} + +void SAL_CALL VLCWindow::setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 /* Flags */ ) +{ + mSize.X = X; + mSize.Y = Y; + mSize.Width = Width; + mSize.Height = Height; +} + +awt::Rectangle SAL_CALL VLCWindow::getPosSize() +{ + return mSize; +} + +void SAL_CALL VLCWindow::setVisible( sal_Bool ) +{ +} + +void SAL_CALL VLCWindow::setEnable( sal_Bool ) +{ +} + +void SAL_CALL VLCWindow::setFocus() +{ +} + +void SAL_CALL VLCWindow::addWindowListener( const uno::Reference< awt::XWindowListener >& ) +{ +} + +void SAL_CALL VLCWindow::removeWindowListener( const uno::Reference< awt::XWindowListener >& ) +{ +} + +void SAL_CALL VLCWindow::addFocusListener( const uno::Reference< awt::XFocusListener >& ) +{ +} + +void SAL_CALL VLCWindow::removeFocusListener( const uno::Reference< awt::XFocusListener >& ) +{ +} + +void SAL_CALL VLCWindow::addKeyListener( const uno::Reference< awt::XKeyListener >& ) +{ +} + +void SAL_CALL VLCWindow::removeKeyListener( const uno::Reference< awt::XKeyListener >& ) +{ +} + +void SAL_CALL VLCWindow::addMouseListener( const uno::Reference< awt::XMouseListener >& ) +{ +} + +void SAL_CALL VLCWindow::removeMouseListener( const uno::Reference< awt::XMouseListener >& ) +{ +} + +void SAL_CALL VLCWindow::addMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& ) +{ +} + +void SAL_CALL VLCWindow::removeMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& ) +{ +} + +void SAL_CALL VLCWindow::addPaintListener( const uno::Reference< awt::XPaintListener >& ) +{ +} + +void SAL_CALL VLCWindow::removePaintListener( const uno::Reference< awt::XPaintListener >& ) +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/vlcwindow.hxx b/avmedia/source/vlc/vlcwindow.hxx new file mode 100644 index 000000000..e70c8e80f --- /dev/null +++ b/avmedia/source/vlc/vlcwindow.hxx @@ -0,0 +1,74 @@ +/* -*- 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 "vlccommon.hxx" +#include + +namespace avmedia { +namespace vlc { +class VLCPlayer; + +class VLCWindow : public ::cppu::WeakImplHelper< css::media::XPlayerWindow, + css::lang::XServiceInfo > +{ + VLCPlayer& mPlayer; + const intptr_t mPrevWinID; + css::media::ZoomLevel meZoomLevel; + css::awt::Rectangle mSize; +public: + VLCWindow( VLCPlayer& player, const intptr_t prevWinID ); + virtual ~VLCWindow() override; + + void SAL_CALL update() override; + sal_Bool SAL_CALL setZoomLevel( css::media::ZoomLevel ZoomLevel ) override; + css::media::ZoomLevel SAL_CALL getZoomLevel() override; + void SAL_CALL setPointerType( ::sal_Int32 SystemPointerType ) override; + + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService( const OUString& serviceName ) override; + css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + void SAL_CALL dispose() override; + void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + void SAL_CALL setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) override; + css::awt::Rectangle SAL_CALL getPosSize() override; + void SAL_CALL setVisible( sal_Bool Visible ) override; + void SAL_CALL setEnable( sal_Bool Enable ) override; + void SAL_CALL setFocus() override; + void SAL_CALL addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override; + void SAL_CALL removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override; + void SAL_CALL addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override; + void SAL_CALL removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override; + void SAL_CALL addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override; + void SAL_CALL removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override; + void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; + void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; +}; + +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/Common.cxx b/avmedia/source/vlc/wrapper/Common.cxx new file mode 100644 index 000000000..4ee3a2977 --- /dev/null +++ b/avmedia/source/vlc/wrapper/Common.cxx @@ -0,0 +1,45 @@ +/* -*- 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 +#include "SymbolLoader.hxx" + +namespace +{ + const char AVMEDIA_NO_ERROR[] = "No error"; + + const char* ( *libvlc_get_version ) (); + char * ( * libvlc_errmsg ) (); +} + +namespace avmedia::vlc::wrapper +{ +bool Common::LoadSymbols() +{ + static ApiMap const VLC_COMMON_API[] = + { + SYM_MAP( libvlc_get_version ), + SYM_MAP( libvlc_errmsg ) + }; + + return InitApiMap( VLC_COMMON_API ); +} + +const char* Common::Version() +{ + return libvlc_get_version(); +} + +const char* Common::LastErrorMessage() +{ + const char *errorMsg = libvlc_errmsg(); + return errorMsg == nullptr ? AVMEDIA_NO_ERROR : errorMsg; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/EventHandler.cxx b/avmedia/source/vlc/wrapper/EventHandler.cxx new file mode 100644 index 000000000..a2a0db16e --- /dev/null +++ b/avmedia/source/vlc/wrapper/EventHandler.cxx @@ -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/. + */ + +#include + +namespace avmedia::vlc::wrapper +{ +EventHandler::EventHandler() + : ::osl::Thread() +{ +} + +void EventHandler::stop() +{ + mCallbackQueue.push(TCallback()); + join(); +} + +void EventHandler::run() +{ + osl_setThreadName("VLC EventHandler"); + + TCallback callback; + do + { + mCallbackQueue.pop( callback ); + + if ( !callback ) + return; + + callback(); + } while ( true ); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/EventManager.cxx b/avmedia/source/vlc/wrapper/EventManager.cxx new file mode 100644 index 000000000..27e56603e --- /dev/null +++ b/avmedia/source/vlc/wrapper/EventManager.cxx @@ -0,0 +1,84 @@ +/* -*- 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 +#include "SymbolLoader.hxx" +#include +#include "Types.hxx" + +namespace +{ + libvlc_event_manager_t* ( *libvlc_media_player_event_manager ) ( libvlc_media_player_t *p_mi ); + int ( *libvlc_event_attach ) ( libvlc_event_manager_t *p_event_manager, + libvlc_event_type_t i_event_type, + libvlc_callback_t f_callback, + void *user_data ); + void ( *libvlc_event_detach ) ( libvlc_event_manager_t *p_event_manager, + libvlc_event_type_t i_event_type, + libvlc_callback_t f_callback, + void *p_user_data ); +} + +namespace avmedia::vlc::wrapper +{ +void EventManager::Handler( const libvlc_event_t *event, void *pData ) +{ + EventManager *instance = static_cast( pData ); + switch ( event->type ) + { + case libvlc_MediaPlayerPaused: + instance->mEventHandler.mCallbackQueue.push( instance->mOnPaused ); + break; + case libvlc_MediaPlayerEndReached: + instance->mEventHandler.mCallbackQueue.push( instance->mOnEndReached ); + break; + } +} + +bool EventManager::LoadSymbols() +{ + static ApiMap const VLC_EVENT_MANAGER_API[] = + { + SYM_MAP( libvlc_media_player_event_manager ), + SYM_MAP( libvlc_event_attach ), + SYM_MAP( libvlc_event_detach ) + }; + + return InitApiMap( VLC_EVENT_MANAGER_API ); +} + +EventManager::EventManager( Player& player, EventHandler& eh ) + : mEventHandler( eh ) + , mManager( libvlc_media_player_event_manager( player ) ) +{ + +} + +void EventManager::registerSignal( int signal, const Callback& callback ) +{ + if ( !callback ) + libvlc_event_detach( mManager, signal, Handler, this ); + else + libvlc_event_attach( mManager, signal, Handler, this ); +} + +void EventManager::onPaused( const EventManager::Callback& callback ) +{ + mOnPaused = callback; + registerSignal( libvlc_MediaPlayerPaused, callback ); +} + +void EventManager::onEndReached( const Callback& callback ) +{ + mOnEndReached = callback; + registerSignal( libvlc_MediaPlayerEndReached, callback ); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/Instance.cxx b/avmedia/source/vlc/wrapper/Instance.cxx new file mode 100644 index 000000000..1226cb7ea --- /dev/null +++ b/avmedia/source/vlc/wrapper/Instance.cxx @@ -0,0 +1,62 @@ +/* -*- 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 +#include "SymbolLoader.hxx" + +namespace +{ + libvlc_instance_t* ( *libvlc_new ) ( int argc, const char * const *argv ); + void ( *libvlc_release ) ( libvlc_instance_t *p_instance ); + void ( *libvlc_retain ) ( libvlc_instance_t *p_instance ); +} + +namespace avmedia::vlc::wrapper +{ + bool Instance::LoadSymbols() + { + static ApiMap const VLC_INSTANCE_API[] = + { + SYM_MAP( libvlc_new ), + SYM_MAP( libvlc_release ), + SYM_MAP( libvlc_retain ) + }; + + return InitApiMap( VLC_INSTANCE_API ); + } + + Instance::Instance( int argc, const char * const argv[] ) + : mInstance( libvlc_new( argc, argv ) ) + { + if ( mInstance == nullptr) + { + //TODO: error + } + } + + Instance::Instance( const Instance& other ) + { + operator=( other ); + } + + Instance& Instance::operator=( const Instance& other ) + { + libvlc_release( mInstance ); + mInstance = other.mInstance; + libvlc_retain( mInstance ); + return *this; + } + + Instance::~Instance() + { + libvlc_release( mInstance ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/Media.cxx b/avmedia/source/vlc/wrapper/Media.cxx new file mode 100644 index 000000000..f09aecd76 --- /dev/null +++ b/avmedia/source/vlc/wrapper/Media.cxx @@ -0,0 +1,109 @@ +/* -*- 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 +#include +#include "SymbolLoader.hxx" +#include +#include "Types.hxx" +#include +#include + +struct libvlc_instance_t; + +namespace avmedia::vlc::wrapper +{ +namespace +{ + libvlc_media_t* ( *libvlc_media_new_path ) ( libvlc_instance_t *p_instance, const char *path ); + libvlc_media_t* ( *libvlc_media_new_location ) (libvlc_instance_t *p_instance, const char *psz_mrl); + void ( *libvlc_media_release ) ( libvlc_media_t *p_md ); + void ( *libvlc_media_retain ) ( libvlc_media_t *p_md ); + libvlc_time_t ( *libvlc_media_get_duration ) ( libvlc_media_t *p_md ); + void ( *libvlc_media_parse ) ( libvlc_media_t *p_md ); + int ( *libvlc_media_is_parsed ) ( libvlc_media_t *p_md ); + char* ( *libvlc_media_get_mrl )(libvlc_media_t *p_md); + + + libvlc_media_t* InitMedia( const OUString& url, Instance& instance ) + { + OString dest; + url.convertToString(&dest, RTL_TEXTENCODING_UTF8, 0); + + return libvlc_media_new_location(instance, dest.getStr()); + } +} + +bool Media::LoadSymbols() +{ + static ApiMap const VLC_MEDIA_API[] = + { + SYM_MAP( libvlc_media_new_path ), + SYM_MAP( libvlc_media_release ), + SYM_MAP( libvlc_media_retain ), + SYM_MAP( libvlc_media_get_duration ), + SYM_MAP( libvlc_media_parse ), + SYM_MAP( libvlc_media_is_parsed ), + SYM_MAP( libvlc_media_get_mrl ), + SYM_MAP( libvlc_media_new_location ) + }; + + return InitApiMap( VLC_MEDIA_API ); +} + +Media::Media( const OUString& url, Instance& instance ) + : mMedia( InitMedia( url, instance ) ) +{ + if (mMedia == nullptr) + { + // TODO: Error + } +} + +Media::Media( const Media& other ) +{ + operator=( other ); +} + +Media& Media::operator=( const Media& other ) +{ + libvlc_media_release( mMedia ); + mMedia = other.mMedia; + + libvlc_media_retain( mMedia ); + return *this; +} + +int Media::getDuration() const +{ + if ( !libvlc_media_is_parsed( mMedia ) ) + libvlc_media_parse( mMedia ); + + const int duration = libvlc_media_get_duration( mMedia ); + if (duration == -1) + { + SAL_WARN("avmedia", Common::LastErrorMessage()); + return 0; + } + else if (duration == 0) + { + // A duration must be greater than 0 + return 1; + } + + return duration; +} + +Media::~Media() +{ + libvlc_media_release( mMedia ); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/Player.cxx b/avmedia/source/vlc/wrapper/Player.cxx new file mode 100644 index 000000000..27e63a21a --- /dev/null +++ b/avmedia/source/vlc/wrapper/Player.cxx @@ -0,0 +1,241 @@ +/* -*- 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 +#include "Types.hxx" +#include +#include +#include "SymbolLoader.hxx" +#include + +struct libvlc_media_t; + +namespace { extern "C" { + void ( *libvlc_media_player_retain ) ( libvlc_media_player_t *p_mi ); + libvlc_media_player_t * ( *libvlc_media_player_new_from_media ) ( libvlc_media_t *p_md ); + void ( *libvlc_media_player_release ) ( libvlc_media_player_t *p_mi ); + int ( *libvlc_media_player_play ) ( libvlc_media_player_t *p_mi ); + void ( *libvlc_media_player_pause ) ( libvlc_media_player_t *p_mi ); + int ( *libvlc_media_player_is_playing ) ( libvlc_media_player_t *p_mi ); + void ( *libvlc_media_player_stop ) ( libvlc_media_player_t *p_mi ); + void ( *libvlc_media_player_set_time ) ( libvlc_media_player_t *p_mi, libvlc_time_t i_time ); + libvlc_time_t ( *libvlc_media_player_get_time ) ( libvlc_media_player_t *p_mi ); + float ( *libvlc_media_player_get_rate )( libvlc_media_player_t *p_mi ); + int ( *libvlc_audio_set_volume ) ( libvlc_media_player_t *p_mi, int i_volume ); + int ( *libvlc_audio_get_volume ) ( libvlc_media_player_t *p_mi ); + int ( *libvlc_audio_get_mute ) ( libvlc_media_player_t *p_mi ); + void ( *libvlc_audio_set_mute ) ( libvlc_media_player_t *p_mi, int status ); + int ( *libvlc_video_take_snapshot ) ( libvlc_media_player_t *p_mi, + unsigned num, + const char *psz_filepath, + unsigned int i_width, + unsigned int i_height ); +#if defined MACOSX + void ( *libvlc_media_player_set_nsobject ) ( libvlc_media_player_t *p_mi, void *drawable ); +#elif defined UNX + void ( *libvlc_media_player_set_xwindow ) ( libvlc_media_player_t *p_mi, uint32_t drawable ); +#elif defined _WIN32 + void ( *libvlc_media_player_set_hwnd ) ( libvlc_media_player_t *p_mi, void *drawable ); +#else +#error unknown OS +#endif + unsigned ( *libvlc_media_player_has_vout ) ( libvlc_media_player_t *p_mi ); + void ( *libvlc_video_set_mouse_input ) ( libvlc_media_player_t *p_mi, unsigned on ); + void ( *libvlc_video_set_scale ) ( libvlc_media_player_t *p_mi, float f_factor ); + int ( *libvlc_video_get_size ) ( libvlc_media_player_t *p_mi, unsigned num, + unsigned *px, unsigned *py ); + int ( *libvlc_video_get_track_count ) ( libvlc_media_player_t *p_mi ); + int ( *libvlc_video_set_track ) ( libvlc_media_player_t *p_mi, int i_track ); + libvlc_track_description_t* ( *libvlc_video_get_track_description ) ( libvlc_media_player_t *p_mi ); + + int ( *libvlc_audio_get_track ) ( libvlc_media_player_t *p_mi ); + libvlc_track_description_t * ( *libvlc_audio_get_track_description ) (libvlc_media_player_t *p_mi ); + int ( *libvlc_audio_set_track ) (libvlc_media_player_t *p_mi, int i_track); +} } + +namespace avmedia::vlc::wrapper +{ + bool Player::LoadSymbols() + { + static ApiMap const VLC_PLAYER_API[] = + { + SYM_MAP( libvlc_media_player_new_from_media ), + SYM_MAP( libvlc_media_player_release ), + SYM_MAP( libvlc_media_player_play ), + SYM_MAP( libvlc_media_player_pause ), + SYM_MAP( libvlc_media_player_is_playing ), + SYM_MAP( libvlc_media_player_stop ), + SYM_MAP( libvlc_media_player_set_time ), + SYM_MAP( libvlc_media_player_get_time ), + SYM_MAP( libvlc_media_player_get_rate ), + SYM_MAP( libvlc_audio_set_volume ), + SYM_MAP( libvlc_audio_get_volume ), + SYM_MAP( libvlc_audio_set_mute ), + SYM_MAP( libvlc_audio_get_mute ), + SYM_MAP( libvlc_video_take_snapshot ), +#if defined MACOSX + SYM_MAP( libvlc_media_player_set_nsobject ), +#elif defined UNX + SYM_MAP( libvlc_media_player_set_xwindow ), +#elif defined _WIN32 + SYM_MAP( libvlc_media_player_set_hwnd ), +#endif + SYM_MAP( libvlc_media_player_has_vout ), + SYM_MAP( libvlc_video_set_mouse_input ), + SYM_MAP( libvlc_media_player_retain ), + SYM_MAP( libvlc_video_set_scale ), + SYM_MAP( libvlc_video_get_size ), + SYM_MAP( libvlc_video_get_track_count ), + SYM_MAP( libvlc_video_set_track ), + SYM_MAP( libvlc_video_get_track_description ), + SYM_MAP( libvlc_audio_get_track ), + SYM_MAP( libvlc_audio_get_track_description ), + SYM_MAP( libvlc_audio_set_track ) + }; + + return InitApiMap( VLC_PLAYER_API ); + } + + Player::Player( Media& media ) + : mPlayer( libvlc_media_player_new_from_media( media ) ) + { + } + + Player::Player( const Player& other ) + { + operator=( other ); + } + + Player& Player::operator=( const Player& other ) + { + libvlc_media_player_release( mPlayer ); + mPlayer = other.mPlayer; + libvlc_media_player_retain( mPlayer ); + return *this; + } + + Player::~Player() + { + libvlc_media_player_release( mPlayer ); + } + + bool Player::play() + { + const bool status = ( libvlc_media_player_play( mPlayer ) == 0 ); + if ( libvlc_video_get_track_count( mPlayer ) > 0 ) + { + const libvlc_track_description_t *description = libvlc_video_get_track_description( mPlayer ); + + for ( ; description->p_next != nullptr; description = description->p_next ); + + libvlc_video_set_track( mPlayer, description->i_id ); + } + + if ( libvlc_audio_get_track( mPlayer ) > 0 ) + { + const libvlc_track_description_t *description = libvlc_audio_get_track_description( mPlayer ); + + for ( ; description->p_next != nullptr; description = description->p_next ); + + libvlc_audio_set_track( mPlayer, description->i_id ); + } + + return status; + } + + void Player::pause() + { + libvlc_media_player_pause( mPlayer ); + } + + void Player::stop() + { + libvlc_media_player_stop( mPlayer ); + } + + void Player::setTime( int time ) + { + libvlc_media_player_set_time( mPlayer, time ); + } + + int Player::getTime() const + { + const int time = libvlc_media_player_get_time( mPlayer ); + + return ( time == -1 ? 0 : time ); + } + + void Player::setScale( float factor ) + { + libvlc_video_set_scale( mPlayer, factor ); + } + + void Player::setMouseHandling(bool flag) + { + libvlc_video_set_mouse_input( mPlayer, flag ); + } + + bool Player::isPlaying() const + { + return libvlc_media_player_is_playing( mPlayer ) == 1; + } + + void Player::setVolume( int volume ) + { + libvlc_audio_set_volume( mPlayer, volume ); + } + + int Player::getVolume() const + { + return libvlc_audio_get_volume( mPlayer ); + } + + void Player::setMute( bool mute) + { + libvlc_audio_set_mute( mPlayer, mute ); + } + + bool Player::getMute() const + { + return libvlc_audio_get_mute( mPlayer ); + } + + void Player::setVideoSize( unsigned width, unsigned ) + { + unsigned currentWidth, currentHeight; + libvlc_video_get_size( mPlayer, 0, ¤tWidth, ¤tHeight ); + if ( currentWidth != 0 ) + setScale( static_cast( width ) / currentWidth ); + } + + void Player::setWindow( intptr_t id ) + { +#if defined MACOSX + libvlc_media_player_set_nsobject( mPlayer, reinterpret_cast( id ) ); +#elif defined UNX + libvlc_media_player_set_xwindow( mPlayer, static_cast(id) ); +#elif defined _WIN32 + libvlc_media_player_set_hwnd( mPlayer, reinterpret_cast( id ) ); +#endif + } + + void Player::takeSnapshot( const OUString& file ) + { + OString dest; + file.convertToString( &dest, RTL_TEXTENCODING_UTF8, 0 ); + libvlc_video_take_snapshot( mPlayer, 0, dest.getStr(), 480, 360 ); + } + + bool Player::hasVout() const + { + return libvlc_media_player_has_vout( mPlayer ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/SymbolLoader.hxx b/avmedia/source/vlc/wrapper/SymbolLoader.hxx new file mode 100644 index 000000000..64789b326 --- /dev/null +++ b/avmedia/source/vlc/wrapper/SymbolLoader.hxx @@ -0,0 +1,126 @@ +/* -*- 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/. + */ + +#pragma once +#if defined(_WIN32) +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +# include +# include +#endif +#include +#include +#include + +#define SYM_MAP(a) { #a, reinterpret_cast(&a) } + +namespace avmedia +{ +namespace vlc +{ +namespace wrapper +{ +typedef void (*SymbolFunc) (void); + +struct ApiMap +{ + OUStringLiteral symName; + SymbolFunc *refValue; +}; + +#if defined( LINUX ) + const char LibName[] = "libvlc.so.5"; +#elif defined( MACOSX ) + const char LibName[] = "/Applications/VLC.app/Contents/MacOS/lib/libvlc.dylib"; +#elif defined( _WIN32 ) + const char LibName[] = "libvlc.dll"; + + inline OUString GetVLCPath() + { + HKEY hKey; + sal_Unicode arCurrent[MAX_PATH]; + DWORD dwType, dwCurrentSize = sizeof( arCurrent ); + + //TODO: This one will work only with LibreOffice 32-bit + VLC 32-bit on Win x86_64. + const LONG errorCore = ::RegOpenKeyExW( HKEY_LOCAL_MACHINE, L"SOFTWARE\\Wow6432Node\\VideoLAN\\VLC", 0, KEY_READ | KEY_WOW64_64KEY, &hKey ); + if ( errorCore == ERROR_SUCCESS ) + { + if ( ::RegQueryValueExW( hKey, L"InstallDir", nullptr, &dwType, reinterpret_cast(arCurrent), &dwCurrentSize ) == ERROR_SUCCESS && + dwType == REG_SZ ) + { + ::RegCloseKey( hKey ); + dwCurrentSize -= 2; + dwCurrentSize /= 2; + + return OUString( arCurrent, dwCurrentSize ) + "\\"; + } + + ::RegCloseKey( hKey ); + } + + return OUString(); + } +#endif + + template + bool tryLink( oslModule &aModule, const ApiMap ( &pMap )[N] ) + { + for (size_t i = 0; i < N; ++i) + { + SymbolFunc aMethod = reinterpret_cast(osl_getFunctionSymbol + ( aModule, OUString( pMap[ i ].symName ).pData )); + if ( !aMethod ) + { + SAL_WARN("avmedia", "Cannot load method " << pMap[ i ].symName); + *pMap[ i ].refValue = nullptr; + return false; + } + else + *pMap[ i ].refValue = aMethod; + } + + return true; + } + + template + bool InitApiMap( const ApiMap ( &pMap )[N] ) + { +#if defined( LINUX ) || defined( MACOSX ) + OUString const fullPath(LibName); +#elif defined( _WIN32 ) + OUString const fullPath(GetVLCPath() + LibName); +#endif + SAL_INFO("avmedia", fullPath); + + oslModule aModule = osl_loadModule( fullPath.pData, + SAL_LOADMODULE_DEFAULT ); + + + if( aModule == nullptr) + { + SAL_WARN("avmedia", "Cannot load libvlc"); + return false; + } + + if (tryLink( aModule, pMap )) + { + return true; + } + + SAL_WARN("avmedia", "Cannot load libvlc"); + osl_unloadModule( aModule ); + + return false; + } +} +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/vlc/wrapper/Types.hxx b/avmedia/source/vlc/wrapper/Types.hxx new file mode 100644 index 000000000..c66a88bdc --- /dev/null +++ b/avmedia/source/vlc/wrapper/Types.hxx @@ -0,0 +1,58 @@ +/* -*- 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/. + */ + +/* Typedefs and structures that represent the libvlc API / ABI */ + +#pragma once + +#include + +#if defined(_WIN32) + typedef __int64 libvlc_time_t; +#else +#include + typedef int64_t libvlc_time_t; +#endif + +extern "C" { + +// basic callback / event types we use +typedef int libvlc_event_type_t; +typedef struct libvlc_event_manager_t libvlc_event_manager_t; +typedef void ( *libvlc_callback_t ) ( const struct libvlc_event_t *, void * ); + +// the enumeration values we use cf. libvlc_events.h +#define libvlc_MediaPlayerPaused 0x105 +#define libvlc_MediaPlayerEndReached 0x109 + +// event structure pieces we use +struct libvlc_event_t +{ + int type; // event type + void *p_obj; // object emitting that event + + union // so far we don't need this. + { + struct { + const char *dummy1; + const char *dummy2; + } padding; + } u; +}; + +struct libvlc_track_description_t +{ + int i_id; + char *psz_name; + libvlc_track_description_t *p_next; +}; + +} + +/* 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..3874439f4 --- /dev/null +++ b/avmedia/source/win/avmediawin.component @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/avmedia/source/win/framegrabber.cxx b/avmedia/source/win/framegrabber.cxx new file mode 100644 index 000000000..21b5dede6 --- /dev/null +++ b/avmedia/source/win/framegrabber.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 + +#include + +#include +#include +#include +#include +#include +#include "interface.hxx" +#include + +#include "framegrabber.hxx" +#include "player.hxx" + +#include +#include +#include +#include +#include +#include + +#define AVMEDIA_WIN_FRAMEGRABBER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.FrameGrabber_DirectX" +#define AVMEDIA_WIN_FRAMEGRABBER_SERVICENAME "com.sun.star.media.FrameGrabber_DirectX" + +using namespace ::com::sun::star; + +namespace avmedia::win { + + +FrameGrabber::FrameGrabber( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) : + mxMgr( rxMgr ) +{ + ::CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ); +} + + +FrameGrabber::~FrameGrabber() +{ + ::CoUninitialize(); +} + +namespace { + +IMediaDet* implCreateMediaDet( const OUString& rURL ) +{ + IMediaDet* pDet = nullptr; + + if( SUCCEEDED( CoCreateInstance( CLSID_MediaDet, nullptr, CLSCTX_INPROC_SERVER, IID_IMediaDet, reinterpret_cast(&pDet) ) ) ) + { + OUString aLocalStr; + + if( osl::FileBase::getSystemPathFromFileURL( rURL, aLocalStr ) + == osl::FileBase::E_None ) + { + BSTR bstrFilename = SysAllocString(o3tl::toW(aLocalStr.getStr())); + if( !SUCCEEDED( pDet->put_Filename( bstrFilename ) ) ) + { + // Shouldn't we free this string unconditionally, not only in case of failure? + // I cannot find information why do we pass a newly allocated BSTR to the put_Filename + // and if it frees the string internally + SysFreeString(bstrFilename); + pDet->Release(); + pDet = nullptr; + } + } + } + + return pDet; +} + +} + +bool FrameGrabber::create( const OUString& rURL ) +{ + // just check if a MediaDet interface can be created with the given URL + IMediaDet* pDet = implCreateMediaDet( rURL ); + + if( pDet ) + { + maURL = rURL; + pDet->Release(); + pDet = nullptr; + } + else + maURL.clear(); + + return !maURL.isEmpty(); +} + + +uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime ) +{ + uno::Reference< graphic::XGraphic > xRet; + IMediaDet* pDet = implCreateMediaDet( maURL ); + + if( pDet ) + { + 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, 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(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() ) + { + const Graphic aGraphic( aBmp ); + xRet = aGraphic.GetXGraphic(); + } + } + } + catch( ... ) + { + } + } + } + + pDet->Release(); + } + + 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..aa5b8945e --- /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 +#include + +struct IMediaDet; + +namespace avmedia::win { + +class FrameGrabber : public ::cppu::WeakImplHelper< css::media::XFrameGrabber, + css::lang::XServiceInfo > +{ +public: + + explicit FrameGrabber( const css::uno::Reference< css::lang::XMultiServiceFactory >& rxMgr ); + ~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: + + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; + 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..3615850a7 --- /dev/null +++ b/avmedia/source/win/manager.cxx @@ -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 +#include + +#define AVMEDIA_WIN_MANAGER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Manager_DirectX" +#define AVMEDIA_WIN_MANAGER_SERVICENAME "com.sun.star.media.Manager" + +using namespace ::com::sun::star; + +namespace avmedia::win { + +Manager::Manager( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) : + mxMgr( rxMgr ) +{ +} + + +Manager::~Manager() +{ +} + + +uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL ) +{ + Player* pPlayer( new Player( mxMgr ) ); + uno::Reference< media::XPlayer > xRet( pPlayer ); + const INetURLObject aURL( rURL ); + + if( !pPlayer->create( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ) ) + xRet.clear(); + + return xRet; +} + + +OUString SAL_CALL Manager::getImplementationName( ) +{ + return AVMEDIA_WIN_MANAGER_IMPLEMENTATIONNAME; +} + + +sal_Bool SAL_CALL Manager::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + + +uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames( ) +{ + return { AVMEDIA_WIN_MANAGER_SERVICENAME }; +} + +} // namespace avmedia::win + + +/* 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..77d1ce6ef --- /dev/null +++ b/avmedia/source/win/manager.hxx @@ -0,0 +1,51 @@ +/* -*- 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 +#include + + +namespace avmedia::win { + +class Manager : public ::cppu::WeakImplHelper< css::media::XManager, + css::lang::XServiceInfo > +{ +public: + + explicit Manager( const css::uno::Reference< css::lang::XMultiServiceFactory >& rxMgr ); + ~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; +private: + + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; +}; + +} // 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..8e08d9e53 --- /dev/null +++ b/avmedia/source/win/player.cxx @@ -0,0 +1,463 @@ +/* -*- 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 +#include +#include +#include +#include + +#include "player.hxx" +#include "framegrabber.hxx" +#include "window.hxx" +#include +#include +#include + +#define AVMEDIA_WIN_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_DirectX" +#define AVMEDIA_WIN_PLAYER_SERVICENAME "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(::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( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) : + Player_BASE(m_aMutex), + mxMgr( rxMgr ), + mpGB( nullptr ), + mpOMF( nullptr ), + mpMC( nullptr ), + mpME( nullptr ), + mpMS( nullptr ), + mpMP( nullptr ), + mpBA( nullptr ), + mpBV( nullptr ), + mpVW( nullptr ), + mpEV( nullptr ), + mnUnmutedVolume( 0 ), + mnFrameWnd( nullptr ), + mbMuted( false ), + mbLooping( false ), + mbAddWindow( true ) +{ + ::CoInitializeEx( nullptr, COINIT_APARTMENTTHREADED ); +} + + +Player::~Player() +{ + if( mnFrameWnd ) + ::DestroyWindow( mnFrameWnd ); + + ::CoUninitialize(); +} + + +void SAL_CALL Player::disposing() +{ + ::osl::MutexGuard aGuard(m_aMutex); + stop(); + if( mpBA ) + mpBA->Release(); + + if( mpBV ) + mpBV->Release(); + + if( mpVW ) + mpVW->Release(); + + if( mpMP ) + mpMP->Release(); + + if( mpMS ) + mpMS->Release(); + + if( mpME ) + { + mpME->SetNotifyWindow( 0, WM_GRAPHNOTIFY, 0); + mpME->Release(); + } + + if( mpMC ) + mpMC->Release(); + + if( mpEV ) + mpEV->Release(); + + if( mpOMF ) + mpOMF->Release(); + + if( mpGB ) + mpGB->Release(); +} + + +bool Player::create( const OUString& rURL ) +{ + HRESULT hR; + bool bRet = false; + + if( SUCCEEDED( hR = CoCreateInstance( CLSID_FilterGraph, nullptr, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, reinterpret_cast(&mpGB) ) ) ) + { + // 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( hR = mpGB->RenderFile( o3tl::toW(aFile.getStr()), nullptr ) ) && + SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaControl, reinterpret_cast(&mpMC) ) ) && + SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaEventEx, reinterpret_cast(&mpME) ) ) && + SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaSeeking, reinterpret_cast(&mpMS) ) ) && + SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaPosition, reinterpret_cast(&mpMP) ) ) ) + { + // Video interfaces + mpGB->QueryInterface( IID_IVideoWindow, reinterpret_cast(&mpVW) ); + mpGB->QueryInterface( IID_IBasicVideo, reinterpret_cast(&mpBV) ); + + // Audio interface + mpGB->QueryInterface( IID_IBasicAudio, reinterpret_cast(&mpBA) ); + + 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(nNotifyWnd), WM_GRAPHNOTIFY, reinterpret_cast< LONG_PTR>( this ) ); +} + + +long 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 ); + } + + return 0; +} + + +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(::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(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(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 ) + { + ::avmedia::win::Window* pWindow = new ::avmedia::win::Window( mxMgr, *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() ) + { + FrameGrabber* pGrabber = new FrameGrabber( mxMgr ); + + 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..370851368 --- /dev/null +++ b/avmedia/source/win/player.hxx @@ -0,0 +1,119 @@ +/* -*- 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 + +#include + +#include "wincommon.hxx" + +#include + +#include +#include + +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: + + explicit Player( const css::uno::Reference< css::lang::XMultiServiceFactory >& rxMgr ); + ~Player() override; + + bool create( const OUString& rURL ); + + void setNotifyWnd( HWND nNotifyWnd ); + long 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: + + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; + + OUString maURL; + IGraphBuilder* mpGB; + IBaseFilter* mpOMF; + IMediaControl* mpMC; + IMediaEventEx* mpME; + IMediaSeeking* mpMS; + IMediaPosition* mpMP; + IBasicAudio* mpBA; + IBasicVideo* mpBV; + IVideoWindow* mpVW; + IDDrawExclModeVideo* mpEV; + long mnUnmutedVolume; + HWND mnFrameWnd; + bool mbMuted; + bool mbLooping; + bool mbAddWindow; + + void ImplLayoutVideoWindow(); +}; + +} // 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..f0277e169 --- /dev/null +++ b/avmedia/source/win/wincommon.hxx @@ -0,0 +1,44 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_AVMEDIA_SOURCE_WIN_WINCOMMON_HXX +#define INCLUDED_AVMEDIA_SOURCE_WIN_WINCOMMON_HXX + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WM_GRAPHNOTIFY (WM_USER + 567) + +#endif // INCLUDED_AVMEDIA_SOURCE_WIN_WINCOMMON_HXX + +/* 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..995ddd92c --- /dev/null +++ b/avmedia/source/win/window.cxx @@ -0,0 +1,495 @@ +/* -*- 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 +#include +#include +#include + +#include +#include + +#include "window.hxx" +#include "player.hxx" + +#define AVMEDIA_WIN_WINDOW_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Window_DirectX" +#define AVMEDIA_WIN_WINDOW_SERVICENAME "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(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(::GetStockObject( BLACK_BRUSH )); + s_pWndClass->hCursor = ::LoadCursor( nullptr, IDC_ARROW ); + + RegisterClassW( s_pWndClass ); + + return s_pWndClass; +} + +Window::Window( const uno::Reference< lang::XMultiServiceFactory >& rxMgr, Player& rPlayer ) : + mxMgr( rxMgr ), + 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(aPrefSize.Width) / aPrefSize.Height; + + if( fPrefWH < ( static_cast(nVideoW) / nVideoH ) ) + nVideoW = static_cast( nVideoH * fPrefWH ); + else + nVideoH = static_cast( 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(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(this) ); + + pVideoWindow->put_Owner( reinterpret_cast(mnFrameWnd) ); + pVideoWindow->put_MessageDrain( reinterpret_cast(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::get(), xListener ); +} + +void SAL_CALL Window::removeWindowListener( const uno::Reference< awt::XWindowListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::addFocusListener( const uno::Reference< awt::XFocusListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::removeFocusListener( const uno::Reference< awt::XFocusListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::addKeyListener( const uno::Reference< awt::XKeyListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::removeKeyListener( const uno::Reference< awt::XKeyListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::addMouseListener( const uno::Reference< awt::XMouseListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::removeMouseListener( const uno::Reference< awt::XMouseListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::addMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::removeMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::addPaintListener( const uno::Reference< awt::XPaintListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::removePaintListener( const uno::Reference< awt::XPaintListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::dispose( ) +{ +} + +void SAL_CALL Window::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + maListeners.addInterface( cppu::UnoType::get(), xListener ); +} + +void SAL_CALL Window::removeEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + maListeners.removeInterface( cppu::UnoType::get(), xListener ); +} + +void Window::fireMousePressedEvent( const css::awt::MouseEvent& rEvt ) +{ + ::cppu::OInterfaceContainerHelper* pContainer = maListeners.getContainer( cppu::UnoType::get()); + + if( pContainer ) + { + ::cppu::OInterfaceIteratorHelper aIter( *pContainer ); + + while( aIter.hasMoreElements() ) + uno::Reference< awt::XMouseListener >( aIter.next(), uno::UNO_QUERY_THROW )->mousePressed( rEvt ); + } +} + +void Window::fireMouseReleasedEvent( const css::awt::MouseEvent& rEvt ) +{ + ::cppu::OInterfaceContainerHelper* pContainer = maListeners.getContainer( cppu::UnoType::get()); + + if( pContainer ) + { + ::cppu::OInterfaceIteratorHelper aIter( *pContainer ); + + while( aIter.hasMoreElements() ) + uno::Reference< awt::XMouseListener >( aIter.next(), uno::UNO_QUERY_THROW )->mouseReleased( rEvt ); + } +} + +void Window::fireMouseMovedEvent( const css::awt::MouseEvent& rEvt ) +{ + ::cppu::OInterfaceContainerHelper* pContainer = maListeners.getContainer( cppu::UnoType::get()); + + if( pContainer ) + { + ::cppu::OInterfaceIteratorHelper aIter( *pContainer ); + + while( aIter.hasMoreElements() ) + uno::Reference< awt::XMouseMotionListener >( aIter.next(), uno::UNO_QUERY_THROW )->mouseMoved( rEvt ); + } +} + +void Window::fireSetFocusEvent( const css::awt::FocusEvent& rEvt ) +{ + ::cppu::OInterfaceContainerHelper* pContainer = maListeners.getContainer( cppu::UnoType::get()); + + if( pContainer ) + { + ::cppu::OInterfaceIteratorHelper aIter( *pContainer ); + + while( aIter.hasMoreElements() ) + uno::Reference< awt::XFocusListener >( aIter.next(), uno::UNO_QUERY_THROW )->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..a947c1717 --- /dev/null +++ b/avmedia/source/win/window.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 + +#include + +#include "wincommon.hxx" +#include +#include + +#include + +struct IVideoWindow; + +namespace avmedia::win { + +class Player; + + +class Window : public ::cppu::WeakImplHelper< css::media::XPlayerWindow, + css::lang::XServiceInfo > +{ +public: + + Window( const css::uno::Reference< css::lang::XMultiServiceFactory >& rxMgr, + 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: + + css::uno::Reference< css::lang::XMultiServiceFactory > mxMgr; + + ::osl::Mutex maMutex; + ::cppu::OMultiTypeInterfaceContainerHelper 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: */ diff --git a/avmedia/source/win/winuno.cxx b/avmedia/source/win/winuno.cxx new file mode 100644 index 000000000..90b1f3e40 --- /dev/null +++ b/avmedia/source/win/winuno.cxx @@ -0,0 +1,59 @@ +/* -*- 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 "wincommon.hxx" +#include "manager.hxx" +#include + +using namespace ::com::sun::star; + + +// - factory methods - + + +static uno::Reference< uno::XInterface > create_MediaPlayer( const uno::Reference< lang::XMultiServiceFactory >& rxFact ) +{ + return uno::Reference< uno::XInterface >( *new ::avmedia::win::Manager( rxFact ) ); +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* avmediawin_component_getFactory( const char* pImplName, void* pServiceManager, void* ) +{ + uno::Reference< lang::XSingleServiceFactory > xFactory; + void* pRet = nullptr; + + if( rtl_str_compare( pImplName, "com.sun.star.comp.avmedia.Manager_DirectX" ) == 0 ) + { + const OUString aServiceName( "com.sun.star.media.Manager_DirectX" ); + + xFactory.set( ::cppu::createSingleFactory( + static_cast< lang::XMultiServiceFactory* >( pServiceManager ), + "com.sun.star.comp.avmedia.Manager_DirectX", + create_MediaPlayer, uno::Sequence< OUString >( &aServiceName, 1 ) ) ); + } + + if( xFactory.is() ) + { + xFactory->acquire(); + pRet = xFactory.get(); + } + + return pRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/util/avmedia.component b/avmedia/util/avmedia.component new file mode 100644 index 000000000..c10946051 --- /dev/null +++ b/avmedia/util/avmedia.component @@ -0,0 +1,26 @@ + + + + + + + + -- cgit v1.2.3