1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <avmedia/mediaitem.hxx>
22 #include <com/sun/star/uno/Sequence.hxx>
24 #include <com/sun/star/beans/XPropertySet.hpp>
25 #include <com/sun/star/embed/ElementModes.hpp>
26 #include <com/sun/star/embed/XTransactedObject.hpp>
27 #include <com/sun/star/frame/XModel.hpp>
28 #include <com/sun/star/document/XStorageBasedDocument.hpp>
29 #include <com/sun/star/ucb/XCommandEnvironment.hpp>
30 #include <com/sun/star/uri/UriReferenceFactory.hpp>
31 #include <com/sun/star/uri/XUriReference.hpp>
32 #include <com/sun/star/uri/XUriReferenceFactory.hpp>
33 #include <com/sun/star/text/GraphicCrop.hpp>
35 #include <sal/log.hxx>
37 #include <ucbhelper/content.hxx>
39 #include <comphelper/mediamimetype.hxx>
40 #include <comphelper/processfactory.hxx>
41 #include <comphelper/storagehelper.hxx>
42 #include <mediamisc.hxx>
43 #include <osl/file.hxx>
44 #include <comphelper/diagnose_ex.hxx>
45 #include <vcl/graph.hxx>
47 using namespace ::com::sun::star
;
52 SfxPoolItem
* MediaItem::CreateDefault() { return new MediaItem
; }
54 struct MediaItem::Impl
57 OUString m_TempFileURL
;
58 OUString m_FallbackURL
;
61 AVMediaSetMask m_nMaskSet
;
65 sal_Int16 m_nVolumeDB
;
68 css::media::ZoomLevel m_eZoom
;
70 text::GraphicCrop m_aCrop
;
72 explicit Impl(AVMediaSetMask nMaskSet
)
73 : m_nMaskSet( nMaskSet
)
74 , m_eState( MediaState::Stop
)
80 , m_eZoom( css::media::ZoomLevel_NOT_AVAILABLE
)
86 MediaItem::MediaItem( sal_uInt16 i_nWhich
, AVMediaSetMask nMaskSet
)
87 : SfxPoolItem( i_nWhich
, SfxItemType::MediaItemType
)
88 , m_pImpl( new Impl(nMaskSet
) )
93 MediaItem::MediaItem( const MediaItem
& rItem
)
94 : SfxPoolItem( rItem
)
95 , m_pImpl( new Impl(*rItem
.m_pImpl
) )
100 MediaItem::~MediaItem()
105 bool MediaItem::operator==( const SfxPoolItem
& rItem
) const
107 assert( SfxPoolItem::operator==(rItem
));
108 MediaItem
const& rOther(static_cast< const MediaItem
& >(rItem
));
109 return m_pImpl
->m_nMaskSet
== rOther
.m_pImpl
->m_nMaskSet
110 && m_pImpl
->m_URL
== rOther
.m_pImpl
->m_URL
111 && m_pImpl
->m_FallbackURL
== rOther
.m_pImpl
->m_FallbackURL
112 && m_pImpl
->m_Referer
== rOther
.m_pImpl
->m_Referer
113 && m_pImpl
->m_sMimeType
== rOther
.m_pImpl
->m_sMimeType
114 && m_pImpl
->m_aGraphic
== rOther
.m_pImpl
->m_aGraphic
115 && m_pImpl
->m_aCrop
== rOther
.m_pImpl
->m_aCrop
116 && m_pImpl
->m_eState
== rOther
.m_pImpl
->m_eState
117 && m_pImpl
->m_fDuration
== rOther
.m_pImpl
->m_fDuration
118 && m_pImpl
->m_fTime
== rOther
.m_pImpl
->m_fTime
119 && m_pImpl
->m_nVolumeDB
== rOther
.m_pImpl
->m_nVolumeDB
120 && m_pImpl
->m_bLoop
== rOther
.m_pImpl
->m_bLoop
121 && m_pImpl
->m_bMute
== rOther
.m_pImpl
->m_bMute
122 && m_pImpl
->m_eZoom
== rOther
.m_pImpl
->m_eZoom
;
125 MediaItem
* MediaItem::Clone( SfxItemPool
* ) const
127 return new MediaItem( *this );
130 bool MediaItem::GetPresentation( SfxItemPresentation
,
134 const IntlWrapper
& ) const
140 bool MediaItem::QueryValue( css::uno::Any
& rVal
, sal_uInt8
) const
142 uno::Sequence
< uno::Any
> aSeq
{ uno::Any(m_pImpl
->m_URL
),
143 uno::Any(static_cast<sal_uInt32
>(m_pImpl
->m_nMaskSet
)),
144 uno::Any(static_cast< sal_Int32
>( m_pImpl
->m_eState
)),
145 uno::Any(m_pImpl
->m_fTime
),
146 uno::Any(m_pImpl
->m_fDuration
),
147 uno::Any(m_pImpl
->m_nVolumeDB
),
148 uno::Any(m_pImpl
->m_bLoop
),
149 uno::Any(m_pImpl
->m_bMute
),
150 uno::Any(m_pImpl
->m_eZoom
),
151 uno::Any(m_pImpl
->m_sMimeType
) };
159 bool MediaItem::PutValue( const css::uno::Any
& rVal
, sal_uInt8
)
161 uno::Sequence
< uno::Any
> aSeq
;
164 if( ( rVal
>>= aSeq
) && ( aSeq
.getLength() == 10 ) )
166 sal_Int32 nInt32
= 0;
168 aSeq
[ 0 ] >>= m_pImpl
->m_URL
;
169 aSeq
[ 1 ] >>= nInt32
;
170 m_pImpl
->m_nMaskSet
= static_cast<AVMediaSetMask
>(nInt32
);
171 aSeq
[ 2 ] >>= nInt32
;
172 m_pImpl
->m_eState
= static_cast< MediaState
>( nInt32
);
173 aSeq
[ 3 ] >>= m_pImpl
->m_fTime
;
174 aSeq
[ 4 ] >>= m_pImpl
->m_fDuration
;
175 aSeq
[ 5 ] >>= m_pImpl
->m_nVolumeDB
;
176 aSeq
[ 6 ] >>= m_pImpl
->m_bLoop
;
177 aSeq
[ 7 ] >>= m_pImpl
->m_bMute
;
178 aSeq
[ 8 ] >>= m_pImpl
->m_eZoom
;
179 aSeq
[ 9 ] >>= m_pImpl
->m_sMimeType
;
187 bool MediaItem::merge(const MediaItem
& rMediaItem
)
189 bool bChanged
= false;
191 const AVMediaSetMask nMaskSet
= rMediaItem
.getMaskSet();
193 if( AVMediaSetMask::URL
& nMaskSet
)
195 bChanged
= m_pImpl
->m_FallbackURL
== rMediaItem
.getFallbackURL();
196 m_pImpl
->m_FallbackURL
= rMediaItem
.getFallbackURL();
197 bChanged
|= setURL(rMediaItem
.getURL(), rMediaItem
.getTempURL(), rMediaItem
.getReferer());
200 if( AVMediaSetMask::MIME_TYPE
& nMaskSet
)
201 bChanged
|= setMimeType(rMediaItem
.getMimeType());
203 if (nMaskSet
& AVMediaSetMask::GRAPHIC
)
204 bChanged
|= setGraphic(rMediaItem
.getGraphic());
206 if (nMaskSet
& AVMediaSetMask::CROP
)
207 bChanged
|= setCrop(rMediaItem
.getCrop());
209 if( AVMediaSetMask::STATE
& nMaskSet
)
210 bChanged
|= setState( rMediaItem
.getState() );
212 if( AVMediaSetMask::DURATION
& nMaskSet
)
213 bChanged
|= setDuration(rMediaItem
.getDuration());
215 if( AVMediaSetMask::TIME
& nMaskSet
)
216 bChanged
|= setTime(rMediaItem
.getTime());
218 if( AVMediaSetMask::LOOP
& nMaskSet
)
219 bChanged
|= setLoop(rMediaItem
.isLoop());
221 if( AVMediaSetMask::MUTE
& nMaskSet
)
222 bChanged
|= setMute(rMediaItem
.isMute());
224 if( AVMediaSetMask::VOLUMEDB
& nMaskSet
)
225 bChanged
|= setVolumeDB(rMediaItem
.getVolumeDB());
227 if( AVMediaSetMask::ZOOM
& nMaskSet
)
228 bChanged
|= setZoom(rMediaItem
.getZoom());
233 AVMediaSetMask
MediaItem::getMaskSet() const
235 return m_pImpl
->m_nMaskSet
;
238 bool MediaItem::setURL(const OUString
& rURL
, const OUString
& rTempURL
, const OUString
& rReferer
)
240 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::URL
;
241 bool bChanged
= rURL
!= m_pImpl
->m_URL
|| rTempURL
!= m_pImpl
->m_TempFileURL
|| rReferer
!= m_pImpl
->m_Referer
;
244 m_pImpl
->m_URL
= rURL
;
245 m_pImpl
->m_TempFileURL
= rTempURL
;
246 m_pImpl
->m_Referer
= rReferer
;
247 setMimeType(::comphelper::GuessMediaMimeType(GetFilename(rURL
)));
252 const OUString
& MediaItem::getURL() const
254 return m_pImpl
->m_URL
;
257 bool MediaItem::setFallbackURL(const OUString
& rURL
)
259 bool bChanged
= rURL
!= m_pImpl
->m_FallbackURL
;
261 m_pImpl
->m_FallbackURL
= rURL
;
264 const OUString
& MediaItem::getFallbackURL() const
266 return m_pImpl
->m_FallbackURL
;
269 const OUString
& MediaItem::getTempURL() const
271 return m_pImpl
->m_TempFileURL
;
274 const OUString
& MediaItem::getReferer() const
276 return m_pImpl
->m_Referer
;
279 bool MediaItem::setMimeType(const OUString
& rMimeType
)
281 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::MIME_TYPE
;
282 bool bChanged
= rMimeType
!= m_pImpl
->m_sMimeType
;
284 m_pImpl
->m_sMimeType
= rMimeType
;
288 const OUString
& MediaItem::getMimeType() const
290 return !m_pImpl
->m_sMimeType
.isEmpty() ? m_pImpl
->m_sMimeType
: AVMEDIA_MIMETYPE_COMMON
;
293 bool MediaItem::setGraphic(const Graphic
& rGraphic
)
295 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::GRAPHIC
;
296 bool bChanged
= rGraphic
!= m_pImpl
->m_aGraphic
;
298 m_pImpl
->m_aGraphic
= rGraphic
;
302 const Graphic
& MediaItem::getGraphic() const { return m_pImpl
->m_aGraphic
; }
304 bool MediaItem::setCrop(const text::GraphicCrop
& rCrop
)
306 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::CROP
;
307 bool bChanged
= rCrop
!= m_pImpl
->m_aCrop
;
309 m_pImpl
->m_aCrop
= rCrop
;
313 const text::GraphicCrop
& MediaItem::getCrop() const { return m_pImpl
->m_aCrop
; }
315 bool MediaItem::setState(MediaState eState
)
317 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::STATE
;
318 bool bChanged
= eState
!= m_pImpl
->m_eState
;
320 m_pImpl
->m_eState
= eState
;
324 MediaState
MediaItem::getState() const
326 return m_pImpl
->m_eState
;
329 bool MediaItem::setDuration(double fDuration
)
331 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::DURATION
;
332 bool bChanged
= fDuration
!= m_pImpl
->m_fDuration
;
334 m_pImpl
->m_fDuration
= fDuration
;
338 double MediaItem::getDuration() const
340 return m_pImpl
->m_fDuration
;
343 bool MediaItem::setTime(double fTime
)
345 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::TIME
;
346 bool bChanged
= fTime
!= m_pImpl
->m_fTime
;
348 m_pImpl
->m_fTime
= fTime
;
352 double MediaItem::getTime() const
354 return m_pImpl
->m_fTime
;
357 bool MediaItem::setLoop(bool bLoop
)
359 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::LOOP
;
360 bool bChanged
= bLoop
!= m_pImpl
->m_bLoop
;
362 m_pImpl
->m_bLoop
= bLoop
;
366 bool MediaItem::isLoop() const
368 return m_pImpl
->m_bLoop
;
371 bool MediaItem::setMute(bool bMute
)
373 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::MUTE
;
374 bool bChanged
= bMute
!= m_pImpl
->m_bMute
;
376 m_pImpl
->m_bMute
= bMute
;
380 bool MediaItem::isMute() const
382 return m_pImpl
->m_bMute
;
385 bool MediaItem::setVolumeDB(sal_Int16 nDB
)
387 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::VOLUMEDB
;
388 bool bChanged
= nDB
!= m_pImpl
->m_nVolumeDB
;
390 m_pImpl
->m_nVolumeDB
= nDB
;
394 sal_Int16
MediaItem::getVolumeDB() const
396 return m_pImpl
->m_nVolumeDB
;
399 bool MediaItem::setZoom(css::media::ZoomLevel eZoom
)
401 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::ZOOM
;
402 bool bChanged
= eZoom
!= m_pImpl
->m_eZoom
;
404 m_pImpl
->m_eZoom
= eZoom
;
408 css::media::ZoomLevel
MediaItem::getZoom() const
410 return m_pImpl
->m_eZoom
;
413 OUString
GetFilename(OUString
const& rSourceURL
)
415 uno::Reference
<uri::XUriReferenceFactory
> const xUriFactory(
416 uri::UriReferenceFactory::create(
417 comphelper::getProcessComponentContext()));
418 uno::Reference
<uri::XUriReference
> const xSourceURI(
419 xUriFactory
->parse(rSourceURL
), uno::UNO_SET_THROW
);
423 sal_Int32
const nSegments(xSourceURI
->getPathSegmentCount());
426 filename
= xSourceURI
->getPathSegment(nSegments
- 1);
429 if (!::comphelper::OStorageHelper::IsValidZipEntryFileName(
430 filename
, false) || !filename
.getLength())
438 uno::Reference
<io::XStream
>
439 CreateStream(uno::Reference
<embed::XStorage
> const& xStorage
,
440 OUString
const& rFilename
)
442 OUString
filename(rFilename
);
444 if (xStorage
->hasByName(filename
))
446 std::u16string_view basename
;
447 std::u16string_view suffix
;
448 sal_Int32
const nIndex(rFilename
.lastIndexOf('.'));
451 basename
= rFilename
.subView(0, nIndex
);
452 suffix
= rFilename
.subView(nIndex
);
454 sal_Int32
count(0); // sigh... try to generate non-existent name
458 filename
= basename
+ OUString::number(count
) + suffix
;
460 while (xStorage
->hasByName(filename
));
463 uno::Reference
<io::XStream
> const xStream(
464 xStorage
->openStreamElement(filename
,
465 embed::ElementModes::WRITE
| embed::ElementModes::TRUNCATE
),
467 uno::Reference
< beans::XPropertySet
> const xStreamProps(xStream
,
469 if (xStreamProps
.is()) { // this is NOT supported in FileSystemStorage
470 OUString
const guessed(::comphelper::GuessMediaMimeType(filename
));
471 xStreamProps
->setPropertyValue(u
"MediaType"_ustr
,
472 uno::Any(guessed
.isEmpty() ? AVMEDIA_MIMETYPE_COMMON
: guessed
));
473 xStreamProps
->setPropertyValue( // turn off compression
474 u
"Compressed"_ustr
, uno::Any(false));
480 bool EmbedMedia(uno::Reference
<frame::XModel
> const& xModel
,
481 OUString
const& rSourceURL
, OUString
& o_rEmbeddedURL
, uno::Reference
<io::XInputStream
> const& xInputStream
)
485 uno::Reference
<document::XStorageBasedDocument
> const xSBD(xModel
,
486 uno::UNO_QUERY_THROW
);
487 uno::Reference
<embed::XStorage
> const xStorage(
488 xSBD
->getDocumentStorage(), uno::UNO_SET_THROW
);
490 static constexpr OUString
media(u
"Media"_ustr
);
491 uno::Reference
<embed::XStorage
> const xSubStorage(
492 xStorage
->openStorageElement(media
, embed::ElementModes::WRITE
));
494 OUString
filename(GetFilename(rSourceURL
));
496 uno::Reference
<io::XStream
> const xStream(
497 CreateStream(xSubStorage
, filename
), uno::UNO_SET_THROW
);
498 uno::Reference
<io::XOutputStream
> const xOutStream(
499 xStream
->getOutputStream(), uno::UNO_SET_THROW
);
501 if (xInputStream
.is())
503 // Throw Exception if failed.
504 ::comphelper::OStorageHelper::CopyInputToOutput(xInputStream
, xOutStream
);
508 ::ucbhelper::Content
sourceContent(rSourceURL
,
509 uno::Reference
<ucb::XCommandEnvironment
>(),
510 comphelper::getProcessComponentContext());
512 if (!sourceContent
.openStream(xOutStream
)) // copy file to storage
514 SAL_INFO("avmedia", "openStream to storage failed");
519 uno::Reference
<embed::XTransactedObject
> const xSubTransaction(
520 xSubStorage
, uno::UNO_QUERY
);
521 if (xSubTransaction
.is()) {
522 xSubTransaction
->commit();
524 uno::Reference
<embed::XTransactedObject
> const xTransaction(
525 xStorage
, uno::UNO_QUERY
);
526 if (xTransaction
.is()) {
527 xTransaction
->commit();
530 o_rEmbeddedURL
= "vnd.sun.star.Package:" + media
+ "/" + filename
;
533 catch (uno::Exception
const&)
536 "Exception while trying to embed media");
541 bool CreateMediaTempFile(uno::Reference
<io::XInputStream
> const& xInStream
,
542 OUString
& o_rTempFileURL
, std::u16string_view rDesiredExtension
)
544 OUString tempFileURL
;
545 ::osl::FileBase::RC
const err
=
546 ::osl::FileBase::createTempFile(nullptr, nullptr, & tempFileURL
);
547 if (::osl::FileBase::E_None
!= err
)
549 SAL_WARN("avmedia", "cannot create temp file");
553 if (!rDesiredExtension
.empty())
555 OUString newTempFileURL
= tempFileURL
+ rDesiredExtension
;
556 if (osl::File::move(tempFileURL
, newTempFileURL
) != osl::FileBase::E_None
)
558 SAL_WARN("avmedia", "Could not rename file '" << tempFileURL
<< "' to '" << newTempFileURL
<< "'");
561 tempFileURL
= newTempFileURL
;
566 ::ucbhelper::Content
tempContent(tempFileURL
,
567 uno::Reference
<ucb::XCommandEnvironment
>(),
568 comphelper::getProcessComponentContext());
569 tempContent
.writeStream(xInStream
, true); // copy stream to file
571 catch (uno::Exception
const&)
573 TOOLS_WARN_EXCEPTION("avmedia", "");
576 o_rTempFileURL
= tempFileURL
;
580 MediaTempFile::~MediaTempFile()
582 ::osl::File::remove(m_TempFileURL
);
585 } // namespace avmedia
587 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */