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 
72 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 
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 
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 
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 
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 
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 
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 
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 
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         duration = CMTimeGetSeconds( [pItem duration] );
222     }
223 
224     return duration;
225 }
226 
227 // ------------------------------------------------------------------------------
228 
229 void SAL_CALL Player::setMediaTime( double fTime )
230     throw (uno::RuntimeException)
231 {
232     OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime);
233     if( mpPlayer )
234         [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
235 }
236 
237 // ------------------------------------------------------------------------------
238 
239 double SAL_CALL Player::getMediaTime()
240     throw (uno::RuntimeException)
241 {
242     if( !mpPlayer )
243         return 0.0;
244 
245     const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
246     OSL_TRACE( "Player::getMediaTime() = %.3fsec", position);
247     if( position >= mfStopTime )
248         if( isPlaying() )
249             stop();
250 
251     return position;
252 }
253 
254 // ------------------------------------------------------------------------------
255 
256 void SAL_CALL Player::setStopTime( double fTime )
257     throw (uno::RuntimeException)
258 {
259     OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime);
260     mfStopTime = fTime;
261 }
262 
263 // ------------------------------------------------------------------------------
264 
265 double SAL_CALL Player::getStopTime()
266     throw (uno::RuntimeException)
267 {
268     return mfStopTime;
269 }
270 
271 // ------------------------------------------------------------------------------
272 
273 void SAL_CALL Player::setRate( double fRate )
274     throw (uno::RuntimeException)
275 {
276     OSL_TRACE ("Player::setRate( %.3f)", fRate);
277     if( !mpPlayer )
278         return;
279 
280     // playback rate: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
281     [mpPlayer setRate: fRate];
282 }
283 
284 // ------------------------------------------------------------------------------
285 
286 double SAL_CALL Player::getRate()
287     throw (uno::RuntimeException)
288 {
289     // macavf: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
290     const double fRate = mpPlayer ? (double)[mpPlayer rate] : 1.0;
291     OSL_TRACE ("Player::getRate() = %.3f", fRate);
292     return fRate;
293 }
294 
295 // ------------------------------------------------------------------------------
296 
297 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
298     throw (uno::RuntimeException)
299 {
300     OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet );
301     mbLooping = bSet;
302 }
303 
304 // ------------------------------------------------------------------------------
305 
306 sal_Bool SAL_CALL Player::isPlaybackLoop()
307     throw (uno::RuntimeException)
308 {
309     const bool bRet = mbLooping;
310     OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet );
311     return bRet;
312 }
313 
314 // ------------------------------------------------------------------------------
315 
316 void SAL_CALL Player::setMute( sal_Bool bSet )
317     throw (uno::RuntimeException)
318 {
319     OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume );
320 
321     if( !mpPlayer )
322         return;
323 
324     mbMuted = (bSet == TRUE);
325     [mpPlayer setMuted:mbMuted];
326 }
327 
328 // ------------------------------------------------------------------------------
329 
330 sal_Bool SAL_CALL Player::isMute()
331     throw (uno::RuntimeException)
332 {
333     OSL_TRACE ("Player::isMuted() = %d", mbMuted);
334     return mbMuted;
335 }
336 
337 // ------------------------------------------------------------------------------
338 
339 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
340 	throw (uno::RuntimeException)
341 {
342     // -40dB <-> AVPlayer volume 0.0
343     //   0dB <-> AVPlayer volume 1.0
344     mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
345     OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume );
346 
347     // change volume
348     if( !mbMuted && mpPlayer )
349         [mpPlayer setVolume:mfUnmutedVolume];
350 }
351 
352 // ------------------------------------------------------------------------------
353 
354 sal_Int16 SAL_CALL Player::getVolumeDB()
355 	throw (uno::RuntimeException)
356 {
357     if( !mpPlayer )
358         return 0;
359 
360     // get the actual volume
361     const float fVolume = [mpPlayer volume];
362 
363     // convert into Dezibel value
364     // -40dB <-> AVPlayer volume 0.0
365     //   0dB <-> AVPlayer volume 1.0
366     const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
367 
368     return (sal_Int16)nVolumeDB;
369 }
370 
371 // ------------------------------------------------------------------------------
372 
373 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
374     throw (uno::RuntimeException)
375 {
376     awt::Size aSize( 0, 0 ); // default size
377 
378     AVAsset* pMovie = [[mpPlayer currentItem] asset];
379     NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
380     AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*)[pVideoTracks firstObject];
381     if( pFirstVideoTrack )
382     {
383         const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
384         aSize = awt::Size( aPrefSize.width, aPrefSize.height );
385     }
386 
387     return aSize;
388 }
389 
390 // ------------------------------------------------------------------------------
391 
392 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
393     throw (uno::RuntimeException)
394 {
395     // get the preferred window size
396     const awt::Size aSize( getPreferredPlayerWindowSize() );
397     OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() );
398 
399     // get the parent view
400     sal_IntPtr nNSViewPtr = NULL;
401     aArguments[0] >>= nNSViewPtr;
402     NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
403 
404     // check the window parameters
405     uno::Reference< ::media::XPlayerWindow > xRet;
406     if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) )
407          return xRet;
408 
409     // create the window
410     ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxMgr, *this, pParentView );
411     xRet = pWindow;
412     return xRet;
413 }
414 
415 // ------------------------------------------------------------------------------
416 
417 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
418     throw (uno::RuntimeException)
419 {
420     uno::Reference< media::XFrameGrabber > xRet;
421     OSL_TRACE ("Player::createFrameGrabber");
422 
423     FrameGrabber* pGrabber = new FrameGrabber( mxMgr );
424     AVAsset* pMovie = [[mpPlayer currentItem] asset];
425     if( pGrabber->create( pMovie ) )
426         xRet = pGrabber;
427 
428     return xRet;
429 }
430 
431 // ------------------------------------------------------------------------------
432 
433 ::rtl::OUString SAL_CALL Player::getImplementationName(  )
434     throw (uno::RuntimeException)
435 {
436     return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) );
437 }
438 
439 // ------------------------------------------------------------------------------
440 
441 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
442     throw (uno::RuntimeException)
443 {
444     return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
445 }
446 
447 // ------------------------------------------------------------------------------
448 
449 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames(  )
450     throw (uno::RuntimeException)
451 {
452     uno::Sequence< ::rtl::OUString > aRet(1);
453     aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
454 
455     return aRet;
456 }
457 
458 } // namespace macavf
459 } // namespace avmedia
460 
461