android_GenericMediaPlayer.cpp revision 81e917a2605e14901b8f5e6cac7eafb5667aad0d
1/*
2 * Copyright (C) 2011 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_GenericMediaPlayer.h"
21
22#include <media/IMediaPlayerService.h>
23#include <surfaceflinger/ISurfaceComposer.h>
24#include <surfaceflinger/SurfaceComposerClient.h>
25#include <media/stagefright/foundation/ADebug.h>
26
27namespace android {
28
29// default delay in Us used when reposting an event when the player is not ready to accept
30// the command yet. This is for instance used when seeking on a MediaPlayer that's still preparing
31#define DEFAULT_COMMAND_DELAY_FOR_REPOST_US (100*1000) // 100ms
32
33static const char* const kDistantProtocolPrefix[] = { "http:", "https:", "ftp:", "rtp:", "rtsp:"};
34#define NB_DISTANT_PROTOCOLS (sizeof(kDistantProtocolPrefix)/sizeof(kDistantProtocolPrefix[0]))
35
36//--------------------------------------------------------------------------------------------------
37MediaPlayerNotificationClient::MediaPlayerNotificationClient(GenericMediaPlayer* gmp) :
38    mGenericMediaPlayer(gmp),
39    mPlayerPrepared(false)
40{
41
42}
43
44MediaPlayerNotificationClient::~MediaPlayerNotificationClient() {
45
46}
47
48//--------------------------------------------------
49// IMediaPlayerClient implementation
50void MediaPlayerNotificationClient::notify(int msg, int ext1, int ext2, const Parcel *obj) {
51    SL_LOGV("MediaPlayerNotificationClient::notify(msg=%d, ext1=%d, ext2=%d)", msg, ext1, ext2);
52
53    switch (msg) {
54      case MEDIA_PREPARED:
55        mPlayerPrepared = true;
56        mPlayerPreparedCondition.signal();
57        break;
58
59      case MEDIA_SET_VIDEO_SIZE:
60        // only send video size updates if the player was flagged as having video, to avoid
61        // sending video size updates of (0,0)
62        if (mGenericMediaPlayer->mHasVideo) {
63            mGenericMediaPlayer->notify(PLAYEREVENT_VIDEO_SIZE_UPDATE,
64                    (int32_t)ext1, (int32_t)ext2, true /*async*/);
65        }
66        break;
67
68      case MEDIA_SEEK_COMPLETE:
69          mGenericMediaPlayer->seekComplete();
70        break;
71
72      case MEDIA_PLAYBACK_COMPLETE:
73        mGenericMediaPlayer->notify(PLAYEREVENT_ENDOFSTREAM, 1, true /*async*/);
74        break;
75
76      case MEDIA_BUFFERING_UPDATE:
77        // values received from Android framework for buffer fill level use percent,
78        //   while SL/XA use permille, so does GenericPlayer
79        mGenericMediaPlayer->bufferingUpdate(ext1 * 10 /*fillLevelPerMille*/);
80        break;
81
82      case MEDIA_ERROR:
83      case MEDIA_NOP:
84      case MEDIA_TIMED_TEXT:
85      case MEDIA_INFO:
86        break;
87
88      default: { }
89    }
90
91}
92
93//--------------------------------------------------
94void MediaPlayerNotificationClient::blockUntilPlayerPrepared() {
95    Mutex::Autolock _l(mLock);
96    while (!mPlayerPrepared) {
97        mPlayerPreparedCondition.wait(mLock);
98    }
99}
100
101//--------------------------------------------------------------------------------------------------
102GenericMediaPlayer::GenericMediaPlayer(const AudioPlayback_Parameters* params, bool hasVideo) :
103    GenericPlayer(params),
104    mHasVideo(hasVideo),
105    mSeekTimeMsec(0),
106    mVideoSurface(0),
107    mVideoSurfaceTexture(0),
108    mPlayer(0),
109    mPlayerClient(0),
110    mGetMediaPlayerInfoGenCount(0)
111{
112    SL_LOGD("GenericMediaPlayer::GenericMediaPlayer()");
113
114    mServiceManager = defaultServiceManager();
115    mBinder = mServiceManager->getService(String16("media.player"));
116    mMediaPlayerService = interface_cast<IMediaPlayerService>(mBinder);
117
118    CHECK(mMediaPlayerService.get() != NULL);
119
120    mPlayerClient = new MediaPlayerNotificationClient(this);
121}
122
123GenericMediaPlayer::~GenericMediaPlayer() {
124    SL_LOGD("GenericMediaPlayer::~GenericMediaPlayer()");
125}
126
127void GenericMediaPlayer::preDestroy() {
128    SL_LOGD("GenericMediaPlayer::preDestroy()");
129    // we might be in the middle of blocking for a getXXX call
130    {
131        android::Mutex::Autolock autoLock(mGetMediaPlayerInfoLock);
132        mGetMediaPlayerInfoGenCount++;
133        mGetMediaPlayerInfoCondition.broadcast();
134    }
135    GenericPlayer::preDestroy();
136}
137
138//--------------------------------------------------
139// overridden from GenericPlayer
140// pre-condition:
141//   msec != NULL
142// post-condition
143//   *msec == mPositionMsec ==
144//                  ANDROID_UNKNOWN_TIME if position is unknown at time of query,
145//               or the current MediaPlayer position
146void GenericMediaPlayer::getPositionMsec(int* msec) {
147    SL_LOGD("GenericMediaPlayer::getPositionMsec()");
148    uint32_t currentGen = 0;
149    {
150        android::Mutex::Autolock autoLock(mGetMediaPlayerInfoLock);
151        currentGen = mGetMediaPlayerInfoGenCount;
152    }
153    // send a message to update the MediaPlayer position in the event loop where it's safe to
154    //   access the MediaPlayer. We block until the message kWhatMediaPlayerInfo has been processed
155    (new AMessage(kWhatMediaPlayerInfo, id()))->post();
156    {
157        android::Mutex::Autolock autoLock(mGetMediaPlayerInfoLock);
158        // mGetMediaPlayerInfoGenCount will be incremented when the kWhatMediaPlayerInfo
159        //  gets processed.
160        while (currentGen == mGetMediaPlayerInfoGenCount) {
161            mGetMediaPlayerInfoCondition.wait(mGetMediaPlayerInfoLock);
162            // if multiple GetPosition calls were issued before any got processed on the event queue
163            // then they will all return the same "recent-enough" position
164        }
165        // at this point mPositionMsec has been updated
166        // so now updates msec from mPositionMsec, while holding the lock protecting it
167        GenericPlayer::getPositionMsec(msec);
168    }
169}
170
171//--------------------------------------------------
172void GenericMediaPlayer::setVideoSurface(const sp<Surface> &surface) {
173    mVideoSurface = surface;
174}
175
176void GenericMediaPlayer::setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
177    mVideoSurfaceTexture = surfaceTexture;
178}
179
180//--------------------------------------------------
181void GenericMediaPlayer::onMessageReceived(const sp<AMessage> &msg) {
182    switch (msg->what()) {
183        case kWhatMediaPlayerInfo:
184            onGetMediaPlayerInfo();
185            break;
186
187        default:
188            GenericPlayer::onMessageReceived(msg);
189            break;
190    }
191}
192
193//--------------------------------------------------
194// Event handlers
195
196void GenericMediaPlayer::onPrepare() {
197    SL_LOGD("GenericMediaPlayer::onPrepare()");
198    if (!(mStateFlags & kFlagPrepared) && (mPlayer != 0)) {
199        if (mHasVideo) {
200            if (mVideoSurface != 0) {
201                mPlayer->setVideoSurface(mVideoSurface);
202            } else if (mVideoSurfaceTexture != 0) {
203                mPlayer->setVideoSurfaceTexture(mVideoSurfaceTexture);
204            }
205        }
206        mPlayer->setAudioStreamType(mPlaybackParams.streamType);
207        mPlayer->prepareAsync();
208        mPlayerClient->blockUntilPlayerPrepared();
209        onAfterMediaPlayerPrepared();
210        GenericPlayer::onPrepare();
211    }
212    SL_LOGD("GenericMediaPlayer::onPrepare() done, mStateFlags=0x%x", mStateFlags);
213}
214
215
216void GenericMediaPlayer::onPlay() {
217    SL_LOGD("GenericMediaPlayer::onPlay()");
218    if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)) {
219        SL_LOGD("starting player");
220        mPlayer->start();
221        mStateFlags |= kFlagPlaying;
222    } else {
223        SL_LOGV("NOT starting player mStateFlags=0x%x", mStateFlags);
224    }
225}
226
227
228void GenericMediaPlayer::onPause() {
229    SL_LOGD("GenericMediaPlayer::onPause()");
230    if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)) {
231        mPlayer->pause();
232        mStateFlags &= ~kFlagPlaying;
233    }
234}
235
236/**
237 * pre-condition: WHATPARAM_SEEK_SEEKTIME_MS parameter value >= 0
238 */
239void GenericMediaPlayer::onSeek(const sp<AMessage> &msg) {
240    SL_LOGV("GenericMediaPlayer::onSeek");
241    int64_t timeMsec = ANDROID_UNKNOWN_TIME;
242    if (!msg->findInt64(WHATPARAM_SEEK_SEEKTIME_MS, &timeMsec)) {
243        // invalid command, drop it
244        return;
245    }
246    if ((mStateFlags & kFlagSeeking) && (timeMsec == mSeekTimeMsec)) {
247        // already seeking to the same time, cancel this command
248        return;
249    } else if (!(mStateFlags & kFlagPrepared)) {
250        // we are not ready to accept a seek command at this time, retry later
251        msg->post(DEFAULT_COMMAND_DELAY_FOR_REPOST_US);
252    } else {
253        if (msg->findInt64(WHATPARAM_SEEK_SEEKTIME_MS, &timeMsec) && (mPlayer != 0)) {
254            mStateFlags |= kFlagSeeking;
255            mSeekTimeMsec = (int32_t)timeMsec;
256            if (OK != mPlayer->seekTo(timeMsec)) {
257                mStateFlags &= ~kFlagSeeking;
258                mSeekTimeMsec = ANDROID_UNKNOWN_TIME;
259            } else {
260                mPositionMsec = mSeekTimeMsec;
261            }
262        }
263    }
264}
265
266
267void GenericMediaPlayer::onLoop(const sp<AMessage> &msg) {
268    SL_LOGV("GenericMediaPlayer::onLoop");
269    int32_t loop = 0;
270    if (msg->findInt32(WHATPARAM_LOOP_LOOPING, &loop)) {
271        if (mPlayer != 0 && OK == mPlayer->setLooping(loop)) {
272            if (loop) {
273                mStateFlags |= kFlagLooping;
274            } else {
275                mStateFlags &= ~kFlagLooping;
276            }
277        }
278    }
279}
280
281
282void GenericMediaPlayer::onVolumeUpdate() {
283    SL_LOGD("GenericMediaPlayer::onVolumeUpdate()");
284    // use settings lock to read the volume settings
285    Mutex::Autolock _l(mSettingsLock);
286    if (mPlayer != 0) {
287        if (mAndroidAudioLevels.mMute) {
288            mPlayer->setVolume(0.0f, 0.0f);
289        } else {
290            mPlayer->setVolume(mAndroidAudioLevels.mFinalVolume[0],
291                    mAndroidAudioLevels.mFinalVolume[1]);
292        }
293    }
294}
295
296
297void GenericMediaPlayer::onBufferingUpdate(const sp<AMessage> &msg) {
298    int32_t fillLevel = 0;
299    if (msg->findInt32(WHATPARAM_BUFFERING_UPDATE, &fillLevel)) {
300        SL_LOGD("GenericMediaPlayer::onBufferingUpdate(fillLevel=%d)", fillLevel);
301
302        Mutex::Autolock _l(mSettingsLock);
303        mCacheFill = fillLevel;
304        // handle cache fill update
305        if (mCacheFill - mLastNotifiedCacheFill >= mCacheFillNotifThreshold) {
306            notifyCacheFill();
307        }
308        // handle prefetch status update
309        //   compute how much time ahead of position is buffered
310        int durationMsec, positionMsec = -1;
311        if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)
312                && (OK == mPlayer->getDuration(&durationMsec))
313                        && (OK == mPlayer->getCurrentPosition(&positionMsec))) {
314            if ((-1 != durationMsec) && (-1 != positionMsec)) {
315                // evaluate prefetch status based on buffer time thresholds
316                int64_t bufferedDurationMsec = (durationMsec * fillLevel / 100) - positionMsec;
317                CacheStatus_t newCacheStatus = mCacheStatus;
318                if (bufferedDurationMsec > DURATION_CACHED_HIGH_MS) {
319                    newCacheStatus = kStatusHigh;
320                } else if (bufferedDurationMsec > DURATION_CACHED_MED_MS) {
321                    newCacheStatus = kStatusEnough;
322                } else if (bufferedDurationMsec > DURATION_CACHED_LOW_MS) {
323                    newCacheStatus = kStatusIntermediate;
324                } else if (bufferedDurationMsec == 0) {
325                    newCacheStatus = kStatusEmpty;
326                } else {
327                    newCacheStatus = kStatusLow;
328                }
329
330                if (newCacheStatus != mCacheStatus) {
331                    mCacheStatus = newCacheStatus;
332                    notifyStatus();
333                }
334            }
335        }
336    }
337}
338
339
340void GenericMediaPlayer::onGetMediaPlayerInfo() {
341    SL_LOGD("GenericMediaPlayer::onGetMediaPlayerInfo()");
342    {
343        android::Mutex::Autolock autoLock(mGetMediaPlayerInfoLock);
344
345        if ((!(mStateFlags & kFlagPrepared)) || (mPlayer == 0)) {
346            mPositionMsec = ANDROID_UNKNOWN_TIME;
347        } else {
348            mPlayer->getCurrentPosition(&mPositionMsec);
349        }
350
351        // the MediaPlayer info has been refreshed
352        mGetMediaPlayerInfoGenCount++;
353        // there might be multiple requests for MediaPlayer info, so use broadcast instead of signal
354        mGetMediaPlayerInfoCondition.broadcast();
355    }
356}
357
358
359//--------------------------------------------------
360/**
361 * called from the event handling loop
362 * pre-condition: mPlayer is prepared
363 */
364void GenericMediaPlayer::onAfterMediaPlayerPrepared() {
365    // the MediaPlayer mPlayer is prepared, retrieve its duration
366    // FIXME retrieve channel count
367    {
368        Mutex::Autolock _l(mSettingsLock);
369        int msec = 0;
370        if (OK == mPlayer->getDuration(&msec)) {
371            mDurationMsec = msec;
372        }
373    }
374    // when the MediaPlayer mPlayer is prepared, there is "sufficient data" in the playback buffers
375    // if the data source was local, and the buffers are considered full so we need to notify that
376    bool isLocalSource = true;
377    if (kDataLocatorUri == mDataLocatorType) {
378        for (unsigned int i = 0 ; i < NB_DISTANT_PROTOCOLS ; i++) {
379            if (!strncasecmp(mDataLocator.uriRef,
380                    kDistantProtocolPrefix[i], strlen(kDistantProtocolPrefix[i]))) {
381                isLocalSource = false;
382                break;
383            }
384        }
385    }
386    if (isLocalSource) {
387        SL_LOGD("media player prepared on local source");
388        {
389            Mutex::Autolock _l(mSettingsLock);
390            mCacheStatus = kStatusHigh;
391            mCacheFill = 1000;
392            notifyStatus();
393            notifyCacheFill();
394        }
395    } else {
396        SL_LOGD("media player prepared on non-local source");
397    }
398}
399
400} // namespace android
401