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