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