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 #include "macavf_player.hxx"
24 #include "macavf_framegrabber.hxx"
25 #include "macavf_window.hxx"
26 
27 #include <cmath> // for log10()
28 
29 using namespace ::com::sun::star;
30 
31 #include <hash_map>
32 typedef std::hash_map<NSObject*,avmedia::macavf::MacAVObserverHandler*> HandlersForObject;
33 
34 @implementation MacAVObserverObject
35 {
36     HandlersForObject maHandlersForObject;
37 }
38 - (void)observeValueForKeyPath:(NSString*)pKeyPath ofObject:(id)pObject change:(NSDictionary*)pChangeDict context:(void*)pContext
39 {
40     NSString* pDictStr = [NSString stringWithFormat:@"%@", pChangeDict];
41     OSL_TRACE( "MacAVObserver::onKeyChange k=\"%s\" c=%s", [pKeyPath UTF8String], [pDictStr UTF8String]);
42     avmedia::macavf::MacAVObserverHandler* pHandler = (avmedia::macavf::MacAVObserverHandler*)pContext;
43     pHandler->handleObservation( pKeyPath );
44 }
45 
46 - (void)onNotification:(NSNotification*)pNotification
47 {
48     NSString* pNoteName = (NSString*)[pNotification name];
49     OSL_TRACE( "MacAVObserver::onNotification key=\"%s\"", [pNoteName UTF8String]);
50     HandlersForObject::iterator it = maHandlersForObject.find( [pNotification object]);
51     if( it != maHandlersForObject.end() )
52         (*it).second->handleObservation( pNoteName );
53 }
54 
55 - (void)setHandlerForObject:(NSObject*)pObject handler:(avmedia::macavf::MacAVObserverHandler*)pHandler
56 {
57     maHandlersForObject[ pObject] = pHandler;
58 }
59 
60 - (void)removeHandlerForObject:(NSObject*)pObject
61 {
62     maHandlersForObject.erase( pObject);
63 }
64 
65 @end
66 
67 
68 namespace avmedia { namespace macavf {
69 
70 MacAVObserverObject* MacAVObserverHandler::mpMacAVObserverObject = NULL;
71 
getObserver() const72 MacAVObserverObject* MacAVObserverHandler::getObserver() const
73 {
74     if( !mpMacAVObserverObject)
75     {
76         mpMacAVObserverObject = [MacAVObserverObject alloc];
77         [mpMacAVObserverObject retain];
78     }
79     return mpMacAVObserverObject;
80 }
81 
82 
83 // ----------------
84 // - Player -
85 // ----------------
86 
Player(const uno::Reference<lang::XMultiServiceFactory> & rxMgr)87 Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr )
88 :   mxMgr( rxMgr )
89 ,   mpPlayer( NULL )
90 ,   mfUnmutedVolume( 0 )
91 ,   mfStopTime( DBL_MAX )
92 ,   mbMuted( false )
93 ,   mbLooping( false )
94 {}
95 
96 // ------------------------------------------------------------------------------
97 
~Player()98 Player::~Player()
99 {
100     if( !mpPlayer )
101         return;
102     // remove the observers
103     [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
104     AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
105     [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
106         name:AVPlayerItemDidPlayToEndTimeNotification
107         object:pOldPlayerItem];
108     [getObserver() removeHandlerForObject:pOldPlayerItem];
109     // release the AVPlayer
110     CFRelease( mpPlayer );
111 }
112 
113 // ------------------------------------------------------------------------------
114 
handleObservation(NSString * pKeyPath)115 bool Player::handleObservation( NSString* pKeyPath )
116 {
117     OSL_TRACE( "AVPlayer::handleObservation key=\"%s\"", [pKeyPath UTF8String]);
118     if( [pKeyPath isEqualToString:AVPlayerItemDidPlayToEndTimeNotification])
119     {
120         OSL_TRACE( "AVPlayer replay=%d", mbLooping);
121         if( mbLooping )
122             setMediaTime( 0.0);
123     }
124     return true;
125 }
126 
127 // ------------------------------------------------------------------------------
128 
create(const::rtl::OUString & rURL)129 bool Player::create( const ::rtl::OUString& rURL )
130 {
131     // get the media asset
132     NSString* aNSStr = [NSString stringWithCharacters:rURL.getStr() length:rURL.getLength()];
133     NSURL* aNSURL = [NSURL URLWithString: [aNSStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
134     // get the matching AVPlayerItem
135     AVPlayerItem* pPlayerItem = [AVPlayerItem playerItemWithURL:aNSURL];
136 
137     // create or update the AVPlayer with the new AVPlayerItem
138     if( !mpPlayer )
139     {
140         mpPlayer = [AVPlayer playerWithPlayerItem:pPlayerItem];
141         CFRetain( mpPlayer );
142         [mpPlayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
143     }
144     else
145     {
146         // remove the obsoleted observers
147         AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
148         [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
149         [getObserver() removeHandlerForObject:pOldPlayerItem];
150         [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
151             name:AVPlayerItemDidPlayToEndTimeNotification
152             object:pOldPlayerItem];
153         // replace the playeritem
154         [mpPlayer replaceCurrentItemWithPlayerItem:pPlayerItem];
155     }
156 
157     // observe the status of the current player item
158     [mpPlayer addObserver:getObserver() forKeyPath:@"currentItem.status" options:0 context:this];
159 
160     // observe playback-end needed for playback looping
161     [[NSNotificationCenter defaultCenter] addObserver:getObserver()
162         selector:@selector(onNotification:)
163         name:AVPlayerItemDidPlayToEndTimeNotification
164         object:pPlayerItem];
165     [getObserver() setHandlerForObject:pPlayerItem handler:this];
166 
167     return true;
168 }
169 
170 // ------------------------------------------------------------------------------
171 
start()172 void SAL_CALL Player::start()
173     throw (uno::RuntimeException)
174 {
175     if( !mpPlayer )
176         return;
177 #if 0
178     const AVPlayerStatus eStatus = [mpPlayer status];
179     OSL_TRACE ("Player::start status=%d", (int)eStatus);
180     if( eStatus == AVPlayerStatusReadyToPlay)
181 #endif
182         [mpPlayer play];
183     // else // TODO: delay until it becomes ready
184 }
185 
186 // ------------------------------------------------------------------------------
187 
stop()188 void SAL_CALL Player::stop()
189     throw (uno::RuntimeException)
190 {
191     if( !mpPlayer )
192         return;
193     const bool bPlaying = isPlaying();
194     OSL_TRACE ("Player::stop() playing=%d", bPlaying);
195     if( bPlaying )
196         [mpPlayer pause];
197 }
198 
199 // ------------------------------------------------------------------------------
200 
isPlaying()201 sal_Bool SAL_CALL Player::isPlaying()
202     throw (uno::RuntimeException)
203 {
204     if( !mpPlayer )
205         return false;
206     const float fRate = [mpPlayer rate];
207     return (fRate != 0.0);
208 }
209 
210 // ------------------------------------------------------------------------------
211 
getDuration()212 double SAL_CALL Player::getDuration()
213     throw (uno::RuntimeException)
214 {
215     // slideshow checks for non-zero duration, so cheat here
216     double duration = 0.01;
217 
218     if( mpPlayer )
219     {
220         AVPlayerItem* pItem = [mpPlayer currentItem];
221         if( [pItem status] == AVPlayerItemStatusReadyToPlay )
222             duration = CMTimeGetSeconds( [pItem duration] );
223         else // fall back to AVAsset's best guess
224             duration = CMTimeGetSeconds( [[pItem asset] duration] );
225     }
226 
227     return duration;
228 }
229 
230 // ------------------------------------------------------------------------------
231 
setMediaTime(double fTime)232 void SAL_CALL Player::setMediaTime( double fTime )
233     throw (uno::RuntimeException)
234 {
235     OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime);
236     if( mpPlayer )
237         [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
238 }
239 
240 // ------------------------------------------------------------------------------
241 
getMediaTime()242 double SAL_CALL Player::getMediaTime()
243     throw (uno::RuntimeException)
244 {
245     if( !mpPlayer )
246         return 0.0;
247 
248     const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
249     OSL_TRACE( "Player::getMediaTime() = %.3fsec", position);
250     if( position >= mfStopTime )
251         if( isPlaying() )
252             stop();
253 
254     return position;
255 }
256 
257 // ------------------------------------------------------------------------------
258 
setStopTime(double fTime)259 void SAL_CALL Player::setStopTime( double fTime )
260     throw (uno::RuntimeException)
261 {
262     OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime);
263     mfStopTime = fTime;
264 }
265 
266 // ------------------------------------------------------------------------------
267 
getStopTime()268 double SAL_CALL Player::getStopTime()
269     throw (uno::RuntimeException)
270 {
271     return mfStopTime;
272 }
273 
274 // ------------------------------------------------------------------------------
275 
setRate(double fRate)276 void SAL_CALL Player::setRate( double fRate )
277     throw (uno::RuntimeException)
278 {
279     OSL_TRACE ("Player::setRate( %.3f)", fRate);
280     if( !mpPlayer )
281         return;
282 
283     // playback rate: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
284     [mpPlayer setRate: fRate];
285 }
286 
287 // ------------------------------------------------------------------------------
288 
getRate()289 double SAL_CALL Player::getRate()
290     throw (uno::RuntimeException)
291 {
292     // macavf: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
293     const double fRate = mpPlayer ? (double)[mpPlayer rate] : 1.0;
294     OSL_TRACE ("Player::getRate() = %.3f", fRate);
295     return fRate;
296 }
297 
298 // ------------------------------------------------------------------------------
299 
setPlaybackLoop(sal_Bool bSet)300 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
301     throw (uno::RuntimeException)
302 {
303     OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet );
304     mbLooping = bSet;
305 }
306 
307 // ------------------------------------------------------------------------------
308 
isPlaybackLoop()309 sal_Bool SAL_CALL Player::isPlaybackLoop()
310     throw (uno::RuntimeException)
311 {
312     const bool bRet = mbLooping;
313     OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet );
314     return bRet;
315 }
316 
317 // ------------------------------------------------------------------------------
318 
setMute(sal_Bool bSet)319 void SAL_CALL Player::setMute( sal_Bool bSet )
320     throw (uno::RuntimeException)
321 {
322     OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume );
323 
324     if( !mpPlayer )
325         return;
326 
327     mbMuted = (bSet == TRUE);
328     [mpPlayer setMuted:mbMuted];
329 }
330 
331 // ------------------------------------------------------------------------------
332 
isMute()333 sal_Bool SAL_CALL Player::isMute()
334     throw (uno::RuntimeException)
335 {
336     OSL_TRACE ("Player::isMuted() = %d", mbMuted);
337     return mbMuted;
338 }
339 
340 // ------------------------------------------------------------------------------
341 
setVolumeDB(sal_Int16 nVolumeDB)342 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
343 	throw (uno::RuntimeException)
344 {
345     // -40dB <-> AVPlayer volume 0.0
346     //   0dB <-> AVPlayer volume 1.0
347     mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
348     OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume );
349 
350     // change volume
351     if( !mbMuted && mpPlayer )
352         [mpPlayer setVolume:mfUnmutedVolume];
353 }
354 
355 // ------------------------------------------------------------------------------
356 
getVolumeDB()357 sal_Int16 SAL_CALL Player::getVolumeDB()
358 	throw (uno::RuntimeException)
359 {
360     if( !mpPlayer )
361         return 0;
362 
363     // get the actual volume
364     const float fVolume = [mpPlayer volume];
365 
366     // convert into Dezibel value
367     // -40dB <-> AVPlayer volume 0.0
368     //   0dB <-> AVPlayer volume 1.0
369     const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
370 
371     return (sal_Int16)nVolumeDB;
372 }
373 
374 // ------------------------------------------------------------------------------
375 
getPreferredPlayerWindowSize()376 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
377     throw (uno::RuntimeException)
378 {
379     awt::Size aSize( 0, 0 ); // default size
380 
381     AVAsset* pMovie = [[mpPlayer currentItem] asset];
382     NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
383     AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*)[pVideoTracks firstObject];
384     if( pFirstVideoTrack )
385     {
386         const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
387         aSize = awt::Size( aPrefSize.width, aPrefSize.height );
388     }
389 
390     return aSize;
391 }
392 
393 // ------------------------------------------------------------------------------
394 
createPlayerWindow(const uno::Sequence<uno::Any> & aArguments)395 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
396     throw (uno::RuntimeException)
397 {
398     // get the preferred window size
399     const awt::Size aSize( getPreferredPlayerWindowSize() );
400     OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() );
401 
402     // get the parent view
403     sal_IntPtr nNSViewPtr = NULL;
404     aArguments[0] >>= nNSViewPtr;
405     NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
406 
407     // check the window parameters
408     uno::Reference< ::media::XPlayerWindow > xRet;
409     if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) )
410          return xRet;
411 
412     // create the window
413     ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxMgr, *this, pParentView );
414     xRet = pWindow;
415     return xRet;
416 }
417 
418 // ------------------------------------------------------------------------------
419 
createFrameGrabber()420 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
421     throw (uno::RuntimeException)
422 {
423     uno::Reference< media::XFrameGrabber > xRet;
424     OSL_TRACE ("Player::createFrameGrabber");
425 
426     FrameGrabber* pGrabber = new FrameGrabber( mxMgr );
427     AVAsset* pMovie = [[mpPlayer currentItem] asset];
428     if( pGrabber->create( pMovie ) )
429         xRet = pGrabber;
430 
431     return xRet;
432 }
433 
434 // ------------------------------------------------------------------------------
435 
getImplementationName()436 ::rtl::OUString SAL_CALL Player::getImplementationName(  )
437     throw (uno::RuntimeException)
438 {
439     return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) );
440 }
441 
442 // ------------------------------------------------------------------------------
443 
supportsService(const::rtl::OUString & ServiceName)444 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
445     throw (uno::RuntimeException)
446 {
447     return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
448 }
449 
450 // ------------------------------------------------------------------------------
451 
getSupportedServiceNames()452 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames(  )
453     throw (uno::RuntimeException)
454 {
455     uno::Sequence< ::rtl::OUString > aRet(1);
456     aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
457 
458     return aRet;
459 }
460 
461 } // namespace macavf
462 } // namespace avmedia
463 
464