public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) { if (mState == STATE_UNINITIALIZED) { return ERROR_INVALID_OPERATION; } if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0) || (offsetInBytes + sizeInBytes < 0) // detect integer overflow || (offsetInBytes + sizeInBytes > audioData.length)) { return ERROR_BAD_VALUE; } int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat); if ((mDataLoadMode == MODE_STATIC) && (mState == STATE_NO_STATIC_DATA) && (ret > 0)) { // benign race with respect to other APIs that read mState mState = STATE_INITIALIZED; } return ret; } static jint android_media_AudioTrack_native_write_byte(JNIEnv *env, jobject thiz, jbyteArray javaAudioData, jint offsetInBytes, jint sizeInBytes, jint javaAudioFormat) { //ALOGV("android_media_AudioTrack_native_write_byte(offset=%d, sizeInBytes=%d) called", // offsetInBytes, sizeInBytes); sp<AudioTrack> lpTrack = getAudioTrack(env, thiz); if (lpTrack == NULL) { jniThrowException(env, "java/lang/IllegalStateException", "Unable to retrieve AudioTrack pointer for write()"); return 0; } // get the pointer for the audio data from the java array // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such // a way that it becomes much more efficient. When doing so, we will have to prevent the // AudioSystem callback to be called while in critical section (in case of media server // process crash for instance) jbyte* cAudioData = NULL; if (javaAudioData) { cAudioData = (jbyte *)env->GetByteArrayElements(javaAudioData, NULL); if (cAudioData == NULL) { ALOGE("Error retrieving source of audio data to play, can't play"); return 0; // out of memory or no data to load } } else { ALOGE("NULL java array of audio data to play, can't play"); return 0; } jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes); env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0); //ALOGV("write wrote %d (tried %d) bytes in the native AudioTrack with offset %d", // (int)written, (int)(sizeInBytes), (int)offsetInBytes); return written; } jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data, jint offsetInBytes, jint sizeInBytes) { // give the data to the native AudioTrack object (the data starts at the offset) ssize_t written = 0; // regular write() or copy the data to the AudioTrack's shared memory? if (track->sharedBuffer() == 0) { written = track->write(data + offsetInBytes, sizeInBytes); // for compatibility with earlier behavior of write(), return 0 in this case if (written == (ssize_t) WOULD_BLOCK) { written = 0; } } else { if (audioFormat == ENCODING_PCM_16BIT) { // writing to shared memory, check for capacity if ((size_t)sizeInBytes > track->sharedBuffer()->size()) { sizeInBytes = track->sharedBuffer()->size(); } memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes); written = sizeInBytes; } else if (audioFormat == ENCODING_PCM_8BIT) { // data contains 8bit data we need to expand to 16bit before copying // to the shared memory // writing to shared memory, check for capacity, // note that input data will occupy 2X the input space due to 8 to 16bit conversion if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) { sizeInBytes = track->sharedBuffer()->size() / 2; } int count = sizeInBytes; int16_t *dst = (int16_t *)track->sharedBuffer()->pointer(); const int8_t *src = (const int8_t *)(data + offsetInBytes); while (count--) { *dst++ = (int16_t)(*src++^0x80) << 8; } // even though we wrote 2*sizeInBytes, we only report sizeInBytes as written to hide // the 8bit mixer restriction from the user of this function written = sizeInBytes; } } return written; } ssize_t AudioTrack::write(const void* buffer, size_t userSize) { if (mTransfer != TRANSFER_SYNC || mIsTimed) { return INVALID_OPERATION; } if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) { // Sanity-check: user is most-likely passing an error code, and it would // make the return value ambiguous (actualSize vs error). ALOGE("AudioTrack::write(buffer=%p, size=%zu (%zd)", buffer, userSize, userSize); return BAD_VALUE; } size_t written = 0; Buffer audioBuffer; while (userSize >= mFrameSize) { audioBuffer.frameCount = userSize / mFrameSize; //获得Buffer status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever); if (err < 0) { if (written > 0) { break; } return ssize_t(err); } size_t toWrite; if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { // Divide capacity by 2 to take expansion into account toWrite = audioBuffer.size >> 1; memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite); } else { toWrite = audioBuffer.size; memcpy(audioBuffer.i8, buffer, toWrite); } buffer = ((const char *) buffer) + toWrite; userSize -= toWrite; written += toWrite; releaseBuffer(&audioBuffer); } return written; } status_t obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, struct timespec *elapsed = NULL, size_t *nonContig = NULL); status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested, struct timespec *elapsed, size_t *nonContig) { // previous and new IAudioTrack sequence numbers are used to detect track re-creation uint32_t oldSequence = 0; uint32_t newSequence; Proxy::Buffer buffer; status_t status = NO_ERROR; static const int32_t kMaxTries = 5; int32_t tryCounter = kMaxTries; do { // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to // keep them from going away if another thread re-creates the track during obtainBuffer() sp<AudioTrackClientProxy> proxy; sp<IMemory> iMem; { // start of lock scope AutoMutex lock(mLock); newSequence = mSequence; // did previous obtainBuffer() fail due to media server death or voluntary invalidation? if (status == DEAD_OBJECT) { // re-create track, unless someone else has already done so if (newSequence == oldSequence) { status = restoreTrack_l("obtainBuffer"); if (status != NO_ERROR) { buffer.mFrameCount = 0; buffer.mRaw = NULL; buffer.mNonContig = 0; break; } } } oldSequence = newSequence; // Keep the extra references proxy = mProxy; iMem = mCblkMemory; if (mState == STATE_STOPPING) { status = -EINTR; buffer.mFrameCount = 0; buffer.mRaw = NULL; buffer.mNonContig = 0; break; } // Non-blocking if track is stopped or paused if (mState != STATE_ACTIVE) { requested = &ClientProxy::kNonBlocking; } } // end of lock scope buffer.mFrameCount = audioBuffer->frameCount; // FIXME starts the requested timeout and elapsed over from scratch status = proxy->obtainBuffer(&buffer, requested, elapsed); } while ((status == DEAD_OBJECT) && (tryCounter-- > 0)); audioBuffer->frameCount = buffer.mFrameCount; audioBuffer->size = buffer.mFrameCount * mFrameSizeAF; audioBuffer->raw = buffer.mRaw; if (nonContig != NULL) { *nonContig = buffer.mNonContig; } return status; } status_t AudioTrack::restoreTrack_l(const char *from) { ALOGW("dead IAudioTrack, %s, creating a new one from %s()", isOffloaded() ? "Offloaded" : "PCM", from); ++mSequence; status_t result; // refresh the audio configuration cache in this process to make sure we get new // output parameters in getOutput_l() and createTrack_l() AudioSystem::clearAudioConfigCache(); if (isOffloaded()) { return DEAD_OBJECT; } // force new output query from audio policy manager; mOutput = 0; audio_io_handle_t output = getOutput_l(); // if the new IAudioTrack is created, createTrack_l() will modify the // following member variables: mAudioTrack, mCblkMemory and mCblk. // It will also delete the strong references on previous IAudioTrack and IMemory // take the frames that will be lost by track recreation into account in saved position size_t position = mProxy->getPosition() + mProxy->getFramesFilled(); size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0; result = createTrack_l(mStreamType, mSampleRate, mFormat, mReqFrameCount, // so that frame count never goes down mFlags, mSharedBuffer, output, position /*epoch*/); if (result == NO_ERROR) { // continue playback from last known position, but // don't attempt to restore loop after invalidation; it's difficult and not worthwhile if (mStaticProxy != NULL) { mLoopPeriod = 0; mStaticProxy->setLoop(bufferPosition, mFrameCount, 0); } // FIXME How do we simulate the fact that all frames present in the buffer at the time of // track destruction have been played? This is critical for SoundPool implementation // This must be broken, and needs to be tested/debugged. if (mState == STATE_ACTIVE) { result = mAudioTrack->start(); } } if (result != NO_ERROR) { //Use of direct and offloaded output streams is ref counted by audio policy manager. // As getOutput was called above and resulted in an output stream to be opened, // we need to release it. AudioSystem::releaseOutput(output); ALOGW("restoreTrack_l() failed status %d", result); mState = STATE_STOPPED; } return result; } audio_io_handle_t AudioTrack::getOutput_l() { if (mOutput) { return mOutput; } else { return AudioSystem::getOutput(mStreamType, mSampleRate, mFormat, mChannelMask, mFlags); } } size_t getPosition() { return mEpoch + mCblk->mServer; } size_t ClientProxy::getFramesFilled() { audio_track_cblk_t* cblk = mCblk; int32_t front; int32_t rear; if (mIsOut) { front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); rear = cblk->u.mStreaming.mRear; } else { rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); front = cblk->u.mStreaming.mFront; } ssize_t filled = rear - front; // pipe should not be overfull if (!(0 <= filled && (size_t) filled <= mFrameCount)) { ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled); return 0; } return (size_t)filled; } size_t StaticAudioTrackClientProxy::getBufferPosition() { size_t bufferPosition; if (mMutator.ack()) { bufferPosition = (size_t) mCblk->u.mStatic.mBufferPosition; if (bufferPosition > mFrameCount) { bufferPosition = mFrameCount; } } else { bufferPosition = mBufferPosition; } return bufferPosition; } // To facilitate quicker recovery from server failure, this value limits the timeout per each futex // wait. However it does not protect infinite timeouts. If defined to be zero, there is no limit. // FIXME May not be compatible with audio tunneling requirements where timeout should be in the // order of minutes. #define MAX_SEC 5 status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested, struct timespec *elapsed) { LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0); struct timespec total; // total elapsed time spent waiting total.tv_sec = 0; total.tv_nsec = 0; bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting status_t status; enum { TIMEOUT_ZERO, // requested == NULL || *requested == 0 TIMEOUT_INFINITE, // *requested == infinity TIMEOUT_FINITE, // 0 < *requested < infinity TIMEOUT_CONTINUE, // additional chances after TIMEOUT_FINITE } timeout; if (requested == NULL) { timeout = TIMEOUT_ZERO; } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) { timeout = TIMEOUT_ZERO; } else if (requested->tv_sec == INT_MAX) { timeout = TIMEOUT_INFINITE; } else { timeout = TIMEOUT_FINITE; if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) { measure = true; } } struct timespec before; bool beforeIsValid = false; audio_track_cblk_t* cblk = mCblk; bool ignoreInitialPendingInterrupt = true; // check for shared memory corruption if (mIsShutdown) { status = NO_INIT; goto end; } for (;;) { int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->mFlags); // check for track invalidation by server, or server death detection if (flags & CBLK_INVALID) { ALOGV("Track invalidated"); status = DEAD_OBJECT; goto end; } // check for obtainBuffer interrupted by client if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) { ALOGV("obtainBuffer() interrupted by client"); status = -EINTR; goto end; } ignoreInitialPendingInterrupt = false; // compute number of frames available to write (AudioTrack) or read (AudioRecord) int32_t front; int32_t rear; if (mIsOut) { // The barrier following the read of mFront is probably redundant. // We're about to perform a conditional branch based on 'filled', // which will force the processor to observe the read of mFront // prior to allowing data writes starting at mRaw. // However, the processor may support speculative execution, // and be unable to undo speculative writes into shared memory. // The barrier will prevent such speculative execution. front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront); rear = cblk->u.mStreaming.mRear; } else { // On the other hand, this barrier is required. rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear); front = cblk->u.mStreaming.mFront; } ssize_t filled = rear - front; // pipe should not be overfull if (!(0 <= filled && (size_t) filled <= mFrameCount)) { ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled); mIsShutdown = true; status = NO_INIT; goto end; } // don't allow filling pipe beyond the nominal size size_t avail = mIsOut ? mFrameCount - filled : filled; if (avail > 0) { // 'avail' may be non-contiguous, so return only the first contiguous chunk size_t part1; if (mIsOut) { rear &= mFrameCountP2 - 1; part1 = mFrameCountP2 - rear; } else { front &= mFrameCountP2 - 1; part1 = mFrameCountP2 - front; } if (part1 > avail) { part1 = avail; } if (part1 > buffer->mFrameCount) { part1 = buffer->mFrameCount; } buffer->mFrameCount = part1; buffer->mRaw = part1 > 0 ? &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL; buffer->mNonContig = avail - part1; mUnreleased = part1; status = NO_ERROR; break; } struct timespec remaining; const struct timespec *ts; switch (timeout) { case TIMEOUT_ZERO: status = WOULD_BLOCK; goto end; case TIMEOUT_INFINITE: ts = NULL; break; case TIMEOUT_FINITE: timeout = TIMEOUT_CONTINUE; if (MAX_SEC == 0) { ts = requested; break; } // fall through case TIMEOUT_CONTINUE: // FIXME we do not retry if requested < 10ms? needs documentation on this state machine if (!measure || requested->tv_sec < total.tv_sec || (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) { status = TIMED_OUT; goto end; } remaining.tv_sec = requested->tv_sec - total.tv_sec; if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) { remaining.tv_nsec += 1000000000; remaining.tv_sec++; } if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) { remaining.tv_sec = MAX_SEC; remaining.tv_nsec = 0; } ts = &remaining; break; default: LOG_FATAL("obtainBuffer() timeout=%d", timeout); ts = NULL; break; } int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex); if (!(old & CBLK_FUTEX_WAKE)) { if (measure && !beforeIsValid) { clock_gettime(CLOCK_MONOTONIC, &before); beforeIsValid = true; } errno = 0; (void) syscall(__NR_futex, &cblk->mFutex, mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts); // update total elapsed time spent waiting if (measure) { struct timespec after; clock_gettime(CLOCK_MONOTONIC, &after); total.tv_sec += after.tv_sec - before.tv_sec; long deltaNs = after.tv_nsec - before.tv_nsec; if (deltaNs < 0) { deltaNs += 1000000000; total.tv_sec--; } if ((total.tv_nsec += deltaNs) >= 1000000000) { total.tv_nsec -= 1000000000; total.tv_sec++; } before = after; beforeIsValid = true; } switch (errno) { case 0: // normal wakeup by server, or by binderDied() case EWOULDBLOCK: // benign race condition with server case EINTR: // wait was interrupted by signal or other spurious wakeup case ETIMEDOUT: // time-out expired // FIXME these error/non-0 status are being dropped break; default: status = errno; ALOGE("%s unexpected error %s", __func__, strerror(status)); goto end; } } } end: if (status != NO_ERROR) { buffer->mFrameCount = 0; buffer->mRaw = NULL; buffer->mNonContig = 0; mUnreleased = 0; } if (elapsed != NULL) { *elapsed = total; } if (requested == NULL) { requested = &kNonBlocking; } if (measure) { ALOGV("requested %ld.%03ld elapsed %ld.%03ld", requested->tv_sec, requested->tv_nsec / 1000000, total.tv_sec, total.tv_nsec / 1000000); } return status; } void AudioTrack::releaseBuffer(Buffer* audioBuffer) { if (mTransfer == TRANSFER_SHARED) { return; } size_t stepCount = audioBuffer->size / mFrameSizeAF; if (stepCount == 0) { return; } Proxy::Buffer buffer; buffer.mFrameCount = stepCount; buffer.mRaw = audioBuffer->raw; AutoMutex lock(mLock); mInUnderrun = false; mProxy->releaseBuffer(&buffer); // restart track if it was disabled by audioflinger due to previous underrun if (mState == STATE_ACTIVE) { audio_track_cblk_t* cblk = mCblk; if (android_atomic_and(~CBLK_DISABLED, &cblk->mFlags) & CBLK_DISABLED) { ALOGW("releaseBuffer() track %p name=%s disabled due to previous underrun, restarting", this, mName.string()); // FIXME ignoring status mAudioTrack->start(); } } } void ClientProxy::releaseBuffer(Buffer* buffer) { LOG_ALWAYS_FATAL_IF(buffer == NULL); size_t stepCount = buffer->mFrameCount; if (stepCount == 0 || mIsShutdown) { // prevent accidental re-use of buffer buffer->mFrameCount = 0; buffer->mRaw = NULL; buffer->mNonContig = 0; return; } LOG_ALWAYS_FATAL_IF(!(stepCount <= mUnreleased && mUnreleased <= mFrameCount)); mUnreleased -= stepCount; audio_track_cblk_t* cblk = mCblk; // Both of these barriers are required if (mIsOut) { //播放时,更新写入数据的指针,应该会同时通知数据读取端? int32_t rear = cblk->u.mStreaming.mRear; android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear); } else { //录音时,更新读取数据的指针,应该会同时通知数据写入端? int32_t front = cblk->u.mStreaming.mFront; android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront); } }