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