//-----------------------------------------------------------------------------
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");
}
示例#10
0
void StreamPlayer::onPullFromAndroidBufferQueue() {
    SL_LOGD("StreamPlayer::onPullFromAndroidBufferQueue()");
    mAppProxy->pullFromBuffQueue();
}
示例#11
0
StreamPlayer::~StreamPlayer() {
    SL_LOGD("StreamPlayer::~StreamPlayer()");
    mAppProxy->disconnect();
}
示例#12
0
//--------------------------------------------------
// 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;
}