android_StreamPlayer.cpp revision 6bc8af4e67051af7c86c311cb9c50e294e547500
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define USE_LOG SLAndroidLogLevel_Verbose
18
19#include "sles_allinclusive.h"
20#include "android_StreamPlayer.h"
21
22#include <media/IStreamSource.h>
23#include <media/IMediaPlayerService.h>
24#include <media/stagefright/foundation/ADebug.h>
25
26//--------------------------------------------------------------------------------------------------
27// FIXME abstract out the diff between CMediaPlayer and CAudioPlayer
28
29void android_StreamPlayer_realize_l(CAudioPlayer *ap, const notif_cbf_t cbf, void* notifUser) {
30    SL_LOGV("android_StreamPlayer_realize_l(%p)", ap);
31
32    AudioPlayback_Parameters ap_params;
33    ap_params.sessionId = ap->mSessionId;
34    ap_params.streamType = ap->mStreamType;
35    ap_params.trackcb = NULL;
36    ap_params.trackcbUser = NULL;
37    android::StreamPlayer* splr = new android::StreamPlayer(&ap_params, false /*hasVideo*/);
38    ap->mAPlayer = splr;
39    splr->init(cbf, notifUser);
40}
41
42
43//--------------------------------------------------------------------------------------------------
44namespace android {
45
46StreamSourceAppProxy::StreamSourceAppProxy(
47        const void* user, bool userIsAudioPlayer,
48        void *context, const void *caller, const sp<CallbackProtector> &callbackProtector,
49        const sp<StreamPlayer> &player) :
50    mUser(user),
51    mUserIsAudioPlayer(userIsAudioPlayer),
52    mAndroidBufferQueue(NULL),
53    mAppContext(context),
54    mCaller(caller),
55    mCallbackProtector(callbackProtector),
56    mPlayer(player)
57{
58    SL_LOGV("StreamSourceAppProxy::StreamSourceAppProxy()");
59
60    if (mUserIsAudioPlayer) {
61        mAndroidBufferQueue = &((CAudioPlayer*)mUser)->mAndroidBufferQueue;
62    } else {
63        mAndroidBufferQueue = &((CMediaPlayer*)mUser)->mAndroidBufferQueue;
64    }
65}
66
67StreamSourceAppProxy::~StreamSourceAppProxy() {
68    SL_LOGD("StreamSourceAppProxy::~StreamSourceAppProxy()");
69    mListener.clear();
70    mBuffers.clear();
71}
72
73const SLuint32 StreamSourceAppProxy::kItemProcessed[NB_BUFFEREVENT_ITEM_FIELDS] = {
74        SL_ANDROID_ITEMKEY_BUFFERQUEUEEVENT, // item key
75        sizeof(SLuint32),                    // item size
76        SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED // item data
77};
78
79//--------------------------------------------------
80// IStreamSource implementation
81void StreamSourceAppProxy::setListener(const sp<IStreamListener> &listener) {
82    Mutex::Autolock _l(mLock);
83    mListener = listener;
84}
85
86void StreamSourceAppProxy::setBuffers(const Vector<sp<IMemory> > &buffers) {
87    mBuffers = buffers;
88}
89
90void StreamSourceAppProxy::onBufferAvailable(size_t index) {
91    //SL_LOGD("StreamSourceAppProxy::onBufferAvailable(%d)", index);
92
93    CHECK_LT(index, mBuffers.size());
94    sp<IMemory> mem = mBuffers.itemAt(index);
95    SLAint64 length = (SLAint64) mem->size();
96
97    {
98        Mutex::Autolock _l(mLock);
99        mAvailableBuffers.push_back(index);
100    }
101    //SL_LOGD("onBufferAvailable() now %d buffers available in queue", mAvailableBuffers.size());
102
103    // a new shared mem buffer is available: let's try to fill immediately
104    pullFromBuffQueue();
105}
106
107void StreamSourceAppProxy::receivedCmd_l(IStreamListener::Command cmd, const sp<AMessage> &msg) {
108    if (mListener != 0) {
109        mListener->issueCommand(cmd, false /* synchronous */, msg);
110    }
111}
112
113void StreamSourceAppProxy::receivedBuffer_l(size_t buffIndex, size_t buffLength) {
114    if (mListener != 0) {
115        mListener->queueBuffer(buffIndex, buffLength);
116    }
117}
118
119//--------------------------------------------------
120// consumption from ABQ: pull from the ABQ, and push to shared memory (media server)
121void StreamSourceAppProxy::pullFromBuffQueue() {
122
123  if (android::CallbackProtector::enterCbIfOk(mCallbackProtector)) {
124
125    size_t bufferId;
126    void* bufferLoc;
127    size_t buffSize;
128
129    slAndroidBufferQueueCallback callback = NULL;
130    void* pBufferContext, *pBufferData, *callbackPContext = NULL;
131    AdvancedBufferHeader *oldFront = NULL;
132    uint32_t dataSize /* , dataUsed */;
133
134    // retrieve data from the buffer queue
135    interface_lock_exclusive(mAndroidBufferQueue);
136
137    // can this read operation cause us to call the buffer queue callback
138    // (either because there was a command with no data, or all the data has been consumed)
139    bool queueCallbackCandidate = false;
140
141    if (mAndroidBufferQueue->mState.count != 0) {
142        // SL_LOGD("nbBuffers in ABQ = %u, buffSize=%u",abq->mState.count, buffSize);
143        assert(mAndroidBufferQueue->mFront != mAndroidBufferQueue->mRear);
144
145        oldFront = mAndroidBufferQueue->mFront;
146        AdvancedBufferHeader *newFront = &oldFront[1];
147
148        // consume events when starting to read data from a buffer for the first time
149        if (oldFront->mDataSizeConsumed == 0) {
150            // note this code assumes at most one event per buffer; see IAndroidBufferQueue_Enqueue
151            if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_EOS) {
152                receivedCmd_l(IStreamListener::EOS);
153                // EOS has no associated data
154                queueCallbackCandidate = true;
155            } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_DISCONTINUITY) {
156                receivedCmd_l(IStreamListener::DISCONTINUITY);
157            } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_DISCON_NEWPTS) {
158                sp<AMessage> msg = new AMessage();
159                msg->setInt64(IStreamListener::kKeyResumeAtPTS,
160                        (int64_t)oldFront->mItems.mTsCmdData.mPts);
161                receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/);
162            } else if (oldFront->mItems.mTsCmdData.mTsCmdCode & ANDROID_MP2TSEVENT_FORMAT_CHANGE) {
163                sp<AMessage> msg = new AMessage();
164                // positive value for format change key makes the discontinuity "hard", see key def
165                msg->setInt32(IStreamListener::kKeyFormatChange, (int32_t) 1);
166                receivedCmd_l(IStreamListener::DISCONTINUITY, msg /*msg*/);
167            }
168            oldFront->mItems.mTsCmdData.mTsCmdCode = ANDROID_MP2TSEVENT_NONE;
169        }
170
171        {
172            // we're going to change the shared mem buffer queue, so lock it
173            Mutex::Autolock _l(mLock);
174            if (!mAvailableBuffers.empty()) {
175                bufferId = *mAvailableBuffers.begin();
176                CHECK_LT(bufferId, mBuffers.size());
177                sp<IMemory> mem = mBuffers.itemAt(bufferId);
178                bufferLoc = mem->pointer();
179                buffSize = mem->size();
180
181                char *pSrc = ((char*)oldFront->mDataBuffer) + oldFront->mDataSizeConsumed;
182                if (oldFront->mDataSizeConsumed + buffSize < oldFront->mDataSize) {
183                    // more available than requested, copy as much as requested
184                    // consume data: 1/ copy to given destination
185                    memcpy(bufferLoc, pSrc, buffSize);
186                    //               2/ keep track of how much has been consumed
187                    oldFront->mDataSizeConsumed += buffSize;
188                    //               3/ notify shared mem listener that new data is available
189                    receivedBuffer_l(bufferId, buffSize);
190                    mAvailableBuffers.erase(mAvailableBuffers.begin());
191                } else {
192                    // requested as much available or more: consume the whole of the current
193                    //   buffer and move to the next
194                    size_t consumed = oldFront->mDataSize - oldFront->mDataSizeConsumed;
195                    //SL_LOGD("consuming rest of buffer: enqueueing=%u", consumed);
196                    oldFront->mDataSizeConsumed = oldFront->mDataSize;
197
198                    // move queue to next
199                    if (newFront == &mAndroidBufferQueue->
200                            mBufferArray[mAndroidBufferQueue->mNumBuffers + 1]) {
201                        // reached the end, circle back
202                        newFront = mAndroidBufferQueue->mBufferArray;
203                    }
204                    mAndroidBufferQueue->mFront = newFront;
205                    mAndroidBufferQueue->mState.count--;
206                    mAndroidBufferQueue->mState.index++;
207
208                    if (consumed > 0) {
209                        // consume data: 1/ copy to given destination
210                        memcpy(bufferLoc, pSrc, consumed);
211                        //               2/ keep track of how much has been consumed
212                        // here nothing to do because we are done with this buffer
213                        //               3/ notify StreamPlayer that new data is available
214                        receivedBuffer_l(bufferId, consumed);
215                        mAvailableBuffers.erase(mAvailableBuffers.begin());
216                    }
217
218                    // data has been consumed, and the buffer queue state has been updated
219                    // we will notify the client if applicable
220                    queueCallbackCandidate = true;
221                }
222            }
223
224            if (queueCallbackCandidate) {
225                if (mAndroidBufferQueue->mCallbackEventsMask &
226                        SL_ANDROIDBUFFERQUEUEEVENT_PROCESSED) {
227                    callback = mAndroidBufferQueue->mCallback;
228                    // save callback data while under lock
229                    callbackPContext = mAndroidBufferQueue->mContext;
230                    pBufferContext = (void *)oldFront->mBufferContext;
231                    pBufferData    = (void *)oldFront->mDataBuffer;
232                    dataSize       = oldFront->mDataSize;
233                    // here a buffer is only dequeued when fully consumed
234                    //dataUsed     = oldFront->mDataSizeConsumed;
235                }
236            }
237            //SL_LOGD("%d buffers available after reading from queue", mAvailableBuffers.size());
238            if (!mAvailableBuffers.empty()) {
239                // there is still room in the shared memory, recheck later if we can pull
240                // data from the buffer queue and write it to shared memory
241                mPlayer->queueRefilled();
242            }
243        }
244
245    } else { // empty queue
246        SL_LOGD("ABQ empty, starving!");
247    }
248#if 0
249        // signal we're at the end of the content, but don't pause (see note in function)
250        if (mUserIsAudioPlayer) {
251            // FIXME declare this external
252            //audioPlayer_dispatch_headAtEnd_lockPlay((CAudioPlayer*)user,
253            //        false /*set state to paused?*/, false);
254        } else {
255            // FIXME implement headAtEnd dispatch for CMediaPlayer
256        }
257    }
258#endif
259
260    interface_unlock_exclusive(mAndroidBufferQueue);
261
262    // notify client
263    if (NULL != callback) {
264        SLresult result = (*callback)(&mAndroidBufferQueue->mItf, callbackPContext,
265                pBufferContext, pBufferData, dataSize,
266                dataSize, /* dataUsed  */
267                // no messages during playback other than marking the buffer as processed
268                (const SLAndroidBufferItem*)(&kItemProcessed) /* pItems */,
269                NB_BUFFEREVENT_ITEM_FIELDS *sizeof(SLuint32) /* itemsLength */ );
270        if (SL_RESULT_SUCCESS != result) {
271            // Reserved for future use
272            SL_LOGW("Unsuccessful result %d returned from AndroidBufferQueueCallback", result);
273        }
274    }
275
276    mCallbackProtector->exitCb();
277  } // enterCbIfOk
278}
279
280
281//--------------------------------------------------------------------------------------------------
282StreamPlayer::StreamPlayer(AudioPlayback_Parameters* params, bool hasVideo) :
283        GenericMediaPlayer(params, hasVideo),
284        mAppProxy(0)
285{
286    SL_LOGD("StreamPlayer::StreamPlayer()");
287
288    mPlaybackParams = *params;
289
290}
291
292StreamPlayer::~StreamPlayer() {
293    SL_LOGD("StreamPlayer::~StreamPlayer()");
294
295    mAppProxy.clear();
296}
297
298
299void StreamPlayer::onMessageReceived(const sp<AMessage> &msg) {
300    switch (msg->what()) {
301        case kWhatPullFromAbq:
302            onPullFromAndroidBufferQueue();
303            break;
304
305        default:
306            GenericMediaPlayer::onMessageReceived(msg);
307            break;
308    }
309}
310
311
312void StreamPlayer::registerQueueCallback(
313        const void* user, bool userIsAudioPlayer,
314        void *context,
315        const void *caller) {
316    SL_LOGD("StreamPlayer::registerQueueCallback");
317    Mutex::Autolock _l(mAppProxyLock);
318
319    mAppProxy = new StreamSourceAppProxy(
320            user, userIsAudioPlayer,
321            context, caller, mCallbackProtector, this);
322
323    CHECK(mAppProxy != 0);
324    SL_LOGD("StreamPlayer::registerQueueCallback end");
325}
326
327
328/**
329 * Asynchronously notify the player that the queue is ready to be pulled from.
330 */
331void StreamPlayer::queueRefilled() {
332    // async notification that the ABQ was refilled: the player should pull from the ABQ, and
333    //    and push to shared memory (to the media server)
334    (new AMessage(kWhatPullFromAbq, id()))->post();
335}
336
337
338void StreamPlayer::appClear_l() {
339    // the user of StreamPlayer has cleared its AndroidBufferQueue:
340    // there's no clear() for the shared memory queue, so this is a no-op
341}
342
343
344//--------------------------------------------------
345// Event handlers
346void StreamPlayer::onPrepare() {
347    SL_LOGD("StreamPlayer::onPrepare()");
348    Mutex::Autolock _l(mAppProxyLock);
349    if (mAppProxy != 0) {
350        sp<IMediaPlayerService> mediaPlayerService(getMediaPlayerService());
351        if (mediaPlayerService != NULL) {
352            mPlayer = mediaPlayerService->create(getpid(), mPlayerClient /*IMediaPlayerClient*/,
353                    mPlaybackParams.sessionId);
354            if (mPlayer == NULL) {
355                SL_LOGE("media player service failed to create player by app proxy");
356            } else if (mPlayer->setDataSource(mAppProxy /*IStreamSource*/) != NO_ERROR) {
357                SL_LOGE("setDataSource failed");
358                mPlayer.clear();
359            }
360        }
361    } else {
362        SL_LOGE("Nothing to do here because there is no registered callback");
363    }
364    if (mPlayer == NULL) {
365        mStateFlags |= kFlagPreparedUnsuccessfully;
366    }
367    GenericMediaPlayer::onPrepare();
368    SL_LOGD("StreamPlayer::onPrepare() done");
369}
370
371
372void StreamPlayer::onPlay() {
373    SL_LOGD("StreamPlayer::onPlay()");
374    // enqueue a message that will cause StreamAppProxy to consume from the queue (again if the
375    // player had starved the shared memory)
376    queueRefilled();
377
378    GenericMediaPlayer::onPlay();
379}
380
381
382void StreamPlayer::onPullFromAndroidBufferQueue() {
383    //SL_LOGD("StreamPlayer::onQueueRefilled()");
384    Mutex::Autolock _l(mAppProxyLock);
385    if (mAppProxy != 0) {
386        mAppProxy->pullFromBuffQueue();
387    }
388}
389
390} // namespace android
391