1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column:100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <svx/svdomedia.hxx>
24 #include <com/sun/star/text/GraphicCrop.hpp>
26 #include <rtl/ustring.hxx>
27 #include <sal/log.hxx>
29 #include <ucbhelper/content.hxx>
30 #include <comphelper/processfactory.hxx>
31 #include <comphelper/storagehelper.hxx>
32 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
33 #include <comphelper/lok.hxx>
34 #include <sfx2/lokhelper.hxx>
35 #include <boost/property_tree/json_parser.hpp>
37 #include <vcl/svapp.hxx>
39 #include <svx/svdmodel.hxx>
40 #include <svx/dialmgr.hxx>
41 #include <svx/strings.hrc>
42 #include <svx/sdr/contact/viewcontactofsdrmediaobj.hxx>
43 #include <avmedia/mediawindow.hxx>
44 #include <comphelper/diagnose_ex.hxx>
46 using namespace ::com::sun::star
;
49 struct SdrMediaObj::Impl
51 ::avmedia::MediaItem m_MediaProperties
;
52 #if HAVE_FEATURE_AVMEDIA
53 // Note: the temp file is read only, until it is deleted!
54 // It may be shared between multiple documents in case of copy/paste,
55 // hence the shared_ptr.
56 std::shared_ptr
< ::avmedia::MediaTempFile
> m_pTempFile
;
58 uno::Reference
< graphic::XGraphic
> m_xCachedSnapshot
;
59 rtl::Reference
<avmedia::PlayerListener
> m_xPlayerListener
;
60 OUString m_LastFailedPkgURL
;
63 SdrMediaObj::SdrMediaObj(SdrModel
& rSdrModel
)
64 : SdrRectObj(rSdrModel
)
69 SdrMediaObj::SdrMediaObj(SdrModel
& rSdrModel
, SdrMediaObj
const & rSource
)
70 : SdrRectObj(rSdrModel
, rSource
)
73 #if HAVE_FEATURE_AVMEDIA
74 m_xImpl
->m_pTempFile
= rSource
.m_xImpl
->m_pTempFile
; // before props
76 setMediaProperties( rSource
.getMediaProperties() );
77 m_xImpl
->m_xCachedSnapshot
= rSource
.m_xImpl
->m_xCachedSnapshot
;
80 SdrMediaObj::SdrMediaObj(
82 const tools::Rectangle
& rRect
)
83 : SdrRectObj(rSdrModel
, rRect
)
86 osl_atomic_increment(&m_refCount
);
88 const bool bUndo(rSdrModel
.IsUndoEnabled());
89 rSdrModel
.EnableUndo(false);
91 rSdrModel
.EnableUndo(bUndo
);
93 osl_atomic_decrement(&m_refCount
);
96 SdrMediaObj::~SdrMediaObj()
100 bool SdrMediaObj::HasTextEdit() const
105 std::unique_ptr
<sdr::contact::ViewContact
> SdrMediaObj::CreateObjectSpecificViewContact()
107 return std::make_unique
<sdr::contact::ViewContactOfSdrMediaObj
>( *this );
110 void SdrMediaObj::TakeObjInfo( SdrObjTransformInfoRec
& rInfo
) const
112 rInfo
.bMoveAllowed
= true;
113 rInfo
.bResizeFreeAllowed
= true;
114 rInfo
.bResizePropAllowed
= true;
115 rInfo
.bRotateFreeAllowed
= false;
116 rInfo
.bRotate90Allowed
= false;
117 rInfo
.bMirrorFreeAllowed
= false;
118 rInfo
.bMirror45Allowed
= false;
119 rInfo
.bMirror90Allowed
= false;
120 rInfo
.bTransparenceAllowed
= false;
121 rInfo
.bShearAllowed
= false;
122 rInfo
.bEdgeRadiusAllowed
= false;
123 rInfo
.bNoOrthoDesired
= false;
124 rInfo
.bNoContortion
= false;
125 rInfo
.bCanConvToPath
= false;
126 rInfo
.bCanConvToPoly
= false;
127 rInfo
.bCanConvToContour
= false;
128 rInfo
.bCanConvToPathLineToArea
= false;
129 rInfo
.bCanConvToPolyLineToArea
= false;
132 SdrObjKind
SdrMediaObj::GetObjIdentifier() const
134 return SdrObjKind::Media
;
137 OUString
SdrMediaObj::TakeObjNameSingul() const
139 OUString
sName(SvxResId(STR_ObjNameSingulMEDIA
));
141 OUString
aName(GetName());
143 if (!aName
.isEmpty())
144 sName
+= " '" + aName
+ "'";
149 OUString
SdrMediaObj::TakeObjNamePlural() const
151 return SvxResId(STR_ObjNamePluralMEDIA
);
154 rtl::Reference
<SdrObject
> SdrMediaObj::CloneSdrObject(SdrModel
& rTargetModel
) const
156 return new SdrMediaObj(rTargetModel
, *this);
159 uno::Reference
< graphic::XGraphic
> const & SdrMediaObj::getSnapshot() const
161 #if HAVE_FEATURE_AVMEDIA
162 if( !m_xImpl
->m_xCachedSnapshot
.is() )
164 Graphic aGraphic
= m_xImpl
->m_MediaProperties
.getGraphic();
165 if (!aGraphic
.IsNone())
167 Size aPref
= aGraphic
.GetPrefSize();
168 Size aPixel
= aGraphic
.GetSizePixel();
169 const text::GraphicCrop
& rCrop
= m_xImpl
->m_MediaProperties
.getCrop();
170 if (rCrop
.Bottom
> 0 || rCrop
.Left
> 0 || rCrop
.Right
> 0 || rCrop
.Top
> 0)
172 tools::Long nLeft
= aPixel
.getWidth() * rCrop
.Left
/ aPref
.getWidth();
173 tools::Long nTop
= aPixel
.getHeight() * rCrop
.Top
/ aPref
.getHeight();
174 tools::Long nRight
= aPixel
.getWidth() * rCrop
.Right
/ aPref
.getWidth();
175 tools::Long nBottom
= aPixel
.getHeight() * rCrop
.Bottom
/ aPref
.getHeight();
176 BitmapEx aBitmapEx
= aGraphic
.GetBitmapEx();
177 aBitmapEx
.Crop({nLeft
, nTop
, aPixel
.getWidth() - nRight
, aPixel
.getHeight() - nBottom
});
178 aGraphic
= aBitmapEx
;
181 // We have an explicit graphic for this media object, then go with that instead of
182 // generating our own one.
183 m_xImpl
->m_xCachedSnapshot
= aGraphic
.GetXGraphic();
184 return m_xImpl
->m_xCachedSnapshot
;
187 OUString aRealURL
= m_xImpl
->m_MediaProperties
.getTempURL();
188 if( aRealURL
.isEmpty() )
189 aRealURL
= m_xImpl
->m_MediaProperties
.getURL();
190 OUString sReferer
= m_xImpl
->m_MediaProperties
.getReferer();
191 OUString sMimeType
= m_xImpl
->m_MediaProperties
.getMimeType();
192 uno::Reference
<graphic::XGraphic
> xCachedSnapshot
= m_xImpl
->m_xCachedSnapshot
;
194 m_xImpl
->m_xPlayerListener
.set(new avmedia::PlayerListener(
195 [this, xCachedSnapshot
, aRealURL
, sReferer
, sMimeType
](const css::uno::Reference
<css::media::XPlayer
>& rPlayer
){
197 uno::Reference
<graphic::XGraphic
> xGraphic
198 = m_xImpl
->m_MediaProperties
.getGraphic().GetXGraphic();
199 m_xImpl
->m_xCachedSnapshot
= avmedia::MediaWindow::grabFrame(rPlayer
, xGraphic
);
203 avmedia::MediaWindow::grabFrame(aRealURL
, sReferer
, sMimeType
, m_xImpl
->m_xPlayerListener
);
206 return m_xImpl
->m_xCachedSnapshot
;
209 void SdrMediaObj::AdjustToMaxRect( const tools::Rectangle
& rMaxRect
, bool bShrinkOnly
/* = false */ )
211 Size
aSize( Application::GetDefaultDevice()->PixelToLogic(
212 static_cast< sdr::contact::ViewContactOfSdrMediaObj
& >( GetViewContact() ).getPreferredSize(),
213 MapMode(MapUnit::Map100thMM
)) );
214 Size
aMaxSize( rMaxRect
.GetSize() );
216 if( aSize
.IsEmpty() )
219 Point
aPos( rMaxRect
.TopLeft() );
221 // if graphic is too large, fit it to the page
222 if ( (!bShrinkOnly
||
223 ( aSize
.Height() > aMaxSize
.Height() ) ||
224 ( aSize
.Width() > aMaxSize
.Width() ) )&&
225 aSize
.Height() && aMaxSize
.Height() )
227 float fGrfWH
= static_cast<float>(aSize
.Width()) /
228 static_cast<float>(aSize
.Height());
229 float fWinWH
= static_cast<float>(aMaxSize
.Width()) /
230 static_cast<float>(aMaxSize
.Height());
232 // scale graphic to page size
233 if ( fGrfWH
< fWinWH
)
235 aSize
.setWidth( static_cast<tools::Long
>(aMaxSize
.Height() * fGrfWH
) );
236 aSize
.setHeight( aMaxSize
.Height() );
238 else if ( fGrfWH
> 0.F
)
240 aSize
.setWidth( aMaxSize
.Width() );
241 aSize
.setHeight( static_cast<tools::Long
>(aMaxSize
.Width() / fGrfWH
) );
244 aPos
= rMaxRect
.Center();
248 aPos
= getRectangle().TopLeft();
250 aPos
.AdjustX( -(aSize
.Width() / 2) );
251 aPos
.AdjustY( -(aSize
.Height() / 2) );
252 SetLogicRect( tools::Rectangle( aPos
, aSize
) );
255 void SdrMediaObj::setURL(const OUString
& rURL
, const OUString
& rReferer
)
257 ::avmedia::MediaItem aURLItem
;
258 #if HAVE_FEATURE_AVMEDIA
259 aURLItem
.setURL( rURL
, "", rReferer
);
264 setMediaProperties( aURLItem
);
267 const OUString
& SdrMediaObj::getURL() const
269 #if HAVE_FEATURE_AVMEDIA
270 return m_xImpl
->m_MediaProperties
.getURL();
277 const OUString
& SdrMediaObj::getTempURL() const
279 #if HAVE_FEATURE_AVMEDIA
280 return m_xImpl
->m_MediaProperties
.getTempURL();
287 void SdrMediaObj::setMediaProperties( const ::avmedia::MediaItem
& rState
)
289 mediaPropertiesChanged( rState
);
290 static_cast< sdr::contact::ViewContactOfSdrMediaObj
& >( GetViewContact() ).executeMediaItem( getMediaProperties() );
293 const ::avmedia::MediaItem
& SdrMediaObj::getMediaProperties() const
295 return m_xImpl
->m_MediaProperties
;
298 uno::Reference
<io::XInputStream
> SdrMediaObj::GetInputStream() const
300 #if HAVE_FEATURE_AVMEDIA
301 if (!m_xImpl
->m_pTempFile
)
303 SAL_WARN("svx", "this is only intended for embedded media");
306 ucbhelper::Content
tempFile(m_xImpl
->m_pTempFile
->m_TempFileURL
,
307 uno::Reference
<ucb::XCommandEnvironment
>(),
308 comphelper::getProcessComponentContext());
309 return tempFile
.openStream();
315 void SdrMediaObj::SetInputStream(uno::Reference
<io::XInputStream
> const& xStream
)
317 #if !HAVE_FEATURE_AVMEDIA
320 if (m_xImpl
->m_pTempFile
|| m_xImpl
->m_LastFailedPkgURL
.isEmpty())
322 SAL_WARN("svx", "this is only intended for embedded media");
326 OUString tempFileURL
;
328 ::avmedia::CreateMediaTempFile(
335 m_xImpl
->m_pTempFile
= std::make_shared
<::avmedia::MediaTempFile
>(tempFileURL
);
336 m_xImpl
->m_MediaProperties
.setURL(
337 m_xImpl
->m_LastFailedPkgURL
, tempFileURL
, "");
339 m_xImpl
->m_LastFailedPkgURL
.clear(); // once only
343 /// copy a stream from XStorage to temp file
344 #if HAVE_FEATURE_AVMEDIA
345 static bool lcl_HandlePackageURL(
346 OUString
const & rURL
,
347 const SdrModel
& rModel
,
348 OUString
& o_rTempFileURL
)
350 ::comphelper::LifecycleProxy sourceProxy
;
351 uno::Reference
<io::XInputStream
> xInStream
;
353 xInStream
= rModel
.GetDocumentStream(rURL
, sourceProxy
);
355 catch (container::NoSuchElementException
const&)
357 SAL_INFO("svx", "not found: '" << rURL
<< "'");
360 catch (uno::Exception
const&)
362 TOOLS_WARN_EXCEPTION("svx", "");
367 SAL_WARN("svx", "no stream?");
370 // Make sure the temporary copy has the same file name extension as the original media file
371 // (like .mp4). That seems to be important for some AVFoundation APIs. For random extension-less
372 // file names, they don't seem to even bother looking inside the file.
373 sal_Int32 nLastDot
= rURL
.lastIndexOf('.');
374 sal_Int32 nLastSlash
= rURL
.lastIndexOf('/');
375 OUString sDesiredExtension
;
376 if (nLastDot
> nLastSlash
&& nLastDot
+1 < rURL
.getLength())
377 sDesiredExtension
= rURL
.copy(nLastDot
);
378 return ::avmedia::CreateMediaTempFile(xInStream
, o_rTempFileURL
, sDesiredExtension
);
382 void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem
& rNewProperties
)
384 bool bBroadcastChanged
= false;
385 #if HAVE_FEATURE_AVMEDIA
386 const AVMediaSetMask nMaskSet
= rNewProperties
.getMaskSet();
388 // use only a subset of MediaItem properties for own properties
389 if( AVMediaSetMask::MIME_TYPE
& nMaskSet
)
390 m_xImpl
->m_MediaProperties
.setMimeType( rNewProperties
.getMimeType() );
392 if (nMaskSet
& AVMediaSetMask::GRAPHIC
)
394 m_xImpl
->m_MediaProperties
.setGraphic(rNewProperties
.getGraphic());
397 if (nMaskSet
& AVMediaSetMask::CROP
)
399 m_xImpl
->m_MediaProperties
.setCrop(rNewProperties
.getCrop());
402 if( ( AVMediaSetMask::URL
& nMaskSet
) &&
403 ( rNewProperties
.getURL() != getURL() ))
405 m_xImpl
->m_xCachedSnapshot
.clear();
406 m_xImpl
->m_xPlayerListener
.clear();
407 OUString
const& url(rNewProperties
.getURL());
408 if (url
.startsWithIgnoreAsciiCase("vnd.sun.star.Package:"))
410 if ( !m_xImpl
->m_pTempFile
411 || (m_xImpl
->m_pTempFile
->m_TempFileURL
!=
412 rNewProperties
.getTempURL()))
414 OUString tempFileURL
;
416 lcl_HandlePackageURL(
418 getSdrModelFromSdrObject(),
423 m_xImpl
->m_pTempFile
=
424 std::make_shared
<::avmedia::MediaTempFile
>(tempFileURL
);
425 m_xImpl
->m_MediaProperties
.setURL(url
, tempFileURL
, "");
427 else // this case is for Clone via operator=
429 m_xImpl
->m_pTempFile
.reset();
430 m_xImpl
->m_MediaProperties
.setURL("", "", "");
431 // UGLY: oox import also gets here, because unlike ODF
432 // getDocumentStorage() is not the imported file...
433 m_xImpl
->m_LastFailedPkgURL
= url
;
438 m_xImpl
->m_MediaProperties
.setURL(url
,
439 rNewProperties
.getTempURL(), "");
444 m_xImpl
->m_pTempFile
.reset();
445 m_xImpl
->m_MediaProperties
.setURL(url
, "", rNewProperties
.getReferer());
447 bBroadcastChanged
= true;
450 if( AVMediaSetMask::LOOP
& nMaskSet
)
451 m_xImpl
->m_MediaProperties
.setLoop( rNewProperties
.isLoop() );
453 if( AVMediaSetMask::MUTE
& nMaskSet
)
454 m_xImpl
->m_MediaProperties
.setMute( rNewProperties
.isMute() );
456 if( AVMediaSetMask::VOLUMEDB
& nMaskSet
)
457 m_xImpl
->m_MediaProperties
.setVolumeDB( rNewProperties
.getVolumeDB() );
459 if( AVMediaSetMask::ZOOM
& nMaskSet
)
460 m_xImpl
->m_MediaProperties
.setZoom( rNewProperties
.getZoom() );
462 (void) rNewProperties
;
465 if( bBroadcastChanged
)
468 BroadcastObjectChange();
472 void SdrMediaObj::notifyPropertiesForLOKit()
474 #if HAVE_FEATURE_AVMEDIA
475 if (!m_xImpl
->m_MediaProperties
.getTempURL().isEmpty())
477 const auto mediaId
= reinterpret_cast<std::size_t>(this);
479 boost::property_tree::ptree json
;
480 json
.put("action", "update");
481 json
.put("id", mediaId
);
482 json
.put("url", m_xImpl
->m_MediaProperties
.getTempURL());
484 const tools::Rectangle aRect
= o3tl::convert(getRectangle(), o3tl::Length::mm100
, o3tl::Length::twip
);
485 json
.put("x", aRect
.getX());
486 json
.put("y", aRect
.getY());
487 json
.put("w", aRect
.getOpenWidth());
488 json
.put("h", aRect
.getOpenHeight());
490 SfxLokHelper::notifyMediaUpdate(json
);
495 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */