| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| //#define USE_LOG SLAndroidLogLevel_Verbose |
| |
| #include "sles_allinclusive.h" |
| #include "android/BufferQueueSource.h" |
| |
| #include <media/stagefright/MediaDebug.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| |
| namespace android { |
| |
| |
| const SLuint32 BufferQueueSource::kItemProcessed[NB_BUFFEREVENT_ITEM_FIELDS] = { |
| SL_ANDROID_ITEMKEY_BUFFERQUEUEEVENT, // item key |
| sizeof(SLuint32), // item size |
| SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED // item data |
| }; |
| |
| |
| BufferQueueSource::BufferQueueSource(const void* user, void *context, const void *caller) : |
| mAndroidBufferQueueSource(NULL), |
| mStreamToBqOffset(0), |
| mEosReached(false) |
| { |
| if (NULL != user) { |
| mAndroidBufferQueueSource = &((CAudioPlayer*)user)->mAndroidBufferQueue; |
| } else { |
| SL_LOGE("Can't create BufferQueueSource with NULL user"); |
| } |
| |
| } |
| |
| |
| BufferQueueSource::~BufferQueueSource() { |
| SL_LOGD("BufferQueueSource::~BufferQueueSource"); |
| } |
| |
| |
| //-------------------------------------------------------------------------- |
| status_t BufferQueueSource::initCheck() const { |
| return mAndroidBufferQueueSource != NULL ? OK : NO_INIT; |
| } |
| |
| 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(mStreamToBqOffset <= offset); |
| |
| if (offset + 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; |
| } |
| |
| |
| 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 OK; |
| } |
| |
| } // namespace android |