1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <objbase.h>
21 #include <strmif.h>
22 #include <control.h>
23 #include <uuids.h>
24 #include <evcode.h>
25 
26 #include "player.hxx"
27 #include "framegrabber.hxx"
28 #include "window.hxx"
29 #include <cppuhelper/supportsservice.hxx>
30 #include <o3tl/char16_t2wchar_t.hxx>
31 #include <osl/file.hxx>
32 
33 #define AVMEDIA_WIN_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_DirectX"
34 #define AVMEDIA_WIN_PLAYER_SERVICENAME "com.sun.star.media.Player_DirectX"
35 
36 using namespace ::com::sun::star;
37 
38 namespace avmedia { namespace win {
39 
MediaPlayerWndProc_2(HWND hWnd,UINT nMsg,WPARAM nPar1,LPARAM nPar2)40 static LRESULT CALLBACK MediaPlayerWndProc_2( HWND hWnd,UINT nMsg, WPARAM nPar1, LPARAM nPar2 )
41 {
42     Player* pPlayer = reinterpret_cast<Player*>(::GetWindowLongPtrW( hWnd, 0 ));
43     bool    bProcessed = true;
44 
45     if( pPlayer )
46     {
47         switch( nMsg )
48         {
49             case WM_GRAPHNOTIFY:
50                 pPlayer->processEvent();
51             break;
52             default:
53                 bProcessed = false;
54             break;
55         }
56     }
57     else
58         bProcessed = false;
59 
60     return( bProcessed ? 0 : DefWindowProcW( hWnd, nMsg, nPar1, nPar2 ) );
61 }
62 
63 
Player(const uno::Reference<lang::XMultiServiceFactory> & rxMgr)64 Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) :
65     Player_BASE(m_aMutex),
66     mxMgr( rxMgr ),
67     mpGB( nullptr ),
68     mpOMF( nullptr ),
69     mpMC( nullptr ),
70     mpME( nullptr ),
71     mpMS( nullptr ),
72     mpMP( nullptr ),
73     mpBA( nullptr ),
74     mpBV( nullptr ),
75     mpVW( nullptr ),
76     mpEV( nullptr ),
77     mnUnmutedVolume( 0 ),
78     mnFrameWnd( nullptr ),
79     mbMuted( false ),
80     mbLooping( false ),
81     mbAddWindow( true )
82 {
83     ::CoInitialize( nullptr );
84 }
85 
86 
~Player()87 Player::~Player()
88 {
89     if( mnFrameWnd )
90         ::DestroyWindow( mnFrameWnd );
91 
92     ::CoUninitialize();
93 }
94 
95 
disposing()96 void SAL_CALL Player::disposing()
97 {
98     ::osl::MutexGuard aGuard(m_aMutex);
99     stop();
100     if( mpBA )
101         mpBA->Release();
102 
103     if( mpBV )
104         mpBV->Release();
105 
106     if( mpVW )
107         mpVW->Release();
108 
109     if( mpMP )
110         mpMP->Release();
111 
112     if( mpMS )
113         mpMS->Release();
114 
115     if( mpME )
116     {
117         mpME->SetNotifyWindow( 0, WM_GRAPHNOTIFY, 0);
118         mpME->Release();
119     }
120 
121     if( mpMC )
122         mpMC->Release();
123 
124     if( mpEV )
125         mpEV->Release();
126 
127     if( mpOMF )
128         mpOMF->Release();
129 
130     if( mpGB )
131         mpGB->Release();
132 }
133 
134 
create(const OUString & rURL)135 bool Player::create( const OUString& rURL )
136 {
137     HRESULT hR;
138     bool    bRet = false;
139 
140     if( SUCCEEDED( hR = CoCreateInstance( CLSID_FilterGraph, nullptr, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, reinterpret_cast<void**>(&mpGB) ) ) )
141     {
142         // Don't use the overlay mixer on Windows Vista
143         // It disables the desktop composition as soon as RenderFile is called
144         // also causes some other problems: video rendering is not reliable
145 
146         // tdf#128057: IGraphBuilder::RenderFile seems to fail to handle file URIs properly when
147         // they contain encoded characters like "%23"; so pass system path in that case instead.
148         OUString aFile(rURL);
149         if (aFile.startsWithIgnoreAsciiCase("file:"))
150             osl::FileBase::getSystemPathFromFileURL(rURL, aFile);
151 
152         if( SUCCEEDED( hR = mpGB->RenderFile( o3tl::toW(aFile.getStr()), nullptr ) ) &&
153             SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaControl, reinterpret_cast<void**>(&mpMC) ) ) &&
154             SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaEventEx, reinterpret_cast<void**>(&mpME) ) ) &&
155             SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaSeeking, reinterpret_cast<void**>(&mpMS) ) ) &&
156             SUCCEEDED( hR = mpGB->QueryInterface( IID_IMediaPosition, reinterpret_cast<void**>(&mpMP) ) ) )
157         {
158             // Video interfaces
159             mpGB->QueryInterface( IID_IVideoWindow, reinterpret_cast<void**>(&mpVW) );
160             mpGB->QueryInterface( IID_IBasicVideo, reinterpret_cast<void**>(&mpBV) );
161 
162             // Audio interface
163             mpGB->QueryInterface( IID_IBasicAudio, reinterpret_cast<void**>(&mpBA) );
164 
165             if( mpBA )
166                 mpBA->put_Volume( mnUnmutedVolume );
167 
168             bRet = true;
169         }
170     }
171 
172     if( bRet )
173         maURL = rURL;
174     else
175         maURL.clear();
176 
177     return bRet;
178 }
179 
180 
getVideoWindow() const181 const IVideoWindow* Player::getVideoWindow() const
182 {
183     return mpVW;
184 }
185 
186 
setNotifyWnd(HWND nNotifyWnd)187 void Player::setNotifyWnd( HWND nNotifyWnd )
188 {
189     mbAddWindow = false;
190     if( mpME )
191         mpME->SetNotifyWindow( reinterpret_cast<OAHWND>(nNotifyWnd), WM_GRAPHNOTIFY, reinterpret_cast< LONG_PTR>( this ) );
192 }
193 
194 
processEvent()195 long Player::processEvent()
196 {
197     long nCode;
198     LONG_PTR nParam1, nParam2;
199 
200     while( mpME && SUCCEEDED( mpME->GetEvent( &nCode, &nParam1, &nParam2, 0 ) ) )
201     {
202         if( EC_COMPLETE == nCode )
203         {
204             if( mbLooping )
205             {
206                 setMediaTime( 0.0 );
207                 start();
208             }
209             else
210             {
211                 setMediaTime( getDuration() );
212                 stop();
213             }
214         }
215 
216         mpME->FreeEventParams( nCode, nParam1, nParam2 );
217     }
218 
219     return 0;
220 }
221 
222 
start()223 void SAL_CALL Player::start(  )
224 {
225     ::osl::MutexGuard aGuard(m_aMutex);
226     if( mpMC )
227     {
228         if ( mbAddWindow )
229         {
230             static WNDCLASSW* mpWndClass = nullptr;
231             if ( !mpWndClass )
232             {
233                 mpWndClass = new WNDCLASSW;
234 
235                 memset( mpWndClass, 0, sizeof( *mpWndClass ) );
236                 mpWndClass->hInstance = GetModuleHandleW( nullptr );
237                 mpWndClass->cbWndExtra = sizeof( DWORD );
238                 mpWndClass->lpfnWndProc = MediaPlayerWndProc_2;
239                 mpWndClass->lpszClassName = L"com_sun_star_media_Sound_Player";
240                 mpWndClass->hbrBackground = static_cast<HBRUSH>(::GetStockObject( BLACK_BRUSH ));
241                 mpWndClass->hCursor = ::LoadCursor( nullptr, IDC_ARROW );
242 
243                 RegisterClassW( mpWndClass );
244             }
245             if ( !mnFrameWnd )
246             {
247                 mnFrameWnd = CreateWindowW( mpWndClass->lpszClassName, nullptr,
248                                             0,
249                                             0, 0, 0, 0,
250                                             nullptr, nullptr, mpWndClass->hInstance, nullptr );
251                 if ( mnFrameWnd )
252                 {
253                     ::ShowWindow(mnFrameWnd, SW_HIDE);
254                     SetWindowLongPtrW( mnFrameWnd, 0, reinterpret_cast<LONG_PTR>(this) );
255                     // mpVW->put_Owner( (OAHWND) mnFrameWnd );
256                     setNotifyWnd( mnFrameWnd );
257                 }
258             }
259         }
260 
261         mpMC->Run();
262     }
263 }
264 
265 
stop()266 void SAL_CALL Player::stop(  )
267 {
268     ::osl::MutexGuard aGuard(m_aMutex);
269     if( mpMC )
270         mpMC->Stop();
271 }
272 
273 
isPlaying()274 sal_Bool SAL_CALL Player::isPlaying()
275 {
276     ::osl::MutexGuard aGuard(m_aMutex);
277 
278     OAFilterState   eFilterState;
279     bool            bRet = false;
280 
281     if( mpMC && SUCCEEDED( mpMC->GetState( 10, &eFilterState ) ) )
282         bRet = ( State_Running == eFilterState );
283 
284     return bRet;
285 }
286 
287 
getDuration()288 double SAL_CALL Player::getDuration(  )
289 {
290     ::osl::MutexGuard aGuard(m_aMutex);
291 
292     REFTIME aRefTime( 0.0 );
293 
294     if( mpMP  )
295         mpMP->get_Duration( &aRefTime );
296 
297     return aRefTime;
298 }
299 
300 
setMediaTime(double fTime)301 void SAL_CALL Player::setMediaTime( double fTime )
302 {
303     ::osl::MutexGuard aGuard(m_aMutex);
304 
305     if( mpMP  )
306     {
307         const bool bPlaying = isPlaying();
308 
309         mpMP->put_CurrentPosition( fTime );
310 
311         if( !bPlaying && mpMC )
312             mpMC->StopWhenReady();
313     }
314 }
315 
316 
getMediaTime()317 double SAL_CALL Player::getMediaTime(  )
318 {
319     ::osl::MutexGuard aGuard(m_aMutex);
320 
321     REFTIME aRefTime( 0.0 );
322 
323     if( mpMP  )
324         mpMP->get_CurrentPosition( &aRefTime );
325 
326     return aRefTime;
327 }
328 
329 
setPlaybackLoop(sal_Bool bSet)330 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
331 {
332     ::osl::MutexGuard aGuard(m_aMutex);
333 
334     mbLooping = bSet;
335 }
336 
337 
isPlaybackLoop()338 sal_Bool SAL_CALL Player::isPlaybackLoop(  )
339 {
340     ::osl::MutexGuard aGuard(m_aMutex);
341 
342     return mbLooping;
343 }
344 
345 
setMute(sal_Bool bSet)346 void SAL_CALL Player::setMute( sal_Bool bSet )
347 {
348     ::osl::MutexGuard aGuard(m_aMutex);
349 
350     if (mpBA && (mbMuted != static_cast<bool>(bSet)))
351     {
352         mbMuted = bSet;
353         mpBA->put_Volume( mbMuted ? -10000 : mnUnmutedVolume );
354     }
355 }
356 
357 
isMute()358 sal_Bool SAL_CALL Player::isMute(  )
359 {
360     ::osl::MutexGuard aGuard(m_aMutex);
361 
362     return mbMuted;
363 }
364 
365 
setVolumeDB(sal_Int16 nVolumeDB)366 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
367 {
368     ::osl::MutexGuard aGuard(m_aMutex);
369 
370     mnUnmutedVolume = static_cast< long >( nVolumeDB ) * 100;
371 
372     if( !mbMuted && mpBA )
373         mpBA->put_Volume( mnUnmutedVolume );
374 }
375 
376 
getVolumeDB()377 sal_Int16 SAL_CALL Player::getVolumeDB(  )
378 {
379     ::osl::MutexGuard aGuard(m_aMutex);
380 
381     return static_cast< sal_Int16 >( mnUnmutedVolume / 100 );
382 }
383 
384 
getPreferredPlayerWindowSize()385 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize(  )
386 {
387     ::osl::MutexGuard aGuard(m_aMutex);
388 
389     awt::Size aSize( 0, 0 );
390 
391     if( mpBV )
392     {
393         long nWidth = 0, nHeight = 0;
394 
395         mpBV->GetVideoSize( &nWidth, &nHeight );
396         aSize.Width = nWidth;
397         aSize.Height = nHeight;
398     }
399 
400     return aSize;
401 }
402 
403 
createPlayerWindow(const uno::Sequence<uno::Any> & aArguments)404 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
405 {
406     ::osl::MutexGuard aGuard(m_aMutex);
407 
408     uno::Reference< ::media::XPlayerWindow >    xRet;
409     awt::Size                                   aSize( getPreferredPlayerWindowSize() );
410 
411     if( mpVW && aSize.Width > 0 && aSize.Height > 0 )
412     {
413         ::avmedia::win::Window* pWindow = new ::avmedia::win::Window( mxMgr, *this );
414 
415         xRet = pWindow;
416 
417         if( !pWindow->create( aArguments ) )
418             xRet.clear();
419     }
420 
421     return xRet;
422 }
423 
424 
createFrameGrabber()425 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber(  )
426 {
427     uno::Reference< media::XFrameGrabber > xRet;
428 
429     if( !maURL.isEmpty() )
430     {
431         FrameGrabber* pGrabber = new FrameGrabber( mxMgr );
432 
433         xRet = pGrabber;
434 
435         if( !pGrabber->create( maURL ) )
436             xRet.clear();
437     }
438 
439     return xRet;
440 }
441 
442 
getImplementationName()443 OUString SAL_CALL Player::getImplementationName(  )
444 {
445     return AVMEDIA_WIN_PLAYER_IMPLEMENTATIONNAME;
446 }
447 
448 
supportsService(const OUString & ServiceName)449 sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
450 {
451     return cppu::supportsService(this, ServiceName);
452 }
453 
454 
getSupportedServiceNames()455 uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames(  )
456 {
457     return { AVMEDIA_WIN_PLAYER_SERVICENAME };
458 }
459 
460 } // namespace win
461 } // namespace avmedia
462 
463 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
464