1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include "framegrabber.hxx"
24 #include <cmath> // for log10()
26 using namespace ::com::sun::star;
28 @implementation MacAVObserverObject
30 - (void)observeValueForKeyPath:(NSString*)pKeyPath ofObject:(id)pObject change:(NSDictionary*)pChangeDict context:(void*)pContext
33 NSString* pDictStr = [NSString stringWithFormat:@"%@", pChangeDict];
34 OSL_TRACE( "MacAVObserver::onKeyChange k=\"%s\" c=%s", [pKeyPath UTF8String], [pDictStr UTF8String]);
35 avmedia::macavf::MacAVObserverHandler* pHandler = static_cast<avmedia::macavf::MacAVObserverHandler*>(pContext);
36 pHandler->handleObservation( pKeyPath );
39 - (void)onNotification:(NSNotification*)pNotification
41 NSString* pNoteName = (NSString*)[pNotification name];
42 OSL_TRACE( "MacAVObserver::onNotification key=\"%s\"", [pNoteName UTF8String]);
43 HandlersForObject::iterator it = maHandlersForObject.find( [pNotification object]);
44 if( it != maHandlersForObject.end() )
45 (*it).second->handleObservation( pNoteName );
48 - (void)setHandlerForObject:(NSObject*)pObject handler:(avmedia::macavf::MacAVObserverHandler*)pHandler
50 maHandlersForObject[ pObject] = pHandler;
53 - (void)removeHandlerForObject:(NSObject*)pObject
55 maHandlersForObject.erase( pObject);
61 namespace avmedia { namespace macavf {
63 MacAVObserverObject* MacAVObserverHandler::mpMacAVObserverObject = NULL;
65 MacAVObserverObject* MacAVObserverHandler::getObserver()
67 if( !mpMacAVObserverObject)
69 mpMacAVObserverObject = [MacAVObserverObject alloc];
70 [mpMacAVObserverObject retain];
72 return mpMacAVObserverObject;
80 Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr )
83 , mfUnmutedVolume( 0 )
84 , mfStopTime( DBL_MAX )
89 // ------------------------------------------------------------------------------
95 // remove the observers
96 [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
97 AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
98 [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
99 name:AVPlayerItemDidPlayToEndTimeNotification
100 object:pOldPlayerItem];
101 [getObserver() removeHandlerForObject:pOldPlayerItem];
102 // release the AVPlayer
103 CFRelease( mpPlayer );
106 // ------------------------------------------------------------------------------
108 bool Player::handleObservation( NSString* pKeyPath )
110 OSL_TRACE( "AVPlayer::handleObservation key=\"%s\"", [pKeyPath UTF8String]);
111 if( [pKeyPath isEqualToString:AVPlayerItemDidPlayToEndTimeNotification])
113 OSL_TRACE( "AVPlayer replay=%d", mbLooping);
120 // ------------------------------------------------------------------------------
122 bool Player::create( const ::rtl::OUString& rURL )
124 // get the media asset
125 NSString* aNSStr = [NSString stringWithCharacters:rURL.getStr() length:rURL.getLength()];
126 NSURL* aNSURL = [NSURL URLWithString: [aNSStr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
127 // get the matching AVPlayerItem
128 AVPlayerItem* pPlayerItem = [AVPlayerItem playerItemWithURL:aNSURL];
130 // create or update the AVPlayer with the new AVPlayerItem
133 mpPlayer = [AVPlayer playerWithPlayerItem:pPlayerItem];
134 CFRetain( mpPlayer );
135 [mpPlayer setActionAtItemEnd:AVPlayerActionAtItemEndNone];
139 // remove the obsoleted observers
140 AVPlayerItem* pOldPlayerItem = [mpPlayer currentItem];
141 [mpPlayer removeObserver:getObserver() forKeyPath:@"currentItem.status"];
142 [getObserver() removeHandlerForObject:pOldPlayerItem];
143 [[NSNotificationCenter defaultCenter] removeObserver:getObserver()
144 name:AVPlayerItemDidPlayToEndTimeNotification
145 object:pOldPlayerItem];
146 // replace the playeritem
147 [mpPlayer replaceCurrentItemWithPlayerItem:pPlayerItem];
150 // observe the status of the current player item
151 [mpPlayer addObserver:getObserver() forKeyPath:@"currentItem.status" options:0 context:this];
153 // observe playback-end needed for playback looping
154 [[NSNotificationCenter defaultCenter] addObserver:getObserver()
155 selector:@selector(onNotification:)
156 name:AVPlayerItemDidPlayToEndTimeNotification
158 [getObserver() setHandlerForObject:pPlayerItem handler:this];
163 // ------------------------------------------------------------------------------
165 void SAL_CALL Player::start()
166 throw (uno::RuntimeException)
171 const AVPlayerStatus eStatus = [mpPlayer status];
172 OSL_TRACE ("Player::start status=%d", (int)eStatus);
173 if( eStatus == AVPlayerStatusReadyToPlay)
178 // else // TODO: delay until it becomes ready
181 // ------------------------------------------------------------------------------
183 void SAL_CALL Player::stop()
184 throw (uno::RuntimeException)
188 const bool bPlaying = isPlaying();
189 OSL_TRACE ("Player::stop() playing=%d", bPlaying);
194 // ------------------------------------------------------------------------------
196 sal_Bool SAL_CALL Player::isPlaying()
197 throw (uno::RuntimeException)
201 const float fRate = [mpPlayer rate];
202 return (fRate != 0.0);
205 // ------------------------------------------------------------------------------
207 double SAL_CALL Player::getDuration()
208 throw (uno::RuntimeException)
210 // slideshow checks for non-zero duration, so cheat here
211 double duration = 0.01;
215 AVPlayerItem* pItem = [mpPlayer currentItem];
216 if( [pItem status] == AVPlayerItemStatusReadyToPlay )
217 duration = CMTimeGetSeconds( [pItem duration] );
218 else // fall back to AVAsset's best guess
219 duration = CMTimeGetSeconds( [[pItem asset] duration] );
225 // ------------------------------------------------------------------------------
227 void SAL_CALL Player::setMediaTime( double fTime )
228 throw (uno::RuntimeException)
230 OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime);
232 [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ];
235 // ------------------------------------------------------------------------------
237 double SAL_CALL Player::getMediaTime()
238 throw (uno::RuntimeException)
243 const double position = CMTimeGetSeconds( [mpPlayer currentTime] );
244 OSL_TRACE( "Player::getMediaTime() = %.3fsec", position);
245 if( position >= mfStopTime )
252 // ------------------------------------------------------------------------------
254 void SAL_CALL Player::setStopTime( double fTime )
255 throw (uno::RuntimeException)
257 OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime);
261 // ------------------------------------------------------------------------------
263 double SAL_CALL Player::getStopTime()
264 throw (uno::RuntimeException)
269 // ------------------------------------------------------------------------------
271 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
272 throw (uno::RuntimeException)
274 OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet );
278 // ------------------------------------------------------------------------------
280 sal_Bool SAL_CALL Player::isPlaybackLoop()
281 throw (uno::RuntimeException)
283 const bool bRet = mbLooping;
284 OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet );
288 // ------------------------------------------------------------------------------
290 void SAL_CALL Player::setMute( sal_Bool bSet )
291 throw (uno::RuntimeException)
293 OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume );
299 [mpPlayer setMuted:mbMuted];
302 // ------------------------------------------------------------------------------
304 sal_Bool SAL_CALL Player::isMute()
305 throw (uno::RuntimeException)
307 OSL_TRACE ("Player::isMuted() = %d", mbMuted);
311 // ------------------------------------------------------------------------------
313 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
314 throw (uno::RuntimeException)
316 // -40dB <-> AVPlayer volume 0.0
317 // 0dB <-> AVPlayer volume 1.0
318 mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 );
319 OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume );
322 if( !mbMuted && mpPlayer )
323 [mpPlayer setVolume:mfUnmutedVolume];
326 // ------------------------------------------------------------------------------
328 sal_Int16 SAL_CALL Player::getVolumeDB()
329 throw (uno::RuntimeException)
334 // get the actual volume
335 const float fVolume = [mpPlayer volume];
337 // convert into Dezibel value
338 // -40dB <-> AVPlayer volume 0.0
339 // 0dB <-> AVPlayer volume 1.0
340 const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume));
342 return (sal_Int16)nVolumeDB;
345 // ------------------------------------------------------------------------------
347 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
348 throw (uno::RuntimeException)
350 awt::Size aSize( 0, 0 ); // default size
352 AVAsset* pMovie = [[mpPlayer currentItem] asset];
353 NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo];
354 if ([pVideoTracks count] > 0)
356 AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*) [pVideoTracks objectAtIndex:0];
357 const CGSize aPrefSize = [pFirstVideoTrack naturalSize];
358 aSize = awt::Size( aPrefSize.width, aPrefSize.height );
364 // ------------------------------------------------------------------------------
366 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments )
367 throw (uno::RuntimeException)
369 // get the preferred window size
370 const awt::Size aSize( getPreferredPlayerWindowSize() );
371 OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() );
373 // get the parent view
374 sal_IntPtr nNSViewPtr = 0;
375 aArguments[0] >>= nNSViewPtr;
376 NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr);
378 // check the window parameters
379 uno::Reference< ::media::XPlayerWindow > xRet;
380 if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) )
384 ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxMgr, *this, pParentView );
389 // ------------------------------------------------------------------------------
391 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
392 throw (uno::RuntimeException)
394 uno::Reference< media::XFrameGrabber > xRet;
395 OSL_TRACE ("Player::createFrameGrabber");
397 FrameGrabber* pGrabber = new FrameGrabber( mxMgr );
398 AVAsset* pMovie = [[mpPlayer currentItem] asset];
399 if( pGrabber->create( pMovie ) )
405 // ------------------------------------------------------------------------------
407 ::rtl::OUString SAL_CALL Player::getImplementationName( )
408 throw (uno::RuntimeException)
410 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) );
413 // ------------------------------------------------------------------------------
415 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
416 throw (uno::RuntimeException)
418 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
421 // ------------------------------------------------------------------------------
423 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames( )
424 throw (uno::RuntimeException)
426 uno::Sequence< ::rtl::OUString > aRet(1);
427 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) );
432 } // namespace macavf
433 } // namespace avmedia
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */