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