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