size_t AudioPlayer::fillBuffer(void *data, size_t size) { if (mNumFramesPlayed == 0) { ALOGV("AudioCallback"); } if (mReachedEOS) { return 0; } bool postSeekComplete = false; bool postEOS = false; int64_t postEOSDelayUs = 0; size_t size_done = 0; size_t size_remaining = size; while (size_remaining > 0) { MediaSource::ReadOptions options; { Mutex::Autolock autoLock(mLock); if (mSeeking) { if (mIsFirstBuffer) { if (mFirstBuffer != NULL) { mFirstBuffer->release(); mFirstBuffer = NULL; } mIsFirstBuffer = false; } options.setSeekTo(mSeekTimeUs); if (mInputBuffer != NULL) { mInputBuffer->release(); mInputBuffer = NULL; } mSeeking = false; if (mObserver) { postSeekComplete = true; } } } if (mInputBuffer == NULL) { status_t err; if (mIsFirstBuffer) { mInputBuffer = mFirstBuffer; mFirstBuffer = NULL; err = mFirstBufferResult; mIsFirstBuffer = false; } else { err = mSource->read(&mInputBuffer, &options); #ifdef QCOM_HARDWARE if (err == OK && mInputBuffer == NULL && mSourcePaused) { ALOGV("mSourcePaused, return 0 from fillBuffer"); return 0; } #endif } CHECK((err == OK && mInputBuffer != NULL) || (err != OK && mInputBuffer == NULL)); Mutex::Autolock autoLock(mLock); if (err != OK) { if (mObserver && !mReachedEOS) { // We don't want to post EOS right away but only // after all frames have actually been played out. // These are the number of frames submitted to the // AudioTrack that you haven't heard yet. uint32_t numFramesPendingPlayout = getNumFramesPendingPlayout(); // These are the number of frames we're going to // submit to the AudioTrack by returning from this // callback. uint32_t numAdditionalFrames = size_done / mFrameSize; numFramesPendingPlayout += numAdditionalFrames; int64_t timeToCompletionUs = (1000000ll * numFramesPendingPlayout) / mSampleRate; ALOGV("total number of frames played: %lld (%lld us)", (mNumFramesPlayed + numAdditionalFrames), 1000000ll * (mNumFramesPlayed + numAdditionalFrames) / mSampleRate); ALOGV("%d frames left to play, %lld us (%.2f secs)", numFramesPendingPlayout, timeToCompletionUs, timeToCompletionUs / 1E6); postEOS = true; if (mAudioSink->needsTrailingPadding()) { postEOSDelayUs = timeToCompletionUs + mLatencyUs; } else { postEOSDelayUs = 0; } } mReachedEOS = true; mFinalStatus = err; break; } if (mAudioSink != NULL) { mLatencyUs = (int64_t)mAudioSink->latency() * 1000; } else { mLatencyUs = (int64_t)mAudioTrack->latency() * 1000; } CHECK(mInputBuffer->meta_data()->findInt64( kKeyTime, &mPositionTimeMediaUs)); mPositionTimeRealUs = ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) / mSampleRate; ALOGV("buffer->size() = %d, " "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", mInputBuffer->range_length(), mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); } if (mInputBuffer->range_length() == 0) { mInputBuffer->release(); mInputBuffer = NULL; continue; } size_t copy = size_remaining; if (copy > mInputBuffer->range_length()) { copy = mInputBuffer->range_length(); } memcpy((char *)data + size_done, (const char *)mInputBuffer->data() + mInputBuffer->range_offset(), copy); mInputBuffer->set_range(mInputBuffer->range_offset() + copy, mInputBuffer->range_length() - copy); size_done += copy; size_remaining -= copy; } { Mutex::Autolock autoLock(mLock); mNumFramesPlayed += size_done / mFrameSize; mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); if (mReachedEOS) { mPinnedTimeUs = mNumFramesPlayedSysTimeUs; } else { mPinnedTimeUs = -1ll; } } if (postEOS) { mObserver->postAudioEOS(postEOSDelayUs); } if (postSeekComplete) { mObserver->postAudioSeekComplete(); } return size_done; }
size_t AudioPlayer::fillBuffer(void *data, size_t size) { ATRACE_CALL(); if (mNumFramesPlayed == 0) { ALOGV("AudioCallback"); } if (mReachedEOS) { return 0; } bool postSeekComplete = false; bool postEOS = false; int64_t postEOSDelayUs = 0; size_t size_done = 0; size_t size_remaining = size; while (size_remaining > 0) { MediaSource::ReadOptions options; bool refreshSeekTime = false; { Mutex::Autolock autoLock(mLock); if (mSeeking) { if (mIsFirstBuffer) { if (mFirstBuffer != NULL) { mFirstBuffer->release(); mFirstBuffer = NULL; } mIsFirstBuffer = false; } options.setSeekTo(mSeekTimeUs); refreshSeekTime = true; if (mInputBuffer != NULL) { mInputBuffer->release(); mInputBuffer = NULL; } mSeeking = false; if (mObserver) { postSeekComplete = true; } } } if (mInputBuffer == NULL) { status_t err; if (mIsFirstBuffer) { mInputBuffer = mFirstBuffer; mFirstBuffer = NULL; err = mFirstBufferResult; mIsFirstBuffer = false; } else { if(!mSourcePaused) { err = mSource->read(&mInputBuffer, &options); if (err == OK && mInputBuffer == NULL && mSourcePaused) { ALOGV("mSourcePaused, return 0 from fillBuffer"); return 0; } } else { break; } } if(err == -EAGAIN) { if(mSourcePaused){ break; } else { continue; } } CHECK((err == OK && mInputBuffer != NULL) || (err != OK && mInputBuffer == NULL)); Mutex::Autolock autoLock(mLock); if (err != OK) { if (!mReachedEOS) { if (useOffload()) { // After seek there is a possible race condition if // OffloadThread is observing state_stopping_1 before // framesReady() > 0. Ensure sink stop is called // after last buffer is released. This ensures the // partial buffer is written to the driver before // stopping one is observed.The drawback is that // there will be an unnecessary call to the parser // after parser signalled EOS. if (size_done > 0) { ALOGW("send Partial buffer down\n"); ALOGW("skip calling stop till next fillBuffer\n"); break; } // no more buffers to push - stop() and wait for STREAM_END // don't set mReachedEOS until stream end received if (mAudioSink != NULL) { mAudioSink->stop(); } else { mAudioTrack->stop(); } } else { if (mObserver) { // We don't want to post EOS right away but only // after all frames have actually been played out. // These are the number of frames submitted to the // AudioTrack that you haven't heard yet. uint32_t numFramesPendingPlayout = getNumFramesPendingPlayout(); // These are the number of frames we're going to // submit to the AudioTrack by returning from this // callback. uint32_t numAdditionalFrames = size_done / mFrameSize; numFramesPendingPlayout += numAdditionalFrames; int64_t timeToCompletionUs = (1000000ll * numFramesPendingPlayout) / mSampleRate; ALOGV("total number of frames played: %lld (%lld us)", (mNumFramesPlayed + numAdditionalFrames), 1000000ll * (mNumFramesPlayed + numAdditionalFrames) / mSampleRate); ALOGV("%d frames left to play, %lld us (%.2f secs)", numFramesPendingPlayout, timeToCompletionUs, timeToCompletionUs / 1E6); postEOS = true; if (mAudioSink->needsTrailingPadding()) { postEOSDelayUs = timeToCompletionUs + mLatencyUs; } else { postEOSDelayUs = 0; } } mReachedEOS = true; } } mFinalStatus = err; break; } if (mAudioSink != NULL) { mLatencyUs = (int64_t)mAudioSink->latency() * 1000; } else { mLatencyUs = (int64_t)mAudioTrack->latency() * 1000; } if(mInputBuffer->range_length() != 0) { CHECK(mInputBuffer->meta_data()->findInt64( kKeyTime, &mPositionTimeMediaUs)); } // need to adjust the mStartPosUs for offload decoding since parser // might not be able to get the exact seek time requested. if (refreshSeekTime) { if (useOffload()) { if (postSeekComplete) { ALOGV("fillBuffer is going to post SEEK_COMPLETE"); mObserver->postAudioSeekComplete(); postSeekComplete = false; } mStartPosUs = mPositionTimeMediaUs; ALOGV("adjust seek time to: %.2f", mStartPosUs/ 1E6); } // clear seek time with mLock locked and once we have valid mPositionTimeMediaUs // and mPositionTimeRealUs // before clearing mSeekTimeUs check if a new seek request has been received while // we were reading from the source with mLock released. if (!mSeeking) { mSeekTimeUs = 0; } } if (!useOffload()) { mPositionTimeRealUs = ((mNumFramesPlayed + size_done / mFrameSize) * 1000000) / mSampleRate; ALOGV("buffer->size() = %d, " "mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", mInputBuffer->range_length(), mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); } } if (mInputBuffer->range_length() == 0) { mInputBuffer->release(); mInputBuffer = NULL; continue; } size_t copy = size_remaining; if (copy > mInputBuffer->range_length()) { copy = mInputBuffer->range_length(); } memcpy((char *)data + size_done, (const char *)mInputBuffer->data() + mInputBuffer->range_offset(), copy); mInputBuffer->set_range(mInputBuffer->range_offset() + copy, mInputBuffer->range_length() - copy); size_done += copy; size_remaining -= copy; } if (useOffload()) { // We must ask the hardware what it has played mPositionTimeRealUs = getOutputPlayPositionUs_l(); ALOGV("mPositionTimeMediaUs=%.2f mPositionTimeRealUs=%.2f", mPositionTimeMediaUs / 1E6, mPositionTimeRealUs / 1E6); } { Mutex::Autolock autoLock(mLock); mNumFramesPlayed += size_done / mFrameSize; if (mReachedEOS) { mPinnedTimeUs = mNumFramesPlayedSysTimeUs; } else { mNumFramesPlayedSysTimeUs = ALooper::GetNowUs(); mPinnedTimeUs = -1ll; } } if (postEOS) { mObserver->postAudioEOS(postEOSDelayUs); } if (postSeekComplete) { mObserver->postAudioSeekComplete(); } return size_done; }