1 /*
   2  * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import "OSXMediaPlayer.h"
  27 #import "OSXPlayerProtocol.h"
  28 #import "com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer.h"
  29 #import <Utils/JObjectPeers.h>
  30 #import <Utils/JavaUtils.h>
  31 #import <CoreAudio/CoreAudio.h>
  32 #import <jni/Logger.h>
  33 
  34 #import <objc/runtime.h>
  35 
  36 #define USE_WEAK_REFS 0
  37 
  38 // Don't access directly, use the OSXMediaPlayer static methods to ensure thread safe access
  39 static JObjectPeers *gMediaPlayerPeers = nil;
  40 static Class gMediaPlayerClass = nil;
  41 
  42 @implementation OSXMediaPlayer
  43 
  44 + (void) initialize
  45 {
  46     gMediaPlayerPeers = [[JObjectPeers alloc] init];
  47 }
  48 
  49 + (OSXMediaPlayer*) peerForPlayer:(jobject)javaPlayer andEnv:(JNIEnv*)javaEnv
  50 {
  51     return [gMediaPlayerPeers peerForJObject:javaPlayer javaEnv:javaEnv];
  52 }
  53 
  54 + (void) setPeer:(OSXMediaPlayer*)player forJavaPlayer:(jobject)javaPlayer andEnv:(JNIEnv*)javaEnv
  55 {
  56     [gMediaPlayerPeers setPeer:player forJObject:javaPlayer javaEnv:javaEnv];
  57 }
  58 
  59 + (void) removePlayerPeers:(OSXMediaPlayer*)player
  60 {
  61     [gMediaPlayerPeers removePeer:player];
  62 }
  63 
  64 + (void) initPlayerPlatform
  65 {
  66     // Determine if we can use QTKMediaPlayer or not, without directly linking and pulling
  67     // in unwanted dependencies
  68     Class klass = objc_getClass("QTKMediaPlayer");
  69     if (klass) {
  70         // And make sure it conforms to the OSXPlayerProtocol
  71         if ([klass conformsToProtocol:@protocol(OSXPlayerProtocol)]) {
  72             gMediaPlayerClass = klass;
  73         }
  74     } // else we can't log yet, so fail silently
  75 }
  76 
  77 - (id) init
  78 {
  79     if ((self = [super init]) != nil) {
  80     }
  81     return self;
  82 }
  83 
  84 - (id) initWithURL:(NSURL *)source javaPlayer:(jobject)jp andEnv:(JNIEnv*)env eventHandler:(CJavaPlayerEventDispatcher*)hdlr
  85 {
  86     if (!gMediaPlayerClass) {
  87         // No player class available, abort
  88         return nil;
  89     }
  90 
  91     if ((self = [super init]) != nil) {
  92         movieURL = [source retain];
  93         
  94         env->GetJavaVM(&javaPlayerVM);
  95 #if USE_WEAK_REFS
  96         javaPlayer = env->NewWeakGlobalRef(jp);
  97 #else
  98         javaPlayer = env->NewGlobalRef(jp);
  99 #endif
 100         // set up the peer association
 101         [OSXMediaPlayer setPeer:self forJavaPlayer:javaPlayer andEnv:env];
 102         
 103         eventHandler = hdlr;
 104         
 105         // create the player object
 106         player = [[gMediaPlayerClass alloc] initWithURL:movieURL eventHandler:eventHandler];
 107     }
 108     return self;
 109 }
 110 
 111 - (id) initWithURL:(NSURL *)source eventHandler:(CJavaPlayerEventDispatcher*)hdlr
 112 {
 113     // stub initWithURL message to satisfy the protocol requirements, this should
 114     // never be called
 115     return nil;
 116 }
 117 
 118 - (void) dealloc
 119 {
 120     [self dispose]; // just in case
 121     [movieURL release];
 122     [super dealloc];
 123 }
 124 
 125 - (void) dispose
 126 {
 127     @synchronized(self) {
 128         [player dispose];
 129         [player release];
 130         player = nil;
 131     
 132         if (eventHandler) {
 133             eventHandler->Dispose();
 134             delete eventHandler;
 135         }
 136         eventHandler = NULL;
 137         
 138         [OSXMediaPlayer removePlayerPeers:self];
 139         if (javaPlayerVM && javaPlayer) {
 140             BOOL attached = NO;
 141             JNIEnv *env = GetJavaEnvironment(javaPlayerVM, &attached);
 142             
 143             // remove peer association
 144             [OSXMediaPlayer removePlayerPeers:self];
 145 #if USE_WEAK_REFS
 146             env->DeleteWeakGlobalRef(javaPlayer);
 147 #else
 148             env->DeleteGlobalRef(javaPlayer);
 149 #endif
 150             if (attached) {
 151                 javaPlayerVM->DetachCurrentThread();
 152             }
 153         }
 154         javaPlayer = 0;
 155         javaPlayerVM = NULL;
 156     }
 157 }
 158 
 159 - (id<OSXPlayerProtocol>) player
 160 {
 161     return [[player retain] autorelease];
 162 }
 163 
 164 - (int64_t) audioSyncDelay
 165 {
 166     return player.audioSyncDelay;
 167 }
 168 
 169 - (void) setAudioSyncDelay:(int64_t)audioSyncDelay
 170 {
 171     player.audioSyncDelay = audioSyncDelay;
 172 }
 173 
 174 - (double) duration
 175 {
 176     return player.duration;
 177 }
 178 
 179 - (float) rate
 180 {
 181     return player.rate;
 182 }
 183 
 184 - (void) setRate:(float)rate
 185 {
 186     player.rate = rate;
 187 }
 188 
 189 - (double) currentTime
 190 {
 191     return player.currentTime;
 192 }
 193 
 194 - (void) setCurrentTime:(double)currentTime
 195 {
 196     player.currentTime = currentTime;
 197 }
 198 
 199 - (BOOL) mute
 200 {
 201     return player.mute;
 202 }
 203 
 204 - (void) setMute:(BOOL)mute
 205 {
 206     player.mute = mute;
 207 }
 208 
 209 - (float) volume
 210 {
 211     return player.volume;
 212 }
 213 
 214 - (void) setVolume:(float)volume
 215 {
 216     player.volume = volume;
 217 }
 218 
 219 - (float) balance
 220 {
 221     return player.balance;
 222 }
 223 
 224 - (void) setBalance:(float)balance
 225 {
 226     player.balance = balance;
 227 }
 228 
 229 - (void) play
 230 {
 231     [player play];
 232 }
 233 
 234 - (void) pause
 235 {
 236     [player pause];
 237 }
 238 
 239 - (void) finish
 240 {
 241     [player finish];
 242 }
 243 
 244 - (void) stop
 245 {
 246     [player stop];
 247 }
 248 
 249 @end
 250 
 251 #pragma mark -
 252 #pragma mark JNI Methods
 253 
 254 /*
 255  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 256  * Method:    osxCreatePlayer
 257  * Signature: (Ljava/lang/String;)V
 258  */
 259 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxCreatePlayer
 260     (JNIEnv *env, jobject playerObject, jstring sourceURI)
 261 {
 262     // create the event dispatcher, init later
 263     CJavaPlayerEventDispatcher *eventHandler = new CJavaPlayerEventDispatcher();
 264     eventHandler->Init(env, playerObject, NULL);
 265 
 266     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 267     NSString *sourceURIString = NSStringFromJavaString(env, sourceURI);
 268     if (!sourceURIString) {        
 269         LOGGER_ERRORMSG("OSXMediaPlayer: Unable to create sourceURIString\n");
 270         eventHandler->SendPlayerMediaErrorEvent(ERROR_OSX_INIT);
 271         return;
 272     }
 273     
 274     NSURL *mediaURL = [[NSURL alloc] initWithString:sourceURIString];
 275     if (!mediaURL) {        
 276         LOGGER_WARNMSG("OSXMediaPlayer: Unable to create mediaURL\n");
 277         eventHandler->SendPlayerMediaErrorEvent(ERROR_OSX_INIT);
 278         return;
 279     }
 280     
 281     OSXMediaPlayer *player = [[OSXMediaPlayer alloc] initWithURL:mediaURL javaPlayer:playerObject andEnv:env eventHandler:eventHandler];
 282     if (!player) {
 283         LOGGER_WARNMSG("OSXMediaPlayer: Unable to create player\n");
 284         eventHandler->SendPlayerMediaErrorEvent(ERROR_OSX_INIT);
 285         return;
 286     }
 287     
 288     [player release]; // The player peer list retains for us
 289     [pool drain];
 290 }
 291 
 292 /*
 293  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 294  * Method:    osxGetAudioSyncDelay
 295  * Signature: ()J
 296  */
 297 JNIEXPORT jlong JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxGetAudioSyncDelay
 298     (JNIEnv *env, jobject playerObject)
 299 {
 300     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 301     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 302     jlong asd = 0;
 303     if (player) {
 304         asd = (jlong)player.audioSyncDelay;
 305     }
 306     [pool drain];
 307     return asd;
 308 }
 309 
 310 /*
 311  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 312  * Method:    osxSetAudioSyncDelay
 313  * Signature: (J)V
 314  */
 315 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxSetAudioSyncDelay
 316     (JNIEnv *env, jobject playerObject, jlong delay)
 317 {
 318     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 319     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 320     if (player) {
 321         player.audioSyncDelay = (int64_t)delay;
 322     }
 323     [pool drain];
 324 }
 325 
 326 /*
 327  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 328  * Method:    osxPlay
 329  * Signature: ()V
 330  */
 331 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxPlay
 332     (JNIEnv *env, jobject playerObject)
 333 {
 334     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 335     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 336     if (player) {
 337         [player play];
 338     }
 339     [pool drain];
 340 }
 341 
 342 /*
 343  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 344  * Method:    osxStop
 345  * Signature: ()V
 346  */
 347 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxStop
 348     (JNIEnv *env, jobject playerObject)
 349 {
 350     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 351     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 352     if (player) {
 353         [player stop];
 354     }
 355     [pool drain];
 356 }
 357 
 358 /*
 359  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 360  * Method:    osxPause
 361  * Signature: ()V
 362  */
 363 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxPause
 364     (JNIEnv *env, jobject playerObject)
 365 {
 366     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 367     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 368     if (player) {
 369         [player pause];
 370     }
 371     [pool drain];
 372 }
 373 
 374 /*
 375  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 376  * Method:    osxFinish
 377  * Signature: ()V
 378  */
 379 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxFinish
 380 (JNIEnv *env, jobject playerObject)
 381 {
 382     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 383     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 384     if (player) {
 385         [player finish];
 386     }
 387     [pool drain];
 388 }
 389 
 390 /*
 391  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 392  * Method:    osxGetRate
 393  * Signature: ()F
 394  */
 395 JNIEXPORT jfloat JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxGetRate
 396     (JNIEnv *env, jobject playerObject)
 397 {
 398     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 399     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 400     jfloat rc = 0.0;
 401     if (player) {
 402         rc = (jfloat)player.rate;
 403     }
 404     [pool drain];
 405     return rc;
 406 }
 407 
 408 /*
 409  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 410  * Method:    osxSetRate
 411  * Signature: (F)V
 412  */
 413 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxSetRate
 414     (JNIEnv *env, jobject playerObject, jfloat rate)
 415 {
 416     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 417     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 418     if (player) {
 419         player.rate = (double)rate;
 420     }
 421     [pool drain];
 422 }
 423 
 424 /*
 425  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 426  * Method:    osxGetPresentationTime
 427  * Signature: ()D
 428  */
 429 JNIEXPORT jdouble JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxGetPresentationTime
 430     (JNIEnv *env, jobject playerObject)
 431 {
 432     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 433     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 434     jdouble rc = 0.0;
 435     if (player) {
 436         rc = player.currentTime;
 437     }
 438     [pool drain];
 439     return rc;
 440 }
 441 
 442 /*
 443  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 444  * Method:    osxGetMute
 445  * Signature: ()Z
 446  */
 447 JNIEXPORT jboolean JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxGetMute
 448     (JNIEnv *env, jobject playerObject)
 449 {
 450     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 451     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 452     jboolean rc = JNI_FALSE;
 453     if (player) {
 454         rc = (player.mute != NO);
 455     }
 456     [pool drain];
 457     return rc;
 458 }
 459 
 460 /*
 461  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 462  * Method:    osxSetMute
 463  * Signature: (Z)V
 464  */
 465 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxSetMute
 466     (JNIEnv *env, jobject playerObject, jboolean mute)
 467 {
 468     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 469     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 470     if (player) {
 471         player.mute = (mute != JNI_FALSE);
 472     }
 473     [pool drain];
 474 }
 475 
 476 /*
 477  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 478  * Method:    osxGetVolume
 479  * Signature: ()F
 480  */
 481 JNIEXPORT jfloat JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxGetVolume
 482     (JNIEnv *env, jobject playerObject)
 483 {
 484     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 485     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 486     jfloat rc = 0.0;
 487     if (player) {
 488         rc = (jfloat)player.volume;
 489     }
 490     [pool drain];
 491     return rc;
 492 }
 493 
 494 /*
 495  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 496  * Method:    osxSetVolume
 497  * Signature: (F)V
 498  */
 499 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxSetVolume
 500     (JNIEnv *env, jobject playerObject, jfloat volume)
 501 {
 502     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 503     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 504     if (player) {
 505         player.volume = (double)volume;
 506     }
 507     [pool drain];
 508 }
 509 
 510 /*
 511  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 512  * Method:    osxGetBalance
 513  * Signature: ()F
 514  */
 515 JNIEXPORT jfloat JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxGetBalance
 516     (JNIEnv *env, jobject playerObject)
 517 {
 518     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 519     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 520     jfloat rc = 0.0;
 521     if (player) {
 522         rc = (jfloat)player.balance;
 523     }
 524     [pool drain];
 525     return rc;
 526 }
 527 
 528 /*
 529  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 530  * Method:    osxSetBalance
 531  * Signature: (F)V
 532  */
 533 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxSetBalance
 534     (JNIEnv *env, jobject playerObject, jfloat balance)
 535 {
 536     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 537     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 538     if (player) {
 539         player.balance = (double)balance;
 540     }
 541     [pool drain];
 542 }
 543 
 544 /*
 545  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 546  * Method:    osxGetDuration
 547  * Signature: ()D
 548  */
 549 JNIEXPORT jdouble JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxGetDuration
 550     (JNIEnv *env, jobject playerObject)
 551 {
 552     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 553     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 554     double duration = -1.0;
 555     if (player) {
 556         duration = (jdouble)player.duration;
 557     }
 558     [pool drain];
 559     return duration;
 560 }
 561 
 562 /*
 563  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 564  * Method:    osxSeek
 565  * Signature: (D)V
 566  */
 567 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxSeek
 568     (JNIEnv *env, jobject playerObject, jdouble time)
 569 {
 570     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 571     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 572     if (player) {
 573         player.currentTime = (double)time;
 574     }
 575     [pool drain];
 576 }
 577 
 578 /*
 579  * Class:     com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer
 580  * Method:    osxDispose
 581  * Signature: ()V
 582  */
 583 JNIEXPORT void JNICALL Java_com_sun_media_jfxmediaimpl_platform_osx_OSXMediaPlayer_osxDispose
 584     (JNIEnv *env, jobject playerObject)
 585 {
 586     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 587     OSXMediaPlayer *player = [OSXMediaPlayer peerForPlayer:playerObject andEnv:env];
 588     if (player) {
 589         [player dispose];
 590         
 591         // This should pop the last retain, aside from the autoreleased reference...
 592         [OSXMediaPlayer removePlayerPeers:player];
 593     }
 594     [pool drain];
 595 }