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