android_media_MediaSync.cpp revision c98f58efd147c574faa4a4f9956b5ab95e3027a5
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_SyncSettings.h" 25#include "android_runtime/AndroidRuntime.h" 26#include "android_runtime/android_view_Surface.h" 27#include "jni.h" 28#include "JNIHelp.h" 29 30#include <gui/Surface.h> 31 32#include <media/AudioTrack.h> 33#include <media/stagefright/MediaClock.h> 34#include <media/stagefright/MediaSync.h> 35#include <media/stagefright/foundation/ADebug.h> 36#include <media/stagefright/foundation/AString.h> 37 38#include <nativehelper/ScopedLocalRef.h> 39 40namespace android { 41 42struct fields_t { 43 jfieldID context; 44 jfieldID mediaTimestampMediaTimeUsID; 45 jfieldID mediaTimestampNanoTimeID; 46 jfieldID mediaTimestampClockRateID; 47}; 48 49static fields_t gFields; 50static SyncSettings::fields_t gSyncSettingsFields; 51 52//////////////////////////////////////////////////////////////////////////////// 53 54JMediaSync::JMediaSync() { 55 mSync = MediaSync::create(); 56} 57 58JMediaSync::~JMediaSync() { 59} 60 61status_t JMediaSync::configureSurface(const sp<IGraphicBufferProducer> &bufferProducer) { 62 return mSync->configureSurface(bufferProducer); 63} 64 65status_t JMediaSync::configureAudioTrack( 66 const sp<AudioTrack> &audioTrack, 67 int32_t nativeSampleRateInHz) { 68 return mSync->configureAudioTrack(audioTrack, nativeSampleRateInHz); 69} 70 71status_t JMediaSync::createInputSurface( 72 sp<IGraphicBufferProducer>* bufferProducer) { 73 return mSync->createInputSurface(bufferProducer); 74} 75 76status_t JMediaSync::setPlaybackRate(float rate) { 77 return mSync->setPlaybackRate(rate); 78} 79 80sp<const MediaClock> JMediaSync::getMediaClock() { 81 return mSync->getMediaClock(); 82} 83 84status_t JMediaSync::updateQueuedAudioData( 85 int sizeInBytes, int64_t presentationTimeUs) { 86 return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs); 87} 88 89} // namespace android 90 91//////////////////////////////////////////////////////////////////////////////// 92 93using namespace android; 94 95static sp<JMediaSync> setMediaSync(JNIEnv *env, jobject thiz, const sp<JMediaSync> &sync) { 96 sp<JMediaSync> old = (JMediaSync *)env->GetLongField(thiz, gFields.context); 97 if (sync != NULL) { 98 sync->incStrong(thiz); 99 } 100 if (old != NULL) { 101 old->decStrong(thiz); 102 } 103 104 env->SetLongField(thiz, gFields.context, (jlong)sync.get()); 105 106 return old; 107} 108 109static sp<JMediaSync> getMediaSync(JNIEnv *env, jobject thiz) { 110 return (JMediaSync *)env->GetLongField(thiz, gFields.context); 111} 112 113static void android_media_MediaSync_release(JNIEnv *env, jobject thiz) { 114 setMediaSync(env, thiz, NULL); 115} 116 117static void throwExceptionAsNecessary( 118 JNIEnv *env, status_t err, const char *msg = NULL) { 119 switch (err) { 120 case NO_ERROR: 121 break; 122 123 case BAD_VALUE: 124 jniThrowException(env, "java/lang/IllegalArgumentException", msg); 125 break; 126 127 case NO_INIT: 128 case INVALID_OPERATION: 129 default: 130 if (err > 0) { 131 break; 132 } 133 AString msgWithErrorCode(msg); 134 msgWithErrorCode.append(" error:"); 135 msgWithErrorCode.append(err); 136 jniThrowException(env, "java/lang/IllegalStateException", msgWithErrorCode.c_str()); 137 break; 138 } 139} 140 141static void android_media_MediaSync_native_configureSurface( 142 JNIEnv *env, jobject thiz, jobject jsurface) { 143 ALOGV("android_media_MediaSync_configureSurface"); 144 145 sp<JMediaSync> sync = getMediaSync(env, thiz); 146 if (sync == NULL) { 147 throwExceptionAsNecessary(env, INVALID_OPERATION); 148 return; 149 } 150 151 sp<IGraphicBufferProducer> bufferProducer; 152 if (jsurface != NULL) { 153 sp<Surface> surface(android_view_Surface_getSurface(env, jsurface)); 154 if (surface != NULL) { 155 bufferProducer = surface->getIGraphicBufferProducer(); 156 } else { 157 throwExceptionAsNecessary(env, BAD_VALUE, "The surface has been released"); 158 return; 159 } 160 } 161 162 status_t err = sync->configureSurface(bufferProducer); 163 164 if (err == INVALID_OPERATION) { 165 throwExceptionAsNecessary( 166 env, INVALID_OPERATION, "Surface has already been configured"); 167 } if (err != NO_ERROR) { 168 AString msg("Failed to connect to surface with error "); 169 msg.append(err); 170 throwExceptionAsNecessary(env, BAD_VALUE, msg.c_str()); 171 } 172} 173 174static void android_media_MediaSync_native_configureAudioTrack( 175 JNIEnv *env, jobject thiz, jobject jaudioTrack, jint nativeSampleRateInHz) { 176 ALOGV("android_media_MediaSync_configureAudioTrack"); 177 178 sp<JMediaSync> sync = getMediaSync(env, thiz); 179 if (sync == NULL) { 180 throwExceptionAsNecessary(env, INVALID_OPERATION); 181 return; 182 } 183 184 sp<AudioTrack> audioTrack; 185 if (jaudioTrack != NULL) { 186 audioTrack = android_media_AudioTrack_getAudioTrack(env, jaudioTrack); 187 if (audioTrack == NULL) { 188 throwExceptionAsNecessary(env, BAD_VALUE, "The audio track has been released"); 189 return; 190 } 191 } 192 193 status_t err = sync->configureAudioTrack(audioTrack, nativeSampleRateInHz); 194 195 if (err == INVALID_OPERATION) { 196 throwExceptionAsNecessary( 197 env, INVALID_OPERATION, "Audio track has already been configured"); 198 } if (err != NO_ERROR) { 199 AString msg("Failed to configure audio track with error "); 200 msg.append(err); 201 throwExceptionAsNecessary(env, BAD_VALUE, msg.c_str()); 202 } 203} 204 205static jobject android_media_MediaSync_createInputSurface( 206 JNIEnv* env, jobject thiz) { 207 ALOGV("android_media_MediaSync_createInputSurface"); 208 209 sp<JMediaSync> sync = getMediaSync(env, thiz); 210 if (sync == NULL) { 211 throwExceptionAsNecessary(env, INVALID_OPERATION); 212 return NULL; 213 } 214 215 // Tell the MediaSync that we want to use a Surface as input. 216 sp<IGraphicBufferProducer> bufferProducer; 217 status_t err = sync->createInputSurface(&bufferProducer); 218 if (err != NO_ERROR) { 219 throwExceptionAsNecessary(env, INVALID_OPERATION); 220 return NULL; 221 } 222 223 // Wrap the IGBP in a Java-language Surface. 224 return android_view_Surface_createFromIGraphicBufferProducer(env, 225 bufferProducer); 226} 227 228static void android_media_MediaSync_native_updateQueuedAudioData( 229 JNIEnv *env, jobject thiz, jint sizeInBytes, jlong presentationTimeUs) { 230 sp<JMediaSync> sync = getMediaSync(env, thiz); 231 if (sync == NULL) { 232 throwExceptionAsNecessary(env, INVALID_OPERATION); 233 return; 234 } 235 236 status_t err = sync->updateQueuedAudioData(sizeInBytes, presentationTimeUs); 237 if (err != NO_ERROR) { 238 throwExceptionAsNecessary(env, err); 239 return; 240 } 241} 242 243static jboolean android_media_MediaSync_native_getTimestamp( 244 JNIEnv *env, jobject thiz, jobject timestamp) { 245 sp<JMediaSync> sync = getMediaSync(env, thiz); 246 if (sync == NULL) { 247 throwExceptionAsNecessary(env, INVALID_OPERATION); 248 return JNI_FALSE; 249 } 250 251 sp<const MediaClock> mediaClock = sync->getMediaClock(); 252 if (mediaClock == NULL) { 253 return JNI_FALSE; 254 } 255 256 int64_t nowUs = ALooper::GetNowUs(); 257 int64_t mediaUs = 0; 258 if (mediaClock->getMediaTime(nowUs, &mediaUs) != OK) { 259 return JNI_FALSE; 260 } 261 262 env->SetLongField(timestamp, gFields.mediaTimestampMediaTimeUsID, 263 (jlong)mediaUs); 264 env->SetLongField(timestamp, gFields.mediaTimestampNanoTimeID, 265 (jlong)(nowUs * 1000)); 266 env->SetFloatField(timestamp, gFields.mediaTimestampClockRateID, 267 (jfloat)mediaClock->getPlaybackRate()); 268 return JNI_TRUE; 269} 270 271static void 272android_media_MediaSync_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings) 273{ 274 sp<JMediaSync> sync = getMediaSync(env, thiz); 275 if (sync == NULL) { 276 throwExceptionAsNecessary(env, INVALID_OPERATION); 277 return; 278 } 279 280 SyncSettings scs; 281 scs.fillFromJobject(env, gSyncSettingsFields, settings); 282 ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f", 283 scs.syncSourceSet, scs.syncSource, 284 scs.audioAdjustModeSet, scs.audioAdjustMode, 285 scs.toleranceSet, scs.tolerance, 286 scs.frameRateSet, scs.frameRate); 287 288 // TODO: pass sync settings to mediasync when it supports it 289} 290 291static jobject 292android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz) 293{ 294 sp<JMediaSync> sync = getMediaSync(env, thiz); 295 if (sync == NULL) { 296 throwExceptionAsNecessary(env, INVALID_OPERATION); 297 return NULL; 298 } 299 300 SyncSettings scs; 301 scs.syncSource = 0; // SYNC_SOURCE_DEFAULT 302 scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT 303 scs.tolerance = 0.f; 304 scs.frameRate = 0.f; 305 306 // TODO: get this from mediaplayer when it supports it 307 // process_media_player_call( 308 // env, thiz, mp->getSyncSettings(&scs), NULL, NULL); 309 ALOGV("getSyncSettings: %d %d %f %f", 310 scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate); 311 312 scs.syncSourceSet = true; 313 scs.audioAdjustModeSet = true; 314 scs.toleranceSet = true; 315 scs.frameRateSet = false; 316 317 return scs.asJobject(env, gSyncSettingsFields); 318} 319 320static void android_media_MediaSync_native_init(JNIEnv *env) { 321 ScopedLocalRef<jclass> clazz(env, env->FindClass("android/media/MediaSync")); 322 CHECK(clazz.get() != NULL); 323 324 gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); 325 CHECK(gFields.context != NULL); 326 327 clazz.reset(env->FindClass("android/media/MediaTimestamp")); 328 CHECK(clazz.get() != NULL); 329 330 gFields.mediaTimestampMediaTimeUsID = 331 env->GetFieldID(clazz.get(), "mediaTimeUs", "J"); 332 CHECK(gFields.mediaTimestampMediaTimeUsID != NULL); 333 334 gFields.mediaTimestampNanoTimeID = 335 env->GetFieldID(clazz.get(), "nanoTime", "J"); 336 CHECK(gFields.mediaTimestampNanoTimeID != NULL); 337 338 gFields.mediaTimestampClockRateID = 339 env->GetFieldID(clazz.get(), "clockRate", "F"); 340 CHECK(gFields.mediaTimestampClockRateID != NULL); 341 342 gSyncSettingsFields.init(env); 343} 344 345static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) { 346 sp<JMediaSync> sync = new JMediaSync(); 347 348 setMediaSync(env, thiz, sync); 349} 350 351static void android_media_MediaSync_native_setPlaybackRate( 352 JNIEnv *env, jobject thiz, jfloat rate) { 353 sp<JMediaSync> sync = getMediaSync(env, thiz); 354 if (sync == NULL) { 355 throwExceptionAsNecessary(env, INVALID_OPERATION); 356 return; 357 } 358 359 status_t err = sync->setPlaybackRate(rate); 360 if (err != NO_ERROR) { 361 throwExceptionAsNecessary(env, err); 362 return; 363 } 364} 365 366static void android_media_MediaSync_native_finalize(JNIEnv *env, jobject thiz) { 367 android_media_MediaSync_release(env, thiz); 368} 369 370static JNINativeMethod gMethods[] = { 371 { "native_configureSurface", 372 "(Landroid/view/Surface;)V", 373 (void *)android_media_MediaSync_native_configureSurface }, 374 375 { "native_configureAudioTrack", 376 "(Landroid/media/AudioTrack;I)V", 377 (void *)android_media_MediaSync_native_configureAudioTrack }, 378 379 { "createInputSurface", "()Landroid/view/Surface;", 380 (void *)android_media_MediaSync_createInputSurface }, 381 382 { "native_updateQueuedAudioData", 383 "(IJ)V", 384 (void *)android_media_MediaSync_native_updateQueuedAudioData }, 385 386 { "native_getTimestamp", 387 "(Landroid/media/MediaTimestamp;)Z", 388 (void *)android_media_MediaSync_native_getTimestamp }, 389 390 { "native_init", "()V", (void *)android_media_MediaSync_native_init }, 391 392 { "native_setup", "()V", (void *)android_media_MediaSync_native_setup }, 393 394 { "native_release", "()V", (void *)android_media_MediaSync_release }, 395 396 { "native_setPlaybackRate", "(F)V", (void *)android_media_MediaSync_native_setPlaybackRate }, 397 398 { "setSyncSettings", "(Landroid/media/SyncSettings;)V", (void *)android_media_MediaSync_setSyncSettings}, 399 400 { "getSyncSettings", "()Landroid/media/SyncSettings;", (void *)android_media_MediaSync_getSyncSettings}, 401 402 { "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize }, 403}; 404 405int register_android_media_MediaSync(JNIEnv *env) { 406 return AndroidRuntime::registerNativeMethods( 407 env, "android/media/MediaSync", gMethods, NELEM(gMethods)); 408} 409