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 #include "JavaPlayerEventDispatcher.h"
  27 #include "JniUtils.h"
  28 #include "Logger.h"
  29 #include <com_sun_media_jfxmedia_track_AudioTrack.h>
  30 #include <com_sun_media_jfxmediaimpl_NativeMediaPlayer.h>
  31 #include <Common/VSMemory.h>
  32 #include <Utils/LowLevelPerf.h>
  33 #include <jni/Logger.h>
  34 
  35 static bool areJMethodIDsInitialized = false;
  36 
  37 jmethodID CJavaPlayerEventDispatcher::m_SendWarningMethod = 0;
  38 
  39 jmethodID CJavaPlayerEventDispatcher::m_SendPlayerMediaErrorEventMethod = 0;
  40 jmethodID CJavaPlayerEventDispatcher::m_SendPlayerHaltEventMethod = 0;
  41 jmethodID CJavaPlayerEventDispatcher::m_SendPlayerStateEventMethod = 0;
  42 jmethodID CJavaPlayerEventDispatcher::m_SendNewFrameEventMethod = 0;
  43 jmethodID CJavaPlayerEventDispatcher::m_SendFrameSizeChangedEventMethod = 0;
  44 jmethodID CJavaPlayerEventDispatcher::m_SendAudioTrackEventMethod = 0;
  45 jmethodID CJavaPlayerEventDispatcher::m_SendVideoTrackEventMethod = 0;
  46 jmethodID CJavaPlayerEventDispatcher::m_SendSubtitleTrackEventMethod = 0;
  47 jmethodID CJavaPlayerEventDispatcher::m_SendMarkerEventMethod = 0;
  48 jmethodID CJavaPlayerEventDispatcher::m_SendBufferProgressEventMethod = 0;
  49 jmethodID CJavaPlayerEventDispatcher::m_SendDurationUpdateEventMethod = 0;
  50 jmethodID CJavaPlayerEventDispatcher::m_SendAudioSpectrumEventMethod = 0;
  51 
  52 CJavaPlayerEventDispatcher::CJavaPlayerEventDispatcher()
  53 : m_PlayerVM(NULL),
  54   m_PlayerInstance(NULL),
  55   m_MediaReference(0L)
  56 {
  57 }
  58 
  59 CJavaPlayerEventDispatcher::~CJavaPlayerEventDispatcher()
  60 {
  61     Dispose();
  62 }
  63 
  64 void CJavaPlayerEventDispatcher::Init(JNIEnv *env, jobject PlayerInstance, CMedia* pMedia)
  65 {
  66     LOWLEVELPERF_EXECTIMESTART("CJavaPlayerEventDispatcher::Init()");
  67 
  68     if (env->GetJavaVM(&m_PlayerVM) != JNI_OK) {
  69         // FIXME: Warning/error message??
  70         return;
  71     }
  72     m_PlayerInstance = env->NewGlobalRef(PlayerInstance);
  73     m_MediaReference = (jlong) ptr_to_jlong(pMedia);
  74 
  75     // Initialize jmethodID data members. These are derived from the class of
  76     // the object and not its instance. No, this particular implementation is
  77     // not thread-safe, but the worst that can happen is that the jmethodIDs are
  78     // initialized more than once which is still better than once per player.
  79     if (false == areJMethodIDsInitialized)
  80     {
  81         jclass klass = env->GetObjectClass(m_PlayerInstance);
  82 
  83         m_SendWarningMethod               = env->GetMethodID(klass, "sendWarning", "(ILjava/lang/String;)V");
  84 
  85         m_SendPlayerMediaErrorEventMethod = env->GetMethodID(klass, "sendPlayerMediaErrorEvent", "(I)V");
  86         m_SendPlayerHaltEventMethod       = env->GetMethodID(klass, "sendPlayerHaltEvent", "(Ljava/lang/String;D)V");
  87         m_SendPlayerStateEventMethod      = env->GetMethodID(klass, "sendPlayerStateEvent", "(ID)V");
  88         m_SendNewFrameEventMethod         = env->GetMethodID(klass, "sendNewFrameEvent", "(J)V");
  89         m_SendFrameSizeChangedEventMethod = env->GetMethodID(klass, "sendFrameSizeChangedEvent", "(II)V");
  90         m_SendAudioTrackEventMethod       = env->GetMethodID(klass, "sendAudioTrack", "(ZJLjava/lang/String;ILjava/lang/String;IIF)V");
  91         m_SendVideoTrackEventMethod       = env->GetMethodID(klass, "sendVideoTrack", "(ZJLjava/lang/String;IIIFZ)V");
  92         m_SendSubtitleTrackEventMethod    = env->GetMethodID(klass, "sendSubtitleTrack", "(ZJLjava/lang/String;ILjava/lang/String;)V");
  93         m_SendMarkerEventMethod           = env->GetMethodID(klass, "sendMarkerEvent", "(Ljava/lang/String;D)V");
  94         m_SendBufferProgressEventMethod   = env->GetMethodID(klass, "sendBufferProgressEvent", "(DJJJ)V");
  95         m_SendDurationUpdateEventMethod  = env->GetMethodID(klass, "sendDurationUpdateEvent", "(D)V");
  96         m_SendAudioSpectrumEventMethod  = env->GetMethodID(klass, "sendAudioSpectrumEvent", "(DD)V");
  97 
  98         env->DeleteLocalRef(klass);
  99 
 100         areJMethodIDsInitialized = true;
 101     }
 102 
 103     LOWLEVELPERF_EXECTIMESTOP("CJavaPlayerEventDispatcher::Init()");
 104 }
 105 
 106 void CJavaPlayerEventDispatcher::Dispose()
 107 {
 108     LOWLEVELPERF_EXECTIMESTART("CJavaPlayerEventDispatcher::Dispose()");
 109     CJavaEnvironment jenv(m_PlayerVM);
 110     JNIEnv *pEnv = jenv.getEnvironment();
 111     if (pEnv) {
 112         pEnv->DeleteGlobalRef(m_PlayerInstance);
 113     }
 114 
 115     LOWLEVELPERF_EXECTIMESTOP("CJavaPlayerEventDispatcher::Dispose()");
 116 }
 117 
 118 void CJavaPlayerEventDispatcher::Warning(int warningCode, const char* warningMessage)
 119 {
 120     if (NULL == m_PlayerInstance)
 121         return;
 122 
 123     CJavaEnvironment jenv(m_PlayerVM);
 124     JNIEnv *pEnv = jenv.getEnvironment();
 125     if (pEnv) {
 126         jstring jmessage = NULL;
 127         if (warningMessage) {
 128             jmessage = pEnv->NewStringUTF(warningMessage);
 129         }
 130         pEnv->CallVoidMethod(m_PlayerInstance, m_SendWarningMethod,
 131                              (jint)warningCode, jmessage);
 132         if (jmessage) {
 133             pEnv->DeleteLocalRef(jmessage);
 134         }
 135     }
 136 }
 137 
 138 bool CJavaPlayerEventDispatcher::SendPlayerMediaErrorEvent(int errorCode)
 139 {
 140     return SendToJava_PlayerMediaErrorEvent(errorCode);
 141 }
 142 
 143 bool CJavaPlayerEventDispatcher::SendPlayerHaltEvent(const char* message, double time)
 144 {
 145     return SendToJava_PlayerHaltEvent(message, time);
 146 }
 147 
 148 bool CJavaPlayerEventDispatcher::SendPlayerStateEvent(int newState, double presentTime)
 149 {
 150     long newJavaState;
 151 
 152     switch(newState)
 153     {
 154     case CPipeline::Unknown:
 155         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerUnknown;
 156         break;
 157     case CPipeline::Ready:
 158         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerReady;
 159         break;
 160     case CPipeline::Playing:
 161         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerPlaying;
 162         break;
 163     case CPipeline::Paused:
 164         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerPaused;
 165         break;
 166     case CPipeline::Stopped:
 167         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerStopped;
 168         break;
 169     case CPipeline::Stalled:
 170         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerStalled;
 171         break;
 172     case CPipeline::Finished:
 173         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerFinished;
 174         break;
 175     case CPipeline::Error:
 176         newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerError;
 177         break;
 178     default:
 179         return false;
 180     }
 181 
 182     return SendToJava_PlayerStateEvent(newJavaState, presentTime);
 183 }
 184 
 185 bool CJavaPlayerEventDispatcher::SendNewFrameEvent(CVideoFrame* pVideoFrame)
 186 {
 187     return SendToJava_NewFrameEvent(pVideoFrame);
 188 }
 189 
 190 bool CJavaPlayerEventDispatcher::SendFrameSizeChangedEvent(int width, int height)
 191 {
 192     return SendToJava_FrameSizeChangedEvent(width, height);
 193 }
 194 
 195 bool CJavaPlayerEventDispatcher::SendAudioTrackEvent(CAudioTrack* pTrack)
 196 {
 197     return SendToJava_AudioTrackEvent(pTrack);
 198 }
 199 
 200 bool CJavaPlayerEventDispatcher::SendVideoTrackEvent(CVideoTrack* pTrack)
 201 {
 202     return SendToJava_VideoTrackEvent(pTrack);
 203 }
 204 
 205 bool CJavaPlayerEventDispatcher::SendSubtitleTrackEvent(CSubtitleTrack* pTrack)
 206 {
 207     return SendToJava_SubtitleTrackEvent(pTrack);
 208 }
 209 
 210 bool CJavaPlayerEventDispatcher::SendMarkerEvent(string name, double time)
 211 {
 212     return SendToJava_MarkerEvent(name, time);
 213 }
 214 
 215 bool CJavaPlayerEventDispatcher::SendBufferProgressEvent(double clipDuration, int64_t start, int64_t stop, int64_t position)
 216 {
 217    return SendToJava_BufferProgressEvent(clipDuration, start, stop, position);
 218 }
 219 
 220 bool CJavaPlayerEventDispatcher::SendDurationUpdateEvent(double time)
 221 {
 222     return SendToJava_DurationUpdateEvent(time);
 223 }
 224 
 225 bool CJavaPlayerEventDispatcher::SendAudioSpectrumEvent(double time, double duration)
 226 {
 227     return SendToJava_AudioSpectrumEvent(time, duration);
 228 }
 229 /*********************************************************************************
 230  * SendToJava methods section
 231  **********************************************************************************/
 232 bool CJavaPlayerEventDispatcher::SendToJava_PlayerMediaErrorEvent(int errorCode)
 233 {
 234     if (NULL == m_PlayerInstance)
 235         return false;
 236 
 237     CJavaEnvironment jenv(m_PlayerVM);
 238     JNIEnv *pEnv = jenv.getEnvironment();
 239     if (pEnv) {
 240         pEnv->CallVoidMethod(m_PlayerInstance, m_SendPlayerMediaErrorEventMethod, errorCode);
 241         return !jenv.reportException();
 242     }
 243 
 244     return false;
 245 }
 246 
 247 bool CJavaPlayerEventDispatcher::SendToJava_PlayerHaltEvent(const char* message, double time)
 248 {
 249     if (NULL == m_PlayerInstance)
 250         return false;
 251 
 252     CJavaEnvironment jenv(m_PlayerVM);
 253     JNIEnv *pEnv = jenv.getEnvironment();
 254     if (pEnv) {
 255         jstring jmessage = pEnv->NewStringUTF(message);
 256         pEnv->CallVoidMethod(m_PlayerInstance, m_SendPlayerHaltEventMethod, jmessage, time);
 257         pEnv->DeleteLocalRef(jmessage);
 258         return !jenv.reportException();
 259     }
 260 
 261     return false;
 262 }
 263 
 264 bool CJavaPlayerEventDispatcher::SendToJava_PlayerStateEvent(long eventID, double presentTime)
 265 {
 266     if (NULL == m_PlayerInstance)
 267         return false;
 268 
 269     switch(eventID) {
 270         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerUnknown:
 271         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerReady:
 272         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerPlaying:
 273         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerPaused:
 274         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerStopped:
 275         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerFinished:
 276         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerStalled:
 277         case com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerError:
 278         {
 279             LOWLEVELPERF_EXECTIMESTOP("gstInitPlatformToSendToJavaPlayerStateEventPaused");
 280             LOWLEVELPERF_EXECTIMESTOP("gstPauseToSendToJavaPlayerStateEventPaused");
 281             LOWLEVELPERF_EXECTIMESTOP("gstStopToSendToJavaPlayerStateEventStopped");
 282             LOWLEVELPERF_EXECTIMESTOP("gstPlayToSendToJavaPlayerStateEventPlaying");
 283             // Send an event only if the ID is valid.
 284             CJavaEnvironment jenv(m_PlayerVM);
 285             JNIEnv *pEnv = jenv.getEnvironment();
 286             if (pEnv) {
 287                 pEnv->CallVoidMethod(m_PlayerInstance, m_SendPlayerStateEventMethod, eventID, presentTime);
 288                 return !jenv.reportException();
 289             }
 290             break;
 291         }
 292         default:
 293             break;
 294     }
 295 
 296     return false;
 297 }
 298 
 299 bool CJavaPlayerEventDispatcher::SendToJava_NewFrameEvent(CVideoFrame* pVideoFrame)
 300 {
 301     LOWLEVELPERF_EXECTIMESTART("CJavaPlayerEventDispatcher::SendToJava_NewFrameEvent()");
 302     bool bSucceeded = false;
 303 
 304     if (NULL == m_PlayerInstance)
 305         return false;
 306 
 307     CJavaEnvironment jenv(m_PlayerVM);
 308     JNIEnv *pEnv = jenv.getEnvironment();
 309     if (pEnv) {
 310         // SendNewFrameEvent will create the NativeVideoBuffer wrapper for the java side
 311         pEnv->CallVoidMethod(m_PlayerInstance, m_SendNewFrameEventMethod, ptr_to_jlong(pVideoFrame));
 312         bSucceeded = !jenv.reportException();
 313     }
 314 
 315     LOWLEVELPERF_EXECTIMESTOP("CJavaPlayerEventDispatcher::SendToJava_NewFrameEvent()");
 316 
 317     return bSucceeded;
 318 }
 319 
 320 bool CJavaPlayerEventDispatcher::SendToJava_FrameSizeChangedEvent(int width, int height)
 321 {
 322     if (NULL == m_PlayerInstance)
 323         return false;
 324 
 325     CJavaEnvironment jenv(m_PlayerVM);
 326     JNIEnv *pEnv = jenv.getEnvironment();
 327     if (pEnv) {
 328         pEnv->CallVoidMethod(m_PlayerInstance, m_SendFrameSizeChangedEventMethod, (jint)width, (jint)height);
 329         return !jenv.reportException();
 330     }
 331 
 332     return false;
 333 }
 334 
 335 bool CJavaPlayerEventDispatcher::SendToJava_AudioTrackEvent(CAudioTrack* pTrack)
 336 {
 337     if (NULL == m_PlayerInstance)
 338         return false;
 339 
 340     CJavaEnvironment jenv(m_PlayerVM);
 341     JNIEnv *pEnv = jenv.getEnvironment();
 342     if (pEnv) {
 343         jstring name = pEnv->NewStringUTF(pTrack->GetName().c_str());
 344         jstring language = pEnv->NewStringUTF(pTrack->GetLanguage().c_str());
 345 
 346         // Translate channel mask bits from native values to Java values.
 347         int nativeChannelMask = pTrack->GetChannelMask();
 348         jint javaChannelMask = 0;
 349         if (nativeChannelMask & CAudioTrack::UNKNOWN)
 350             javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_UNKNOWN;
 351         if (nativeChannelMask & CAudioTrack::FRONT_LEFT)
 352             javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_FRONT_LEFT;
 353         if (nativeChannelMask & CAudioTrack::FRONT_RIGHT)
 354             javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_FRONT_RIGHT;
 355         if (nativeChannelMask & CAudioTrack::FRONT_CENTER)
 356             javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_FRONT_CENTER;
 357         if (nativeChannelMask & CAudioTrack::REAR_LEFT)
 358             javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_REAR_LEFT;
 359         if (nativeChannelMask & CAudioTrack::REAR_RIGHT)
 360             javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_REAR_RIGHT;
 361         if (nativeChannelMask & CAudioTrack::REAR_CENTER)
 362             javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_REAR_CENTER;
 363 
 364         pEnv->CallVoidMethod(m_PlayerInstance, m_SendAudioTrackEventMethod,
 365                              (jboolean)pTrack->isEnabled(), (jlong)pTrack->GetTrackID(), name, pTrack->GetEncoding(), language,
 366                              pTrack->GetNumChannels(), javaChannelMask, pTrack->GetSampleRate());
 367 
 368         pEnv->DeleteLocalRef(name);
 369         pEnv->DeleteLocalRef(language);
 370         return !jenv.reportException();
 371     }
 372 
 373     return false;
 374 }
 375 
 376 bool CJavaPlayerEventDispatcher::SendToJava_VideoTrackEvent(CVideoTrack* pTrack)
 377 {
 378     if (NULL == m_PlayerInstance)
 379         return false;
 380 
 381     CJavaEnvironment jenv(m_PlayerVM);
 382     JNIEnv *pEnv = jenv.getEnvironment();
 383     if (pEnv) {
 384         jstring name = pEnv->NewStringUTF(pTrack->GetName().c_str());
 385         pEnv->CallVoidMethod(m_PlayerInstance, m_SendVideoTrackEventMethod,
 386                              (jboolean)pTrack->isEnabled(), (jlong)pTrack->GetTrackID(), name, pTrack->GetEncoding(),
 387                              pTrack->GetWidth(), pTrack->GetHeight(),
 388                              pTrack->GetFrameRate(), pTrack->HasAlphaChannel());
 389         pEnv->DeleteLocalRef(name);
 390         return !jenv.reportException();
 391     }
 392 
 393     return false;
 394 }
 395 
 396 bool CJavaPlayerEventDispatcher::SendToJava_SubtitleTrackEvent(CSubtitleTrack* pTrack)
 397 {
 398     if (NULL == m_PlayerInstance)
 399         return false;
 400 
 401     CJavaEnvironment jenv(m_PlayerVM);
 402     JNIEnv *pEnv = jenv.getEnvironment();
 403     if (pEnv) {
 404         jstring name = pEnv->NewStringUTF(pTrack->GetName().c_str());
 405         jstring language = pEnv->NewStringUTF(pTrack->GetLanguage().c_str());
 406 
 407         pEnv->CallVoidMethod(m_PlayerInstance, m_SendSubtitleTrackEventMethod,
 408                              (jboolean)pTrack->isEnabled(), (jlong)pTrack->GetTrackID(),
 409                              name, pTrack->GetEncoding(), language);
 410         pEnv->DeleteLocalRef(name);
 411         pEnv->DeleteLocalRef(language);
 412 
 413         return !jenv.reportException();
 414     }
 415 
 416     return false;
 417 }
 418 
 419 /******************************************************************************************
 420  * Creates any object with any arguments
 421  ******************************************************************************************/
 422 jobject CJavaPlayerEventDispatcher::CreateObject(JNIEnv *env, jmethodID *cid,
 423                                                  const char* class_name, const char* signature,
 424                                                  jvalue* value)
 425 {
 426     jclass  classe;
 427     jobject result;
 428 
 429     classe = env->FindClass(class_name);
 430     if( classe == NULL )
 431         return NULL; /* can't find/load the class, exception thrown */
 432 
 433     if( *cid == NULL)
 434     {
 435         *cid = env->GetMethodID(classe, "<init>", signature);
 436         if( *cid == NULL )
 437         {
 438             env->DeleteLocalRef(classe);
 439             return NULL; /* can't find/get the method, exception thrown */
 440         }
 441     }
 442 
 443     result = env->NewObjectA(classe, *cid, value);
 444 
 445     env->DeleteLocalRef(classe);
 446     return result;
 447 }
 448 
 449 jobject CJavaPlayerEventDispatcher::CreateBoolean(JNIEnv *env, jboolean boolean_value)
 450 {
 451     static jmethodID cid = NULL;
 452     jvalue value;
 453 
 454     value.z = boolean_value;
 455 
 456     return CreateObject(env, &cid, "java/lang/Boolean", "(Z)V", &value);
 457 }
 458 
 459 jobject CJavaPlayerEventDispatcher::CreateInteger(JNIEnv *env, jint int_value)
 460 {
 461     static jmethodID cid = NULL;
 462     jvalue value;
 463 
 464     value.i = int_value;
 465 
 466     return CreateObject(env, &cid, "java/lang/Integer", "(I)V", &value);
 467 }
 468 
 469 jobject CJavaPlayerEventDispatcher::CreateLong(JNIEnv *env, jlong long_value)
 470 {
 471     static jmethodID cid = NULL;
 472     jvalue value;
 473 
 474     value.j = long_value;
 475 
 476     return CreateObject(env, &cid, "java/lang/Long", "(J)V", &value);
 477 }
 478 
 479 jobject CJavaPlayerEventDispatcher::CreateDouble(JNIEnv *env, jdouble double_value)
 480 {
 481     static jmethodID cid = NULL;
 482     jvalue value;
 483 
 484     value.d = double_value;
 485 
 486     return CreateObject(env, &cid, "java/lang/Double", "(D)V", &value);
 487 }
 488 
 489 jobject CJavaPlayerEventDispatcher::CreateDuration(JNIEnv *env, jlong duration)
 490 {
 491     static jmethodID constructorID = NULL;
 492     // We receive duration in nanoseconds, but javafx.util.Duration needs in milliseconds
 493     jdouble millis = duration/1000000.0;
 494 
 495     jclass durationClass = env->FindClass("javafx/util/Duration");
 496     if (durationClass == NULL)
 497         return NULL; /* can't find/load the class, exception thrown */
 498 
 499     if (constructorID == NULL)
 500     {
 501         constructorID = env->GetMethodID(durationClass, "<init>", "(D)V");
 502         if( constructorID == NULL )
 503         {
 504             env->DeleteLocalRef(durationClass);
 505             return NULL; /* can't find/get the method, exception thrown */
 506         }
 507     }
 508 
 509     jobject result = env->NewObject(durationClass, constructorID, millis);
 510 
 511     env->DeleteLocalRef(durationClass);
 512 
 513     return result;
 514 }
 515 
 516 bool CJavaPlayerEventDispatcher::SendToJava_MarkerEvent(string name, double time)
 517 {
 518     if (NULL == m_PlayerInstance)
 519         return false;
 520 
 521     CJavaEnvironment jenv(m_PlayerVM);
 522     JNIEnv *pEnv = jenv.getEnvironment();
 523     if (pEnv) {
 524         jobject jname = pEnv->NewStringUTF(name.c_str());
 525         pEnv->CallVoidMethod(m_PlayerInstance, m_SendMarkerEventMethod,
 526                              jname, time);
 527         pEnv->DeleteLocalRef(jname);
 528         return !jenv.reportException();
 529     }
 530 
 531     return false;
 532 }
 533 
 534 bool CJavaPlayerEventDispatcher::SendToJava_BufferProgressEvent(double clipDuration, int64_t start, int64_t stop, int64_t position)
 535 {
 536     if (NULL == m_PlayerInstance)
 537         return false;
 538 
 539     CJavaEnvironment jenv(m_PlayerVM);
 540     JNIEnv *pEnv = jenv.getEnvironment();
 541     if (pEnv) {
 542         pEnv->CallVoidMethod(m_PlayerInstance, m_SendBufferProgressEventMethod, clipDuration, start, stop, position);
 543         return !jenv.reportException();
 544     }
 545 
 546     return false;
 547 }
 548 
 549 bool CJavaPlayerEventDispatcher::SendToJava_DurationUpdateEvent(double time)
 550 {
 551     if (NULL == m_PlayerInstance)
 552         return false;
 553 
 554     CJavaEnvironment jenv(m_PlayerVM);
 555     JNIEnv *pEnv = jenv.getEnvironment();
 556     if (pEnv) {
 557         pEnv->CallVoidMethod(m_PlayerInstance, m_SendDurationUpdateEventMethod,
 558                              (jdouble)time);
 559         return !jenv.reportException();
 560     }
 561 
 562     return false;
 563 }
 564 
 565 bool CJavaPlayerEventDispatcher::SendToJava_AudioSpectrumEvent(double time, double duration)
 566 {
 567     if (NULL == m_PlayerInstance)
 568         return false;
 569 
 570     CJavaEnvironment jenv(m_PlayerVM);
 571     JNIEnv *pEnv = jenv.getEnvironment();
 572     if (pEnv) {
 573         pEnv->CallVoidMethod(m_PlayerInstance, m_SendAudioSpectrumEventMethod, time, duration);
 574         return !jenv.reportException();
 575     }
 576 
 577     return false;
 578 }