diff options
Diffstat (limited to 'avmedia/source/framework')
-rw-r--r-- | avmedia/source/framework/MediaControlBase.cxx | 267 | ||||
-rw-r--r-- | avmedia/source/framework/mediacontrol.cxx | 225 | ||||
-rw-r--r-- | avmedia/source/framework/mediaitem.cxx | 568 | ||||
-rw-r--r-- | avmedia/source/framework/mediaplayer.cxx | 145 | ||||
-rw-r--r-- | avmedia/source/framework/mediatoolbox.cxx | 134 | ||||
-rw-r--r-- | avmedia/source/framework/soundhandler.cxx | 323 | ||||
-rw-r--r-- | avmedia/source/framework/soundhandler.hxx | 120 |
7 files changed, 1782 insertions, 0 deletions
diff --git a/avmedia/source/framework/MediaControlBase.cxx b/avmedia/source/framework/MediaControlBase.cxx new file mode 100644 index 000000000..534c27c2c --- /dev/null +++ b/avmedia/source/framework/MediaControlBase.cxx @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include <avmedia/MediaControlBase.hxx> +#include <avmedia/mediaplayer.hxx> +#include <avmedia/mediaitem.hxx> +#include <tools/time.hxx> +#include <unotools/localedatawrapper.hxx> +#include <strings.hrc> +#include <helpids.h> +#include <mediamisc.hxx> + +constexpr sal_Int32 AVMEDIA_DB_RANGE = -40; +constexpr double AVMEDIA_LINEINCREMENT = 1.0; +constexpr double AVMEDIA_PAGEINCREMENT = 10.0; + +namespace avmedia { + +MediaControlBase::MediaControlBase() + : mbCurrentlySettingZoom(false) +{ +} + +void MediaControlBase::UpdateTimeField( MediaItem const & aMediaItem, double fTime ) +{ + if( aMediaItem.getURL().isEmpty()) + return; + + OUString aTimeString; + + SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocaleData = aSysLocale.GetLocaleData(); + + aTimeString += rLocaleData.getDuration( tools::Time( 0, 0, static_cast< sal_uInt32 >( floor( fTime ) ) ) ) + + " / " + + rLocaleData.getDuration( tools::Time( 0, 0, static_cast< sal_uInt32 >( floor( aMediaItem.getDuration() ) )) ); + + if( mxTimeEdit->get_text() != aTimeString ) + mxTimeEdit->set_text( aTimeString ); +} + +void MediaControlBase::UpdateVolumeSlider( MediaItem const & aMediaItem ) +{ + if( aMediaItem.getURL().isEmpty() ) + mxVolumeSlider->set_sensitive(false); + else + { + mxVolumeSlider->set_sensitive(true); + const sal_Int32 nVolumeDB = aMediaItem.getVolumeDB(); + mxVolumeSlider->set_value( std::clamp( nVolumeDB, AVMEDIA_DB_RANGE, sal_Int32(0)) ); + } +} + +void MediaControlBase::UpdateTimeSlider( MediaItem const & aMediaItem ) +{ + if( aMediaItem.getURL().isEmpty() ) + mxTimeSlider->set_sensitive(false); + else + { + mxTimeSlider->set_sensitive(true); + + const double fDuration = aMediaItem.getDuration(); + + if( fDuration > 0.0 ) + { + const double fTime = std::min( aMediaItem.getTime(), fDuration ); + + bool bChanged(false); + int nStep(0), nPage(0); + mxTimeSlider->get_increments(nStep, nPage); + if (!nStep) + { + nStep = AVMEDIA_TIME_RANGE * AVMEDIA_LINEINCREMENT / fDuration; + bChanged = true; + } + if (!nPage) + { + nPage = AVMEDIA_TIME_RANGE * AVMEDIA_PAGEINCREMENT / fDuration; + bChanged = true; + } + if (bChanged) + mxTimeSlider->set_increments(nStep, nPage); + + mxTimeSlider->set_value(fTime / fDuration * AVMEDIA_TIME_RANGE); + } + } +} + +void MediaControlBase::InitializeWidgets() +{ + mxPlayToolBox->set_item_help_id("play", HID_AVMEDIA_TOOLBOXITEM_PLAY); + mxPlayToolBox->set_item_help_id("pause", HID_AVMEDIA_TOOLBOXITEM_PAUSE); + mxPlayToolBox->set_item_help_id("stop", HID_AVMEDIA_TOOLBOXITEM_STOP); + mxPlayToolBox->set_item_help_id("loop", HID_AVMEDIA_TOOLBOXITEM_LOOP); + mxMuteToolBox->set_item_help_id("mute", HID_AVMEDIA_TOOLBOXITEM_MUTE); + + mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_50), AvmResId( AVMEDIA_STR_ZOOM_50 )); + mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_100), AvmResId( AVMEDIA_STR_ZOOM_100 )); + mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_200), AvmResId( AVMEDIA_STR_ZOOM_200 )); + mxZoomListBox->append(OUString::number(AVMEDIA_ZOOMLEVEL_FIT), AvmResId( AVMEDIA_STR_ZOOM_FIT )); + mxZoomListBox->set_help_id( HID_AVMEDIA_ZOOMLISTBOX ); + mxZoomListBox->set_tooltip_text(AvmResId( AVMEDIA_STR_ZOOM_TOOLTIP )); + + mxTimeEdit->set_text( " 00:00:00/00:00:00 " ); + mxTimeEdit->set_help_id( HID_AVMEDIA_TIMEEDIT ); + mxTimeEdit->set_sensitive(false); + + mxVolumeSlider->set_range(AVMEDIA_DB_RANGE, 0); + mxVolumeSlider->set_tooltip_text( AvmResId( AVMEDIA_STR_VOLUME )); + mxVolumeSlider->set_help_id( HID_AVMEDIA_VOLUMESLIDER ); + + mxTimeSlider->set_range( 0, AVMEDIA_TIME_RANGE ); + mxTimeSlider->set_tooltip_text( AvmResId( AVMEDIA_STR_POSITION )); +} + +void MediaControlBase::UpdatePlayState(const MediaItem& rMediaItem) +{ + if (rMediaItem.getState() == MediaState::Play) + { + mxPlayToolBox->set_item_active("play", true); + mxPlayToolBox->set_item_active("pause", false); + mxPlayToolBox->set_item_active("stop", false); + } + else if( rMediaItem.getState() == MediaState::Pause ) + { + mxPlayToolBox->set_item_active("play", false); + mxPlayToolBox->set_item_active("pause", true); + mxPlayToolBox->set_item_active("stop", false); + } + else + { + mxPlayToolBox->set_item_active("play", false); + mxPlayToolBox->set_item_active("pause", false); + mxPlayToolBox->set_item_active("stop", true); + } +} + +void MediaControlBase::UpdateToolBoxes(const MediaItem& rMediaItem) +{ + const bool bValidURL = !rMediaItem.getURL().isEmpty(); + mxPlayToolBox->set_item_sensitive("play", bValidURL); + mxPlayToolBox->set_item_sensitive("pause", bValidURL); + mxPlayToolBox->set_item_sensitive("stop", bValidURL); + mxPlayToolBox->set_item_sensitive("loop", bValidURL); + mxMuteToolBox->set_item_sensitive("mute", bValidURL); + if( !bValidURL ) + { + mxZoomListBox->set_sensitive(false); + mxMuteToolBox->set_sensitive(false); + } + else + { + mxPlayToolBox->set_sensitive(true); + mxMuteToolBox->set_sensitive(true); + UpdatePlayState(rMediaItem); + mxPlayToolBox->set_item_active("loop", rMediaItem.isLoop()); + mxMuteToolBox->set_item_active("mute", rMediaItem.isMute()); + if (!mbCurrentlySettingZoom) + { + sal_uInt16 nSelectEntryPos ; + + switch( rMediaItem.getZoom() ) + { + case css::media::ZoomLevel_ZOOM_1_TO_2: + nSelectEntryPos = AVMEDIA_ZOOMLEVEL_50; + break; + case css::media::ZoomLevel_ORIGINAL: + nSelectEntryPos = AVMEDIA_ZOOMLEVEL_100; + break; + case css::media::ZoomLevel_ZOOM_2_TO_1: + nSelectEntryPos = AVMEDIA_ZOOMLEVEL_200; + break; + case css::media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT: + nSelectEntryPos = AVMEDIA_ZOOMLEVEL_FIT; + break; + case css::media::ZoomLevel_FIT_TO_WINDOW: + nSelectEntryPos = AVMEDIA_ZOOMLEVEL_SCALED; + break; + + default: + nSelectEntryPos = AVMEDIA_ZOOMLEVEL_INVALID; + break; + } + + if( nSelectEntryPos != AVMEDIA_ZOOMLEVEL_INVALID ) + { + mxZoomListBox->show(); + mxZoomListBox->set_sensitive(true); + mxZoomListBox->set_active(nSelectEntryPos); + } + else + mxZoomListBox->set_sensitive(false); + } + } +} + +void MediaControlBase::SelectPlayToolBoxItem( MediaItem& aExecItem, MediaItem const & aItem, std::string_view rId) +{ + if (rId == "apply") + { + MediaFloater* pFloater = avmedia::getMediaFloater(); + + if( pFloater ) + pFloater->dispatchCurrentURL(); + } + else if (rId == "play") + { + aExecItem.setState( MediaState::Play ); + + if( aItem.getTime() == aItem.getDuration() ) + aExecItem.setTime( 0.0 ); + else + aExecItem.setTime( aItem.getTime() ); + + UpdatePlayState(aExecItem); + } + else if (rId == "pause") + { + aExecItem.setState( MediaState::Pause ); + + UpdatePlayState(aExecItem); + } + else if (rId == "stop") + { + aExecItem.setState( MediaState::Stop ); + aExecItem.setTime( 0.0 ); + + UpdatePlayState(aExecItem); + } + else if (rId == "mute") + { + aExecItem.setMute( mxMuteToolBox->get_item_active("mute") ); + } + else if (rId == "loop") + { + aExecItem.setLoop( mxPlayToolBox->get_item_active("loop") ); + } +} + +void MediaControlBase::disposeWidgets() +{ + mxZoomListBox.reset(); + mxTimeEdit.reset(); + mxVolumeSlider.reset(); + mxMuteToolBox.reset(); + mxTimeSlider.reset(); + mxPlayToolBox.reset(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/framework/mediacontrol.cxx b/avmedia/source/framework/mediacontrol.cxx new file mode 100644 index 000000000..23d700595 --- /dev/null +++ b/avmedia/source/framework/mediacontrol.cxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <mediacontrol.hxx> +#include <strings.hrc> +#include <mediamisc.hxx> +#include <avmedia/mediawindow.hxx> +#include <helpids.h> +#include <vcl/weld.hxx> +#include <avmedia/MediaControlBase.hxx> + +namespace avmedia +{ + +MediaControl::MediaControl( vcl::Window* pParent, MediaControlStyle eControlStyle ) : + // MediaControlStyle::MultiLine is the normal docking windows of tools->media player + // MediaControlStyle::SingleLine is the toolbar of view->toolbar->media playback + InterimItemWindow(pParent, eControlStyle == MediaControlStyle::MultiLine ? + OUString("svx/ui/mediawindow.ui") : + OUString("svx/ui/medialine.ui"), + "MediaWindow"), + maIdle( "avmedia MediaControl Idle" ), + maChangeTimeIdle( "avmedia MediaControl Change Time Idle" ), + maItem( 0, AVMediaSetMask::ALL ), + mbLocked( false ), + meControlStyle( eControlStyle ) +{ + mxPlayToolBox = m_xBuilder->weld_toolbar("playtoolbox"); + mxTimeSlider = m_xBuilder->weld_scale("timeslider"); + mxMuteToolBox = m_xBuilder->weld_toolbar("mutetoolbox"); + mxVolumeSlider = m_xBuilder->weld_scale("volumeslider"); + mxZoomListBox = m_xBuilder->weld_combo_box("zoombox"); + mxTimeEdit = m_xBuilder->weld_entry("timeedit"); + mxMediaPath = m_xBuilder->weld_label("url"); + + InitializeWidgets(); + + mxPlayToolBox->connect_clicked( LINK( this, MediaControl, implSelectHdl ) ); + + mxTimeSlider->connect_value_changed( LINK( this, MediaControl, implTimeHdl ) ); + // when changing the time, use this to do the time change after active scrolling + // has stopped for a little which + maChangeTimeIdle.SetPriority( TaskPriority::LOWEST ); + maChangeTimeIdle.SetInvokeHandler( LINK( this, MediaControl, implTimeEndHdl ) ); + + mxTimeEdit->set_text(" 00:00:00/00:00:00 "); + Size aTextSize = mxTimeEdit->get_preferred_size(); + mxTimeEdit->set_size_request(aTextSize.Width(), aTextSize.Height()); + mxTimeEdit->set_text(OUString()); + + mxMuteToolBox->connect_clicked( LINK( this, MediaControl, implSelectHdl ) ); + mxVolumeSlider->connect_value_changed( LINK( this, MediaControl, implVolumeHdl ) ); + + mxZoomListBox->connect_changed( LINK( this, MediaControl, implZoomSelectHdl ) ); + mxZoomListBox->set_help_id(HID_AVMEDIA_ZOOMLISTBOX); + + const OUString aMediaPath( AvmResId( AVMEDIA_MEDIA_PATH_DEFAULT ) ); + mxMediaPath->set_label(aMediaPath); + if (meControlStyle == MediaControlStyle::SingleLine) + mxMediaPath->set_size_request(mxMediaPath->get_preferred_size().Width() + 400, -1); // maybe extend the no. 400 to span the screen width + + // we want time field + progress slider to update as the media plays + // give this task a lower prio than REPAINT so that UI updates are not starved + maIdle.SetPriority( TaskPriority::POST_PAINT ); + maIdle.SetInvokeHandler( LINK( this, MediaControl, implTimeoutHdl ) ); +} + +void MediaControl::InitializeWidgets() +{ + if( meControlStyle != MediaControlStyle::SingleLine ) + { + mxPlayToolBox->set_item_help_id("open", HID_AVMEDIA_TOOLBOXITEM_OPEN); + mxPlayToolBox->set_item_help_id("apply", HID_AVMEDIA_TOOLBOXITEM_INSERT); + } + avmedia::MediaControlBase::InitializeWidgets(); +} + +MediaControl::~MediaControl() +{ + disposeOnce(); +} + +void MediaControl::dispose() +{ + disposeWidgets(); + mxMediaPath.reset(); + InterimItemWindow::dispose(); +} + +void MediaControl::UpdateURLField(MediaItem const & tempItem) +{ + const OUString aURL( AvmResId(AVMEDIA_MEDIA_PATH) + ": " + tempItem.getURL() ) ; + mxMediaPath->set_label(aURL); +} + +void MediaControl::setState( const MediaItem& rItem ) +{ + if (mbLocked) + return; + bool bChanged = maItem.merge(rItem); + if (bChanged) + { + if( rItem.getURL().isEmpty() && meControlStyle == MediaControlStyle::SingleLine ) + mxPlayToolBox->set_sensitive(false); + UpdateToolBoxes( maItem ); + UpdateTimeSlider( maItem ); + UpdateVolumeSlider( maItem ); + UpdateTimeField( maItem, maItem.getTime() ); + UpdateURLField(maItem); + } +} + +IMPL_LINK( MediaControl, implTimeHdl, weld::Scale&, rSlider, void ) +{ + mbLocked = true; + maIdle.Stop(); + UpdateTimeField(maItem, rSlider.get_value() * maItem.getDuration() / AVMEDIA_TIME_RANGE); + maChangeTimeIdle.Start(); +} + +IMPL_LINK_NOARG(MediaControl, implTimeEndHdl, Timer*, void) +{ + MediaItem aExecItem; + + aExecItem.setTime( mxTimeSlider->get_value() * maItem.getDuration() / AVMEDIA_TIME_RANGE ); + // keep state (if the media was playing, keep it playing) + aExecItem.setState(maItem.getState()); + execute( aExecItem ); + update(); + maIdle.Start(); + mbLocked = false; +} + +IMPL_LINK( MediaControl, implVolumeHdl, weld::Scale&, rSlider, void ) +{ + MediaItem aExecItem; + + aExecItem.setVolumeDB(rSlider.get_value()); + execute( aExecItem ); + update(); +} + +IMPL_LINK( MediaControl, implSelectHdl, const OString&, rIdent, void ) +{ + MediaItem aExecItem; + if (rIdent == "open") + { + OUString aURL; + if (MediaWindow::executeMediaURLDialog(GetFrameWeld(), aURL, nullptr)) + { + if( !MediaWindow::isMediaURL( aURL, ""/*TODO?*/, true ) ) + MediaWindow::executeFormatErrorBox(GetFrameWeld()); + else + { + aExecItem.setURL( aURL, "", ""/*TODO?*/ ); + aExecItem.setState( MediaState::Play ); + } + } + } + else + SelectPlayToolBoxItem( aExecItem, maItem, rIdent ); + + if (aExecItem.getState() == MediaState::Play) + maIdle.Start(); + else if (aExecItem.getState() == MediaState::Pause || + aExecItem.getState() == MediaState::Stop) + maIdle.Stop(); + + if( aExecItem.getMaskSet() != AVMediaSetMask::NONE ) + execute( aExecItem ); + + update(); +} + +IMPL_LINK( MediaControl, implZoomSelectHdl, weld::ComboBox&, rBox, void ) +{ + bool bCurrentlySettingZoom = mbCurrentlySettingZoom; + mbCurrentlySettingZoom = true; + + MediaItem aExecItem; + css::media::ZoomLevel eLevel; + + switch (rBox.get_active()) + { + case AVMEDIA_ZOOMLEVEL_50: eLevel = css::media::ZoomLevel_ZOOM_1_TO_2; break; + case AVMEDIA_ZOOMLEVEL_100: eLevel = css::media::ZoomLevel_ORIGINAL; break; + case AVMEDIA_ZOOMLEVEL_200: eLevel = css::media::ZoomLevel_ZOOM_2_TO_1; break; + case AVMEDIA_ZOOMLEVEL_FIT: eLevel = css::media::ZoomLevel_FIT_TO_WINDOW_FIXED_ASPECT; break; + case AVMEDIA_ZOOMLEVEL_SCALED: eLevel = css::media::ZoomLevel_FIT_TO_WINDOW; break; + + default: eLevel = css::media::ZoomLevel_NOT_AVAILABLE; break; + } + + aExecItem.setZoom( eLevel ); + execute( aExecItem ); + update(); + + mbCurrentlySettingZoom = bCurrentlySettingZoom; +} + +IMPL_LINK_NOARG(MediaControl, implTimeoutHdl, Timer *, void) +{ + update(); + maIdle.Start(); +} + +} // namespace avmedia + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/framework/mediaitem.cxx b/avmedia/source/framework/mediaitem.cxx new file mode 100644 index 000000000..ac8c09b79 --- /dev/null +++ b/avmedia/source/framework/mediaitem.cxx @@ -0,0 +1,568 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <avmedia/mediaitem.hxx> + +#include <com/sun/star/uno/Sequence.hxx> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/XTransactedObject.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/document/XStorageBasedDocument.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> +#include <com/sun/star/text/GraphicCrop.hpp> + +#include <sal/log.hxx> + +#include <ucbhelper/content.hxx> + +#include <comphelper/processfactory.hxx> +#include <comphelper/storagehelper.hxx> +#include <mediamisc.hxx> +#include <osl/file.hxx> +#include <tools/diagnose_ex.h> +#include <vcl/graph.hxx> + +using namespace ::com::sun::star; + +namespace avmedia +{ + +SfxPoolItem* MediaItem::CreateDefault() { return new MediaItem; } + +struct MediaItem::Impl +{ + OUString m_URL; + OUString m_TempFileURL; + OUString m_Referer; + OUString m_sMimeType; + AVMediaSetMask m_nMaskSet; + MediaState m_eState; + double m_fTime; + double m_fDuration; + sal_Int16 m_nVolumeDB; + bool m_bLoop; + bool m_bMute; + css::media::ZoomLevel m_eZoom; + Graphic m_aGraphic; + text::GraphicCrop m_aCrop; + + explicit Impl(AVMediaSetMask nMaskSet) + : m_nMaskSet( nMaskSet ) + , m_eState( MediaState::Stop ) + , m_fTime( 0.0 ) + , m_fDuration( 0.0 ) + , m_nVolumeDB( 0 ) + , m_bLoop( false ) + , m_bMute( false ) + , m_eZoom( css::media::ZoomLevel_NOT_AVAILABLE ) + { + } +}; + + +MediaItem::MediaItem( sal_uInt16 i_nWhich, AVMediaSetMask nMaskSet ) + : SfxPoolItem( i_nWhich ) + , m_pImpl( new Impl(nMaskSet) ) +{ +} + + +MediaItem::MediaItem( const MediaItem& rItem ) + : SfxPoolItem( rItem ) + , m_pImpl( new Impl(*rItem.m_pImpl) ) +{ +} + + +MediaItem::~MediaItem() +{ +} + + +bool MediaItem::operator==( const SfxPoolItem& rItem ) const +{ + assert( SfxPoolItem::operator==(rItem)); + MediaItem const& rOther(static_cast< const MediaItem& >(rItem)); + return m_pImpl->m_nMaskSet == rOther.m_pImpl->m_nMaskSet + && m_pImpl->m_URL == rOther.m_pImpl->m_URL + && m_pImpl->m_Referer == rOther.m_pImpl->m_Referer + && m_pImpl->m_sMimeType == rOther.m_pImpl->m_sMimeType + && m_pImpl->m_aGraphic == rOther.m_pImpl->m_aGraphic + && m_pImpl->m_aCrop == rOther.m_pImpl->m_aCrop + && m_pImpl->m_eState == rOther.m_pImpl->m_eState + && m_pImpl->m_fDuration == rOther.m_pImpl->m_fDuration + && m_pImpl->m_fTime == rOther.m_pImpl->m_fTime + && m_pImpl->m_nVolumeDB == rOther.m_pImpl->m_nVolumeDB + && m_pImpl->m_bLoop == rOther.m_pImpl->m_bLoop + && m_pImpl->m_bMute == rOther.m_pImpl->m_bMute + && m_pImpl->m_eZoom == rOther.m_pImpl->m_eZoom; +} + +MediaItem* MediaItem::Clone( SfxItemPool* ) const +{ + return new MediaItem( *this ); +} + +bool MediaItem::GetPresentation( SfxItemPresentation, + MapUnit, + MapUnit, + OUString& rText, + const IntlWrapper& ) const +{ + rText.clear(); + return false; +} + +bool MediaItem::QueryValue( css::uno::Any& rVal, sal_uInt8 ) const +{ + uno::Sequence< uno::Any > aSeq{ uno::Any(m_pImpl->m_URL), + uno::Any(static_cast<sal_uInt32>(m_pImpl->m_nMaskSet)), + uno::Any(static_cast< sal_Int32 >( m_pImpl->m_eState )), + uno::Any(m_pImpl->m_fTime), + uno::Any(m_pImpl->m_fDuration), + uno::Any(m_pImpl->m_nVolumeDB), + uno::Any(m_pImpl->m_bLoop), + uno::Any(m_pImpl->m_bMute), + uno::Any(m_pImpl->m_eZoom), + uno::Any(m_pImpl->m_sMimeType) }; + + rVal <<= aSeq; + + return true; +} + + +bool MediaItem::PutValue( const css::uno::Any& rVal, sal_uInt8 ) +{ + uno::Sequence< uno::Any > aSeq; + bool bRet = false; + + if( ( rVal >>= aSeq ) && ( aSeq.getLength() == 10 ) ) + { + sal_Int32 nInt32 = 0; + + aSeq[ 0 ] >>= m_pImpl->m_URL; + aSeq[ 1 ] >>= nInt32; + m_pImpl->m_nMaskSet = static_cast<AVMediaSetMask>(nInt32); + aSeq[ 2 ] >>= nInt32; + m_pImpl->m_eState = static_cast< MediaState >( nInt32 ); + aSeq[ 3 ] >>= m_pImpl->m_fTime; + aSeq[ 4 ] >>= m_pImpl->m_fDuration; + aSeq[ 5 ] >>= m_pImpl->m_nVolumeDB; + aSeq[ 6 ] >>= m_pImpl->m_bLoop; + aSeq[ 7 ] >>= m_pImpl->m_bMute; + aSeq[ 8 ] >>= m_pImpl->m_eZoom; + aSeq[ 9 ] >>= m_pImpl->m_sMimeType; + + bRet = true; + } + + return bRet; +} + +bool MediaItem::merge(const MediaItem& rMediaItem) +{ + bool bChanged = false; + + const AVMediaSetMask nMaskSet = rMediaItem.getMaskSet(); + + if( AVMediaSetMask::URL & nMaskSet ) + bChanged |= setURL(rMediaItem.getURL(), rMediaItem.getTempURL(), rMediaItem.getReferer()); + + if( AVMediaSetMask::MIME_TYPE & nMaskSet ) + bChanged |= setMimeType(rMediaItem.getMimeType()); + + if (nMaskSet & AVMediaSetMask::GRAPHIC) + bChanged |= setGraphic(rMediaItem.getGraphic()); + + if (nMaskSet & AVMediaSetMask::CROP) + bChanged |= setCrop(rMediaItem.getCrop()); + + if( AVMediaSetMask::STATE & nMaskSet ) + bChanged |= setState( rMediaItem.getState() ); + + if( AVMediaSetMask::DURATION & nMaskSet ) + bChanged |= setDuration(rMediaItem.getDuration()); + + if( AVMediaSetMask::TIME & nMaskSet ) + bChanged |= setTime(rMediaItem.getTime()); + + if( AVMediaSetMask::LOOP & nMaskSet ) + bChanged |= setLoop(rMediaItem.isLoop()); + + if( AVMediaSetMask::MUTE & nMaskSet ) + bChanged |= setMute(rMediaItem.isMute()); + + if( AVMediaSetMask::VOLUMEDB & nMaskSet ) + bChanged |= setVolumeDB(rMediaItem.getVolumeDB()); + + if( AVMediaSetMask::ZOOM & nMaskSet ) + bChanged |= setZoom(rMediaItem.getZoom()); + + return bChanged; +} + +AVMediaSetMask MediaItem::getMaskSet() const +{ + return m_pImpl->m_nMaskSet; +} + +bool MediaItem::setURL(const OUString& rURL, const OUString& rTempURL, const OUString& rReferer) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::URL; + bool bChanged = rURL != m_pImpl->m_URL || rTempURL != m_pImpl->m_TempFileURL || rReferer != m_pImpl->m_Referer; + if (bChanged) + { + m_pImpl->m_URL = rURL; + m_pImpl->m_TempFileURL = rTempURL; + m_pImpl->m_Referer = rReferer; + } + return bChanged; +} + +const OUString& MediaItem::getURL() const +{ + return m_pImpl->m_URL; +} + +const OUString& MediaItem::getTempURL() const +{ + return m_pImpl->m_TempFileURL; +} + +const OUString& MediaItem::getReferer() const +{ + return m_pImpl->m_Referer; +} + +bool MediaItem::setMimeType(const OUString& rMimeType) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::MIME_TYPE; + bool bChanged = rMimeType != m_pImpl->m_sMimeType; + if (bChanged) + m_pImpl->m_sMimeType = rMimeType; + return bChanged; +} + +OUString MediaItem::getMimeType() const +{ + return !m_pImpl->m_sMimeType.isEmpty() ? m_pImpl->m_sMimeType : AVMEDIA_MIMETYPE_COMMON; +} + +bool MediaItem::setGraphic(const Graphic& rGraphic) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::GRAPHIC; + bool bChanged = rGraphic != m_pImpl->m_aGraphic; + if (bChanged) + m_pImpl->m_aGraphic = rGraphic; + return bChanged; +} + +const Graphic & MediaItem::getGraphic() const { return m_pImpl->m_aGraphic; } + +bool MediaItem::setCrop(const text::GraphicCrop& rCrop) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::CROP; + bool bChanged = rCrop != m_pImpl->m_aCrop; + if (bChanged) + m_pImpl->m_aCrop = rCrop; + return bChanged; +} + +const text::GraphicCrop& MediaItem::getCrop() const { return m_pImpl->m_aCrop; } + +bool MediaItem::setState(MediaState eState) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::STATE; + bool bChanged = eState != m_pImpl->m_eState; + if (bChanged) + m_pImpl->m_eState = eState; + return bChanged; +} + +MediaState MediaItem::getState() const +{ + return m_pImpl->m_eState; +} + +bool MediaItem::setDuration(double fDuration) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::DURATION; + bool bChanged = fDuration != m_pImpl->m_fDuration; + if (bChanged) + m_pImpl->m_fDuration = fDuration; + return bChanged; +} + +double MediaItem::getDuration() const +{ + return m_pImpl->m_fDuration; +} + +bool MediaItem::setTime(double fTime) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::TIME; + bool bChanged = fTime != m_pImpl->m_fTime; + if (bChanged) + m_pImpl->m_fTime = fTime; + return bChanged; +} + +double MediaItem::getTime() const +{ + return m_pImpl->m_fTime; +} + +bool MediaItem::setLoop(bool bLoop) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::LOOP; + bool bChanged = bLoop != m_pImpl->m_bLoop; + if (bChanged) + m_pImpl->m_bLoop = bLoop; + return bChanged; +} + +bool MediaItem::isLoop() const +{ + return m_pImpl->m_bLoop; +} + +bool MediaItem::setMute(bool bMute) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::MUTE; + bool bChanged = bMute != m_pImpl->m_bMute; + if (bChanged) + m_pImpl->m_bMute = bMute; + return bChanged; +} + +bool MediaItem::isMute() const +{ + return m_pImpl->m_bMute; +} + +bool MediaItem::setVolumeDB(sal_Int16 nDB) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::VOLUMEDB; + bool bChanged = nDB != m_pImpl->m_nVolumeDB; + if (bChanged) + m_pImpl->m_nVolumeDB = nDB; + return bChanged; +} + +sal_Int16 MediaItem::getVolumeDB() const +{ + return m_pImpl->m_nVolumeDB; +} + +bool MediaItem::setZoom(css::media::ZoomLevel eZoom) +{ + m_pImpl->m_nMaskSet |= AVMediaSetMask::ZOOM; + bool bChanged = eZoom != m_pImpl->m_eZoom; + if (bChanged) + m_pImpl->m_eZoom = eZoom; + return bChanged; +} + +css::media::ZoomLevel MediaItem::getZoom() const +{ + return m_pImpl->m_eZoom; +} + +OUString GetFilename(OUString const& rSourceURL) +{ + uno::Reference<uri::XUriReferenceFactory> const xUriFactory( + uri::UriReferenceFactory::create( + comphelper::getProcessComponentContext())); + uno::Reference<uri::XUriReference> const xSourceURI( + xUriFactory->parse(rSourceURL), uno::UNO_SET_THROW); + + OUString filename; + { + sal_Int32 const nSegments(xSourceURI->getPathSegmentCount()); + if (0 < nSegments) + { + filename = xSourceURI->getPathSegment(nSegments - 1); + } + } + if (!::comphelper::OStorageHelper::IsValidZipEntryFileName( + filename, false) || !filename.getLength()) + { + filename = "media"; + } + return filename; +} + + +uno::Reference<io::XStream> +CreateStream(uno::Reference<embed::XStorage> const& xStorage, + OUString const& rFilename) +{ + OUString filename(rFilename); + + if (xStorage->hasByName(filename)) + { + OUString basename; + OUString suffix; + sal_Int32 const nIndex(rFilename.lastIndexOf('.')); + if (0 < nIndex) + { + basename = rFilename.copy(0, nIndex); + suffix = rFilename.copy(nIndex); + } + sal_Int32 count(0); // sigh... try to generate non-existent name + do + { + ++count; + filename = basename + OUString::number(count) + suffix; + } + while (xStorage->hasByName(filename)); + } + + uno::Reference<io::XStream> const xStream( + xStorage->openStreamElement(filename, + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE), + uno::UNO_SET_THROW); + uno::Reference< beans::XPropertySet > const xStreamProps(xStream, + uno::UNO_QUERY); + if (xStreamProps.is()) { // this is NOT supported in FileSystemStorage + xStreamProps->setPropertyValue("MediaType", uno::Any(OUString( + //FIXME how to detect real media type? + //but currently xmloff has this one hardcoded anyway... + "application/vnd.sun.star.media"))); + xStreamProps->setPropertyValue( // turn off compression + "Compressed", uno::Any(false)); + } + return xStream; +} + + +bool EmbedMedia(uno::Reference<frame::XModel> const& xModel, + OUString const& rSourceURL, OUString & o_rEmbeddedURL, uno::Reference<io::XInputStream> const& xInputStream) +{ + try + { + uno::Reference<document::XStorageBasedDocument> const xSBD(xModel, + uno::UNO_QUERY_THROW); + uno::Reference<embed::XStorage> const xStorage( + xSBD->getDocumentStorage(), uno::UNO_SET_THROW); + + OUString const media("Media"); + uno::Reference<embed::XStorage> const xSubStorage( + xStorage->openStorageElement(media, embed::ElementModes::WRITE)); + + OUString filename(GetFilename(rSourceURL)); + + uno::Reference<io::XStream> const xStream( + CreateStream(xSubStorage, filename), uno::UNO_SET_THROW); + uno::Reference<io::XOutputStream> const xOutStream( + xStream->getOutputStream(), uno::UNO_SET_THROW); + + if (xInputStream.is()) + { + // Throw Exception if failed. + ::comphelper::OStorageHelper::CopyInputToOutput(xInputStream, xOutStream); + } + else + { + ::ucbhelper::Content sourceContent(rSourceURL, + uno::Reference<ucb::XCommandEnvironment>(), + comphelper::getProcessComponentContext()); + + if (!sourceContent.openStream(xOutStream)) // copy file to storage + { + SAL_INFO("avmedia", "openStream to storage failed"); + return false; + } + } + + uno::Reference<embed::XTransactedObject> const xSubTransaction( + xSubStorage, uno::UNO_QUERY); + if (xSubTransaction.is()) { + xSubTransaction->commit(); + } + uno::Reference<embed::XTransactedObject> const xTransaction( + xStorage, uno::UNO_QUERY); + if (xTransaction.is()) { + xTransaction->commit(); + } + + o_rEmbeddedURL = "vnd.sun.star.Package:" + media + "/" + filename; + return true; + } + catch (uno::Exception const&) + { + SAL_WARN("avmedia", + "Exception while trying to embed media"); + } + return false; +} + +bool CreateMediaTempFile(uno::Reference<io::XInputStream> const& xInStream, + OUString& o_rTempFileURL, std::u16string_view rDesiredExtension) +{ + OUString tempFileURL; + ::osl::FileBase::RC const err = + ::osl::FileBase::createTempFile(nullptr, nullptr, & tempFileURL); + if (::osl::FileBase::E_None != err) + { + SAL_WARN("avmedia", "cannot create temp file"); + return false; + } + + if (!rDesiredExtension.empty()) + { + OUString newTempFileURL = tempFileURL + rDesiredExtension; + if (osl::File::move(tempFileURL, newTempFileURL) != osl::FileBase::E_None) + { + SAL_WARN("avmedia", "Could not rename file '" << tempFileURL << "' to '" << newTempFileURL << "'"); + return false; + } + tempFileURL = newTempFileURL; + } + + try + { + ::ucbhelper::Content tempContent(tempFileURL, + uno::Reference<ucb::XCommandEnvironment>(), + comphelper::getProcessComponentContext()); + tempContent.writeStream(xInStream, true); // copy stream to file + } + catch (uno::Exception const&) + { + TOOLS_WARN_EXCEPTION("avmedia", ""); + return false; + } + o_rTempFileURL = tempFileURL; + return true; +} + +MediaTempFile::~MediaTempFile() +{ + ::osl::File::remove(m_TempFileURL); +} + +} // namespace avmedia + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/framework/mediaplayer.cxx b/avmedia/source/framework/mediaplayer.cxx new file mode 100644 index 000000000..258f23ff4 --- /dev/null +++ b/avmedia/source/framework/mediaplayer.cxx @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <avmedia/mediaplayer.hxx> +#include <avmedia/mediawindow.hxx> +#include <avmedia/mediaitem.hxx> +#include <mediamisc.hxx> +#include <strings.hrc> +#include <helpids.h> + +#include <svl/stritem.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> + +namespace avmedia +{ + +MediaPlayer::MediaPlayer( vcl::Window* _pParent, sal_uInt16 nId, SfxBindings* _pBindings, SfxChildWinInfo* pInfo ) : + SfxChildWindow( _pParent, nId ) +{ + SetWindow( VclPtr<MediaFloater>::Create( _pBindings, this, _pParent ) ); + static_cast< MediaFloater* >( GetWindow() )->Initialize( pInfo ); +}; + + +MediaPlayer::~MediaPlayer() +{ +} + + +SFX_IMPL_DOCKINGWINDOW_WITHID( MediaPlayer, SID_AVMEDIA_PLAYER ) + + +MediaFloater::MediaFloater( SfxBindings* _pBindings, SfxChildWindow* pCW, vcl::Window* pParent ) : + SfxDockingWindow( _pBindings, pCW, pParent, WB_CLOSEABLE | WB_MOVEABLE | WB_SIZEABLE | WB_DOCKABLE ), + mpMediaWindow( new MediaWindow( this, true ) ) +{ + const Size aSize( mpMediaWindow->getPreferredSize() ); + + SetPosSizePixel( Point( 0, 0 ), aSize ); + SetMinOutputSizePixel( aSize ); + SetText( AvmResId( AVMEDIA_STR_MEDIAPLAYER ) ); + mpMediaWindow->show(); +} + + +MediaFloater::~MediaFloater() +{ + disposeOnce(); +} + +void MediaFloater::dispose() +{ + if (IsFloatingMode()) + { + Show(false, ShowFlags::NoFocusChange); + SetFloatingMode(false); + } + mpMediaWindow.reset(); + SfxDockingWindow::dispose(); +} + +void MediaFloater::Resize() +{ + SfxDockingWindow::Resize(); + + if( mpMediaWindow ) + mpMediaWindow->setPosSize( tools::Rectangle( Point(), GetOutputSizePixel() ) ); +} + +void MediaFloater::ToggleFloatingMode() +{ + ::avmedia::MediaItem aRestoreItem; + + if (mpMediaWindow) + mpMediaWindow->updateMediaItem( aRestoreItem ); + mpMediaWindow.reset(); + + SfxDockingWindow::ToggleFloatingMode(); + + if (isDisposed()) + return; + + mpMediaWindow.reset( new MediaWindow( this, true ) ); + + mpMediaWindow->setPosSize( tools::Rectangle( Point(), GetOutputSizePixel() ) ); + mpMediaWindow->executeMediaItem( aRestoreItem ); + + vcl::Window* pWindow = mpMediaWindow->getWindow(); + + if( pWindow ) + pWindow->SetHelpId( HID_AVMEDIA_PLAYERWINDOW ); + + mpMediaWindow->show(); +} + + +void MediaFloater::setURL( const OUString& rURL, const OUString& rReferer, bool bPlayImmediately ) +{ + if( mpMediaWindow ) + { + mpMediaWindow->setURL( rURL, rReferer ); + + if( mpMediaWindow->isValid() && bPlayImmediately ) + mpMediaWindow->start(); + } +} + + +void MediaFloater::dispatchCurrentURL() +{ + SfxDispatcher* pDispatcher = GetBindings().GetDispatcher(); + + if( pDispatcher ) + { + OUString url; + if (mpMediaWindow != nullptr) { + url = mpMediaWindow->getURL(); + } + const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, url ); + pDispatcher->ExecuteList(SID_INSERT_AVMEDIA, SfxCallMode::RECORD, + { &aMediaURLItem }); + } +} + +} // namespace avmedia + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/framework/mediatoolbox.cxx b/avmedia/source/framework/mediatoolbox.cxx new file mode 100644 index 000000000..55a19ac66 --- /dev/null +++ b/avmedia/source/framework/mediatoolbox.cxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <avmedia/mediatoolbox.hxx> +#include <avmedia/mediaitem.hxx> +#include <mediacontrol.hxx> + +#include <tools/debug.hxx> +#include <sfx2/sfxsids.hrc> +#include <vcl/toolbox.hxx> + +#include <comphelper/propertysequence.hxx> + +using namespace ::com::sun::star; + +namespace avmedia +{ + +class MediaToolBoxControl_Impl : public MediaControl +{ +public: + + MediaToolBoxControl_Impl( vcl::Window& rParent, MediaToolBoxControl& rControl ); + + void update() override; + void execute( const MediaItem& rItem ) override; + +private: + + MediaToolBoxControl* mpToolBoxControl; +}; + +MediaToolBoxControl_Impl::MediaToolBoxControl_Impl( vcl::Window& rParent, MediaToolBoxControl& rControl ) : + MediaControl( &rParent, MediaControlStyle::SingleLine ), + mpToolBoxControl( &rControl ) +{ + SetSizePixel(m_xContainer->get_preferred_size()); +} + +void MediaToolBoxControl_Impl::update() +{ + mpToolBoxControl->implUpdateMediaControl(); +} + + +void MediaToolBoxControl_Impl::execute( const MediaItem& rItem ) +{ + mpToolBoxControl->implExecuteMediaControl( rItem ); +} + + +SFX_IMPL_TOOLBOX_CONTROL( MediaToolBoxControl, ::avmedia::MediaItem ); + + +MediaToolBoxControl::MediaToolBoxControl( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ + rTbx.Invalidate(); +} + + +MediaToolBoxControl::~MediaToolBoxControl() +{ +} + + +void MediaToolBoxControl::StateChangedAtToolBoxControl( sal_uInt16, SfxItemState eState, const SfxPoolItem* pState ) +{ + MediaToolBoxControl_Impl* pCtrl = static_cast< MediaToolBoxControl_Impl* >( GetToolBox().GetItemWindow( GetId() ) ); + + DBG_ASSERT( pCtrl, "MediaToolBoxControl::StateChanged: media control not found" ); + + if( eState == SfxItemState::DISABLED ) + { + pCtrl->Enable( false, false ); + pCtrl->SetText( OUString() ); + + const MediaItem aEmptyMediaItem( 0, AVMediaSetMask::ALL ); + pCtrl->setState( aEmptyMediaItem ); + } + else + { + pCtrl->Enable( true, false ); + + const MediaItem* pMediaItem = dynamic_cast<const MediaItem*>( pState ); + + if( pMediaItem && ( eState == SfxItemState::DEFAULT ) ) + pCtrl->setState( *pMediaItem ); + } +} + +VclPtr<InterimItemWindow> MediaToolBoxControl::CreateItemWindow( vcl::Window *pParent ) +{ + return ( pParent ? VclPtr<MediaToolBoxControl_Impl>::Create( *pParent, *this ) : nullptr ); +} + +void MediaToolBoxControl::implUpdateMediaControl() +{ + updateStatus( ".uno:AVMediaToolBox" ); +} + +void MediaToolBoxControl::implExecuteMediaControl( const MediaItem& rItem ) +{ + MediaItem aExecItem( SID_AVMEDIA_TOOLBOX ); + uno::Any aAny; + + aExecItem.merge( rItem ); + aExecItem.QueryValue( aAny ); + auto aArgs(::comphelper::InitPropertySequence({ + { "AVMediaToolBox", aAny } + })); + + Dispatch( ".uno:AVMediaToolBox" , aArgs ); +} + +} // namespace avmedia + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/framework/soundhandler.cxx b/avmedia/source/framework/soundhandler.cxx new file mode 100644 index 000000000..2392c3dd1 --- /dev/null +++ b/avmedia/source/framework/soundhandler.cxx @@ -0,0 +1,323 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "soundhandler.hxx" + +#include <unotools/mediadescriptor.hxx> + +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> + +#include <avmedia/mediawindow.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/supportsservice.hxx> + +namespace avmedia{ + + +// XInterface, XTypeProvider, XServiceInfo + + +void SAL_CALL SoundHandler::acquire() noexcept +{ + /* Don't use mutex in methods of XInterface! */ + OWeakObject::acquire(); +} + +void SAL_CALL SoundHandler::release() noexcept +{ + /* Don't use mutex in methods of XInterface! */ + OWeakObject::release(); +} + +css::uno::Any SAL_CALL SoundHandler::queryInterface( const css::uno::Type& aType ) +{ + /* Attention: Don't use mutex or guard in this method!!! Is a method of XInterface. */ + /* Ask for my own supported interfaces ...*/ + css::uno::Any aReturn( ::cppu::queryInterface( aType, + static_cast< css::lang::XTypeProvider* >(this), + static_cast< css::lang::XServiceInfo* >(this), + static_cast< css::frame::XNotifyingDispatch* >(this), + static_cast< css::frame::XDispatch* >(this), + static_cast< css::document::XExtendedFilterDetection* >(this))); + /* If searched interface not supported by this class ... */ + if ( !aReturn.hasValue() ) + { + /* ... ask baseclass for interfaces! */ + aReturn = OWeakObject::queryInterface( aType ); + } + /* Return result of this search. */ + return aReturn; +} + +css::uno::Sequence< sal_Int8 > SAL_CALL SoundHandler::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +css::uno::Sequence< css::uno::Type > SAL_CALL SoundHandler::getTypes() +{ + static ::cppu::OTypeCollection aTypeCollection( + cppu::UnoType<css::lang::XTypeProvider>::get(), + cppu::UnoType<css::lang::XServiceInfo>::get(), + cppu::UnoType<css::frame::XNotifyingDispatch>::get(), + cppu::UnoType<css::frame::XDispatch>::get(), + cppu::UnoType<css::document::XExtendedFilterDetection>::get()); + + return aTypeCollection.getTypes(); +} + +constexpr OUStringLiteral IMPLEMENTATIONNAME_SOUNDHANDLER = u"com.sun.star.comp.framework.SoundHandler"; + +/*===========================================================================================================*/ +/* XServiceInfo */ +/*===========================================================================================================*/ +OUString SAL_CALL SoundHandler::getImplementationName() +{ + return IMPLEMENTATIONNAME_SOUNDHANDLER; +} + +// XServiceInfo +sal_Bool SAL_CALL SoundHandler::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +// XServiceInfo +css::uno::Sequence< OUString > SAL_CALL SoundHandler::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ContentHandler" }; +} + +/*-************************************************************************************************************ + @short standard ctor + @descr These initialize a new instance of this class with needed information for work. + + @seealso using at owner + + @param "xFactory", reference to service manager for creation of new services + @onerror Show an assertion and do nothing else. + @threadsafe yes +*//*-*************************************************************************************************************/ +SoundHandler::SoundHandler() + // Init member + : m_bError ( false ) + , m_aUpdateIdle ( "avmedia SoundHandler Update" ) +{ + m_aUpdateIdle.SetInvokeHandler(LINK(this, SoundHandler, implts_PlayerNotify)); +} + +/*-************************************************************************************************************ + @short standard dtor +*//*-*************************************************************************************************************/ +SoundHandler::~SoundHandler() +{ + if (m_xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + aEvent.State = css::frame::DispatchResultState::FAILURE; + m_xListener->dispatchFinished(aEvent); + m_xListener.clear(); + } +} + +/*-************************************************************************************************************ + @interface css::frame::XDispatch + + @short try to load audio file + @descr This method try to load given audio file by URL and play it. We use vcl/Sound class to do that. + Playing of sound is asynchronous every time. + + @attention We must hold us alive by ourself ... because we use async. vcl sound player ... but playing is started + in async interface call "dispatch()" too. And caller forget us immediately. But then our uno ref count + will decreased to 0 and will die. The only solution is to use own reference to our implementation. + But we do it for really started jobs only and release it during call back of vcl. + + @seealso class vcl/Sound + @seealso method implts_PlayerNotify() + + @param "aURL" , URL to dispatch. + @param "lArguments", list of optional arguments. + @onerror We do nothing. + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL SoundHandler::dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lDescriptor, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) +{ + // SAFE { + const ::osl::MutexGuard aLock( m_aMutex ); + + utl::MediaDescriptor aDescriptor(lDescriptor); + + { + //close streams otherwise on windows we can't reopen the file in the + //media player when we pass the url to directx as it'll already be open + css::uno::Reference< css::io::XInputStream > xInputStream = + aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_INPUTSTREAM, + css::uno::Reference< css::io::XInputStream >()); + if (xInputStream.is()) xInputStream->closeInput(); + } + + // If player currently used for other dispatch() requests ... + // cancel it by calling stop()! + m_aUpdateIdle.Stop(); + if (m_xPlayer.is()) + { + if (m_xPlayer->isPlaying()) + m_xPlayer->stop(); + m_xPlayer.clear(); + } + + // Try to initialize player. + m_xListener = xListener; + try + { + m_bError = false; + m_xPlayer.set( avmedia::MediaWindow::createPlayer( aURL.Complete, aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REFERRER, OUString()) ), css::uno::UNO_SET_THROW ); + // OK- we can start async playing ... + // Count this request and initialize self-holder against dying by uno ref count ... + m_xSelfHold.set(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + m_xPlayer->start(); + m_aUpdateIdle.SetPriority( TaskPriority::HIGH_IDLE ); + m_aUpdateIdle.Start(); + } + catch( css::uno::Exception& ) + { + m_bError = true; + m_xPlayer.clear(); + } + + // } SAFE +} + +void SAL_CALL SoundHandler::dispatch( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) +{ + dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >()); +} + +/*-************************************************************************************************************ + @interface css::document::XExtendedFilterDetection + + @short try to detect file (given as argument included in "lDescriptor") + @descr We try to detect, if given file could be handled by this class and is a well known one. + If it is - we return right internal type name - otherwise we return nothing! + So call can search for another detect service and ask him too. + + @attention a) We don't need any mutex here ... because we don't use any member! + b) Don't use internal player instance "m_pPlayer" to detect given sound file! + It's not necessary to do that ... and we can use temp. variable to do the same. + This way is easy - we don't must synchronize it with currently played sounds! + Another reason to do so ... We are a listener on our internal ma_Player object. + If you would call "IsSoundFile()" on this instance, he would call us back and + we make some unnecessary things ... + @param "lDescriptor", description of file to detect + @return Internal type name which match this file ... or nothing if it is unknown. + + @onerror We return nothing. + @threadsafe yes +*//*-*************************************************************************************************************/ +OUString SAL_CALL SoundHandler::detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) +{ + // Our default is "nothing". So we can return it, if detection failed or file type is really unknown. + OUString sTypeName; + + // Analyze given descriptor to find filename or input stream or ... + utl::MediaDescriptor aDescriptor(lDescriptor); + OUString sURL = aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_URL, OUString()); + OUString sReferer = aDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_REFERRER, OUString()); + + if ( + !sURL.isEmpty() && + (avmedia::MediaWindow::isMediaURL(sURL, sReferer)) + ) + { + // If the file type is supported depends on the OS, so... + // I think we can the following ones: + // a) look for given extension of url to map our type decision HARD CODED!!! + // b) return preferred type every time... it's easy :-) + sTypeName = "wav_Wave_Audio_File"; + aDescriptor[utl::MediaDescriptor::PROP_TYPENAME] <<= sTypeName; + aDescriptor >> lDescriptor; + } + + // Return our decision. + return sTypeName; +} + +/*-************************************************************************************************************ + @short call back of sound player + @descr Our player call us back to give us some information. + We use this information to callback our might existing listener. + + @seealso method dispatchWithNotification() + @return 0 every time... it doesn't matter for us. + @threadsafe yes +*//*-*************************************************************************************************************/ +IMPL_LINK_NOARG(SoundHandler, implts_PlayerNotify, Timer *, void) +{ + // SAFE { + ::osl::ClearableMutexGuard aLock( m_aMutex ); + + if (m_xPlayer.is() && m_xPlayer->isPlaying() && m_xPlayer->getMediaTime() < m_xPlayer->getDuration()) + { + m_aUpdateIdle.Start(); + return; + } + m_xPlayer.clear(); + + // We use m_xSelfHold to let us die ... but we must live till real finishing of this method too!!! + // So we SHOULD use another "self-holder" temp. to provide that ... + css::uno::Reference< css::uno::XInterface > xOperationHold = m_xSelfHold; + m_xSelfHold.clear(); + + // notify might existing listener + // And forget this listener! + // Because the corresponding dispatch was finished. + if (m_xListener.is()) + { + css::frame::DispatchResultEvent aEvent; + if (!m_bError) + aEvent.State = css::frame::DispatchResultState::SUCCESS; + else + aEvent.State = css::frame::DispatchResultState::FAILURE; + m_xListener->dispatchFinished(aEvent); + m_xListener.clear(); + } + + // } SAFE + //release aLock before end of method at which point xOperationHold goes out of scope and pThis dies + aLock.clear(); +} + +} // namespace framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_framework_SoundHandler_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new avmedia::SoundHandler); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/avmedia/source/framework/soundhandler.hxx b/avmedia/source/framework/soundhandler.hxx new file mode 100644 index 000000000..648cbe59d --- /dev/null +++ b/avmedia/source/framework/soundhandler.hxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include <com/sun/star/lang/XTypeProvider.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/frame/XStatusListener.hpp> +#include <com/sun/star/document/XExtendedFilterDetection.hpp> +#include <com/sun/star/media/XPlayer.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/util/URL.hpp> + +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/weak.hxx> + +#include <vcl/timer.hxx> +#include <vcl/idle.hxx> +#include <tools/link.hxx> + +namespace avmedia{ + +/*-************************************************************************************************************ + @short handler to detect and play sounds ("wav" and "au" only!) + @descr Register this implementation as a content handler to detect and/or play wav- and au-sounds. + It doesn't depend from the target platform. But one instance of this class + can play one sound at the same time only. Means every new dispatch request will stop the + might still running one. So we support one operation/one URL/one listener at the same time + only. + + @devstatus ready + @threadsafe yes +*//*-*************************************************************************************************************/ +class SoundHandler : // interfaces + public css::lang::XTypeProvider + , public css::lang::XServiceInfo + , public css::frame::XNotifyingDispatch // => XDispatch + , public css::document::XExtendedFilterDetection + // baseclasses + // Order is necessary for right initialization! + , private cppu::BaseMutex + , public ::cppu::OWeakObject +{ + // public methods + public: + + // constructor / destructor + SoundHandler(); + virtual ~SoundHandler( ) override; + + // XInterface, XTypeProvider, XServiceInfo + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes () override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + /* interface XServiceInfo */ + virtual OUString SAL_CALL getImplementationName ( ) override; + virtual sal_Bool SAL_CALL supportsService ( const OUString& sServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames ( ) override; + + // XNotifyingDispatch + virtual void SAL_CALL dispatchWithNotification(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments, + const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) override; + + // XDispatch + virtual void SAL_CALL dispatch ( const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) override; + // not supported ! + virtual void SAL_CALL addStatusListener ( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) override {}; + virtual void SAL_CALL removeStatusListener ( const css::uno::Reference< css::frame::XStatusListener >& /*xListener*/ , + const css::util::URL& /*aURL*/ ) override {}; + + // XExtendedFilterDetection + virtual OUString SAL_CALL detect ( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override; + + // protected methods + protected: + + // private methods + private: + DECL_LINK( implts_PlayerNotify, Timer*, void ); + + // variables + // (should be private everyway!) + private: + + bool m_bError; + css::uno::Reference< css::uno::XInterface > m_xSelfHold ; // we must protect us against dying during async(!) dispatch() call! + css::uno::Reference< css::media::XPlayer > m_xPlayer ; // uses avmedia player to play sounds... + + css::uno::Reference< css::frame::XDispatchResultListener > m_xListener ; + Idle m_aUpdateIdle; + +}; // class SoundHandler + +} // namespace avmedia + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |