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 #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 m_PlayerInstance = NULL; // prevent further calls to this object 114 } 115 116 LOWLEVELPERF_EXECTIMESTOP("CJavaPlayerEventDispatcher::Dispose()"); 117 } 118 119 void CJavaPlayerEventDispatcher::Warning(int warningCode, const char* warningMessage) 120 { 121 CJavaEnvironment jenv(m_PlayerVM); 122 JNIEnv *pEnv = jenv.getEnvironment(); 123 if (pEnv) { 124 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 125 if (localPlayer) { 126 jstring jmessage = NULL; 127 if (warningMessage) { 128 jmessage = pEnv->NewStringUTF(warningMessage); 129 } 130 pEnv->CallVoidMethod(localPlayer, m_SendWarningMethod, 131 (jint)warningCode, jmessage); 132 if (jmessage) { 133 pEnv->DeleteLocalRef(jmessage); 134 } 135 pEnv->DeleteLocalRef(localPlayer); 136 } 137 } 138 } 139 140 bool CJavaPlayerEventDispatcher::SendPlayerMediaErrorEvent(int errorCode) 141 { 142 bool bSucceeded = false; 143 CJavaEnvironment jenv(m_PlayerVM); 144 JNIEnv *pEnv = jenv.getEnvironment(); 145 if (pEnv) { 146 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 147 if (localPlayer) { 148 pEnv->CallVoidMethod(localPlayer, m_SendPlayerMediaErrorEventMethod, errorCode); 149 pEnv->DeleteLocalRef(localPlayer); 150 151 bSucceeded = !jenv.reportException(); 152 } 153 } 154 155 return bSucceeded; 156 } 157 158 bool CJavaPlayerEventDispatcher::SendPlayerHaltEvent(const char* message, double time) 159 { 160 bool bSucceeded = false; 161 CJavaEnvironment jenv(m_PlayerVM); 162 JNIEnv *pEnv = jenv.getEnvironment(); 163 if (pEnv) { 164 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 165 if (localPlayer) { 166 jstring jmessage = pEnv->NewStringUTF(message); 167 pEnv->CallVoidMethod(localPlayer, m_SendPlayerHaltEventMethod, jmessage, time); 168 pEnv->DeleteLocalRef(jmessage); 169 pEnv->DeleteLocalRef(localPlayer); 170 171 bSucceeded = !jenv.reportException(); 172 } 173 } 174 175 return bSucceeded; 176 } 177 178 bool CJavaPlayerEventDispatcher::SendPlayerStateEvent(int newState, double presentTime) 179 { 180 long newJavaState; 181 182 switch(newState) { 183 case CPipeline::Unknown: 184 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerUnknown; 185 break; 186 case CPipeline::Ready: 187 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerReady; 188 break; 189 case CPipeline::Playing: 190 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerPlaying; 191 break; 192 case CPipeline::Paused: 193 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerPaused; 194 break; 195 case CPipeline::Stopped: 196 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerStopped; 197 break; 198 case CPipeline::Stalled: 199 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerStalled; 200 break; 201 case CPipeline::Finished: 202 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerFinished; 203 break; 204 case CPipeline::Error: 205 newJavaState = com_sun_media_jfxmediaimpl_NativeMediaPlayer_eventPlayerError; 206 break; 207 default: 208 return false; 209 } 210 211 LOWLEVELPERF_EXECTIMESTOP("gstInitPlatformToSendToJavaPlayerStateEventPaused"); 212 LOWLEVELPERF_EXECTIMESTOP("gstPauseToSendToJavaPlayerStateEventPaused"); 213 LOWLEVELPERF_EXECTIMESTOP("gstStopToSendToJavaPlayerStateEventStopped"); 214 LOWLEVELPERF_EXECTIMESTOP("gstPlayToSendToJavaPlayerStateEventPlaying"); 215 216 bool bSucceeded = false; 217 CJavaEnvironment jenv(m_PlayerVM); 218 JNIEnv *pEnv = jenv.getEnvironment(); 219 if (pEnv) { 220 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 221 if (localPlayer) { 222 pEnv->CallVoidMethod(localPlayer, m_SendPlayerStateEventMethod, newJavaState, presentTime); 223 pEnv->DeleteLocalRef(localPlayer); 224 225 bSucceeded = !jenv.reportException(); 226 } 227 } 228 229 return bSucceeded; 230 } 231 232 bool CJavaPlayerEventDispatcher::SendNewFrameEvent(CVideoFrame* pVideoFrame) 233 { 234 LOWLEVELPERF_EXECTIMESTART("CJavaPlayerEventDispatcher::SendNewFrameEvent()"); 235 bool bSucceeded = false; 236 237 CJavaEnvironment jenv(m_PlayerVM); 238 JNIEnv *pEnv = jenv.getEnvironment(); 239 if (pEnv) { 240 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 241 if (localPlayer) { 242 // SendNewFrameEvent will create the NativeVideoBuffer wrapper for the java side 243 pEnv->CallVoidMethod(localPlayer, m_SendNewFrameEventMethod, ptr_to_jlong(pVideoFrame)); 244 pEnv->DeleteLocalRef(localPlayer); 245 246 bSucceeded = !jenv.reportException(); 247 } 248 } 249 250 LOWLEVELPERF_EXECTIMESTOP("CJavaPlayerEventDispatcher::SendNewFrameEvent()"); 251 252 return bSucceeded; 253 } 254 255 bool CJavaPlayerEventDispatcher::SendFrameSizeChangedEvent(int width, int height) 256 { 257 bool bSucceeded = false; 258 CJavaEnvironment jenv(m_PlayerVM); 259 JNIEnv *pEnv = jenv.getEnvironment(); 260 if (pEnv) { 261 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 262 if (localPlayer) { 263 pEnv->CallVoidMethod(localPlayer, m_SendFrameSizeChangedEventMethod, (jint)width, (jint)height); 264 pEnv->DeleteLocalRef(localPlayer); 265 266 bSucceeded = !jenv.reportException(); 267 } 268 } 269 270 return bSucceeded; 271 } 272 273 bool CJavaPlayerEventDispatcher::SendAudioTrackEvent(CAudioTrack* pTrack) 274 { 275 bool bSucceeded = false; 276 CJavaEnvironment jenv(m_PlayerVM); 277 JNIEnv *pEnv = jenv.getEnvironment(); 278 if (pEnv) { 279 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 280 if (localPlayer) { 281 jstring name = pEnv->NewStringUTF(pTrack->GetName().c_str()); 282 jstring language = pEnv->NewStringUTF(pTrack->GetLanguage().c_str()); 283 284 // Translate channel mask bits from native values to Java values. 285 int nativeChannelMask = pTrack->GetChannelMask(); 286 jint javaChannelMask = 0; 287 if (nativeChannelMask & CAudioTrack::UNKNOWN) 288 javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_UNKNOWN; 289 if (nativeChannelMask & CAudioTrack::FRONT_LEFT) 290 javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_FRONT_LEFT; 291 if (nativeChannelMask & CAudioTrack::FRONT_RIGHT) 292 javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_FRONT_RIGHT; 293 if (nativeChannelMask & CAudioTrack::FRONT_CENTER) 294 javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_FRONT_CENTER; 295 if (nativeChannelMask & CAudioTrack::REAR_LEFT) 296 javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_REAR_LEFT; 297 if (nativeChannelMask & CAudioTrack::REAR_RIGHT) 298 javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_REAR_RIGHT; 299 if (nativeChannelMask & CAudioTrack::REAR_CENTER) 300 javaChannelMask |= com_sun_media_jfxmedia_track_AudioTrack_REAR_CENTER; 301 302 pEnv->CallVoidMethod(localPlayer, 303 m_SendAudioTrackEventMethod, 304 (jboolean)pTrack->isEnabled(), 305 (jlong)pTrack->GetTrackID(), 306 name, 307 pTrack->GetEncoding(), 308 language, 309 pTrack->GetNumChannels(), 310 javaChannelMask, 311 pTrack->GetSampleRate()); 312 313 pEnv->DeleteLocalRef(name); 314 pEnv->DeleteLocalRef(language); 315 pEnv->DeleteLocalRef(localPlayer); 316 317 bSucceeded = !jenv.reportException(); 318 } 319 } 320 321 return bSucceeded; 322 } 323 324 bool CJavaPlayerEventDispatcher::SendVideoTrackEvent(CVideoTrack* pTrack) 325 { 326 bool bSucceeded = false; 327 CJavaEnvironment jenv(m_PlayerVM); 328 JNIEnv *pEnv = jenv.getEnvironment(); 329 if (pEnv) { 330 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 331 if (localPlayer) { 332 jstring name = pEnv->NewStringUTF(pTrack->GetName().c_str()); 333 pEnv->CallVoidMethod(localPlayer, m_SendVideoTrackEventMethod, 334 (jboolean)pTrack->isEnabled(), (jlong)pTrack->GetTrackID(), name, pTrack->GetEncoding(), 335 pTrack->GetWidth(), pTrack->GetHeight(), 336 pTrack->GetFrameRate(), pTrack->HasAlphaChannel()); 337 pEnv->DeleteLocalRef(name); 338 pEnv->DeleteLocalRef(localPlayer); 339 340 bSucceeded = !jenv.reportException(); 341 } 342 } 343 344 return bSucceeded; 345 } 346 347 bool CJavaPlayerEventDispatcher::SendSubtitleTrackEvent(CSubtitleTrack* pTrack) 348 { 349 bool bSucceeded = false; 350 CJavaEnvironment jenv(m_PlayerVM); 351 JNIEnv *pEnv = jenv.getEnvironment(); 352 if (pEnv) { 353 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 354 if (localPlayer) { 355 jstring name = pEnv->NewStringUTF(pTrack->GetName().c_str()); 356 jstring language = pEnv->NewStringUTF(pTrack->GetLanguage().c_str()); 357 358 pEnv->CallVoidMethod(localPlayer, m_SendSubtitleTrackEventMethod, 359 (jboolean)pTrack->isEnabled(), (jlong)pTrack->GetTrackID(), 360 name, pTrack->GetEncoding(), language); 361 pEnv->DeleteLocalRef(name); 362 pEnv->DeleteLocalRef(language); 363 pEnv->DeleteLocalRef(localPlayer); 364 365 bSucceeded = !jenv.reportException(); 366 } 367 } 368 369 return bSucceeded; 370 } 371 372 bool CJavaPlayerEventDispatcher::SendMarkerEvent(string name, double time) 373 { 374 bool bSucceeded = false; 375 CJavaEnvironment jenv(m_PlayerVM); 376 JNIEnv *pEnv = jenv.getEnvironment(); 377 if (pEnv) { 378 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 379 if (localPlayer) { 380 jobject jname = pEnv->NewStringUTF(name.c_str()); 381 pEnv->CallVoidMethod(localPlayer, m_SendMarkerEventMethod, 382 jname, time); 383 pEnv->DeleteLocalRef(jname); 384 pEnv->DeleteLocalRef(localPlayer); 385 386 bSucceeded = !jenv.reportException(); 387 } 388 } 389 390 return bSucceeded; 391 } 392 393 bool CJavaPlayerEventDispatcher::SendBufferProgressEvent(double clipDuration, int64_t start, int64_t stop, int64_t position) 394 { 395 bool bSucceeded = false; 396 CJavaEnvironment jenv(m_PlayerVM); 397 JNIEnv *pEnv = jenv.getEnvironment(); 398 if (pEnv) { 399 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 400 if (localPlayer) { 401 pEnv->CallVoidMethod(localPlayer, m_SendBufferProgressEventMethod, clipDuration, start, stop, position); 402 pEnv->DeleteLocalRef(localPlayer); 403 404 bSucceeded = !jenv.reportException(); 405 } 406 } 407 408 return bSucceeded; 409 } 410 411 bool CJavaPlayerEventDispatcher::SendDurationUpdateEvent(double time) 412 { 413 bool bSucceeded = false; 414 CJavaEnvironment jenv(m_PlayerVM); 415 JNIEnv *pEnv = jenv.getEnvironment(); 416 if (pEnv) { 417 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 418 if (localPlayer) { 419 pEnv->CallVoidMethod(localPlayer, m_SendDurationUpdateEventMethod, 420 (jdouble)time); 421 pEnv->DeleteLocalRef(localPlayer); 422 423 bSucceeded = !jenv.reportException(); 424 } 425 } 426 427 return bSucceeded; 428 } 429 430 bool CJavaPlayerEventDispatcher::SendAudioSpectrumEvent(double time, double duration) 431 { 432 bool bSucceeded = false; 433 CJavaEnvironment jenv(m_PlayerVM); 434 JNIEnv *pEnv = jenv.getEnvironment(); 435 if (pEnv) { 436 jobject localPlayer = pEnv->NewLocalRef(m_PlayerInstance); 437 if (localPlayer) { 438 pEnv->CallVoidMethod(localPlayer, m_SendAudioSpectrumEventMethod, time, duration); 439 pEnv->DeleteLocalRef(localPlayer); 440 441 bSucceeded = !jenv.reportException(); 442 } 443 } 444 445 return bSucceeded; 446 } 447 448 /****************************************************************************************** 449 * Creates any object with any arguments 450 ******************************************************************************************/ 451 jobject CJavaPlayerEventDispatcher::CreateObject(JNIEnv *env, jmethodID *cid, 452 const char* class_name, const char* signature, 453 jvalue* value) 454 { 455 jclass classe; 456 jobject result; 457 458 classe = env->FindClass(class_name); 459 if( classe == NULL ) 460 return NULL; /* can't find/load the class, exception thrown */ 461 462 if( *cid == NULL) 463 { 464 *cid = env->GetMethodID(classe, "<init>", signature); 465 if( *cid == NULL ) 466 { 467 env->DeleteLocalRef(classe); 468 return NULL; /* can't find/get the method, exception thrown */ 469 } 470 } 471 472 result = env->NewObjectA(classe, *cid, value); 473 474 env->DeleteLocalRef(classe); 475 return result; 476 } 477 478 jobject CJavaPlayerEventDispatcher::CreateBoolean(JNIEnv *env, jboolean boolean_value) 479 { 480 static jmethodID cid = NULL; 481 jvalue value; 482 483 value.z = boolean_value; 484 485 return CreateObject(env, &cid, "java/lang/Boolean", "(Z)V", &value); 486 } 487 488 jobject CJavaPlayerEventDispatcher::CreateInteger(JNIEnv *env, jint int_value) 489 { 490 static jmethodID cid = NULL; 491 jvalue value; 492 493 value.i = int_value; 494 495 return CreateObject(env, &cid, "java/lang/Integer", "(I)V", &value); 496 } 497 498 jobject CJavaPlayerEventDispatcher::CreateLong(JNIEnv *env, jlong long_value) 499 { 500 static jmethodID cid = NULL; 501 jvalue value; 502 503 value.j = long_value; 504 505 return CreateObject(env, &cid, "java/lang/Long", "(J)V", &value); 506 } 507 508 jobject CJavaPlayerEventDispatcher::CreateDouble(JNIEnv *env, jdouble double_value) 509 { 510 static jmethodID cid = NULL; 511 jvalue value; 512 513 value.d = double_value; 514 515 return CreateObject(env, &cid, "java/lang/Double", "(D)V", &value); 516 } 517 518 jobject CJavaPlayerEventDispatcher::CreateDuration(JNIEnv *env, jlong duration) 519 { 520 static jmethodID constructorID = NULL; 521 // We receive duration in nanoseconds, but javafx.util.Duration needs in milliseconds 522 jdouble millis = duration/1000000.0; 523 524 jclass durationClass = env->FindClass("javafx/util/Duration"); 525 if (durationClass == NULL) 526 return NULL; /* can't find/load the class, exception thrown */ 527 528 if (constructorID == NULL) 529 { 530 constructorID = env->GetMethodID(durationClass, "<init>", "(D)V"); 531 if( constructorID == NULL ) 532 { 533 env->DeleteLocalRef(durationClass); 534 return NULL; /* can't find/get the method, exception thrown */ 535 } 536 } 537 538 jobject result = env->NewObject(durationClass, constructorID, millis); 539 540 env->DeleteLocalRef(durationClass); 541 542 return result; 543 }