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
;
60 AVMediaSetMask m_nMaskSet
;
64 sal_Int16 m_nVolumeDB
;
67 css::media::ZoomLevel m_eZoom
;
69 text::GraphicCrop m_aCrop
;
71 explicit Impl(AVMediaSetMask nMaskSet
)
72 : m_nMaskSet( nMaskSet
)
73 , m_eState( MediaState::Stop
)
79 , m_eZoom( css::media::ZoomLevel_NOT_AVAILABLE
)
85 MediaItem::MediaItem( sal_uInt16 i_nWhich
, AVMediaSetMask nMaskSet
)
86 : SfxPoolItem( i_nWhich
)
87 , m_pImpl( new Impl(nMaskSet
) )
92 MediaItem::MediaItem( const MediaItem
& rItem
)
93 : SfxPoolItem( rItem
)
94 , m_pImpl( new Impl(*rItem
.m_pImpl
) )
99 MediaItem::~MediaItem()
104 bool MediaItem::operator==( const SfxPoolItem
& rItem
) const
106 assert( SfxPoolItem::operator==(rItem
));
107 MediaItem
const& rOther(static_cast< const MediaItem
& >(rItem
));
108 return m_pImpl
->m_nMaskSet
== rOther
.m_pImpl
->m_nMaskSet
109 && m_pImpl
->m_URL
== rOther
.m_pImpl
->m_URL
110 && m_pImpl
->m_Referer
== rOther
.m_pImpl
->m_Referer
111 && m_pImpl
->m_sMimeType
== rOther
.m_pImpl
->m_sMimeType
112 && m_pImpl
->m_aGraphic
== rOther
.m_pImpl
->m_aGraphic
113 && m_pImpl
->m_aCrop
== rOther
.m_pImpl
->m_aCrop
114 && m_pImpl
->m_eState
== rOther
.m_pImpl
->m_eState
115 && m_pImpl
->m_fDuration
== rOther
.m_pImpl
->m_fDuration
116 && m_pImpl
->m_fTime
== rOther
.m_pImpl
->m_fTime
117 && m_pImpl
->m_nVolumeDB
== rOther
.m_pImpl
->m_nVolumeDB
118 && m_pImpl
->m_bLoop
== rOther
.m_pImpl
->m_bLoop
119 && m_pImpl
->m_bMute
== rOther
.m_pImpl
->m_bMute
120 && m_pImpl
->m_eZoom
== rOther
.m_pImpl
->m_eZoom
;
123 MediaItem
* MediaItem::Clone( SfxItemPool
* ) const
125 return new MediaItem( *this );
128 bool MediaItem::GetPresentation( SfxItemPresentation
,
132 const IntlWrapper
& ) const
138 bool MediaItem::QueryValue( css::uno::Any
& rVal
, sal_uInt8
) const
140 uno::Sequence
< uno::Any
> aSeq
{ uno::Any(m_pImpl
->m_URL
),
141 uno::Any(static_cast<sal_uInt32
>(m_pImpl
->m_nMaskSet
)),
142 uno::Any(static_cast< sal_Int32
>( m_pImpl
->m_eState
)),
143 uno::Any(m_pImpl
->m_fTime
),
144 uno::Any(m_pImpl
->m_fDuration
),
145 uno::Any(m_pImpl
->m_nVolumeDB
),
146 uno::Any(m_pImpl
->m_bLoop
),
147 uno::Any(m_pImpl
->m_bMute
),
148 uno::Any(m_pImpl
->m_eZoom
),
149 uno::Any(m_pImpl
->m_sMimeType
) };
157 bool MediaItem::PutValue( const css::uno::Any
& rVal
, sal_uInt8
)
159 uno::Sequence
< uno::Any
> aSeq
;
162 if( ( rVal
>>= aSeq
) && ( aSeq
.getLength() == 10 ) )
164 sal_Int32 nInt32
= 0;
166 aSeq
[ 0 ] >>= m_pImpl
->m_URL
;
167 aSeq
[ 1 ] >>= nInt32
;
168 m_pImpl
->m_nMaskSet
= static_cast<AVMediaSetMask
>(nInt32
);
169 aSeq
[ 2 ] >>= nInt32
;
170 m_pImpl
->m_eState
= static_cast< MediaState
>( nInt32
);
171 aSeq
[ 3 ] >>= m_pImpl
->m_fTime
;
172 aSeq
[ 4 ] >>= m_pImpl
->m_fDuration
;
173 aSeq
[ 5 ] >>= m_pImpl
->m_nVolumeDB
;
174 aSeq
[ 6 ] >>= m_pImpl
->m_bLoop
;
175 aSeq
[ 7 ] >>= m_pImpl
->m_bMute
;
176 aSeq
[ 8 ] >>= m_pImpl
->m_eZoom
;
177 aSeq
[ 9 ] >>= m_pImpl
->m_sMimeType
;
185 bool MediaItem::merge(const MediaItem
& rMediaItem
)
187 bool bChanged
= false;
189 const AVMediaSetMask nMaskSet
= rMediaItem
.getMaskSet();
191 if( AVMediaSetMask::URL
& nMaskSet
)
192 bChanged
|= setURL(rMediaItem
.getURL(), rMediaItem
.getTempURL(), rMediaItem
.getReferer());
194 if( AVMediaSetMask::MIME_TYPE
& nMaskSet
)
195 bChanged
|= setMimeType(rMediaItem
.getMimeType());
197 if (nMaskSet
& AVMediaSetMask::GRAPHIC
)
198 bChanged
|= setGraphic(rMediaItem
.getGraphic());
200 if (nMaskSet
& AVMediaSetMask::CROP
)
201 bChanged
|= setCrop(rMediaItem
.getCrop());
203 if( AVMediaSetMask::STATE
& nMaskSet
)
204 bChanged
|= setState( rMediaItem
.getState() );
206 if( AVMediaSetMask::DURATION
& nMaskSet
)
207 bChanged
|= setDuration(rMediaItem
.getDuration());
209 if( AVMediaSetMask::TIME
& nMaskSet
)
210 bChanged
|= setTime(rMediaItem
.getTime());
212 if( AVMediaSetMask::LOOP
& nMaskSet
)
213 bChanged
|= setLoop(rMediaItem
.isLoop());
215 if( AVMediaSetMask::MUTE
& nMaskSet
)
216 bChanged
|= setMute(rMediaItem
.isMute());
218 if( AVMediaSetMask::VOLUMEDB
& nMaskSet
)
219 bChanged
|= setVolumeDB(rMediaItem
.getVolumeDB());
221 if( AVMediaSetMask::ZOOM
& nMaskSet
)
222 bChanged
|= setZoom(rMediaItem
.getZoom());
227 AVMediaSetMask
MediaItem::getMaskSet() const
229 return m_pImpl
->m_nMaskSet
;
232 bool MediaItem::setURL(const OUString
& rURL
, const OUString
& rTempURL
, const OUString
& rReferer
)
234 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::URL
;
235 bool bChanged
= rURL
!= m_pImpl
->m_URL
|| rTempURL
!= m_pImpl
->m_TempFileURL
|| rReferer
!= m_pImpl
->m_Referer
;
238 m_pImpl
->m_URL
= rURL
;
239 m_pImpl
->m_TempFileURL
= rTempURL
;
240 m_pImpl
->m_Referer
= rReferer
;
241 setMimeType(::comphelper::GuessMediaMimeType(GetFilename(rURL
)));
246 const OUString
& MediaItem::getURL() const
248 return m_pImpl
->m_URL
;
251 const OUString
& MediaItem::getTempURL() const
253 return m_pImpl
->m_TempFileURL
;
256 const OUString
& MediaItem::getReferer() const
258 return m_pImpl
->m_Referer
;
261 bool MediaItem::setMimeType(const OUString
& rMimeType
)
263 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::MIME_TYPE
;
264 bool bChanged
= rMimeType
!= m_pImpl
->m_sMimeType
;
266 m_pImpl
->m_sMimeType
= rMimeType
;
270 OUString
MediaItem::getMimeType() const
272 return !m_pImpl
->m_sMimeType
.isEmpty() ? m_pImpl
->m_sMimeType
: AVMEDIA_MIMETYPE_COMMON
;
275 bool MediaItem::setGraphic(const Graphic
& rGraphic
)
277 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::GRAPHIC
;
278 bool bChanged
= rGraphic
!= m_pImpl
->m_aGraphic
;
280 m_pImpl
->m_aGraphic
= rGraphic
;
284 const Graphic
& MediaItem::getGraphic() const { return m_pImpl
->m_aGraphic
; }
286 bool MediaItem::setCrop(const text::GraphicCrop
& rCrop
)
288 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::CROP
;
289 bool bChanged
= rCrop
!= m_pImpl
->m_aCrop
;
291 m_pImpl
->m_aCrop
= rCrop
;
295 const text::GraphicCrop
& MediaItem::getCrop() const { return m_pImpl
->m_aCrop
; }
297 bool MediaItem::setState(MediaState eState
)
299 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::STATE
;
300 bool bChanged
= eState
!= m_pImpl
->m_eState
;
302 m_pImpl
->m_eState
= eState
;
306 MediaState
MediaItem::getState() const
308 return m_pImpl
->m_eState
;
311 bool MediaItem::setDuration(double fDuration
)
313 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::DURATION
;
314 bool bChanged
= fDuration
!= m_pImpl
->m_fDuration
;
316 m_pImpl
->m_fDuration
= fDuration
;
320 double MediaItem::getDuration() const
322 return m_pImpl
->m_fDuration
;
325 bool MediaItem::setTime(double fTime
)
327 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::TIME
;
328 bool bChanged
= fTime
!= m_pImpl
->m_fTime
;
330 m_pImpl
->m_fTime
= fTime
;
334 double MediaItem::getTime() const
336 return m_pImpl
->m_fTime
;
339 bool MediaItem::setLoop(bool bLoop
)
341 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::LOOP
;
342 bool bChanged
= bLoop
!= m_pImpl
->m_bLoop
;
344 m_pImpl
->m_bLoop
= bLoop
;
348 bool MediaItem::isLoop() const
350 return m_pImpl
->m_bLoop
;
353 bool MediaItem::setMute(bool bMute
)
355 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::MUTE
;
356 bool bChanged
= bMute
!= m_pImpl
->m_bMute
;
358 m_pImpl
->m_bMute
= bMute
;
362 bool MediaItem::isMute() const
364 return m_pImpl
->m_bMute
;
367 bool MediaItem::setVolumeDB(sal_Int16 nDB
)
369 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::VOLUMEDB
;
370 bool bChanged
= nDB
!= m_pImpl
->m_nVolumeDB
;
372 m_pImpl
->m_nVolumeDB
= nDB
;
376 sal_Int16
MediaItem::getVolumeDB() const
378 return m_pImpl
->m_nVolumeDB
;
381 bool MediaItem::setZoom(css::media::ZoomLevel eZoom
)
383 m_pImpl
->m_nMaskSet
|= AVMediaSetMask::ZOOM
;
384 bool bChanged
= eZoom
!= m_pImpl
->m_eZoom
;
386 m_pImpl
->m_eZoom
= eZoom
;
390 css::media::ZoomLevel
MediaItem::getZoom() const
392 return m_pImpl
->m_eZoom
;
395 OUString
GetFilename(OUString
const& rSourceURL
)
397 uno::Reference
<uri::XUriReferenceFactory
> const xUriFactory(
398 uri::UriReferenceFactory::create(
399 comphelper::getProcessComponentContext()));
400 uno::Reference
<uri::XUriReference
> const xSourceURI(
401 xUriFactory
->parse(rSourceURL
), uno::UNO_SET_THROW
);
405 sal_Int32
const nSegments(xSourceURI
->getPathSegmentCount());
408 filename
= xSourceURI
->getPathSegment(nSegments
- 1);
411 if (!::comphelper::OStorageHelper::IsValidZipEntryFileName(
412 filename
, false) || !filename
.getLength())
420 uno::Reference
<io::XStream
>
421 CreateStream(uno::Reference
<embed::XStorage
> const& xStorage
,
422 OUString
const& rFilename
)
424 OUString
filename(rFilename
);
426 if (xStorage
->hasByName(filename
))
428 std::u16string_view basename
;
429 std::u16string_view suffix
;
430 sal_Int32
const nIndex(rFilename
.lastIndexOf('.'));
433 basename
= rFilename
.subView(0, nIndex
);
434 suffix
= rFilename
.subView(nIndex
);
436 sal_Int32
count(0); // sigh... try to generate non-existent name
440 filename
= basename
+ OUString::number(count
) + suffix
;
442 while (xStorage
->hasByName(filename
));
445 uno::Reference
<io::XStream
> const xStream(
446 xStorage
->openStreamElement(filename
,
447 embed::ElementModes::WRITE
| embed::ElementModes::TRUNCATE
),
449 uno::Reference
< beans::XPropertySet
> const xStreamProps(xStream
,
451 if (xStreamProps
.is()) { // this is NOT supported in FileSystemStorage
452 OUString
const guessed(::comphelper::GuessMediaMimeType(filename
));
453 xStreamProps
->setPropertyValue("MediaType",
454 uno::Any(guessed
.isEmpty() ? AVMEDIA_MIMETYPE_COMMON
: guessed
));
455 xStreamProps
->setPropertyValue( // turn off compression
456 "Compressed", uno::Any(false));
462 bool EmbedMedia(uno::Reference
<frame::XModel
> const& xModel
,
463 OUString
const& rSourceURL
, OUString
& o_rEmbeddedURL
, uno::Reference
<io::XInputStream
> const& xInputStream
)
467 uno::Reference
<document::XStorageBasedDocument
> const xSBD(xModel
,
468 uno::UNO_QUERY_THROW
);
469 uno::Reference
<embed::XStorage
> const xStorage(
470 xSBD
->getDocumentStorage(), uno::UNO_SET_THROW
);
472 OUString
const media("Media");
473 uno::Reference
<embed::XStorage
> const xSubStorage(
474 xStorage
->openStorageElement(media
, embed::ElementModes::WRITE
));
476 OUString
filename(GetFilename(rSourceURL
));
478 uno::Reference
<io::XStream
> const xStream(
479 CreateStream(xSubStorage
, filename
), uno::UNO_SET_THROW
);
480 uno::Reference
<io::XOutputStream
> const xOutStream(
481 xStream
->getOutputStream(), uno::UNO_SET_THROW
);
483 if (xInputStream
.is())
485 // Throw Exception if failed.
486 ::comphelper::OStorageHelper::CopyInputToOutput(xInputStream
, xOutStream
);
490 ::ucbhelper::Content
sourceContent(rSourceURL
,
491 uno::Reference
<ucb::XCommandEnvironment
>(),
492 comphelper::getProcessComponentContext());
494 if (!sourceContent
.openStream(xOutStream
)) // copy file to storage
496 SAL_INFO("avmedia", "openStream to storage failed");
501 uno::Reference
<embed::XTransactedObject
> const xSubTransaction(
502 xSubStorage
, uno::UNO_QUERY
);
503 if (xSubTransaction
.is()) {
504 xSubTransaction
->commit();
506 uno::Reference
<embed::XTransactedObject
> const xTransaction(
507 xStorage
, uno::UNO_QUERY
);
508 if (xTransaction
.is()) {
509 xTransaction
->commit();
512 o_rEmbeddedURL
= "vnd.sun.star.Package:" + media
+ "/" + filename
;
515 catch (uno::Exception
const&)
518 "Exception while trying to embed media");
523 bool CreateMediaTempFile(uno::Reference
<io::XInputStream
> const& xInStream
,
524 OUString
& o_rTempFileURL
, std::u16string_view rDesiredExtension
)
526 OUString tempFileURL
;
527 ::osl::FileBase::RC
const err
=
528 ::osl::FileBase::createTempFile(nullptr, nullptr, & tempFileURL
);
529 if (::osl::FileBase::E_None
!= err
)
531 SAL_WARN("avmedia", "cannot create temp file");
535 if (!rDesiredExtension
.empty())
537 OUString newTempFileURL
= tempFileURL
+ rDesiredExtension
;
538 if (osl::File::move(tempFileURL
, newTempFileURL
) != osl::FileBase::E_None
)
540 SAL_WARN("avmedia", "Could not rename file '" << tempFileURL
<< "' to '" << newTempFileURL
<< "'");
543 tempFileURL
= newTempFileURL
;
548 ::ucbhelper::Content
tempContent(tempFileURL
,
549 uno::Reference
<ucb::XCommandEnvironment
>(),
550 comphelper::getProcessComponentContext());
551 tempContent
.writeStream(xInStream
, true); // copy stream to file
553 catch (uno::Exception
const&)
555 TOOLS_WARN_EXCEPTION("avmedia", "");
558 o_rTempFileURL
= tempFileURL
;
562 MediaTempFile::~MediaTempFile()
564 ::osl::File::remove(m_TempFileURL
);
567 } // namespace avmedia
569 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */