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