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