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 "player.hxx"
24 #include "framegrabber.hxx"
25 #include "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 AVAsset* Player::getMovie()
116 {
117     if( !mpPlayer )
118         return nil;
119     AVAsset* pMovie = [[mpPlayer currentItem] asset];
120     OSL_ASSERT( pMovie );
121     return pMovie;
122 }
123 
124 // ------------------------------------------------------------------------------
125 
126 bool Player::handleObservation( NSString* pKeyPath )
127 {
128     OSL_TRACE( "AVPlayer::handleObservation key=\"%s\"", [pKeyPath UTF8String]);
129     if( [pKeyPath isEqualToString:AVPlayerItemDidPlayToEndTimeNotification])
130     {
131         OSL_TRACE( "AVPlayer replay=%d", mbLooping);
132         if( mbLooping )
133             setMediaTime( 0.0);
134     }
135     return true;
136 }
137 
138 // ------------------------------------------------------------------------------
139 
140 bool Player::create( const ::rtl::OUString& rURL )
141 {
142     maURL = rURL;
143 
144     // get the media asset
145     NSString* aNSStr = [NSString stringWithCharacters:rURL.getStr() length:rURL.getLength()];
146     NSURL* aNSURL = [NSURL URLWithString: [aNSStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
147     // get the matching AVPlayerItem
148     AVPlayerItem* pPlayerItem = [AVPlayerItem playerItemWithURL:aNSURL];
149 
150     // create or update the AVPlayer with the new AVPlayerItem
151     if( !mpPlayer )
152     {
153         mpPlayer = [AVPlayer playerWithPlayerItem:pPlayerItem];
154         CFRetain( mpPlayer );
155         [mpPlayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
156     }
157     else
158     {
159         // remove the obsoleted observers
160         AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
161         [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
162         [getObserver() removeHandlerForObject:pOldPlayerItem];
163         [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
164             name:AVPlayerItemDidPlayToEndTimeNotification
165             object:pOldPlayerItem];
166         // replace the playeritem
167         [mpPlayer replaceCurrentItemWithPlayerItem:pPlayerItem];
168     }
169 
170     // observe the status of the current player item
171     [mpPlayer addObserver:getObserver() forKeyPath:@"currentItem.status" options:0 context:this];
172 
173     // observe playback-end needed for playback looping
174     [[NSNotificationCenter defaultCenter] addObserver:getObserver()
175         selector:@selector(onNotification:)
176         name:AVPlayerItemDidPlayToEndTimeNotification
177         object:pPlayerItem];
178     [getObserver() setHandlerForObject:pPlayerItem handler:this];
179 
180     return true;
181 }
182 
183 // ------------------------------------------------------------------------------
184 
185 void SAL_CALL Player::start()
186     throw (uno::RuntimeException)
187 {
188     if( !mpPlayer )
189         return;
190 #if 0
191     const AVPlayerStatus eStatus = [mpPlayer status];
192     OSL_TRACE ("Player::start status=%d", (int)eStatus);
193     if( eStatus == AVPlayerStatusReadyToPlay)
194 #endif
195         [mpPlayer play];
196     // else // TODO: delay until it becomes ready
197 }
198 
199 // ------------------------------------------------------------------------------
200 
201 void SAL_CALL Player::stop()
202     throw (uno::RuntimeException)
203 {
204     if( !mpPlayer )
205         return;
206     const bool bPlaying = isPlaying();
207     OSL_TRACE ("Player::stop() playing=%d", bPlaying);
208     if( bPlaying )
209         [mpPlayer pause];
210 }
211 
212 // ------------------------------------------------------------------------------
213 
214 sal_Bool SAL_CALL Player::isPlaying()
215     throw (uno::RuntimeException)
216 {
217     if( !mpPlayer )
218         return false;
219     const float fRate = [mpPlayer rate];
220     return (fRate != 0.0);
221 }
222 
223 // ------------------------------------------------------------------------------
224 
225 double SAL_CALL Player::getDuration()
226     throw (uno::RuntimeException)
227 {
228     // slideshow checks for non-zero duration, so cheat here
229     double duration = 0.01;
230 
231     if( mpPlayer )
232     {
233         AVPlayerItem* pItem = [mpPlayer currentItem];
234         duration = CMTimeGetSeconds( [pItem duration] );
235     }
236 
237     return duration;
238 }
239 
240 // ------------------------------------------------------------------------------
241 
242 void SAL_CALL Player::setMediaTime( double fTime )
243     throw (uno::RuntimeException)
244 {
245     OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime);
246     if( mpPlayer )
247         [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
248 }
249 
250 // ------------------------------------------------------------------------------
251 
252 double SAL_CALL Player::getMediaTime()
253     throw (uno::RuntimeException)
254 {
255     if( !mpPlayer )
256         return 0.0;
257 
258     const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
259     OSL_TRACE( "Player::getMediaTime() = %.3fsec", position);
260     if( position >= mfStopTime )
261         if( isPlaying() )
262             stop();
263 
264     return position;
265 }
266 
267 // ------------------------------------------------------------------------------
268 
269 void SAL_CALL Player::setStopTime( double fTime )
270     throw (uno::RuntimeException)
271 {
272     OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime);
273     mfStopTime = fTime;
274 }
275 
276 // ------------------------------------------------------------------------------
277 
278 double SAL_CALL Player::getStopTime()
279     throw (uno::RuntimeException)
280 {
281     return mfStopTime;
282 }
283 
284 // ------------------------------------------------------------------------------
285 
286 void SAL_CALL Player::setRate( double fRate )
287     throw (uno::RuntimeException)
288 {
289     OSL_TRACE ("Player::setRate( %.3f)", fRate);
290     if( !mpPlayer )
291         return;
292 
293     // playback rate: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
294     [mpPlayer setRate: fRate];
295 }
296 
297 // ------------------------------------------------------------------------------
298 
299 double SAL_CALL Player::getRate()
300     throw (uno::RuntimeException)
301 {
302     // macavf: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
303     const double fRate = mpPlayer ? (double)[mpPlayer rate] : 1.0;
304     OSL_TRACE ("Player::getRate() = %.3f", fRate);
305     return fRate;
306 }
307 
308 // ------------------------------------------------------------------------------
309 
310 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
311     throw (uno::RuntimeException)
312 {
313     OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet );
314     mbLooping = bSet;
315 }
316 
317 // ------------------------------------------------------------------------------
318 
319 sal_Bool SAL_CALL Player::isPlaybackLoop()
320     throw (uno::RuntimeException)
321 {
322     const bool bRet = mbLooping;
323     OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet );
324     return bRet;
325 }
326 
327 // ------------------------------------------------------------------------------
328 
329 void SAL_CALL Player::setMute( sal_Bool bSet )
330     throw (uno::RuntimeException)
331 {
332     OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume );
333 
334     if( !mpPlayer )
335         return;
336 
337     mbMuted = (bSet == TRUE);
338     [mpPlayer setMuted:mbMuted];
339 }
340 
341 // ------------------------------------------------------------------------------
342 
343 sal_Bool SAL_CALL Player::isMute()
344     throw (uno::RuntimeException)
345 {
346     OSL_TRACE ("Player::isMuted() = %d", mbMuted);
347     return mbMuted;
348 }
349 
350 // ------------------------------------------------------------------------------
351 
352 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
353 	throw (uno::RuntimeException)
354 {
355     // -40dB <-> AVPlayer volume 0.0
356     //   0dB <-> AVPlayer volume 1.0
357     mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
358     OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume );
359 
360     // change volume
361     if( !mbMuted && mpPlayer )
362         [mpPlayer setVolume:mfUnmutedVolume];
363 }
364 
365 // ------------------------------------------------------------------------------
366 
367 sal_Int16 SAL_CALL Player::getVolumeDB()
368 	throw (uno::RuntimeException)
369 {
370     if( !mpPlayer )
371         return 0;
372 
373     // get the actual volume
374     const float fVolume = [mpPlayer volume];
375 
376     // convert into Dezibel value
377     // -40dB <-> AVPlayer volume 0.0
378     //   0dB <-> AVPlayer volume 1.0
379     const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
380 
381     return (sal_Int16)nVolumeDB;
382 }
383 
384 // ------------------------------------------------------------------------------
385 
386 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
387     throw (uno::RuntimeException)
388 {
389     awt::Size aSize( 0, 0 ); // default size
390 
391     AVAsset* pMovie = [[mpPlayer currentItem] asset];
392     NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
393     AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*)[pVideoTracks firstObject];
394     if( pFirstVideoTrack )
395     {
396         const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
397         aSize = awt::Size( aPrefSize.width, aPrefSize.height );
398     }
399 
400     return aSize;
401 }
402 
403 // ------------------------------------------------------------------------------
404 
405 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
406     throw (uno::RuntimeException)
407 {
408     // get the preferred window size
409     const awt::Size aSize( getPreferredPlayerWindowSize() );
410     OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() );
411 
412     // get the parent view
413     sal_IntPtr nNSViewPtr = NULL;
414     aArguments[0] >>= nNSViewPtr;
415     NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
416 
417     // check the window parameters
418     uno::Reference< ::media::XPlayerWindow > xRet;
419     if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) )
420          return xRet;
421 
422     // create the window
423     ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxMgr, *this, pParentView );
424     xRet = pWindow;
425     return xRet;
426 }
427 
428 // ------------------------------------------------------------------------------
429 
430 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
431     throw (uno::RuntimeException)
432 {
433     uno::Reference< media::XFrameGrabber > xRet;
434     OSL_TRACE ("Player::createFrameGrabber");
435 
436     if( !maURL.isEmpty() )
437     {
438         FrameGrabber* pGrabber = new FrameGrabber( mxMgr );
439         if( pGrabber->create( maURL ) )
440             xRet = pGrabber;
441     }
442 
443     return xRet;
444 }
445 
446 // ------------------------------------------------------------------------------
447 
448 ::rtl::OUString SAL_CALL Player::getImplementationName(  )
449     throw (uno::RuntimeException)
450 {
451     return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) );
452 }
453 
454 // ------------------------------------------------------------------------------
455 
456 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
457     throw (uno::RuntimeException)
458 {
459     return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
460 }
461 
462 // ------------------------------------------------------------------------------
463 
464 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames(  )
465     throw (uno::RuntimeException)
466 {
467     uno::Sequence< ::rtl::OUString > aRet(1);
468     aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
469 
470     return aRet;
471 }
472 
473 } // namespace macavf
474 } // namespace avmedia
475 
476