android_media_MediaSync.cpp revision d08debcf42d820fa8ef9916077a7bfc0a36f2db5
1/* 2 * Copyright 2015, The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17//#define LOG_NDEBUG 0 18#define LOG_TAG "MediaSync-JNI" 19#include <utils/Log.h> 20 21#include "android_media_MediaSync.h" 22 23#include "android_media_AudioTrack.h" 24#include "android_media_PlaybackParams.h" 25#include "android_media_SyncParams.h" 26#include "android_runtime/AndroidRuntime.h" 27#include "android_runtime/android_view_Surface.h" 28#include "jni.h" 29#include "JNIHelp.h" 30 31#include <gui/Surface.h> 32 33#include <media/AudioResamplerPublic.h> 34#include <media/AudioTrack.h> 35#include <media/stagefright/MediaClock.h> 36#include <media/stagefright/MediaSync.h> 37#include <media/stagefright/foundation/ADebug.h> 38#include <media/stagefright/foundation/AString.h> 39 40#include <nativehelper/ScopedLocalRef.h> 41 42namespace android { 43 44struct fields_t { 45 jfieldID context; 46 jfieldID mediaTimestampMediaTimeUsID; 47 jfieldID mediaTimestampNanoTimeID; 48 jfieldID mediaTimestampClockRateID; 49}; 50 51static fields_t gFields; 52static PlaybackParams::fields_t gPlaybackParamsFields; 53static SyncParams::fields_t gSyncParamsFields; 54 55//////////////////////////////////////////////////////////////////////////////// 56 57JMediaSync::JMediaSync() { 58 mSync = MediaSync::create(); 59} 60 61JMediaSync::~JMediaSync() { 62} 63 64status_t JMediaSync::setSurface(const sp<IGraphicBufferProducer> &bufferProducer) { 65 return mSync->setSurface(bufferProducer); 66} 67 68status_t JMediaSync::setAudioTrack(const sp<AudioTrack> &audioTrack) { 69 return mSync->setAudioTrack(audioTrack); 70} 71 72status_t JMediaSync::createInputSurface( 73 sp<IGraphicBufferProducer>* bufferProducer) { 74 return mSync->createInputSurface(bufferProducer); 75} 76 77sp<const MediaClock> JMediaSync::getMediaClock() { 78 return mSync->getMediaClock(); 79} 80 81status_t JMediaSync::setPlaybackParams(const AudioPlaybackRate& rate) { 82 return mSync->setPlaybackSettings(rate); 83} 84 85void JMediaSync::getPlaybackParams(AudioPlaybackRate* rate /* nonnull */) { 86 mSync->getPlaybackSettings(rate); 87} 88 89status_t JMediaSync::setSyncParams(const AVSyncSettings& syncParams) { 90 return mSync->setSyncSettings(syncParams); 91} 92 93void JMediaSync::getSyncParams(AVSyncSettings* syncParams /* nonnull */) { 94 mSync->getSyncSettings(syncParams); 95} 96 97status_t JMediaSync::setVideoFrameRateHint(float rate) { 98 return mSync->setVideoFrameRateHint(rate); 99} 100 101float JMediaSync::getVideoFrameRate() { 102 return mSync->getVideoFrameRate(); 103} 104 105void JMediaSync::flush() { 106 mSync->flush(); 107} 108 109status_t JMediaSync::updateQueuedAudioData( 110 int sizeInBytes, int64_t presentationTimeUs) { 111 return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs); 112} 113 114status_t JMediaSync::getPlayTimeForPendingAudioFrames(int64_t *outTimeUs) { 115 return mSync->getPlayTimeForPendingAudioFrames(outTimeUs); 116} 117 118} // namespace android 119 120//////////////////////////////////////////////////////////////////////////////// 121 122using namespace android; 123 124static sp<JMediaSync> setMediaSync(JNIEnv *env, jobject thiz, const sp<JMediaSync> &sync) { 125 sp<JMediaSync> old = (JMediaSync *)env->GetLongField(thiz, gFields.context); 126 if (sync != NULL) { 127 sync->incStrong(thiz); 128 } 129 if (old != NULL) { 130 old->decStrong(thiz); 131 } 132 133 env->SetLongField(thiz, gFields.context, (jlong)sync.get()); 134 135 return old; 136} 137 138static sp<JMediaSync> getMediaSync(JNIEnv *env, jobject thiz) { 139 return (JMediaSync *)env->GetLongField(thiz, gFields.context); 140} 141 142static void android_media_MediaSync_release(JNIEnv *env, jobject thiz) { 143 setMediaSync(env, thiz, NULL); 144} 145 146static void throwExceptionAsNecessary( 147 JNIEnv *env, status_t err, const char *msg = NULL) { 148 switch (err) { 149 case NO_ERROR: 150 break; 151 152 case BAD_VALUE: 153 jniThrowException(env, "java/lang/IllegalArgumentException", msg); 154 break; 155 156 case NO_INIT: 157 case INVALID_OPERATION: 158 default: 159 if (err > 0) { 160 break; 161 } 162 AString msgWithErrorCode(msg); 163 msgWithErrorCode.append(" error:"); 164 msgWithErrorCode.append(err); 165 jniThrowException(env, "java/lang/IllegalStateException", msgWithErrorCode.c_str()); 166 break; 167 } 168} 169 170static void android_media_MediaSync_native_setSurface( 171 JNIEnv *env, jobject thiz, jobject jsurface) { 172 ALOGV("android_media_MediaSync_setSurface"); 173 174 sp<JMediaSync> sync = getMediaSync(env, thiz); 175 if (sync == NULL) { 176 throwExceptionAsNecessary(env, INVALID_OPERATION); 177 return; 178 } 179 180 sp<IGraphicBufferProducer> bufferProducer; 181 if (jsurface != NULL) { 182 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface)); 183 if (surface != NULL) { 184 bufferProducer = surface->getIGraphicBufferProducer(); 185 } else { 186 throwExceptionAsNecessary(env, BAD_VALUE, "The surface has been released"); 187 return; 188 } 189 } 190 191 status_t err = sync->setSurface(bufferProducer); 192 193 if (err == INVALID_OPERATION) { 194 throwExceptionAsNecessary( 195 env, INVALID_OPERATION, "Surface has already been configured"); 196 } if (err != NO_ERROR) { 197 AString msg("Failed to connect to surface with error "); 198 msg.append(err); 199 throwExceptionAsNecessary(env, BAD_VALUE, msg.c_str()); 200 } 201} 202 203static void android_media_MediaSync_native_setAudioTrack( 204 JNIEnv *env, jobject thiz, jobject jaudioTrack) { 205 ALOGV("android_media_MediaSync_setAudioTrack"); 206 207 sp<JMediaSync> sync = getMediaSync(env, thiz); 208 if (sync == NULL) { 209 throwExceptionAsNecessary(env, INVALID_OPERATION); 210 return; 211 } 212 213 sp<AudioTrack> audioTrack; 214 if (jaudioTrack != NULL) { 215 audioTrack = android_media_AudioTrack_getAudioTrack(env, jaudioTrack); 216 if (audioTrack == NULL) { 217 throwExceptionAsNecessary(env, BAD_VALUE, "The audio track has been released"); 218 return; 219 } 220 } 221 222 status_t err = sync->setAudioTrack(audioTrack); 223 224 if (err == INVALID_OPERATION) { 225 throwExceptionAsNecessary( 226 env, INVALID_OPERATION, "Audio track has already been configured"); 227 } if (err != NO_ERROR) { 228 AString msg("Failed to configure audio track with error "); 229 msg.append(err); 230 throwExceptionAsNecessary(env, BAD_VALUE, msg.c_str()); 231 } 232} 233 234static jobject android_media_MediaSync_createInputSurface( 235 JNIEnv* env, jobject thiz) { 236 ALOGV("android_media_MediaSync_createInputSurface"); 237 238 sp<JMediaSync> sync = getMediaSync(env, thiz); 239 if (sync == NULL) { 240 throwExceptionAsNecessary(env, INVALID_OPERATION); 241 return NULL; 242 } 243 244 // Tell the MediaSync that we want to use a Surface as input. 245 sp<IGraphicBufferProducer> bufferProducer; 246 status_t err = sync->createInputSurface(&bufferProducer); 247 if (err != NO_ERROR) { 248 throwExceptionAsNecessary(env, INVALID_OPERATION); 249 return NULL; 250 } 251 252 // Wrap the IGBP in a Java-language Surface. 253 return android_view_Surface_createFromIGraphicBufferProducer(env, 254 bufferProducer); 255} 256 257static void android_media_MediaSync_native_updateQueuedAudioData( 258 JNIEnv *env, jobject thiz, jint sizeInBytes, jlong presentationTimeUs) { 259 sp<JMediaSync> sync = getMediaSync(env, thiz); 260 if (sync == NULL) { 261 throwExceptionAsNecessary(env, INVALID_OPERATION); 262 return; 263 } 264 265 status_t err = sync->updateQueuedAudioData(sizeInBytes, presentationTimeUs); 266 if (err != NO_ERROR) { 267 throwExceptionAsNecessary(env, err); 268 return; 269 } 270} 271 272static jboolean android_media_MediaSync_native_getTimestamp( 273 JNIEnv *env, jobject thiz, jobject timestamp) { 274 sp<JMediaSync> sync = getMediaSync(env, thiz); 275 if (sync == NULL) { 276 throwExceptionAsNecessary(env, INVALID_OPERATION); 277 return JNI_FALSE; 278 } 279 280 sp<const MediaClock> mediaClock = sync->getMediaClock(); 281 if (mediaClock == NULL) { 282 return JNI_FALSE; 283 } 284 285 int64_t nowUs = ALooper::GetNowUs(); 286 int64_t mediaUs = 0; 287 if (mediaClock->getMediaTime(nowUs, &mediaUs) != OK) { 288 return JNI_FALSE; 289 } 290 291 env->SetLongField(timestamp, gFields.mediaTimestampMediaTimeUsID, 292 (jlong)mediaUs); 293 env->SetLongField(timestamp, gFields.mediaTimestampNanoTimeID, 294 (jlong)(nowUs * 1000)); 295 env->SetFloatField(timestamp, gFields.mediaTimestampClockRateID, 296 (jfloat)mediaClock->getPlaybackRate()); 297 return JNI_TRUE; 298} 299 300static jlong android_media_MediaSync_native_getPlayTimeForPendingAudioFrames( 301 JNIEnv *env, jobject thiz) { 302 sp<JMediaSync> sync = getMediaSync(env, thiz); 303 if (sync == NULL) { 304 throwExceptionAsNecessary(env, INVALID_OPERATION); 305 } 306 307 int64_t playTimeUs = 0; 308 status_t err = sync->getPlayTimeForPendingAudioFrames(&playTimeUs); 309 if (err != NO_ERROR) { 310 throwExceptionAsNecessary(env, err); 311 } 312 return (jlong)playTimeUs; 313} 314 315static jfloat android_media_MediaSync_setPlaybackParams( 316 JNIEnv *env, jobject thiz, jobject params) { 317 sp<JMediaSync> sync = getMediaSync(env, thiz); 318 if (sync == NULL) { 319 throwExceptionAsNecessary(env, INVALID_OPERATION); 320 return (jfloat)0.f; 321 } 322 323 PlaybackParams pbs; 324 pbs.fillFromJobject(env, gPlaybackParamsFields, params); 325 ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u", 326 pbs.speedSet, pbs.audioRate.mSpeed, 327 pbs.pitchSet, pbs.audioRate.mPitch, 328 pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode, 329 pbs.audioStretchModeSet, pbs.audioRate.mStretchMode); 330 331 AudioPlaybackRate rate; 332 sync->getPlaybackParams(&rate); 333 bool updatedRate = false; 334 if (pbs.speedSet) { 335 rate.mSpeed = pbs.audioRate.mSpeed; 336 updatedRate = true; 337 } 338 if (pbs.pitchSet) { 339 rate.mPitch = pbs.audioRate.mPitch; 340 updatedRate = true; 341 } 342 if (pbs.audioFallbackModeSet) { 343 rate.mFallbackMode = pbs.audioRate.mFallbackMode; 344 updatedRate = true; 345 } 346 if (pbs.audioStretchModeSet) { 347 rate.mStretchMode = pbs.audioRate.mStretchMode; 348 updatedRate = true; 349 } 350 if (updatedRate) { 351 status_t err = sync->setPlaybackParams(rate); 352 if (err != OK) { 353 throwExceptionAsNecessary(env, err); 354 return (jfloat)0.f; 355 } 356 } 357 358 sp<const MediaClock> mediaClock = sync->getMediaClock(); 359 if (mediaClock == NULL) { 360 return (jfloat)0.f; 361 } 362 363 return (jfloat)mediaClock->getPlaybackRate(); 364} 365 366static jobject android_media_MediaSync_getPlaybackParams( 367 JNIEnv *env, jobject thiz) { 368 sp<JMediaSync> sync = getMediaSync(env, thiz); 369 if (sync == NULL) { 370 throwExceptionAsNecessary(env, INVALID_OPERATION); 371 return NULL; 372 } 373 374 PlaybackParams pbs; 375 AudioPlaybackRate &audioRate = pbs.audioRate; 376 sync->getPlaybackParams(&audioRate); 377 ALOGV("getPlaybackParams: %f %f %d %d", 378 audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode); 379 380 pbs.speedSet = true; 381 pbs.pitchSet = true; 382 pbs.audioFallbackModeSet = true; 383 pbs.audioStretchModeSet = true; 384 385 return pbs.asJobject(env, gPlaybackParamsFields); 386} 387 388static jfloat android_media_MediaSync_setSyncParams( 389 JNIEnv *env, jobject thiz, jobject params) { 390 sp<JMediaSync> sync = getMediaSync(env, thiz); 391 if (sync == NULL) { 392 throwExceptionAsNecessary(env, INVALID_OPERATION); 393 return (jfloat)0.f; 394 } 395 396 SyncParams scs; 397 scs.fillFromJobject(env, gSyncParamsFields, params); 398 ALOGV("setSyncParams: %d:%d %d:%d %d:%f %d:%f", 399 scs.syncSourceSet, scs.sync.mSource, 400 scs.audioAdjustModeSet, scs.sync.mAudioAdjustMode, 401 scs.toleranceSet, scs.sync.mTolerance, 402 scs.frameRateSet, scs.frameRate); 403 404 AVSyncSettings avsync; 405 sync->getSyncParams(&avsync); 406 bool updatedSync = false; 407 status_t err = OK; 408 if (scs.syncSourceSet) { 409 avsync.mSource = scs.sync.mSource; 410 updatedSync = true; 411 } 412 if (scs.audioAdjustModeSet) { 413 avsync.mAudioAdjustMode = scs.sync.mAudioAdjustMode; 414 updatedSync = true; 415 } 416 if (scs.toleranceSet) { 417 avsync.mTolerance = scs.sync.mTolerance; 418 updatedSync = true; 419 } 420 if (updatedSync) { 421 err = sync->setSyncParams(avsync); 422 } 423 424 if (scs.frameRateSet && err == OK) { 425 err = sync->setVideoFrameRateHint(scs.frameRate); 426 } 427 if (err != OK) { 428 throwExceptionAsNecessary(env, err); 429 return (jfloat)0.f; 430 } 431 432 sp<const MediaClock> mediaClock = sync->getMediaClock(); 433 if (mediaClock == NULL) { 434 return (jfloat)0.f; 435 } 436 437 return (jfloat)mediaClock->getPlaybackRate(); 438} 439 440static jobject android_media_MediaSync_getSyncParams(JNIEnv *env, jobject thiz) { 441 sp<JMediaSync> sync = getMediaSync(env, thiz); 442 if (sync == NULL) { 443 throwExceptionAsNecessary(env, INVALID_OPERATION); 444 return NULL; 445 } 446 447 SyncParams scs; 448 sync->getSyncParams(&scs.sync); 449 scs.frameRate = sync->getVideoFrameRate(); 450 451 ALOGV("getSyncParams: %d %d %f %f", 452 scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate); 453 454 // sanity check params 455 if (scs.sync.mSource >= AVSYNC_SOURCE_MAX 456 || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX 457 || scs.sync.mTolerance < 0.f 458 || scs.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) { 459 throwExceptionAsNecessary(env, INVALID_OPERATION); 460 return NULL; 461 } 462 463 scs.syncSourceSet = true; 464 scs.audioAdjustModeSet = true; 465 scs.toleranceSet = true; 466 scs.frameRateSet = scs.frameRate >= 0.f; 467 468 return scs.asJobject(env, gSyncParamsFields); 469} 470 471static void android_media_MediaSync_native_flush(JNIEnv *env, jobject thiz) { 472 sp<JMediaSync> sync = getMediaSync(env, thiz); 473 if (sync == NULL) { 474 throwExceptionAsNecessary(env, INVALID_OPERATION); 475 return; 476 } 477 478 sync->flush(); 479} 480 481static void android_media_MediaSync_native_init(JNIEnv *env) { 482 ScopedLocalRef<jclass> clazz(env, env->FindClass("android/media/MediaSync")); 483 CHECK(clazz.get() != NULL); 484 485 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); 486 CHECK(gFields.context != NULL); 487 488 clazz.reset(env->FindClass("android/media/MediaTimestamp")); 489 CHECK(clazz.get() != NULL); 490 491 gFields.mediaTimestampMediaTimeUsID = 492 env->GetFieldID(clazz.get(), "mediaTimeUs", "J"); 493 CHECK(gFields.mediaTimestampMediaTimeUsID != NULL); 494 495 gFields.mediaTimestampNanoTimeID = 496 env->GetFieldID(clazz.get(), "nanoTime", "J"); 497 CHECK(gFields.mediaTimestampNanoTimeID != NULL); 498 499 gFields.mediaTimestampClockRateID = 500 env->GetFieldID(clazz.get(), "clockRate", "F"); 501 CHECK(gFields.mediaTimestampClockRateID != NULL); 502 503 gSyncParamsFields.init(env); 504 gPlaybackParamsFields.init(env); 505} 506 507static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) { 508 sp<JMediaSync> sync = new JMediaSync(); 509 510 setMediaSync(env, thiz, sync); 511} 512 513static void android_media_MediaSync_native_finalize(JNIEnv *env, jobject thiz) { 514 android_media_MediaSync_release(env, thiz); 515} 516 517static JNINativeMethod gMethods[] = { 518 { "native_setSurface", 519 "(Landroid/view/Surface;)V", 520 (void *)android_media_MediaSync_native_setSurface }, 521 522 { "native_setAudioTrack", 523 "(Landroid/media/AudioTrack;)V", 524 (void *)android_media_MediaSync_native_setAudioTrack }, 525 526 { "createInputSurface", "()Landroid/view/Surface;", 527 (void *)android_media_MediaSync_createInputSurface }, 528 529 { "native_updateQueuedAudioData", 530 "(IJ)V", 531 (void *)android_media_MediaSync_native_updateQueuedAudioData }, 532 533 { "native_getTimestamp", 534 "(Landroid/media/MediaTimestamp;)Z", 535 (void *)android_media_MediaSync_native_getTimestamp }, 536 537 { "native_getPlayTimeForPendingAudioFrames", 538 "()J", 539 (void *)android_media_MediaSync_native_getPlayTimeForPendingAudioFrames }, 540 541 { "native_flush", "()V", (void *)android_media_MediaSync_native_flush }, 542 543 { "native_init", "()V", (void *)android_media_MediaSync_native_init }, 544 545 { "native_setup", "()V", (void *)android_media_MediaSync_native_setup }, 546 547 { "native_release", "()V", (void *)android_media_MediaSync_release }, 548 549 { "native_setPlaybackParams", "(Landroid/media/PlaybackParams;)F", 550 (void *)android_media_MediaSync_setPlaybackParams }, 551 552 { "getPlaybackParams", "()Landroid/media/PlaybackParams;", 553 (void *)android_media_MediaSync_getPlaybackParams }, 554 555 { "native_setSyncParams", "(Landroid/media/SyncParams;)F", 556 (void *)android_media_MediaSync_setSyncParams }, 557 558 { "getSyncParams", "()Landroid/media/SyncParams;", 559 (void *)android_media_MediaSync_getSyncParams }, 560 561 { "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize }, 562}; 563 564int register_android_media_MediaSync(JNIEnv *env) { 565 return AndroidRuntime::registerNativeMethods( 566 env, "android/media/MediaSync", gMethods, NELEM(gMethods)); 567} 568