1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 //_________________________________________________________________________________________________________________
29 //	my own includes
30 //_________________________________________________________________________________________________________________
31 
32 #ifndef __FRAMEWORK_DISPATCH_SOUNDHANDLER_HXX_
33 #include "soundhandler.hxx"
34 #endif
35 
36 #ifndef __COMPHELPER_MEDIADESCRIPTOR_HXX_
37 #include <comphelper/mediadescriptor.hxx>
38 #endif
39 
40 //_________________________________________________________________________________________________________________
41 //	interface includes
42 //_________________________________________________________________________________________________________________
43 #include <com/sun/star/io/XInputStream.hpp>
44 #include <com/sun/star/frame/DispatchResultState.hpp>
45 
46 //_________________________________________________________________________________________________________________
47 //	includes of other projects
48 //_________________________________________________________________________________________________________________
49 #include <comphelper/sequenceashashmap.hxx>
50 #include <rtl/ustrbuf.hxx>
51 
52 #include <cppuhelper/typeprovider.hxx>
53 #include <cppuhelper/factory.hxx>
54 
55 //_________________________________________________________________________________________________________________
56 //	namespace
57 //_________________________________________________________________________________________________________________
58 
59 namespace avmedia{
60 
61 //_________________________________________________________________________________________________________________
62 //	non exported const
63 //_________________________________________________________________________________________________________________
64 
65 //_________________________________________________________________________________________________________________
66 //	non exported definitions
67 //_________________________________________________________________________________________________________________
68 
69 //_________________________________________________________________________________________________________________
70 //	declarations
71 //_________________________________________________________________________________________________________________
72 
73 //*****************************************************************************************************************
74 //  XInterface, XTypeProvider, XServiceInfo
75 //*****************************************************************************************************************
76 
77 void SAL_CALL SoundHandler::acquire() throw()
78 {
79        /* Don't use mutex in methods of XInterface! */
80        OWeakObject::acquire();
81 }
82 
83 void SAL_CALL SoundHandler::release() throw()
84 {
85        /* Don't use mutex in methods of XInterface! */
86        OWeakObject::release();
87 }
88 
89 css::uno::Any SAL_CALL SoundHandler::queryInterface( const css::uno::Type& aType ) throw( css::uno::RuntimeException )
90 {
91        /* Attention: Don't use mutex or guard in this method!!! Is a method of XInterface.     */
92         /* Ask for my own supported interfaces ...*/
93        css::uno::Any aReturn( ::cppu::queryInterface( aType,
94                static_cast< css::lang::XTypeProvider* >(this),
95                static_cast< css::lang::XServiceInfo* >(this),
96                static_cast< css::frame::XNotifyingDispatch* >(this),
97                static_cast< css::frame::XDispatch* >(this),
98                static_cast< css::document::XExtendedFilterDetection* >(this)));
99        /* If searched interface not supported by this class ... */
100        if ( aReturn.hasValue() == sal_False )
101        {
102                /* ... ask baseclass for interfaces! */
103                aReturn = OWeakObject::queryInterface( aType );
104        }
105         /* Return result of this search. */
106        return aReturn;
107 }
108 
109 css::uno::Sequence< sal_Int8 > SAL_CALL SoundHandler::getImplementationId() throw( css::uno::RuntimeException )
110 {
111     /* Create one Id for all instances of this class.                                               */
112     /* Use ethernet address to do this! (sal_True)                                                  */
113     /* Optimize this method                                                                         */
114     /* We initialize a static variable only one time. And we don't must use a mutex at every call!  */
115     /* For the first call; pID is NULL - for the second call pID is different from NULL!            */
116     static ::cppu::OImplementationId* pID = NULL ;
117     if ( pID == NULL )
118     {
119         /* Ready for multithreading; get global mutex for first call of this method only! see before   */
120         ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
121         /* Control these pointer again ... it can be, that another instance will be faster then these! */
122         if ( pID == NULL )
123         {
124             /* Create a new static ID ... */
125             static ::cppu::OImplementationId aID( sal_False );
126             /* ... and set his address to static pointer! */
127             pID = &aID ;
128         }
129     }
130     return pID->getImplementationId();
131 }
132 
133 css::uno::Sequence< css::uno::Type > SAL_CALL SoundHandler::getTypes() throw( css::uno::RuntimeException )
134 {
135     /* Optimize this method !                                       */
136     /* We initialize a static variable only one time.               */
137     /* And we don't must use a mutex at every call!                 */
138     /* For the first call; pTypeCollection is NULL -                */
139     /* for the second call pTypeCollection is different from NULL!  */
140     static ::cppu::OTypeCollection* pTypeCollection = NULL ;
141     if ( pTypeCollection == NULL )
142     {
143         /* Ready for multithreading; get global mutex for first call of this method only! see before   */
144         ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
145         /* Control these pointer again ... it can be, that another instance will be faster then these! */
146         if ( pTypeCollection == NULL )
147         {
148             /* Create a static typecollection ...           */
149             static ::cppu::OTypeCollection aTypeCollection
150                 (
151                     ::getCppuType(( const ::com::sun::star::uno::Reference< css::lang::XTypeProvider >*)NULL ),
152                     ::getCppuType(( const ::com::sun::star::uno::Reference< css::lang::XServiceInfo >*)NULL ),
153                     ::getCppuType(( const ::com::sun::star::uno::Reference< css::frame::XNotifyingDispatch >*)NULL ),
154                     ::getCppuType(( const ::com::sun::star::uno::Reference< css::frame::XDispatch >*)NULL ),
155                     ::getCppuType(( const ::com::sun::star::uno::Reference< css::document::XExtendedFilterDetection >*)NULL )
156                 );
157             /* ... and set his address to static pointer! */
158             pTypeCollection = &aTypeCollection ;
159         }
160     }
161     return pTypeCollection->getTypes();
162 }
163 
164 #define DECLARE_ASCII( SASCIIVALUE ) \
165         ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SASCIIVALUE ) )
166 
167 #define IMPLEMENTATIONNAME_SOUNDHANDLER DECLARE_ASCII("com.sun.star.comp.framework.SoundHandler")
168 #define SERVICENAME_CONTENTHANDLER DECLARE_ASCII("com.sun.star.frame.ContentHandler")
169 
170 /*===========================================================================================================*/
171 /* XServiceInfo */
172 /*===========================================================================================================*/
173 ::rtl::OUString SAL_CALL SoundHandler::getImplementationName() throw( css::uno::RuntimeException )
174 {
175     return impl_getStaticImplementationName();
176 }
177 
178 /*===========================================================================================================*/
179 /* XServiceInfo */
180 /*===========================================================================================================*/
181 sal_Bool SAL_CALL SoundHandler::supportsService( const ::rtl::OUString& sServiceName ) throw( css::uno::RuntimeException )
182 {
183     /* Set default return value. */
184     sal_Bool bReturn = sal_False ;
185     /* Get names of all supported servicenames. */
186     css::uno::Sequence< ::rtl::OUString >  seqServiceNames =   getSupportedServiceNames();
187     const ::rtl::OUString*                 pArray          =   seqServiceNames.getConstArray();
188     sal_Int32                              nCounter        =   0;
189     sal_Int32                              nLength         =   seqServiceNames.getLength();
190     /* Search for right name in list. */
191     while   (
192               ( nCounter      <       nLength         )       &&
193               ( bReturn       ==      sal_False       )
194             )
195     {
196         /* Is name was found, say "YES, SERVICE IS SUPPORTED." and break loop. */
197         if ( pArray[nCounter] == sServiceName )
198         {
199             bReturn = sal_True ;
200         }
201         /* Else step to next element in list. */
202         ++nCounter;
203     }
204     /* Return state of search. */
205     return bReturn;
206 }
207 
208 /*===========================================================================================================*/
209 /* XServiceInfo */
210 /*===========================================================================================================*/
211 css::uno::Sequence< ::rtl::OUString > SAL_CALL SoundHandler::getSupportedServiceNames() throw( css::uno::RuntimeException )
212 {
213     return impl_getStaticSupportedServiceNames();
214 }
215 
216 /*===========================================================================================================*/
217 /* Helper for XServiceInfo                                                                                   */
218 /*===========================================================================================================*/
219 css::uno::Sequence< ::rtl::OUString > SoundHandler::impl_getStaticSupportedServiceNames()
220 {
221     css::uno::Sequence< ::rtl::OUString > seqServiceNames( 1 );
222     seqServiceNames.getArray() [0] = SERVICENAME_CONTENTHANDLER;
223     return seqServiceNames;
224 }
225 
226 /*===========================================================================================================*/
227 /* Helper for XServiceInfo */
228 /*===========================================================================================================*/
229 ::rtl::OUString SoundHandler::impl_getStaticImplementationName()
230 {
231     return IMPLEMENTATIONNAME_SOUNDHANDLER;
232 }
233 
234 css::uno::Reference< css::uno::XInterface > SAL_CALL SoundHandler::impl_createInstance( const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager ) throw( css::uno::Exception )
235 {
236     /* create new instance of service */
237     SoundHandler* pClass = new SoundHandler( xServiceManager );
238     /* hold it alive by increasing his ref count!!! */
239     css::uno::Reference< css::uno::XInterface > xService( static_cast< ::cppu::OWeakObject* >(pClass), css::uno::UNO_QUERY );
240     /* initialize new service instance ... he can use his own refcount ... we hold it! */
241     pClass->impl_initService();
242     /* return new created service as reference */
243     return xService;
244 }
245 
246 css::uno::Reference< css::lang::XSingleServiceFactory > SoundHandler::impl_createFactory( const css::uno::Reference< css::lang::XMultiServiceFactory >& xServiceManager )
247 {
248     css::uno::Reference< css::lang::XSingleServiceFactory > xReturn ( cppu::createSingleFactory (
249        xServiceManager,
250         SoundHandler::impl_getStaticImplementationName(),
251         SoundHandler::impl_createInstance,
252         SoundHandler::impl_getStaticSupportedServiceNames()
253         )
254     );
255     return xReturn;
256 }
257 
258 void SAL_CALL SoundHandler::impl_initService()
259 {
260 }
261 
262 
263 /*-************************************************************************************************************//**
264     @short      standard ctor
265     @descr      These initialize a new instance of this class with needed informations for work.
266 
267     @seealso    using at owner
268 
269     @param      "xFactory", reference to service manager for creation of new services
270     @return     -
271 
272     @onerror    Show an assertion and do nothing else.
273     @threadsafe yes
274 *//*-*************************************************************************************************************/
275 SoundHandler::SoundHandler( const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory )
276 		//	Init baseclasses first
277         :   ThreadHelpBase      (          )
278         ,   ::cppu::OWeakObject (          )
279         // Init member
280 	,   m_bError		( false    )
281         ,   m_xFactory          ( xFactory )
282 {
283     m_aUpdateTimer.SetTimeoutHdl(LINK(this, SoundHandler, implts_PlayerNotify));
284 }
285 
286 /*-************************************************************************************************************//**
287     @short      standard dtor
288     @descr      -
289 
290     @seealso    -
291 
292     @param      -
293     @return     -
294 
295     @onerror    -
296     @threadsafe -
297 *//*-*************************************************************************************************************/
298 SoundHandler::~SoundHandler()
299 {
300     if (m_xListener.is())
301     {
302         css::frame::DispatchResultEvent aEvent;
303         aEvent.State = css::frame::DispatchResultState::FAILURE;
304         m_xListener->dispatchFinished(aEvent);
305         m_xListener = css::uno::Reference< css::frame::XDispatchResultListener >();
306     }
307 }
308 
309 /*-************************************************************************************************************//**
310     @interface  ::com::sun::star::frame::XDispatch
311 
312     @short      try to load audio file
313     @descr      This method try to load given audio file by URL and play it. We use vcl/Sound class to do that.
314                 Playing of sound is asynchron everytime.
315 
316     @attention  We must hold us alive by ourself ... because we use async. vcl sound player ... but playing is started
317                 in async interface call "dispatch()" too. And caller forget us imediatly. But then our uno ref count
318                 will decreased to 0 and will die. The only solution is to use own reference to our implementation.
319                 But we do it for realy started jobs only and release it during call back of vcl.
320 
321     @seealso    class vcl/Sound
322     @seealso    method implts_PlayerNotify()
323 
324     @param      "aURL"      , URL to dispatch.
325     @param      "lArguments", list of optional arguments.
326     @return     -
327 
328     @onerror    We do nothing.
329     @threadsafe yes
330 *//*-*************************************************************************************************************/
331 void SAL_CALL SoundHandler::dispatchWithNotification(const css::util::URL&                                             aURL      ,
332                                                      const css::uno::Sequence< css::beans::PropertyValue >&            lDescriptor,
333                                                      const css::uno::Reference< css::frame::XDispatchResultListener >& xListener ) throw(css::uno::RuntimeException)
334 {
335     // SAFE {
336     const ::vos::OGuard aLock( m_aLock );
337 
338     {
339 	//close streams otherwise on windows we can't reopen the file in the
340 	//media player when we pass the url to directx as it'll already be open
341         ::comphelper::MediaDescriptor aDescriptor(lDescriptor);
342 
343 	css::uno::Reference< css::io::XInputStream > xInputStream =
344 		aDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_INPUTSTREAM(),
345 		css::uno::Reference< css::io::XInputStream >());
346 	if (xInputStream.is()) xInputStream->closeInput();
347     }
348 
349     // If player currently used for other dispatch() requests ...
350     // cancel it by calling stop()!
351     m_aUpdateTimer.Stop();
352     if (m_xPlayer.is())
353     {
354         if (m_xPlayer->isPlaying())
355             m_xPlayer->stop();
356         m_xPlayer.clear();
357     }
358 
359     // Try to initialize player.
360     m_xListener = xListener;
361     try
362     {
363         m_bError = false;
364         m_xPlayer.set( avmedia::MediaWindow::createPlayer( aURL.Complete ), css::uno::UNO_QUERY_THROW );
365         // OK- we can start async playing ...
366         // Count this request and initialize self-holder against dieing by uno ref count ...
367         m_xSelfHold = css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY);
368         m_xPlayer->start();
369         m_aUpdateTimer.SetTimeout( 200 );
370         m_aUpdateTimer.Start();
371     }
372     catch( css::uno::Exception& e )
373     {
374         m_bError = true;
375         (void)e;
376         m_xPlayer.clear();
377     }
378 
379     // } SAFE
380 }
381 
382 void SAL_CALL SoundHandler::dispatch( const css::util::URL&                                  aURL       ,
383                                       const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException )
384 {
385     dispatchWithNotification(aURL, lArguments, css::uno::Reference< css::frame::XDispatchResultListener >());
386 }
387 
388 /*-************************************************************************************************************//**
389     @interface  ::com::sun::star::document::XExtendedFilterDetection
390 
391     @short      try to detect file (given as argument included in "lDescriptor")
392     @descr      We try to detect, if given file could be handled by this class and is a well known one.
393                 If it is - we return right internal type name - otherwise we return nothing!
394                 So call can search for another detect service and ask him too.
395 
396     @attention  a) We don't need any mutex here ... because we don't use any member!
397                 b) Dont' use internal player instance "m_pPlayer" to detect given sound file!
398                    It's not neccessary to do that ... and we can use temp. variable to do the same.
399                    This way is easy - we don't must synchronize it with currently played sounds!
400                    Another reason to do so ... We are a listener on our internal ma_Player object.
401                    If you would call "IsSoundFile()" on this instance, he would call us back and
402                    we make some uneccssary things ...
403 
404     @seealso    -
405 
406     @param      "lDescriptor", description of file to detect
407     @return     Internal type name which match this file ... or nothing if it is unknown.
408 
409     @onerror    We return nothing.
410     @threadsafe yes
411 *//*-*************************************************************************************************************/
412 ::rtl::OUString SAL_CALL SoundHandler::detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) throw( css::uno::RuntimeException )
413 {
414     // Our default is "nothing". So we can return it, if detection failed or fily type is realy unknown.
415     ::rtl::OUString sTypeName;
416 
417     // Analyze given descriptor to find filename or input stream or ...
418     ::comphelper::MediaDescriptor aDescriptor(lDescriptor);
419     ::rtl::OUString               sURL       = aDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_URL(), ::rtl::OUString());
420 
421     if (
422         (sURL.getLength()           ) &&
423         (avmedia::MediaWindow::isMediaURL(sURL))
424        )
425     {
426         // If the file type is supported depends on the OS, so...
427         // I think we can the following ones:
428         //  a) look for given extension of url to map our type decision HARD CODED!!!
429         //  b) return preferred type every time... it's easy :-)
430         sTypeName = ::rtl::OUString::createFromAscii("wav_Wave_Audio_File");
431         aDescriptor[::comphelper::MediaDescriptor::PROP_TYPENAME()] <<= sTypeName;
432         aDescriptor >> lDescriptor;
433     }
434 
435     // Return our decision.
436     return sTypeName;
437 }
438 
439 /*-************************************************************************************************************//**
440     @short      call back of sound player
441     @descr      Our player call us back to give us some informations.
442                 We use this informations to callback our might existing listener.
443 
444     @seealso    method dispatchWithNotification()
445 
446     @param      -
447     @return     0 everytime ... it doesnt matter for us.
448 
449     @onerror    -
450     @threadsafe yes
451 *//*-*************************************************************************************************************/
452 IMPL_LINK( SoundHandler, implts_PlayerNotify, void*, EMPTYARG )
453 {
454     // SAFE {
455     ::vos::OClearableGuard aLock( m_aLock );
456 
457     if (m_xPlayer.is() && m_xPlayer->isPlaying() && m_xPlayer->getMediaTime() < m_xPlayer->getDuration())
458     {
459         m_aUpdateTimer.Start();
460         return 0L;
461     }
462     m_xPlayer.clear();
463 
464     // We use m_xSelfHold to let us die ... but we must live till real finishing of this method too!!!
465     // So we SHOULD use another "self-holder" temp. to provide that ...
466     css::uno::Reference< css::uno::XInterface > xOperationHold = m_xSelfHold;
467     m_xSelfHold = css::uno::Reference< css::uno::XInterface >();
468 
469     // notify might existing listener
470     // And forget this listener!
471     // Because the corresponding dispatch was finished.
472     if (m_xListener.is())
473     {
474         css::frame::DispatchResultEvent aEvent;
475         if (!m_bError)
476             aEvent.State = css::frame::DispatchResultState::SUCCESS;
477         else
478             aEvent.State = css::frame::DispatchResultState::FAILURE;
479         m_xListener->dispatchFinished(aEvent);
480         m_xListener = css::uno::Reference< css::frame::XDispatchResultListener >();
481     }
482 
483     // } SAFE
484 	//release aLock before end of method at which point xOperationHold goes out of scope and pThis dies
485 	aLock.clear();
486     return 0;
487 }
488 
489 } // namespace framework
490 
491 // ------------------------------------------
492 // - component_getImplementationEnvironment -
493 // ------------------------------------------
494 
495 extern "C" void SAL_CALL component_getImplementationEnvironment( const sal_Char ** ppEnvTypeName, uno_Environment ** /*ppEnv*/ )
496 {
497        *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
498 }
499 
500 // ------------------------
501 // - component_getFactory -
502 // ------------------------
503 
504 extern "C" void* SAL_CALL component_getFactory(const sal_Char* pImplementationName, void* pServiceManager, void* /*pRegistryKey*/ )
505 {
506     void* pReturn = NULL;
507     if  (pServiceManager !=  NULL )
508     {
509         /* Define variables which are used in following macros. */
510         css::uno::Reference< ::com::sun::star::lang::XSingleServiceFactory > xFactory;
511         css::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xServiceManager;
512             xServiceManager = reinterpret_cast< ::com::sun::star::lang::XMultiServiceFactory* >( pServiceManager )  ;
513 
514         if ( avmedia::SoundHandler::impl_getStaticImplementationName().equals( ::rtl::OUString::createFromAscii( pImplementationName ) ) )
515             xFactory = avmedia::SoundHandler::impl_createFactory( xServiceManager );
516 
517         if ( xFactory.is() == sal_True )
518         {
519             xFactory->acquire();
520             pReturn = xFactory.get();
521         }
522     }
523     /* Return with result of this operation. */
524     return pReturn;
525 }
526