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