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