//----------------------------------------------------------------------------- void android_audioRecorder_useRecordEventMask(CAudioRecorder *ar) { IRecord *pRecordItf = &ar->mRecord; SLuint32 eventFlags = pRecordItf->mCallbackEventsMask; if (ar->mAudioRecord == 0) { return; } if ((eventFlags & SL_RECORDEVENT_HEADATMARKER) && (pRecordItf->mMarkerPosition != 0)) { ar->mAudioRecord->setMarkerPosition((uint32_t)((((int64_t)pRecordItf->mMarkerPosition * sles_to_android_sampleRate(ar->mSampleRateMilliHz)))/1000)); } else { // clear marker ar->mAudioRecord->setMarkerPosition(0); } if (eventFlags & SL_RECORDEVENT_HEADATNEWPOS) { SL_LOGV("pos update period %d", pRecordItf->mPositionUpdatePeriod); ar->mAudioRecord->setPositionUpdatePeriod( (uint32_t)((((int64_t)pRecordItf->mPositionUpdatePeriod * sles_to_android_sampleRate(ar->mSampleRateMilliHz)))/1000)); } else { // clear periodic update ar->mAudioRecord->setPositionUpdatePeriod(0); } if (eventFlags & SL_RECORDEVENT_HEADATLIMIT) { // FIXME support SL_RECORDEVENT_HEADATLIMIT SL_LOGD("[ FIXME: IRecord_SetCallbackEventsMask(SL_RECORDEVENT_HEADATLIMIT) on an " "SL_OBJECTID_AUDIORECORDER to be implemented ]"); } if (eventFlags & SL_RECORDEVENT_HEADMOVING) { // FIXME support SL_RECORDEVENT_HEADMOVING SL_LOGD("[ FIXME: IRecord_SetCallbackEventsMask(SL_RECORDEVENT_HEADMOVING) on an " "SL_OBJECTID_AUDIORECORDER to be implemented ]"); } if (eventFlags & SL_RECORDEVENT_BUFFER_FULL) { // nothing to do for SL_RECORDEVENT_BUFFER_FULL since this will not be encountered on // recording to buffer queues } if (eventFlags & SL_RECORDEVENT_HEADSTALLED) { // nothing to do for SL_RECORDEVENT_HEADSTALLED, callback event will be checked against mask // when AudioRecord::EVENT_OVERRUN is encountered } }
//-------------------------------------------------- // Event handlers void LocAVPlayer::onPrepare() { SL_LOGD("LocAVPlayer::onPrepare()"); sp<IMediaPlayerService> mediaPlayerService(getMediaPlayerService()); if (mediaPlayerService != NULL) { switch (mDataLocatorType) { case kDataLocatorUri: mPlayer = mediaPlayerService->create(mPlayerClient /*IMediaPlayerClient*/, mPlaybackParams.sessionId); if (mPlayer == NULL) { SL_LOGE("media player service failed to create player by URI"); } else if (mPlayer->setDataSource( CreateHTTPServiceInCurrentJavaContext(), mDataLocator.uriRef, NULL /*headers*/) != NO_ERROR) { SL_LOGE("setDataSource failed"); mPlayer.clear(); } break; case kDataLocatorFd: mPlayer = mediaPlayerService->create(mPlayerClient /*IMediaPlayerClient*/, mPlaybackParams.sessionId); if (mPlayer == NULL) { SL_LOGE("media player service failed to create player by FD"); } else if (mPlayer->setDataSource(mDataLocator.fdi.fd, mDataLocator.fdi.offset, mDataLocator.fdi.length) != NO_ERROR) { SL_LOGE("setDataSource failed"); mPlayer.clear(); } // Binder dups the fd for use by mediaserver, so if we own the fd then OK to close now if (mDataLocator.fdi.mCloseAfterUse) { (void) ::close(mDataLocator.fdi.fd); mDataLocator.fdi.fd = -1; mDataLocator.fdi.mCloseAfterUse = false; } break; case kDataLocatorNone: SL_LOGE("no data locator for MediaPlayer object"); break; default: SL_LOGE("unsupported data locator %d for MediaPlayer object", mDataLocatorType); break; } } if (mPlayer == NULL) { mStateFlags |= kFlagPreparedUnsuccessfully; } // blocks until mPlayer is prepared GenericMediaPlayer::onPrepare(); SL_LOGD("LocAVPlayer::onPrepare() done"); }
//-------------------------------------------------------------------------------------------------- AacBqToPcmCbRenderer::AacBqToPcmCbRenderer(const AudioPlayback_Parameters* params, IAndroidBufferQueue *androidBufferQueue) : AudioToCbRenderer(params), mBqSource(new BufferQueueSource(androidBufferQueue)) { SL_LOGD("AacBqToPcmCbRenderer::AacBqToPcmCbRenderer()"); }
//-------------------------------------------------------------------------------------------------- AudioToCbRenderer::AudioToCbRenderer(AudioPlayback_Parameters* params) : AudioSfDecoder(params), mDecodeCbf(NULL), mDecodeUser(NULL) { SL_LOGD("AudioToCbRenderer::AudioToCbRenderer()"); }
XAresult android_Player_getPosition(IPlay *pPlayItf, XAmillisecond *pPosMsec) { SL_LOGD("android_Player_getPosition()"); XAresult result = XA_RESULT_SUCCESS; CMediaPlayer *avp = (CMediaPlayer *)pPlayItf->mThis; switch (avp->mAndroidObjType) { case AUDIOVIDEOPLAYER_FROM_TS_ANDROIDBUFFERQUEUE: // intended fall-through case AUDIOVIDEOPLAYER_FROM_URIFD: { int pos = ANDROID_UNKNOWN_TIME; if (avp->mAVPlayer != 0) { avp->mAVPlayer->getPositionMsec(&pos); } if (pos == ANDROID_UNKNOWN_TIME) { *pPosMsec = 0; } else { *pPosMsec = (XAmillisecond)pos; } } break; default: // we shouldn't be here assert(false); break; } return result; }
void StreamPlayer::onPlay() { SL_LOGD("StreamPlayer::onPlay()"); // enqueue a message that will cause StreamAppProxy to consume from the queue (again if the // player had starved the shared memory) queueRefilled(); GenericMediaPlayer::onPlay(); }
//-------------------------------------------------------------------------------------------------- StreamPlayer::StreamPlayer(const AudioPlayback_Parameters* params, bool hasVideo, IAndroidBufferQueue *androidBufferQueue, const sp<CallbackProtector> &callbackProtector) : GenericMediaPlayer(params, hasVideo), mAppProxy(new StreamSourceAppProxy(androidBufferQueue, callbackProtector, this)), mStopForDestroyCompleted(false) { SL_LOGD("StreamPlayer::StreamPlayer()"); }
static SLresult IAndroidBufferQueue_Enqueue(SLAndroidBufferQueueItf self, void *pBufferContext, void *pData, SLuint32 dataLength, const SLAndroidBufferItem *pItems, SLuint32 itemsLength) { SL_ENTER_INTERFACE SL_LOGD("IAndroidBufferQueue_Enqueue pData=%p dataLength=%d", pData, dataLength); if ((dataLength > 0) && (NULL == pData)) { SL_LOGE("Enqueue failure: non-zero data length %u but NULL data pointer", dataLength); result = SL_RESULT_PARAMETER_INVALID; } else if ((itemsLength > 0) && (NULL == pItems)) { SL_LOGE("Enqueue failure: non-zero items length %u but NULL items pointer", itemsLength); result = SL_RESULT_PARAMETER_INVALID; } else if ((0 == dataLength) && (0 == itemsLength)) { // no data and no msg SL_LOGE("Enqueue failure: trying to enqueue buffer with no data and no items."); result = SL_RESULT_PARAMETER_INVALID; // Note that a non-NULL data pointer with zero data length is allowed. // We track that data pointer as it moves through the queue // to assist the application in accounting for data buffers. // A non-NULL items pointer with zero items length is also allowed, but has no value. } else { IAndroidBufferQueue *thiz = (IAndroidBufferQueue *) self; // buffer size check, can be done outside of lock because buffer type can't change switch (thiz->mBufferType) { case kAndroidBufferTypeMpeg2Ts: if (dataLength % MPEG2_TS_PACKET_SIZE == 0) { // The downstream Stagefright MPEG-2 TS parser is sensitive to format errors, // so do a quick sanity check beforehand on the first packet of the buffer. // We don't check all the packets to avoid thrashing the data cache. if ((dataLength > 0) && (*(SLuint8 *)pData != MPEG2_TS_PACKET_SYNC)) { SL_LOGE("Error enqueueing MPEG-2 TS data: incorrect packet sync"); result = SL_RESULT_CONTENT_CORRUPTED; SL_LEAVE_INTERFACE } break; } SL_LOGE("Error enqueueing MPEG-2 TS data: size must be a multiple of %d (packet size)", MPEG2_TS_PACKET_SIZE); result = SL_RESULT_PARAMETER_INVALID; SL_LEAVE_INTERFACE break; case kAndroidBufferTypeAacadts: // zero dataLength is permitted in case of EOS command only if (dataLength > 0) { result = android::AacBqToPcmCbRenderer::validateBufferStartEndOnFrameBoundaries( pData, dataLength); if (SL_RESULT_SUCCESS != result) { SL_LOGE("Error enqueueing ADTS data: data must start and end on frame " "boundaries"); SL_LEAVE_INTERFACE } }
//-------------------------------------------------- // Event handlers void StreamPlayer::onPrepare() { SL_LOGD("StreamPlayer::onPrepare()"); sp<IMediaPlayerService> mediaPlayerService(getMediaPlayerService()); if (mediaPlayerService != NULL) { mPlayer = mediaPlayerService->create(getpid(), mPlayerClient /*IMediaPlayerClient*/, mPlaybackParams.sessionId); if (mPlayer == NULL) { SL_LOGE("media player service failed to create player by app proxy"); } else if (mPlayer->setDataSource(mAppProxy /*IStreamSource*/) != NO_ERROR) { SL_LOGE("setDataSource failed"); mPlayer.clear(); } } if (mPlayer == NULL) { mStateFlags |= kFlagPreparedUnsuccessfully; } GenericMediaPlayer::onPrepare(); SL_LOGD("StreamPlayer::onPrepare() done"); }
void StreamPlayer::onPullFromAndroidBufferQueue() { SL_LOGD("StreamPlayer::onPullFromAndroidBufferQueue()"); mAppProxy->pullFromBuffQueue(); }
StreamPlayer::~StreamPlayer() { SL_LOGD("StreamPlayer::~StreamPlayer()"); mAppProxy->disconnect(); }
//-------------------------------------------------- // consumption from ABQ: pull from the ABQ, and push to shared memory (media server) void StreamSourceAppProxy::pullFromBuffQueue() { if (android::CallbackProtector::enterCbIfOk(mCallbackProtector)) { size_t bufferId; void* bufferLoc; size_t buffSize; slAndroidBufferQueueCallback callback = NULL; void* pBufferContext, *pBufferData, *callbackPContext = NULL; AdvancedBufferHeader *oldFront = NULL; uint32_t dataSize /* , dataUsed */; // retrieve data from the buffer queue interface_lock_exclusive(mAndroidBufferQueue); // can this read operation cause us to call the buffer queue callback // (either because there was a command with no data, or all the data has been consumed) bool queueCallbackCandidate = false; if (mAndroidBufferQueue->mState.count != 0) { // SL_LOGD("nbBuffers in ABQ = %u, buffSize=%u",abq->mState.count, buffSize); assert(mAndroidBufferQueue->mFront != mAndroidBufferQueue->mRear); oldFront = mAndroidBufferQueue->mFront; AdvancedBufferHeader *newFront = &oldFront[1]; // consume events when starting to read data from a buffer for the first time if (oldFront->mDataSizeConsumed == 0) { // note this code assumes at most one event per buffer; see IAndroidBufferQueue_Enqueue if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_EOS) { receivedCmd_l(IStreamListener::EOS); // EOS has no associated data queueCallbackCandidate = true; } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_DISCONTINUITY) { receivedCmd_l(IStreamListener::DISCONTINUITY); } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_DISCON_NEWPTS) { sp<AMessage> msg = new AMessage(); msg->setInt64(IStreamListener::kKeyResumeAtPTS, (int64_t)oldFront->mItems.mTsCmdData.mPts); receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/); } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_FORMAT_CHANGE_FULL) { sp<AMessage> msg = new AMessage(); msg->setInt32( IStreamListener::kKeyDiscontinuityMask, ATSParser::DISCONTINUITY_FORMATCHANGE); receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/); } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_FORMAT_CHANGE_VIDEO) { sp<AMessage> msg = new AMessage(); msg->setInt32( IStreamListener::kKeyDiscontinuityMask, ATSParser::DISCONTINUITY_VIDEO_FORMAT); receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/); } // note that here we are intentionally only supporting // ANDROID_MP2TSEVENT_FORMAT_CHANGE_VIDEO, see IAndroidBufferQueue.c // some commands may introduce a time discontinuity, reevaluate position if needed if (oldFront->mItems.mTsCmdData.mTsCmdCode & (ANDROID_MP2TSEVENT_DISCONTINUITY | ANDROID_MP2TSEVENT_DISCON_NEWPTS | ANDROID_MP2TSEVENT_FORMAT_CHANGE_FULL)) { const sp<StreamPlayer> player(mPlayer.promote()); if (player != NULL) { // FIXME see note at onSeek player->seek(ANDROID_UNKNOWN_TIME); } } oldFront->mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE; } { // we're going to change the shared mem buffer queue, so lock it Mutex::Autolock _l(mLock); if (!mAvailableBuffers.empty()) { bufferId = *mAvailableBuffers.begin(); CHECK_LT(bufferId, mBuffers.size()); sp<IMemory> mem = mBuffers.itemAt(bufferId); bufferLoc = mem->pointer(); buffSize = mem->size(); char *pSrc = ((char*)oldFront->mDataBuffer) + oldFront->mDataSizeConsumed; if (oldFront->mDataSizeConsumed + buffSize < oldFront->mDataSize) { // more available than requested, copy as much as requested // consume data: 1/ copy to given destination memcpy(bufferLoc, pSrc, buffSize); // 2/ keep track of how much has been consumed oldFront->mDataSizeConsumed += buffSize; // 3/ notify shared mem listener that new data is available receivedBuffer_l(bufferId, buffSize); mAvailableBuffers.erase(mAvailableBuffers.begin()); } else { // requested as much available or more: consume the whole of the current // buffer and move to the next size_t consumed = oldFront->mDataSize - oldFront->mDataSizeConsumed; //SL_LOGD("consuming rest of buffer: enqueueing=%u", consumed); oldFront->mDataSizeConsumed = oldFront->mDataSize; // move queue to next if (newFront == &mAndroidBufferQueue-> mBufferArray[mAndroidBufferQueue->mNumBuffers + 1]) { // reached the end, circle back newFront = mAndroidBufferQueue->mBufferArray; } mAndroidBufferQueue->mFront = newFront; mAndroidBufferQueue->mState.count--; mAndroidBufferQueue->mState.index++; if (consumed > 0) { // consume data: 1/ copy to given destination memcpy(bufferLoc, pSrc, consumed); // 2/ keep track of how much has been consumed // here nothing to do because we are done with this buffer // 3/ notify StreamPlayer that new data is available receivedBuffer_l(bufferId, consumed); mAvailableBuffers.erase(mAvailableBuffers.begin()); } // data has been consumed, and the buffer queue state has been updated // we will notify the client if applicable queueCallbackCandidate = true; } } if (queueCallbackCandidate) { if (mAndroidBufferQueue->mCallbackEventsMask & SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED) { callback = mAndroidBufferQueue->mCallback; // save callback data while under lock callbackPContext = mAndroidBufferQueue->mContext; pBufferContext = (void *)oldFront->mBufferContext; pBufferData = (void *)oldFront->mDataBuffer; dataSize = oldFront->mDataSize; // here a buffer is only dequeued when fully consumed //dataUsed = oldFront->mDataSizeConsumed; } } //SL_LOGD("%d buffers available after reading from queue", mAvailableBuffers.size()); if (!mAvailableBuffers.empty()) { // there is still room in the shared memory, recheck later if we can pull // data from the buffer queue and write it to shared memory const sp<StreamPlayer> player(mPlayer.promote()); if (player != NULL) { player->queueRefilled(); } } } } else { // empty queue SL_LOGD("ABQ empty, starving!"); } interface_unlock_exclusive(mAndroidBufferQueue); // notify client of buffer processed if (NULL != callback) { SLresult result = (*callback)(&mAndroidBufferQueue->mItf, callbackPContext, pBufferContext, pBufferData, dataSize, dataSize, /* dataUsed */ // no messages during playback other than marking the buffer as processed (const SLAndroidBufferItem*)(&kItemProcessed) /* pItems */, NB_BUFFEREVENT_ITEM_FIELDS *sizeof(SLuint32) /* itemsLength */ ); if (SL_RESULT_SUCCESS != result) { // Reserved for future use SL_LOGW("Unsuccessful result %d returned from AndroidBufferQueueCallback", result); } } mCallbackProtector->exitCb(); } // enterCbIfOk }
void AudioToCbRenderer::updateAudioSink() { SL_LOGD("AudioToCbRenderer::updateAudioSink()"); }
void AudioToCbRenderer::startAudioSink() { SL_LOGD("AudioToCbRenderer::startAudioSink()"); }
AacBqToPcmCbRenderer::~AacBqToPcmCbRenderer() { SL_LOGD("AacBqToPcmCbRenderer::~AacBqToPcmCbRenderer()"); }
status_t BufferQueueSource::getSize(off64_t *size) { SL_LOGD("BufferQueueSource::getSize()"); // we're streaming, we don't know how much there is *size = 0; return ERROR_UNSUPPORTED; }
void AudioToCbRenderer::pauseAudioSink() { SL_LOGD("AudioToCbRenderer::pauseAudioSink()"); }
LocAVPlayer::~LocAVPlayer() { SL_LOGD("LocAVPlayer::~LocAVPlayer()"); }
//-------------------------------------------------------------------------------------------------- LocAVPlayer::LocAVPlayer(const AudioPlayback_Parameters* params, bool hasVideo) : GenericMediaPlayer(params, hasVideo) { SL_LOGD("LocAVPlayer::LocAVPlayer()"); }
BufferQueueSource::~BufferQueueSource() { SL_LOGD("BufferQueueSource::~BufferQueueSource"); }
AudioToCbRenderer::~AudioToCbRenderer() { SL_LOGD("AudioToCbRenderer::~AudioToCbRenderer()"); }
//-------------------------------------------------- // Event handlers void AacBqToPcmCbRenderer::onPrepare() { SL_LOGD("AacBqToPcmCbRenderer::onPrepare()"); Mutex::Autolock _l(mBufferSourceLock); // Initialize the PCM format info with the known parameters before the start of the decode { android::Mutex::Autolock autoLock(mPcmFormatLock); mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_BITSPERSAMPLE] = SL_PCMSAMPLEFORMAT_FIXED_16; mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CONTAINERSIZE] = 16; //FIXME not true on all platforms mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_ENDIANNESS] = SL_BYTEORDER_LITTLEENDIAN; // initialization with the default values: they will be replaced by the actual values // once the decoder has figured them out mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = UNKNOWN_NUMCHANNELS; mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = UNKNOWN_SAMPLERATE; mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = SL_ANDROID_UNKNOWN_CHANNELMASK; } sp<MediaExtractor> extractor = new AacAdtsExtractor(mBqSource); // only decoding a single track of data const size_t kTrackToDecode = 0; sp<IMediaSource> source = extractor->getTrack(kTrackToDecode); if (source == 0) { SL_LOGE("AacBqToPcmCbRenderer::onPrepare: error getting source from extractor"); notifyPrepared(ERROR_UNSUPPORTED); return; } // the audio content is not raw PCM, so we need a decoder source = SimpleDecodingSource::Create(source); if (source == NULL) { SL_LOGE("AacBqToPcmCbRenderer::onPrepare: Could not instantiate decoder."); notifyPrepared(ERROR_UNSUPPORTED); return; } sp<MetaData> meta = source->getFormat(); SL_LOGD("AacBqToPcmCbRenderer::onPrepare() after instantiating decoder"); if (source->start() != OK) { SL_LOGE("AacBqToPcmCbRenderer::onPrepare() Failed to start source/decoder."); notifyPrepared(MEDIA_ERROR_BASE); return; } //--------------------------------- int32_t channelCount; CHECK(meta->findInt32(kKeyChannelCount, &channelCount)); int32_t sr; CHECK(meta->findInt32(kKeySampleRate, &sr)); // FIXME similar to AudioSfDecoder::onPrepare() // already "good to go" (compare to AudioSfDecoder::onPrepare) mCacheStatus = kStatusHigh; mCacheFill = 1000; notifyStatus(); notifyCacheFill(); { android::Mutex::Autolock autoLock(mPcmFormatLock); mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_SAMPLERATE] = sr; mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_NUMCHANNELS] = channelCount; mPcmFormatValues[ANDROID_KEY_INDEX_PCMFORMAT_CHANNELMASK] = sles_channel_out_mask_from_count(channelCount); } SL_LOGV("AacBqToPcmCbRenderer::onPrepare() channel count=%d SR=%d", channelCount, sr); //--------------------------------- // The data source, and audio source (a decoder) are ready to be used mDataSource = mBqSource; mAudioSource = source; mAudioSourceStarted = true; //------------------------------------- // signal successful completion of prepare mStateFlags |= kFlagPrepared; // skipping past AudioToCbRenderer and AudioSfDecoder GenericPlayer::onPrepare(); SL_LOGD("AacBqToPcmCbRenderer::onPrepare() done, mStateFlags=0x%x", mStateFlags); }
ssize_t BufferQueueSource::readAt(off64_t offset, void *data, size_t size) { SL_LOGD("BufferQueueSource::readAt(offset=%lld, data=%p, size=%d)", offset, data, size); if (mEosReached) { // once EOS has been received from the buffer queue, you can't read anymore return 0; } ssize_t readSize; slAndroidBufferQueueCallback callback = NULL; void* pBufferContext, *pBufferData, *callbackPContext; uint32_t dataSize, dataUsed; interface_lock_exclusive(mAndroidBufferQueueSource); if (mAndroidBufferQueueSource->mState.count == 0) { readSize = 0; } else { assert(mAndroidBufferQueueSource->mFront != mAndroidBufferQueueSource->mRear); AdvancedBufferHeader *oldFront = mAndroidBufferQueueSource->mFront; AdvancedBufferHeader *newFront = &oldFront[1]; // where to read from char *pSrc = NULL; // can this read operation cause us to call the buffer queue callback // (either because there was a command with no data, or all the data has been consumed) bool queueCallbackCandidate = false; // consume events when starting to read data from a buffer for the first time if (oldFront->mDataSizeConsumed == 0) { if (oldFront->mItems.mAdtsCmdData.mAdtsCmdCode & ANDROID_ADTSEVENT_EOS) { mEosReached = true; // EOS has no associated data queueCallbackCandidate = true; } oldFront->mItems.mAdtsCmdData.mAdtsCmdCode = ANDROID_ADTSEVENT_NONE; } //assert(mStreamToBqOffset <= offset); CHECK_LE(mStreamToBqOffset, offset); if (offset + (off64_t) size <= mStreamToBqOffset + oldFront->mDataSize) { pSrc = ((char*)oldFront->mDataBuffer) + (offset - mStreamToBqOffset); if (offset - mStreamToBqOffset + size == oldFront->mDataSize) { // consumed buffer entirely oldFront->mDataSizeConsumed = oldFront->mDataSize; mStreamToBqOffset += oldFront->mDataSize; queueCallbackCandidate = true; // move queue to next buffer if (newFront == &mAndroidBufferQueueSource-> mBufferArray[mAndroidBufferQueueSource->mNumBuffers + 1]) { // reached the end, circle back newFront = mAndroidBufferQueueSource->mBufferArray; } mAndroidBufferQueueSource->mFront = newFront; // update the queue state mAndroidBufferQueueSource->mState.count--; mAndroidBufferQueueSource->mState.index++; SL_LOGV("BufferQueueSource moving to next buffer"); } } // consume data: copy to given destination if (NULL != pSrc) { memcpy(data, pSrc, size); readSize = size; } else { readSize = 0; } if (queueCallbackCandidate) { // data has been consumed, and the buffer queue state has been updated // we will notify the client if applicable if (mAndroidBufferQueueSource->mCallbackEventsMask & SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED) { callback = mAndroidBufferQueueSource->mCallback; // save callback data while under lock callbackPContext = mAndroidBufferQueueSource->mContext; pBufferContext = (void *)oldFront->mBufferContext; pBufferData = (void *)oldFront->mDataBuffer; dataSize = oldFront->mDataSize; dataUsed = oldFront->mDataSizeConsumed; } } } interface_unlock_exclusive(mAndroidBufferQueueSource); // notify client if (NULL != callback) { SLresult result = (*callback)(&mAndroidBufferQueueSource->mItf, callbackPContext, pBufferContext, pBufferData, dataSize, dataUsed, // no messages during playback other than marking the buffer as processed (const SLAndroidBufferItem*)(&kItemProcessed) /* pItems */, NB_BUFFEREVENT_ITEM_FIELDS * sizeof(SLuint32) /* itemsLength */ ); if (SL_RESULT_SUCCESS != result) { // Reserved for future use SL_LOGW("Unsuccessful result %d returned from AndroidBufferQueueCallback", result); } } return readSize; }