android_GenericMediaPlayer.cpp revision e2e8fa36bd7448b59fbcdf141e0b6d21e5401d91
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
27// default delay in Us used when reposting an event when the player is not ready to accept
28// the command yet. This is for instance used when seeking on a MediaPlayer that's still preparing
29#define DEFAULT_COMMAND_DELAY_FOR_REPOST_US (100*1000) // 100ms
30
31// table of prefixes for known distant protocols; these are immediately dispatched to mediaserver
32static const char* const kDistantProtocolPrefix[] = { "http://", "https://", "rtsp://"};
33#define NB_DISTANT_PROTOCOLS (sizeof(kDistantProtocolPrefix)/sizeof(kDistantProtocolPrefix[0]))
34
35// is the specified URI a known distant protocol?
36bool isDistantProtocol(const char *uri)
37{
38    for (unsigned int i = 0; i < NB_DISTANT_PROTOCOLS; i++) {
39        if (!strncasecmp(uri, kDistantProtocolPrefix[i], strlen(kDistantProtocolPrefix[i]))) {
40            return true;
41        }
42    }
43    return false;
44}
45
46namespace android {
47
48//--------------------------------------------------------------------------------------------------
49MediaPlayerNotificationClient::MediaPlayerNotificationClient(GenericMediaPlayer* gmp) :
50    mGenericMediaPlayer(gmp),
51    mPlayerPrepared(PREPARE_NOT_STARTED)
52{
53    SL_LOGV("MediaPlayerNotificationClient::MediaPlayerNotificationClient()");
54}
55
56MediaPlayerNotificationClient::~MediaPlayerNotificationClient() {
57    SL_LOGV("MediaPlayerNotificationClient::~MediaPlayerNotificationClient()");
58}
59
60// Map a MEDIA_* enum to a string
61static const char *media_to_string(int msg)
62{
63    switch (msg) {
64#define _(x) case MEDIA_##x: return "MEDIA_" #x;
65      _(PREPARED)
66      _(SET_VIDEO_SIZE)
67      _(SEEK_COMPLETE)
68      _(PLAYBACK_COMPLETE)
69      _(BUFFERING_UPDATE)
70      _(ERROR)
71      _(NOP)
72      _(TIMED_TEXT)
73      _(INFO)
74#undef _
75    default:
76        return NULL;
77    }
78}
79
80//--------------------------------------------------
81// IMediaPlayerClient implementation
82void MediaPlayerNotificationClient::notify(int msg, int ext1, int ext2, const Parcel *obj) {
83    SL_LOGV("MediaPlayerNotificationClient::notify(msg=%s (%d), ext1=%d, ext2=%d)",
84            media_to_string(msg), msg, ext1, ext2);
85
86    sp<GenericMediaPlayer> genericMediaPlayer(mGenericMediaPlayer.promote());
87    if (genericMediaPlayer == NULL) {
88        SL_LOGW("MediaPlayerNotificationClient::notify after GenericMediaPlayer destroyed");
89        return;
90    }
91
92    switch (msg) {
93      case MEDIA_PREPARED:
94        {
95        Mutex::Autolock _l(mLock);
96        if (PREPARE_IN_PROGRESS == mPlayerPrepared) {
97            mPlayerPrepared = PREPARE_COMPLETED_SUCCESSFULLY;
98            mPlayerPreparedCondition.signal();
99        } else {
100            SL_LOGE("Unexpected MEDIA_PREPARED");
101        }
102        }
103        break;
104
105      case MEDIA_SET_VIDEO_SIZE:
106        // only send video size updates if the player was flagged as having video, to avoid
107        // sending video size updates of (0,0)
108        // We're running on a different thread than genericMediaPlayer's ALooper thread,
109        // so it would normally be racy to access fields within genericMediaPlayer.
110        // But in this case mHasVideo is const, so it is safe to access.
111        // Or alternatively, we could notify unconditionally and let it decide whether to handle.
112        if (genericMediaPlayer->mHasVideo) {
113            genericMediaPlayer->notify(PLAYEREVENT_VIDEO_SIZE_UPDATE,
114                    (int32_t)ext1, (int32_t)ext2, true /*async*/);
115        }
116        break;
117
118      case MEDIA_SEEK_COMPLETE:
119        genericMediaPlayer->seekComplete();
120        break;
121
122      case MEDIA_PLAYBACK_COMPLETE:
123        genericMediaPlayer->notify(PLAYEREVENT_ENDOFSTREAM, 1, true /*async*/);
124        break;
125
126      case MEDIA_BUFFERING_UPDATE:
127        // values received from Android framework for buffer fill level use percent,
128        //   while SL/XA use permille, so does GenericPlayer
129        genericMediaPlayer->bufferingUpdate(ext1 * 10 /*fillLevelPerMille*/);
130        break;
131
132      case MEDIA_ERROR:
133        {
134        Mutex::Autolock _l(mLock);
135        if (PREPARE_IN_PROGRESS == mPlayerPrepared) {
136            mPlayerPrepared = PREPARE_COMPLETED_UNSUCCESSFULLY;
137            mPlayerPreparedCondition.signal();
138        } else {
139            // inform client of errors after preparation
140            genericMediaPlayer->notify(PLAYEREVENT_ERRORAFTERPREPARE, ext1, true /*async*/);
141        }
142        }
143        break;
144
145      case MEDIA_NOP:
146      case MEDIA_TIMED_TEXT:
147      case MEDIA_INFO:
148        break;
149
150      default: { }
151    }
152
153}
154
155//--------------------------------------------------
156void MediaPlayerNotificationClient::beforePrepare()
157{
158    Mutex::Autolock _l(mLock);
159    assert(mPlayerPrepared == PREPARE_NOT_STARTED);
160    mPlayerPrepared = PREPARE_IN_PROGRESS;
161}
162
163//--------------------------------------------------
164bool MediaPlayerNotificationClient::blockUntilPlayerPrepared() {
165    Mutex::Autolock _l(mLock);
166    assert(mPlayerPrepared != PREPARE_NOT_STARTED);
167    while (mPlayerPrepared == PREPARE_IN_PROGRESS) {
168        mPlayerPreparedCondition.wait(mLock);
169    }
170    assert(mPlayerPrepared == PREPARE_COMPLETED_SUCCESSFULLY ||
171            mPlayerPrepared == PREPARE_COMPLETED_UNSUCCESSFULLY);
172    return mPlayerPrepared == PREPARE_COMPLETED_SUCCESSFULLY;
173}
174
175//--------------------------------------------------------------------------------------------------
176GenericMediaPlayer::GenericMediaPlayer(const AudioPlayback_Parameters* params, bool hasVideo) :
177    GenericPlayer(params),
178    mHasVideo(hasVideo),
179    mSeekTimeMsec(0),
180    mVideoSurface(0),
181    mVideoSurfaceTexture(0),
182    mPlayer(0),
183    mPlayerClient(new MediaPlayerNotificationClient(this))
184{
185    SL_LOGD("GenericMediaPlayer::GenericMediaPlayer()");
186
187}
188
189GenericMediaPlayer::~GenericMediaPlayer() {
190    SL_LOGD("GenericMediaPlayer::~GenericMediaPlayer()");
191}
192
193void GenericMediaPlayer::preDestroy() {
194    // FIXME can't access mPlayer from outside the looper (no mutex!) so using mPreparedPlayer
195    sp<IMediaPlayer> player;
196    getPreparedPlayer(player);
197    if (player != NULL) {
198        player->stop();
199        // causes CHECK failure in Nuplayer, but commented out in the subclass preDestroy
200        // randomly causes a NPE in StagefrightPlayer, heap corruption, or app hang
201        //player->setDataSource(NULL);
202        player->setVideoSurface(NULL);
203        player->disconnect();
204        // release all references to the IMediaPlayer
205        // FIXME illegal if not on looper
206        //mPlayer.clear();
207        {
208            Mutex::Autolock _l(mPreparedPlayerLock);
209            mPreparedPlayer.clear();
210        }
211    }
212    GenericPlayer::preDestroy();
213}
214
215//--------------------------------------------------
216// overridden from GenericPlayer
217// pre-condition:
218//   msec != NULL
219// post-condition
220//   *msec ==
221//                  ANDROID_UNKNOWN_TIME if position is unknown at time of query,
222//               or the current MediaPlayer position
223void GenericMediaPlayer::getPositionMsec(int* msec) {
224    SL_LOGD("GenericMediaPlayer::getPositionMsec()");
225    sp<IMediaPlayer> player;
226    getPreparedPlayer(player);
227    // To avoid deadlock, directly call the MediaPlayer object
228    if (player == 0 || player->getCurrentPosition(msec) != NO_ERROR) {
229        *msec = ANDROID_UNKNOWN_TIME;
230    }
231}
232
233//--------------------------------------------------
234void GenericMediaPlayer::setVideoSurface(const sp<Surface> &surface) {
235    SL_LOGV("GenericMediaPlayer::setVideoSurface()");
236    // FIXME bug - race condition, should do in looper
237    if (mVideoSurface.get() == surface.get()) {
238        return;
239    }
240    if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)) {
241        mPlayer->setVideoSurface(surface);
242    }
243    mVideoSurface = surface;
244    mVideoSurfaceTexture = NULL;
245}
246
247void GenericMediaPlayer::setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
248    SL_LOGV("GenericMediaPlayer::setVideoSurfaceTexture()");
249    // FIXME bug - race condition, should do in looper
250    if (mVideoSurfaceTexture.get() == surfaceTexture.get()) {
251        return;
252    }
253    if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)) {
254        mPlayer->setVideoSurfaceTexture(surfaceTexture);
255    }
256    mVideoSurfaceTexture = surfaceTexture;
257    mVideoSurface = NULL;
258}
259
260
261//--------------------------------------------------
262// Event handlers
263
264// blocks until mPlayer is prepared
265void GenericMediaPlayer::onPrepare() {
266    SL_LOGD("GenericMediaPlayer::onPrepare()");
267    // Attempt to prepare at most once, and only if there is a MediaPlayer
268    if (!(mStateFlags & (kFlagPrepared | kFlagPreparedUnsuccessfully)) && (mPlayer != 0)) {
269        if (mHasVideo) {
270            if (mVideoSurface != 0) {
271                mPlayer->setVideoSurface(mVideoSurface);
272            } else if (mVideoSurfaceTexture != 0) {
273                mPlayer->setVideoSurfaceTexture(mVideoSurfaceTexture);
274            }
275        }
276        mPlayer->setAudioStreamType(mPlaybackParams.streamType);
277        mPlayerClient->beforePrepare();
278        mPlayer->prepareAsync();
279        if (mPlayerClient->blockUntilPlayerPrepared()) {
280            mStateFlags |= kFlagPrepared;
281            afterMediaPlayerPreparedSuccessfully();
282        } else {
283            mStateFlags |= kFlagPreparedUnsuccessfully;
284        }
285    }
286    GenericPlayer::onPrepare();
287    SL_LOGD("GenericMediaPlayer::onPrepare() done, mStateFlags=0x%x", mStateFlags);
288}
289
290
291void GenericMediaPlayer::onPlay() {
292    SL_LOGD("GenericMediaPlayer::onPlay()");
293    if (((mStateFlags & (kFlagPrepared | kFlagPlaying)) == kFlagPrepared) && (mPlayer != 0)) {
294        mPlayer->start();
295    }
296    GenericPlayer::onPlay();
297}
298
299
300void GenericMediaPlayer::onPause() {
301    SL_LOGD("GenericMediaPlayer::onPause()");
302    if (!(~mStateFlags & (kFlagPrepared | kFlagPlaying)) && (mPlayer != 0)) {
303        mPlayer->pause();
304    }
305    GenericPlayer::onPause();
306}
307
308
309void GenericMediaPlayer::onSeekComplete() {
310    SL_LOGV("GenericMediaPlayer::onSeekComplete()");
311    // did we initiate the seek?
312    if (!(mStateFlags & kFlagSeeking)) {
313        // no, are we looping?
314        if (mStateFlags & kFlagLooping) {
315            // yes, per OpenSL ES 1.0.1 and 1.1 do NOT report it to client
316            // notify(PLAYEREVENT_ENDOFSTREAM, 1, true /*async*/);
317        // no, well that's surprising, but it's probably just a benign race condition
318        } else {
319            SL_LOGW("Unexpected seek complete event ignored");
320        }
321    }
322    GenericPlayer::onSeekComplete();
323}
324
325
326/**
327 * pre-condition: WHATPARAM_SEEK_SEEKTIME_MS parameter value >= 0
328 */
329void GenericMediaPlayer::onSeek(const sp<AMessage> &msg) {
330    SL_LOGV("GenericMediaPlayer::onSeek");
331    int64_t timeMsec = ANDROID_UNKNOWN_TIME;
332    if (!msg->findInt64(WHATPARAM_SEEK_SEEKTIME_MS, &timeMsec)) {
333        // invalid command, drop it
334        return;
335    }
336    if ((mStateFlags & kFlagSeeking) && (timeMsec == mSeekTimeMsec) &&
337            (timeMsec != ANDROID_UNKNOWN_TIME)) {
338        // already seeking to the same non-unknown time, cancel this command
339        return;
340    } else if (mStateFlags & kFlagPreparedUnsuccessfully) {
341        // discard seeks after unsuccessful prepare
342    } else if (!(mStateFlags & kFlagPrepared)) {
343        // we are not ready to accept a seek command at this time, retry later
344        msg->post(DEFAULT_COMMAND_DELAY_FOR_REPOST_US);
345    } else {
346        if (mPlayer != 0) {
347            mStateFlags |= kFlagSeeking;
348            mSeekTimeMsec = (int32_t)timeMsec;
349            // seek to unknown time is used by StreamPlayer after discontinuity
350            if (timeMsec == ANDROID_UNKNOWN_TIME) {
351                // FIXME simulate a MEDIA_SEEK_COMPLETE event in 250 ms;
352                // this is a terrible hack to make up for mediaserver not sending one
353                (new AMessage(kWhatSeekComplete, id()))->post(250000);
354            } else if (OK != mPlayer->seekTo(timeMsec)) {
355                mStateFlags &= ~kFlagSeeking;
356                mSeekTimeMsec = ANDROID_UNKNOWN_TIME;
357                // don't call updateOneShot because seek not yet done
358            }
359        }
360    }
361}
362
363
364void GenericMediaPlayer::onLoop(const sp<AMessage> &msg) {
365    SL_LOGV("GenericMediaPlayer::onLoop");
366    int32_t loop = 0;
367    if (msg->findInt32(WHATPARAM_LOOP_LOOPING, &loop)) {
368        if (loop) {
369            mStateFlags |= kFlagLooping;
370        } else {
371            mStateFlags &= ~kFlagLooping;
372        }
373        // if we have a MediaPlayer then tell it now, otherwise we'll tell it after it's created
374        if (mPlayer != 0) {
375            (void) mPlayer->setLooping(loop);
376        }
377    }
378}
379
380
381void GenericMediaPlayer::onVolumeUpdate() {
382    SL_LOGD("GenericMediaPlayer::onVolumeUpdate()");
383    if (mPlayer != 0) {
384        // use settings lock to read the volume settings
385        Mutex::Autolock _l(mSettingsLock);
386        mPlayer->setVolume(mAndroidAudioLevels.mFinalVolume[0],
387                mAndroidAudioLevels.mFinalVolume[1]);
388    }
389}
390
391
392void GenericMediaPlayer::onAttachAuxEffect(const sp<AMessage> &msg) {
393    SL_LOGD("GenericMediaPlayer::onAttachAuxEffect()");
394    int32_t effectId = 0;
395    if (msg->findInt32(WHATPARAM_ATTACHAUXEFFECT, &effectId)) {
396        if (mPlayer != 0) {
397            status_t status;
398            status = mPlayer->attachAuxEffect(effectId);
399            // attachAuxEffect returns a status but we have no way to report it back to app
400            (void) status;
401        }
402    }
403}
404
405
406void GenericMediaPlayer::onSetAuxEffectSendLevel(const sp<AMessage> &msg) {
407    SL_LOGD("GenericMediaPlayer::onSetAuxEffectSendLevel()");
408    float level = 0.0f;
409    if (msg->findFloat(WHATPARAM_SETAUXEFFECTSENDLEVEL, &level)) {
410        if (mPlayer != 0) {
411            status_t status;
412            status = mPlayer->setAuxEffectSendLevel(level);
413            // setAuxEffectSendLevel returns a status but we have no way to report it back to app
414            (void) status;
415        }
416    }
417}
418
419
420void GenericMediaPlayer::onBufferingUpdate(const sp<AMessage> &msg) {
421    int32_t fillLevel = 0;
422    if (msg->findInt32(WHATPARAM_BUFFERING_UPDATE, &fillLevel)) {
423        SL_LOGD("GenericMediaPlayer::onBufferingUpdate(fillLevel=%d)", fillLevel);
424
425        Mutex::Autolock _l(mSettingsLock);
426        mCacheFill = fillLevel;
427        // handle cache fill update
428        if (mCacheFill - mLastNotifiedCacheFill >= mCacheFillNotifThreshold) {
429            notifyCacheFill();
430        }
431        // handle prefetch status update
432        //   compute how much time ahead of position is buffered
433        int durationMsec, positionMsec = -1;
434        if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)
435                && (OK == mPlayer->getDuration(&durationMsec))
436                        && (OK == mPlayer->getCurrentPosition(&positionMsec))) {
437            if ((-1 != durationMsec) && (-1 != positionMsec)) {
438                // evaluate prefetch status based on buffer time thresholds
439                int64_t bufferedDurationMsec = (durationMsec * fillLevel / 100) - positionMsec;
440                CacheStatus_t newCacheStatus = mCacheStatus;
441                if (bufferedDurationMsec > DURATION_CACHED_HIGH_MS) {
442                    newCacheStatus = kStatusHigh;
443                } else if (bufferedDurationMsec > DURATION_CACHED_MED_MS) {
444                    newCacheStatus = kStatusEnough;
445                } else if (bufferedDurationMsec > DURATION_CACHED_LOW_MS) {
446                    newCacheStatus = kStatusIntermediate;
447                } else if (bufferedDurationMsec == 0) {
448                    newCacheStatus = kStatusEmpty;
449                } else {
450                    newCacheStatus = kStatusLow;
451                }
452
453                if (newCacheStatus != mCacheStatus) {
454                    mCacheStatus = newCacheStatus;
455                    notifyStatus();
456                }
457            }
458        }
459    } else {
460        SL_LOGV("GenericMediaPlayer::onBufferingUpdate(fillLevel=unknown)");
461    }
462}
463
464
465//--------------------------------------------------
466/**
467 * called from GenericMediaPlayer::onPrepare after the MediaPlayer mPlayer is prepared successfully
468 * pre-conditions:
469 *  mPlayer != 0
470 *  mPlayer is prepared successfully
471 */
472void GenericMediaPlayer::afterMediaPlayerPreparedSuccessfully() {
473    SL_LOGV("GenericMediaPlayer::afterMediaPlayerPrepared()");
474    assert(mPlayer != 0);
475    assert(mStateFlags & kFlagPrepared);
476    // Mark this player as prepared successfully, so safe to directly call getCurrentPosition
477    {
478        Mutex::Autolock _l(mPreparedPlayerLock);
479        assert(mPreparedPlayer == 0);
480        mPreparedPlayer = mPlayer;
481    }
482    // retrieve channel count
483    int32_t channelCount;
484    Parcel *reply = new Parcel();
485    status_t status = mPlayer->getParameter(KEY_PARAMETER_AUDIO_CHANNEL_COUNT, reply);
486    if (status == NO_ERROR) {
487        channelCount = reply->readInt32();
488    } else {
489        // FIXME MPEG-2 TS doesn't yet implement this key, so default to stereo
490        channelCount = 2;
491    }
492    if (UNKNOWN_NUMCHANNELS != channelCount) {
493        // now that we know the channel count, re-calculate the volumes
494        notify(PLAYEREVENT_CHANNEL_COUNT, channelCount, true /*async*/);
495    } else {
496        LOGW("channel count is still unknown after prepare");
497    }
498    delete reply;
499    // retrieve duration
500    {
501        int msec = 0;
502        if (OK == mPlayer->getDuration(&msec)) {
503            Mutex::Autolock _l(mSettingsLock);
504            mDurationMsec = msec;
505        }
506    }
507    // now that we have a MediaPlayer, set the looping flag
508    if (mStateFlags & kFlagLooping) {
509        (void) mPlayer->setLooping(1);
510    }
511    // when the MediaPlayer mPlayer is prepared, there is "sufficient data" in the playback buffers
512    // if the data source was local, and the buffers are considered full so we need to notify that
513    bool isLocalSource = true;
514    if (kDataLocatorUri == mDataLocatorType) {
515        isLocalSource = !isDistantProtocol(mDataLocator.uriRef);
516    }
517    if (isLocalSource) {
518        SL_LOGD("media player prepared on local source");
519        {
520            Mutex::Autolock _l(mSettingsLock);
521            mCacheStatus = kStatusHigh;
522            mCacheFill = 1000;
523            notifyStatus();
524            notifyCacheFill();
525        }
526    } else {
527        SL_LOGD("media player prepared on non-local source");
528    }
529}
530
531
532//--------------------------------------------------
533// If player is prepared successfully, set output parameter to that reference, otherwise NULL
534void GenericMediaPlayer::getPreparedPlayer(sp<IMediaPlayer> &preparedPlayer)
535{
536    Mutex::Autolock _l(mPreparedPlayerLock);
537    preparedPlayer = mPreparedPlayer;
538}
539
540} // namespace android
541