1 /* 2 * Copyright (C) 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 "audio-track-native" 19 20 #include "Blob.h" 21 #include "Gate.h" 22 #include "sl-utils.h" 23 24 #include <deque> 25 #include <utils/Errors.h> 26 27 // Select whether to use STL shared pointer or to use Android strong pointer. 28 // We really don't promote any sharing of this object for its lifetime, but nevertheless could 29 // change the shared pointer value on the fly if desired. 30 #define USE_SHARED_POINTER 31 32 #ifdef USE_SHARED_POINTER 33 #include <memory> 34 template <typename T> using shared_pointer = std::shared_ptr<T>; 35 #else 36 #include <utils/RefBase.h> 37 template <typename T> using shared_pointer = android::sp<T>; 38 #endif 39 40 using namespace android; 41 42 // Must be kept in sync with Java android.media.cts.AudioTrackNative.WriteFlags 43 enum { 44 WRITE_FLAG_BLOCKING = (1 << 0), 45 }; 46 47 // TODO: Add a single buffer blocking write mode which does not require additional memory. 48 // TODO: Add internal buffer memory (e.g. use circular buffer, right now mallocs on heap). 49 50 class AudioTrackNative 51 #ifndef USE_SHARED_POINTER 52 : public RefBase // android strong pointers require RefBase 53 #endif 54 { 55 public: 56 AudioTrackNative() : 57 mEngineObj(NULL), 58 mEngine(NULL), 59 mOutputMixObj(NULL), 60 mPlayerObj(NULL), 61 mPlay(NULL), 62 mBufferQueue(NULL), 63 mPlayState(SL_PLAYSTATE_STOPPED), 64 mNumBuffers(0) 65 { } 66 67 ~AudioTrackNative() { 68 close(); 69 } 70 71 typedef std::lock_guard<std::recursive_mutex> auto_lock; 72 73 status_t open(jint numChannels, jint channelMask, 74 jint sampleRate, jboolean useFloat, jint numBuffers) { 75 close(); 76 auto_lock l(mLock); 77 mEngineObj = OpenSLEngine(); 78 if (mEngineObj == NULL) { 79 ALOGW("cannot create OpenSL ES engine"); 80 return INVALID_OPERATION; 81 } 82 83 SLresult res; 84 for (;;) { 85 /* Get the SL Engine Interface which is implicit */ 86 res = (*mEngineObj)->GetInterface(mEngineObj, SL_IID_ENGINE, (void *)&mEngine); 87 if (res != SL_RESULT_SUCCESS) break; 88 89 // Create Output Mix object to be used by player 90 res = (*mEngine)->CreateOutputMix( 91 mEngine, &mOutputMixObj, 0 /* numInterfaces */, 92 NULL /* pInterfaceIds */, NULL /* pInterfaceRequired */); 93 if (res != SL_RESULT_SUCCESS) break; 94 95 // Realizing the Output Mix object in synchronous mode. 96 res = (*mOutputMixObj)->Realize(mOutputMixObj, SL_BOOLEAN_FALSE /* async */); 97 if (res != SL_RESULT_SUCCESS) break; 98 99 /* Setup the data source structure for the buffer queue */ 100 SLDataLocator_BufferQueue bufferQueue; 101 bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE; 102 bufferQueue.numBuffers = numBuffers; 103 mNumBuffers = numBuffers; 104 105 /* Setup the format of the content in the buffer queue */ 106 107 SLAndroidDataFormat_PCM_EX pcm; 108 pcm.formatType = useFloat ? SL_ANDROID_DATAFORMAT_PCM_EX : SL_DATAFORMAT_PCM; 109 pcm.numChannels = numChannels; 110 pcm.sampleRate = sampleRate * 1000; 111 pcm.bitsPerSample = useFloat ? 112 SL_PCMSAMPLEFORMAT_FIXED_32 : SL_PCMSAMPLEFORMAT_FIXED_16; 113 pcm.containerSize = pcm.bitsPerSample; 114 pcm.channelMask = channelMask; 115 pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; 116 // additional 117 pcm.representation = useFloat ? SL_ANDROID_PCM_REPRESENTATION_FLOAT 118 : SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; 119 SLDataSource audioSource; 120 audioSource.pFormat = (void *)&pcm; 121 audioSource.pLocator = (void *)&bufferQueue; 122 123 /* Setup the data sink structure */ 124 SLDataLocator_OutputMix locator_outputmix; 125 locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; 126 locator_outputmix.outputMix = mOutputMixObj; 127 128 SLDataSink audioSink; 129 audioSink.pLocator = (void *)&locator_outputmix; 130 audioSink.pFormat = NULL; 131 132 SLboolean required[1]; 133 SLInterfaceID iidArray[1]; 134 required[0] = SL_BOOLEAN_TRUE; 135 iidArray[0] = SL_IID_BUFFERQUEUE; 136 137 res = (*mEngine)->CreateAudioPlayer(mEngine, &mPlayerObj, 138 &audioSource, &audioSink, 1 /* numInterfaces */, iidArray, required); 139 if (res != SL_RESULT_SUCCESS) break; 140 141 res = (*mPlayerObj)->Realize(mPlayerObj, SL_BOOLEAN_FALSE /* async */); 142 if (res != SL_RESULT_SUCCESS) break; 143 144 res = (*mPlayerObj)->GetInterface(mPlayerObj, SL_IID_PLAY, (void*)&mPlay); 145 if (res != SL_RESULT_SUCCESS) break; 146 147 res = (*mPlayerObj)->GetInterface( 148 mPlayerObj, SL_IID_BUFFERQUEUE, (void*)&mBufferQueue); 149 if (res != SL_RESULT_SUCCESS) break; 150 151 /* Setup to receive buffer queue event callbacks */ 152 res = (*mBufferQueue)->RegisterCallback(mBufferQueue, BufferQueueCallback, this); 153 if (res != SL_RESULT_SUCCESS) break; 154 155 // success 156 break; 157 } 158 if (res != SL_RESULT_SUCCESS) { 159 close(); // should be safe to close even with lock held 160 ALOGW("open error %s", android::getSLErrStr(res)); 161 return INVALID_OPERATION; 162 } 163 return OK; 164 } 165 166 void close() { 167 SLObjectItf engineObj; 168 SLObjectItf outputMixObj; 169 SLObjectItf playerObj; 170 { 171 auto_lock l(mLock); 172 if (mPlay != NULL && mPlayState != SL_PLAYSTATE_STOPPED) { 173 (void)stop(); 174 } 175 // once stopped, we can unregister the callback 176 if (mBufferQueue != NULL) { 177 (void)(*mBufferQueue)->RegisterCallback( 178 mBufferQueue, NULL /* callback */, NULL /* *pContext */); 179 } 180 (void)flush(); 181 engineObj = mEngineObj; 182 outputMixObj = mOutputMixObj; 183 playerObj = mPlayerObj; 184 // clear out interfaces and objects 185 mPlay = NULL; 186 mBufferQueue = NULL; 187 mEngine = NULL; 188 mPlayerObj = NULL; 189 mOutputMixObj = NULL; 190 mEngineObj = NULL; 191 mPlayState = SL_PLAYSTATE_STOPPED; 192 } 193 // destroy without lock 194 if (playerObj != NULL) { 195 (*playerObj)->Destroy(playerObj); 196 } 197 if (outputMixObj != NULL) { 198 (*outputMixObj)->Destroy(outputMixObj); 199 } 200 if (engineObj != NULL) { 201 CloseSLEngine(engineObj); 202 } 203 } 204 205 status_t setPlayState(SLuint32 playState) { 206 auto_lock l(mLock); 207 if (mPlay == NULL) { 208 return INVALID_OPERATION; 209 } 210 SLresult res = (*mPlay)->SetPlayState(mPlay, playState); 211 if (res != SL_RESULT_SUCCESS) { 212 ALOGW("setPlayState %d error %s", playState, android::getSLErrStr(res)); 213 return INVALID_OPERATION; 214 } 215 mPlayState = playState; 216 return OK; 217 } 218 219 SLuint32 getPlayState() { 220 auto_lock l(mLock); 221 if (mPlay == NULL) { 222 return SL_PLAYSTATE_STOPPED; 223 } 224 SLuint32 playState; 225 SLresult res = (*mPlay)->GetPlayState(mPlay, &playState); 226 if (res != SL_RESULT_SUCCESS) { 227 ALOGW("getPlayState error %s", android::getSLErrStr(res)); 228 return SL_PLAYSTATE_STOPPED; 229 } 230 return playState; 231 } 232 233 status_t getPositionInMsec(int64_t *position) { 234 auto_lock l(mLock); 235 if (mPlay == NULL) { 236 return INVALID_OPERATION; 237 } 238 if (position == NULL) { 239 return BAD_VALUE; 240 } 241 SLuint32 pos; 242 SLresult res = (*mPlay)->GetPosition(mPlay, &pos); 243 if (res != SL_RESULT_SUCCESS) { 244 ALOGW("getPosition error %s", android::getSLErrStr(res)); 245 return INVALID_OPERATION; 246 } 247 // only lower 32 bits valid 248 *position = pos; 249 return OK; 250 } 251 252 status_t start() { 253 return setPlayState(SL_PLAYSTATE_PLAYING); 254 } 255 256 status_t pause() { 257 return setPlayState(SL_PLAYSTATE_PAUSED); 258 } 259 260 status_t stop() { 261 return setPlayState(SL_PLAYSTATE_STOPPED); 262 } 263 264 status_t flush() { 265 auto_lock l(mLock); 266 status_t result = OK; 267 if (mBufferQueue != NULL) { 268 SLresult res = (*mBufferQueue)->Clear(mBufferQueue); 269 if (res != SL_RESULT_SUCCESS) { 270 return INVALID_OPERATION; 271 } 272 } 273 274 // possible race if the engine is in the callback 275 // safety is only achieved if the player is paused or stopped. 276 mDeliveredQueue.clear(); 277 return result; 278 } 279 280 status_t write(const void *buffer, size_t size, bool isBlocking = false) { 281 std::lock_guard<std::mutex> rl(mWriteLock); 282 // not needed if we assume that a single thread is doing the reading 283 // or we always operate in non-blocking mode. 284 285 { 286 auto_lock l(mLock); 287 if (mBufferQueue == NULL) { 288 return INVALID_OPERATION; 289 } 290 if (mDeliveredQueue.size() < mNumBuffers) { 291 auto b = std::make_shared<BlobReadOnly>(buffer, size, false /* byReference */); 292 mDeliveredQueue.emplace_back(b); 293 (*mBufferQueue)->Enqueue(mBufferQueue, b->mData, b->mSize); 294 return size; 295 } 296 if (!isBlocking) { 297 return 0; 298 } 299 mWriteReady.closeGate(); // we're full. 300 } 301 if (mWriteReady.wait()) { 302 auto_lock l(mLock); 303 if (mDeliveredQueue.size() < mNumBuffers) { 304 auto b = std::make_shared<BlobReadOnly>(buffer, size, false /* byReference */); 305 mDeliveredQueue.emplace_back(b); 306 (*mBufferQueue)->Enqueue(mBufferQueue, b->mData, b->mSize); 307 return size; 308 } 309 } 310 ALOGW("unable to deliver write"); 311 return 0; 312 } 313 314 void logBufferState() { 315 auto_lock l(mLock); 316 SLBufferQueueState state; 317 SLresult res = (*mBufferQueue)->GetState(mBufferQueue, &state); 318 CheckErr(res); 319 ALOGD("logBufferState state.count:%d state.playIndex:%d", state.count, state.playIndex); 320 } 321 322 size_t getBuffersPending() { 323 auto_lock l(mLock); 324 return mDeliveredQueue.size(); 325 } 326 327 private: 328 void bufferQueueCallback(SLBufferQueueItf queueItf) { 329 auto_lock l(mLock); 330 if (queueItf != mBufferQueue) { 331 ALOGW("invalid buffer queue interface, ignoring"); 332 return; 333 } 334 // logBufferState(); 335 336 // remove from delivered queue 337 if (mDeliveredQueue.size()) { 338 mDeliveredQueue.pop_front(); 339 } else { 340 ALOGW("no delivered data!"); 341 } 342 if (!mWriteReady.isOpen()) { 343 mWriteReady.openGate(); 344 } 345 } 346 347 static void BufferQueueCallback(SLBufferQueueItf queueItf, void *pContext) { 348 // naked native track 349 AudioTrackNative *track = (AudioTrackNative *)pContext; 350 track->bufferQueueCallback(queueItf); 351 } 352 353 SLObjectItf mEngineObj; 354 SLEngineItf mEngine; 355 SLObjectItf mOutputMixObj; 356 SLObjectItf mPlayerObj; 357 SLPlayItf mPlay; 358 SLBufferQueueItf mBufferQueue; 359 SLuint32 mPlayState; 360 SLuint32 mNumBuffers; 361 std::recursive_mutex mLock; // monitor lock - locks public API methods and callback. 362 // recursive since it may call itself through API. 363 std::mutex mWriteLock; // write lock - for blocking mode, prevents multiple 364 // writer threads from overlapping writes. this is 365 // generally unnecessary as writes occur from 366 // one thread only. acquire this before mLock. 367 Gate mWriteReady; 368 std::deque<std::shared_ptr<BlobReadOnly>> mDeliveredQueue; // delivered to mBufferQueue 369 }; 370 371 /* Java static methods. 372 * 373 * These are not directly exposed to the user, so we can assume a valid "jtrack" handle 374 * to be passed in. 375 */ 376 377 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeTest( 378 JNIEnv * /* env */, jclass /* clazz */, 379 jint numChannels, jint channelMask, jint sampleRate, jboolean useFloat, 380 jint msecPerBuffer, jint numBuffers) 381 { 382 AudioTrackNative track; 383 const size_t frameSize = numChannels * (useFloat ? sizeof(float) : sizeof(int16_t)); 384 const size_t framesPerBuffer = msecPerBuffer * sampleRate / 1000; 385 386 status_t res; 387 void *buffer = calloc(framesPerBuffer * numBuffers, frameSize); 388 for (;;) { 389 res = track.open(numChannels, channelMask, sampleRate, useFloat, numBuffers); 390 if (res != OK) break; 391 392 for (int i = 0; i < numBuffers; ++i) { 393 track.write((char *)buffer + i * (framesPerBuffer * frameSize), 394 framesPerBuffer * frameSize); 395 } 396 397 track.logBufferState(); 398 res = track.start(); 399 if (res != OK) break; 400 401 size_t buffers; 402 while ((buffers = track.getBuffersPending()) > 0) { 403 // ALOGD("outstanding buffers: %zu", buffers); 404 usleep(5 * 1000 /* usec */); 405 } 406 res = track.stop(); 407 break; 408 } 409 track.close(); 410 free(buffer); 411 return res; 412 } 413 414 extern "C" jlong Java_android_media_cts_AudioTrackNative_nativeCreateTrack( 415 JNIEnv * /* env */, jclass /* clazz */) 416 { 417 return (jlong)(new shared_pointer<AudioTrackNative>(new AudioTrackNative())); 418 } 419 420 extern "C" void Java_android_media_cts_AudioTrackNative_nativeDestroyTrack( 421 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack) 422 { 423 delete (shared_pointer<AudioTrackNative> *)jtrack; 424 } 425 426 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeOpen( 427 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack, 428 jint numChannels, jint channelMask, jint sampleRate, 429 jboolean useFloat, jint numBuffers) 430 { 431 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 432 if (track.get() == NULL) { 433 return (jint)INVALID_OPERATION; 434 } 435 return (jint) track->open(numChannels, 436 channelMask, 437 sampleRate, 438 useFloat == JNI_TRUE, 439 numBuffers); 440 } 441 442 extern "C" void Java_android_media_cts_AudioTrackNative_nativeClose( 443 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack) 444 { 445 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 446 if (track.get() != NULL) { 447 track->close(); 448 } 449 } 450 451 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeStart( 452 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack) 453 { 454 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 455 if (track.get() == NULL) { 456 return (jint)INVALID_OPERATION; 457 } 458 return (jint)track->start(); 459 } 460 461 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeStop( 462 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack) 463 { 464 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 465 if (track.get() == NULL) { 466 return (jint)INVALID_OPERATION; 467 } 468 return (jint)track->stop(); 469 } 470 471 extern "C" jint Java_android_media_cts_AudioTrackNative_nativePause( 472 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack) 473 { 474 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 475 if (track.get() == NULL) { 476 return (jint)INVALID_OPERATION; 477 } 478 return (jint)track->pause(); 479 } 480 481 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeFlush( 482 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack) 483 { 484 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 485 if (track.get() == NULL) { 486 return (jint)INVALID_OPERATION; 487 } 488 return (jint)track->flush(); 489 } 490 491 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeGetPositionInMsec( 492 JNIEnv *env, jclass /* clazz */, jlong jtrack, jlongArray jPosition) 493 { 494 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 495 if (track.get() == NULL) { 496 return (jint)INVALID_OPERATION; 497 } 498 int64_t pos; 499 status_t res = track->getPositionInMsec(&pos); 500 if (res != OK) { 501 return res; 502 } 503 jlong *nPostition = (jlong *) env->GetPrimitiveArrayCritical(jPosition, NULL /* isCopy */); 504 if (nPostition == NULL) { 505 ALOGE("Unable to get array for nativeGetPositionInMsec()"); 506 return BAD_VALUE; 507 } 508 nPostition[0] = (jlong)pos; 509 env->ReleasePrimitiveArrayCritical(jPosition, nPostition, 0 /* mode */); 510 return OK; 511 } 512 513 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeGetBuffersPending( 514 JNIEnv * /* env */, jclass /* clazz */, jlong jtrack) 515 { 516 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 517 if (track.get() == NULL) { 518 return (jint)0; 519 } 520 return (jint)track->getBuffersPending(); 521 } 522 523 template <typename T> 524 static inline jint writeToTrack(jlong jtrack, const T *data, 525 jint offsetInSamples, jint sizeInSamples, jint writeFlags) 526 { 527 auto track = *(shared_pointer<AudioTrackNative> *)jtrack; 528 if (track.get() == NULL) { 529 return (jint)INVALID_OPERATION; 530 } 531 532 const bool isBlocking = writeFlags & WRITE_FLAG_BLOCKING; 533 const size_t sizeInBytes = sizeInSamples * sizeof(T); 534 ssize_t ret = track->write(data + offsetInSamples, sizeInBytes, isBlocking); 535 return (jint)(ret > 0 ? ret / sizeof(T) : ret); 536 } 537 538 template <typename T> 539 static inline jint writeArray(JNIEnv *env, jclass /* clazz */, jlong jtrack, 540 T javaAudioData, jint offsetInSamples, jint sizeInSamples, jint writeFlags) 541 { 542 if (javaAudioData == NULL) { 543 return (jint)INVALID_OPERATION; 544 } 545 546 auto cAudioData = envGetArrayElements(env, javaAudioData, NULL /* isCopy */); 547 if (cAudioData == NULL) { 548 ALOGE("Error retrieving source of audio data to play"); 549 return (jint)BAD_VALUE; 550 } 551 552 jint ret = writeToTrack(jtrack, cAudioData, offsetInSamples, sizeInSamples, writeFlags); 553 envReleaseArrayElements(env, javaAudioData, cAudioData, 0 /* mode */); 554 return ret; 555 } 556 557 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteByteArray( 558 JNIEnv *env, jclass clazz, jlong jtrack, 559 jbyteArray byteArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags) 560 { 561 ALOGV("nativeWriteByteArray(%p, %d, %d, %d)", 562 byteArray, offsetInSamples, sizeInSamples, writeFlags); 563 return writeArray(env, clazz, jtrack, byteArray, offsetInSamples, sizeInSamples, writeFlags); 564 } 565 566 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteShortArray( 567 JNIEnv *env, jclass clazz, jlong jtrack, 568 jshortArray shortArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags) 569 { 570 ALOGV("nativeWriteShortArray(%p, %d, %d, %d)", 571 shortArray, offsetInSamples, sizeInSamples, writeFlags); 572 return writeArray(env, clazz, jtrack, shortArray, offsetInSamples, sizeInSamples, writeFlags); 573 } 574 575 extern "C" jint Java_android_media_cts_AudioTrackNative_nativeWriteFloatArray( 576 JNIEnv *env, jclass clazz, jlong jtrack, 577 jfloatArray floatArray, jint offsetInSamples, jint sizeInSamples, jint writeFlags) 578 { 579 ALOGV("nativeWriteFloatArray(%p, %d, %d, %d)", 580 floatArray, offsetInSamples, sizeInSamples, writeFlags); 581 return writeArray(env, clazz, jtrack, floatArray, offsetInSamples, sizeInSamples, writeFlags); 582 } 583