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