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 if( [pItem status] == AVPlayerItemStatusReadyToPlay ) 222 duration = CMTimeGetSeconds( [pItem duration] ); 223 else // fall back to AVAsset's best guess 224 duration = CMTimeGetSeconds( [[pItem asset] duration] ); 225 } 226 227 return duration; 228 } 229 230 // ------------------------------------------------------------------------------ 231 232 void SAL_CALL Player::setMediaTime( double fTime ) 233 throw (uno::RuntimeException) 234 { 235 OSL_TRACE ("Player::setMediaTime( %.3fsec)", fTime); 236 if( mpPlayer ) 237 [mpPlayer seekToTime: CMTimeMakeWithSeconds(fTime,1000) ]; 238 } 239 240 // ------------------------------------------------------------------------------ 241 242 double SAL_CALL Player::getMediaTime() 243 throw (uno::RuntimeException) 244 { 245 if( !mpPlayer ) 246 return 0.0; 247 248 const double position = CMTimeGetSeconds( [mpPlayer currentTime] ); 249 OSL_TRACE( "Player::getMediaTime() = %.3fsec", position); 250 if( position >= mfStopTime ) 251 if( isPlaying() ) 252 stop(); 253 254 return position; 255 } 256 257 // ------------------------------------------------------------------------------ 258 259 void SAL_CALL Player::setStopTime( double fTime ) 260 throw (uno::RuntimeException) 261 { 262 OSL_TRACE ("Player::setStopTime( %.3fsec)", fTime); 263 mfStopTime = fTime; 264 } 265 266 // ------------------------------------------------------------------------------ 267 268 double SAL_CALL Player::getStopTime() 269 throw (uno::RuntimeException) 270 { 271 return mfStopTime; 272 } 273 274 // ------------------------------------------------------------------------------ 275 276 void SAL_CALL Player::setRate( double fRate ) 277 throw (uno::RuntimeException) 278 { 279 OSL_TRACE ("Player::setRate( %.3f)", fRate); 280 if( !mpPlayer ) 281 return; 282 283 // playback rate: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards 284 [mpPlayer setRate: fRate]; 285 } 286 287 // ------------------------------------------------------------------------------ 288 289 double SAL_CALL Player::getRate() 290 throw (uno::RuntimeException) 291 { 292 // macavf: 0 = stop, 1 = normal speed, 2 = double speed, -1 = normal speed backwards 293 const double fRate = mpPlayer ? (double)[mpPlayer rate] : 1.0; 294 OSL_TRACE ("Player::getRate() = %.3f", fRate); 295 return fRate; 296 } 297 298 // ------------------------------------------------------------------------------ 299 300 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet ) 301 throw (uno::RuntimeException) 302 { 303 OSL_TRACE ("Player::setPlaybackLoop( %d)", bSet ); 304 mbLooping = bSet; 305 } 306 307 // ------------------------------------------------------------------------------ 308 309 sal_Bool SAL_CALL Player::isPlaybackLoop() 310 throw (uno::RuntimeException) 311 { 312 const bool bRet = mbLooping; 313 OSL_TRACE ("Player::isPlaybackLoop() = %d", bRet ); 314 return bRet; 315 } 316 317 // ------------------------------------------------------------------------------ 318 319 void SAL_CALL Player::setMute( sal_Bool bSet ) 320 throw (uno::RuntimeException) 321 { 322 OSL_TRACE( "Player::setMute(%d), was-muted: %d unmuted-volume: %.3f", bSet, mbMuted, mfUnmutedVolume ); 323 324 if( !mpPlayer ) 325 return; 326 327 mbMuted = (bSet == TRUE); 328 [mpPlayer setMuted:mbMuted]; 329 } 330 331 // ------------------------------------------------------------------------------ 332 333 sal_Bool SAL_CALL Player::isMute() 334 throw (uno::RuntimeException) 335 { 336 OSL_TRACE ("Player::isMuted() = %d", mbMuted); 337 return mbMuted; 338 } 339 340 // ------------------------------------------------------------------------------ 341 342 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB ) 343 throw (uno::RuntimeException) 344 { 345 // -40dB <-> AVPlayer volume 0.0 346 // 0dB <-> AVPlayer volume 1.0 347 mfUnmutedVolume = (nVolumeDB <= -40) ? 0.0 : pow( 10.0, nVolumeDB / 20.0 ); 348 OSL_TRACE( "Player::setVolume(%ddB), muted=%d, unmuted-volume: %.3f", nVolumeDB, mbMuted, mfUnmutedVolume ); 349 350 // change volume 351 if( !mbMuted && mpPlayer ) 352 [mpPlayer setVolume:mfUnmutedVolume]; 353 } 354 355 // ------------------------------------------------------------------------------ 356 357 sal_Int16 SAL_CALL Player::getVolumeDB() 358 throw (uno::RuntimeException) 359 { 360 if( !mpPlayer ) 361 return 0; 362 363 // get the actual volume 364 const float fVolume = [mpPlayer volume]; 365 366 // convert into Dezibel value 367 // -40dB <-> AVPlayer volume 0.0 368 // 0dB <-> AVPlayer volume 1.0 369 const int nVolumeDB = (fVolume <= 0) ? -40 : lrint( 20.0*log10(fVolume)); 370 371 return (sal_Int16)nVolumeDB; 372 } 373 374 // ------------------------------------------------------------------------------ 375 376 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize() 377 throw (uno::RuntimeException) 378 { 379 awt::Size aSize( 0, 0 ); // default size 380 381 AVAsset* pMovie = [[mpPlayer currentItem] asset]; 382 NSArray* pVideoTracks = [pMovie tracksWithMediaType:AVMediaTypeVideo]; 383 AVAssetTrack* pFirstVideoTrack = (AVAssetTrack*)[pVideoTracks firstObject]; 384 if( pFirstVideoTrack ) 385 { 386 const CGSize aPrefSize = [pFirstVideoTrack naturalSize]; 387 aSize = awt::Size( aPrefSize.width, aPrefSize.height ); 388 } 389 390 return aSize; 391 } 392 393 // ------------------------------------------------------------------------------ 394 395 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& aArguments ) 396 throw (uno::RuntimeException) 397 { 398 // get the preferred window size 399 const awt::Size aSize( getPreferredPlayerWindowSize() ); 400 OSL_TRACE( "Player::createPlayerWindow %dx%d argsLength: %d", aSize.Width, aSize.Height, aArguments.getLength() ); 401 402 // get the parent view 403 sal_IntPtr nNSViewPtr = NULL; 404 aArguments[0] >>= nNSViewPtr; 405 NSView* pParentView = reinterpret_cast<NSView*>(nNSViewPtr); 406 407 // check the window parameters 408 uno::Reference< ::media::XPlayerWindow > xRet; 409 if( (aSize.Width <= 0) || (aSize.Height <= 0) || (pParentView == NULL) ) 410 return xRet; 411 412 // create the window 413 ::avmedia::macavf::Window* pWindow = new ::avmedia::macavf::Window( mxMgr, *this, pParentView ); 414 xRet = pWindow; 415 return xRet; 416 } 417 418 // ------------------------------------------------------------------------------ 419 420 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber() 421 throw (uno::RuntimeException) 422 { 423 uno::Reference< media::XFrameGrabber > xRet; 424 OSL_TRACE ("Player::createFrameGrabber"); 425 426 FrameGrabber* pGrabber = new FrameGrabber( mxMgr ); 427 AVAsset* pMovie = [[mpPlayer currentItem] asset]; 428 if( pGrabber->create( pMovie ) ) 429 xRet = pGrabber; 430 431 return xRet; 432 } 433 434 // ------------------------------------------------------------------------------ 435 436 ::rtl::OUString SAL_CALL Player::getImplementationName( ) 437 throw (uno::RuntimeException) 438 { 439 return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_MACAVF_PLAYER_IMPLEMENTATIONNAME ) ); 440 } 441 442 // ------------------------------------------------------------------------------ 443 444 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName ) 445 throw (uno::RuntimeException) 446 { 447 return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) ); 448 } 449 450 // ------------------------------------------------------------------------------ 451 452 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames( ) 453 throw (uno::RuntimeException) 454 { 455 uno::Sequence< ::rtl::OUString > aRet(1); 456 aRet[0] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM ( AVMEDIA_MACAVF_PLAYER_SERVICENAME ) ); 457 458 return aRet; 459 } 460 461 } // namespace macavf 462 } // namespace avmedia 463 464