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
32 static 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?
isDistantProtocol(const char * uri)36 bool 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
46 namespace android {
47
48 //--------------------------------------------------------------------------------------------------
MediaPlayerNotificationClient(GenericMediaPlayer * gmp)49 MediaPlayerNotificationClient::MediaPlayerNotificationClient(GenericMediaPlayer* gmp) :
50 mGenericMediaPlayer(gmp),
51 mPlayerPrepared(PREPARE_NOT_STARTED)
52 {
53 SL_LOGV("MediaPlayerNotificationClient::MediaPlayerNotificationClient()");
54 }
55
~MediaPlayerNotificationClient()56 MediaPlayerNotificationClient::~MediaPlayerNotificationClient() {
57 SL_LOGV("MediaPlayerNotificationClient::~MediaPlayerNotificationClient()");
58 }
59
60 // Map a MEDIA_* enum to a string
media_to_string(int msg)61 static 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
notify(int msg,int ext1,int ext2,const Parcel * obj)82 void 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 //--------------------------------------------------
beforePrepare()156 void MediaPlayerNotificationClient::beforePrepare()
157 {
158 Mutex::Autolock _l(mLock);
159 assert(mPlayerPrepared == PREPARE_NOT_STARTED);
160 mPlayerPrepared = PREPARE_IN_PROGRESS;
161 }
162
163 //--------------------------------------------------
blockUntilPlayerPrepared()164 bool 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 //--------------------------------------------------------------------------------------------------
GenericMediaPlayer(const AudioPlayback_Parameters * params,bool hasVideo)176 GenericMediaPlayer::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
~GenericMediaPlayer()189 GenericMediaPlayer::~GenericMediaPlayer() {
190 SL_LOGD("GenericMediaPlayer::~GenericMediaPlayer()");
191 }
192
preDestroy()193 void 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
getPositionMsec(int * msec)223 void 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 //--------------------------------------------------
setVideoSurface(const sp<Surface> & surface)234 void 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
setVideoSurfaceTexture(const sp<ISurfaceTexture> & surfaceTexture)247 void 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
onPrepare()265 void 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
onPlay()291 void 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
onPause()300 void GenericMediaPlayer::onPause() {
301 SL_LOGD("GenericMediaPlayer::onPause()");
302 if (!(~mStateFlags & (kFlagPrepared | kFlagPlaying)) && (mPlayer != 0)) {
303 mPlayer->pause();
304 }
305 GenericPlayer::onPause();
306 }
307
308
onSeekComplete()309 void 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 */
onSeek(const sp<AMessage> & msg)329 void 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 }
358 }
359 }
360 }
361
362
onLoop(const sp<AMessage> & msg)363 void GenericMediaPlayer::onLoop(const sp<AMessage> &msg) {
364 SL_LOGV("GenericMediaPlayer::onLoop");
365 int32_t loop = 0;
366 if (msg->findInt32(WHATPARAM_LOOP_LOOPING, &loop)) {
367 if (loop) {
368 mStateFlags |= kFlagLooping;
369 } else {
370 mStateFlags &= ~kFlagLooping;
371 }
372 // if we have a MediaPlayer then tell it now, otherwise we'll tell it after it's created
373 if (mPlayer != 0) {
374 (void) mPlayer->setLooping(loop);
375 }
376 }
377 }
378
379
onVolumeUpdate()380 void GenericMediaPlayer::onVolumeUpdate() {
381 SL_LOGD("GenericMediaPlayer::onVolumeUpdate()");
382 // use settings lock to read the volume settings
383 Mutex::Autolock _l(mSettingsLock);
384 if (mPlayer != 0) {
385 mPlayer->setVolume(mAndroidAudioLevels.mFinalVolume[0],
386 mAndroidAudioLevels.mFinalVolume[1]);
387 }
388 }
389
390
onAttachAuxEffect(const sp<AMessage> & msg)391 void GenericMediaPlayer::onAttachAuxEffect(const sp<AMessage> &msg) {
392 SL_LOGD("GenericMediaPlayer::onAttachAuxEffect()");
393 int32_t effectId = 0;
394 if (msg->findInt32(WHATPARAM_ATTACHAUXEFFECT, &effectId)) {
395 if (mPlayer != 0) {
396 status_t status;
397 status = mPlayer->attachAuxEffect(effectId);
398 // attachAuxEffect returns a status but we have no way to report it back to app
399 (void) status;
400 }
401 }
402 }
403
404
onSetAuxEffectSendLevel(const sp<AMessage> & msg)405 void GenericMediaPlayer::onSetAuxEffectSendLevel(const sp<AMessage> &msg) {
406 SL_LOGD("GenericMediaPlayer::onSetAuxEffectSendLevel()");
407 float level = 0.0f;
408 if (msg->findFloat(WHATPARAM_SETAUXEFFECTSENDLEVEL, &level)) {
409 if (mPlayer != 0) {
410 status_t status;
411 status = mPlayer->setAuxEffectSendLevel(level);
412 // setAuxEffectSendLevel returns a status but we have no way to report it back to app
413 (void) status;
414 }
415 }
416 }
417
418
onBufferingUpdate(const sp<AMessage> & msg)419 void GenericMediaPlayer::onBufferingUpdate(const sp<AMessage> &msg) {
420 int32_t fillLevel = 0;
421 if (msg->findInt32(WHATPARAM_BUFFERING_UPDATE, &fillLevel)) {
422 SL_LOGD("GenericMediaPlayer::onBufferingUpdate(fillLevel=%d)", fillLevel);
423
424 Mutex::Autolock _l(mSettingsLock);
425 mCacheFill = fillLevel;
426 // handle cache fill update
427 if (mCacheFill - mLastNotifiedCacheFill >= mCacheFillNotifThreshold) {
428 notifyCacheFill();
429 }
430 // handle prefetch status update
431 // compute how much time ahead of position is buffered
432 int durationMsec, positionMsec = -1;
433 if ((mStateFlags & kFlagPrepared) && (mPlayer != 0)
434 && (OK == mPlayer->getDuration(&durationMsec))
435 && (OK == mPlayer->getCurrentPosition(&positionMsec))) {
436 if ((-1 != durationMsec) && (-1 != positionMsec)) {
437 // evaluate prefetch status based on buffer time thresholds
438 int64_t bufferedDurationMsec = (durationMsec * fillLevel / 100) - positionMsec;
439 CacheStatus_t newCacheStatus = mCacheStatus;
440 if (bufferedDurationMsec > DURATION_CACHED_HIGH_MS) {
441 newCacheStatus = kStatusHigh;
442 } else if (bufferedDurationMsec > DURATION_CACHED_MED_MS) {
443 newCacheStatus = kStatusEnough;
444 } else if (bufferedDurationMsec > DURATION_CACHED_LOW_MS) {
445 newCacheStatus = kStatusIntermediate;
446 } else if (bufferedDurationMsec == 0) {
447 newCacheStatus = kStatusEmpty;
448 } else {
449 newCacheStatus = kStatusLow;
450 }
451
452 if (newCacheStatus != mCacheStatus) {
453 mCacheStatus = newCacheStatus;
454 notifyStatus();
455 }
456 }
457 }
458 } else {
459 SL_LOGV("GenericMediaPlayer::onBufferingUpdate(fillLevel=unknown)");
460 }
461 }
462
463
464 //--------------------------------------------------
465 /**
466 * called from GenericMediaPlayer::onPrepare after the MediaPlayer mPlayer is prepared successfully
467 * pre-conditions:
468 * mPlayer != 0
469 * mPlayer is prepared successfully
470 */
afterMediaPlayerPreparedSuccessfully()471 void GenericMediaPlayer::afterMediaPlayerPreparedSuccessfully() {
472 SL_LOGV("GenericMediaPlayer::afterMediaPlayerPrepared()");
473 assert(mPlayer != 0);
474 assert(mStateFlags & kFlagPrepared);
475 // Mark this player as prepared successfully, so safe to directly call getCurrentPosition
476 {
477 Mutex::Autolock _l(mPreparedPlayerLock);
478 assert(mPreparedPlayer == 0);
479 mPreparedPlayer = mPlayer;
480 }
481 // retrieve channel count
482 int32_t channelCount;
483 Parcel *reply = new Parcel();
484 status_t status = mPlayer->getParameter(KEY_PARAMETER_AUDIO_CHANNEL_COUNT, reply);
485 if (status == NO_ERROR) {
486 channelCount = reply->readInt32();
487 } else {
488 // FIXME MPEG-2 TS doesn't yet implement this key, so default to stereo
489 channelCount = 2;
490 }
491 if (UNKNOWN_NUMCHANNELS != channelCount) {
492 // now that we know the channel count, re-calculate the volumes
493 notify(PLAYEREVENT_CHANNEL_COUNT, channelCount, true /*async*/);
494 } else {
495 LOGW("channel count is still unknown after prepare");
496 }
497 delete reply;
498 // retrieve duration
499 {
500 Mutex::Autolock _l(mSettingsLock);
501 int msec = 0;
502 if (OK == mPlayer->getDuration(&msec)) {
503 mDurationMsec = msec;
504 }
505 }
506 // now that we have a MediaPlayer, set the looping flag
507 if (mStateFlags & kFlagLooping) {
508 (void) mPlayer->setLooping(1);
509 }
510 // when the MediaPlayer mPlayer is prepared, there is "sufficient data" in the playback buffers
511 // if the data source was local, and the buffers are considered full so we need to notify that
512 bool isLocalSource = true;
513 if (kDataLocatorUri == mDataLocatorType) {
514 isLocalSource = !isDistantProtocol(mDataLocator.uriRef);
515 }
516 if (isLocalSource) {
517 SL_LOGD("media player prepared on local source");
518 {
519 Mutex::Autolock _l(mSettingsLock);
520 mCacheStatus = kStatusHigh;
521 mCacheFill = 1000;
522 notifyStatus();
523 notifyCacheFill();
524 }
525 } else {
526 SL_LOGD("media player prepared on non-local source");
527 }
528 }
529
530
531 //--------------------------------------------------
532 // If player is prepared successfully, set output parameter to that reference, otherwise NULL
getPreparedPlayer(sp<IMediaPlayer> & preparedPlayer)533 void GenericMediaPlayer::getPreparedPlayer(sp<IMediaPlayer> &preparedPlayer)
534 {
535 Mutex::Autolock _l(mPreparedPlayerLock);
536 preparedPlayer = mPreparedPlayer;
537 }
538
539 } // namespace android
540