xref: /trunk/main/avmedia/source/macavf/macavf_player.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
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<uno::XComponentContext> & rxContext)87 Player::Player( const uno::Reference< uno::XComponentContext >& rxContext )
88 :   mxContext( rxContext )
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 {
174     if( !mpPlayer )
175         return;
176 #if 0
177     const AVPlayerStatus eStatus = [mpPlayer status];
178     OSL_TRACE ("Player::start status=%d", (int)eStatus);
179     if( eStatus == AVPlayerStatusReadyToPlay)
180 #endif
181         [mpPlayer play];
182     // else // TODO: delay until it becomes ready
183 }
184 
185 // ------------------------------------------------------------------------------
186 
stop()187 void SAL_CALL Player::stop()
188 {
189     if( !mpPlayer )
190         return;
191     const bool bPlaying = isPlaying();
192     OSL_TRACE ("Player::stop() playing=%d", bPlaying);
193     if( bPlaying )
194         [mpPlayer pause];
195 }
196 
197 // ------------------------------------------------------------------------------
198 
isPlaying()199 sal_Bool SAL_CALL Player::isPlaying()
200 {
201     if( !mpPlayer )
202         return false;
203     const float fRate = [mpPlayer rate];
204     return (fRate != 0.0);
205 }
206 
207 // ------------------------------------------------------------------------------
208 
getDuration()209 double SAL_CALL Player::getDuration()
210 {
211     // slideshow checks for non-zero duration, so cheat here
212     double duration = 0.01;
213 
214     if( mpPlayer )
215     {
216         AVPlayerItem* pItem = [mpPlayer currentItem];
217         if( [pItem status] == AVPlayerItemStatusReadyToPlay )
218             duration = CMTimeGetSeconds( [pItem duration] );
219         else // fall back to AVAsset's best guess
220             duration = CMTimeGetSeconds( [[pItem asset] duration] );
221     }
222 
223     return duration;
224 }
225 
226 // ------------------------------------------------------------------------------
227 
setMediaTime(double fTime)228 void SAL_CALL Player::setMediaTime( double fTime )
229 {
230     OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime);
231     if( mpPlayer )
232         [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
233 }
234 
235 // ------------------------------------------------------------------------------
236 
getMediaTime()237 double SAL_CALL Player::getMediaTime()
238 {
239     if( !mpPlayer )
240         return 0.0;
241 
242     const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
243     OSL_TRACE( "Player::getMediaTime() = %.3fsec", position);
244     if( position >= mfStopTime )
245         if( isPlaying() )
246             stop();
247 
248     return position;
249 }
250 
251 // ------------------------------------------------------------------------------
252 
setStopTime(double fTime)253 void SAL_CALL Player::setStopTime( double fTime )
254 {
255     OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime);
256     mfStopTime = fTime;
257 }
258 
259 // ------------------------------------------------------------------------------
260 
getStopTime()261 double SAL_CALL Player::getStopTime()
262 {
263     return mfStopTime;
264 }
265 
266 // ------------------------------------------------------------------------------
267 
setRate(double fRate)268 void SAL_CALL Player::setRate( double fRate )
269 {
270     OSL_TRACE ("Player::setRate( %.3f)", fRate);
271     if( !mpPlayer )
272         return;
273 
274     // playback rate: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
275     [mpPlayer setRate: fRate];
276 }
277 
278 // ------------------------------------------------------------------------------
279 
getRate()280 double SAL_CALL Player::getRate()
281 {
282     // macavf: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards
283     const double fRate = mpPlayer ? (double)[mpPlayer rate] : 1.0;
284     OSL_TRACE ("Player::getRate() = %.3f", fRate);
285     return fRate;
286 }
287 
288 // ------------------------------------------------------------------------------
289 
setPlaybackLoop(sal_Bool bSet)290 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
291 {
292     OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet );
293     mbLooping = bSet;
294 }
295 
296 // ------------------------------------------------------------------------------
297 
isPlaybackLoop()298 sal_Bool SAL_CALL Player::isPlaybackLoop()
299 {
300     const bool bRet = mbLooping;
301     OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet );
302     return bRet;
303 }
304 
305 // ------------------------------------------------------------------------------
306 
setMute(sal_Bool bSet)307 void SAL_CALL Player::setMute( sal_Bool bSet )
308 {
309     OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume );
310 
311     if( !mpPlayer )
312         return;
313 
314     mbMuted = (bSet == TRUE);
315     [mpPlayer setMuted:mbMuted];
316 }
317 
318 // ------------------------------------------------------------------------------
319 
isMute()320 sal_Bool SAL_CALL Player::isMute()
321 {
322     OSL_TRACE ("Player::isMuted() = %d", mbMuted);
323     return mbMuted;
324 }
325 
326 // ------------------------------------------------------------------------------
327 
setVolumeDB(sal_Int16 nVolumeDB)328 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
329 {
330     // -40dB <-> AVPlayer volume 0.0
331     //   0dB <-> AVPlayer volume 1.0
332     mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
333     OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume );
334 
335     // change volume
336     if( !mbMuted && mpPlayer )
337         [mpPlayer setVolume:mfUnmutedVolume];
338 }
339 
340 // ------------------------------------------------------------------------------
341 
getVolumeDB()342 sal_Int16 SAL_CALL Player::getVolumeDB()
343 {
344     if( !mpPlayer )
345         return 0;
346 
347     // get the actual volume
348     const float fVolume = [mpPlayer volume];
349 
350     // convert into Dezibel value
351     // -40dB <-> AVPlayer volume 0.0
352     //   0dB <-> AVPlayer volume 1.0
353     const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
354 
355     return (sal_Int16)nVolumeDB;
356 }
357 
358 // ------------------------------------------------------------------------------
359 
getPreferredPlayerWindowSize()360 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
361 {
362     awt::Size aSize( 0, 0 ); // default size
363 
364     AVAsset* pMovie = [[mpPlayer currentItem] asset];
365     NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
366     AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*)[pVideoTracks firstObject];
367     if( pFirstVideoTrack )
368     {
369         const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
370         aSize = awt::Size( aPrefSize.width, aPrefSize.height );
371     }
372 
373     return aSize;
374 }
375 
376 // ------------------------------------------------------------------------------
377 
createPlayerWindow(const uno::Sequence<uno::Any> & aArguments)378 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
379 {
380     // get the preferred window size
381     const awt::Size aSize( getPreferredPlayerWindowSize() );
382     OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() );
383 
384     // get the parent view
385     sal_IntPtr nNSViewPtr = NULL;
386     aArguments[0] >>= nNSViewPtr;
387     NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
388 
389     // check the window parameters
390     uno::Reference< ::media::XPlayerWindow > xRet;
391     if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) )
392          return xRet;
393 
394     // create the window
395     ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxContext, *this, pParentView );
396     xRet = pWindow;
397     return xRet;
398 }
399 
400 // ------------------------------------------------------------------------------
401 
createFrameGrabber()402 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
403 {
404     uno::Reference< media::XFrameGrabber > xRet;
405     OSL_TRACE ("Player::createFrameGrabber");
406 
407     FrameGrabber* pGrabber = new FrameGrabber( mxContext );
408     AVAsset* pMovie = [[mpPlayer currentItem] asset];
409     if( pGrabber->create( pMovie ) )
410         xRet = pGrabber;
411 
412     return xRet;
413 }
414 
415 // ------------------------------------------------------------------------------
416 
getImplementationName()417 ::rtl::OUString SAL_CALL Player::getImplementationName(  )
418 {
419     return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) );
420 }
421 
422 // ------------------------------------------------------------------------------
423 
supportsService(const::rtl::OUString & ServiceName)424 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
425 {
426     return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
427 }
428 
429 // ------------------------------------------------------------------------------
430 
getSupportedServiceNames()431 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames(  )
432 {
433     uno::Sequence< ::rtl::OUString > aRet(1);
434     aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
435 
436     return aRet;
437 }
438 
439 } // namespace macavf
440 } // namespace avmedia
441