1 /*
2 * Copyright (C) 2010 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 LOG_NDEBUG 0
18 #define LOG_TAG "NuPlayerDriver"
19 #include <inttypes.h>
20 #include <utils/Log.h>
21 #include <cutils/properties.h>
22
23 #include "NuPlayerDriver.h"
24
25 #include "NuPlayer.h"
26 #include "NuPlayerSource.h"
27
28 #include <media/stagefright/foundation/ADebug.h>
29 #include <media/stagefright/foundation/ALooper.h>
30 #include <media/stagefright/foundation/AUtils.h>
31 #include <media/stagefright/MetaData.h>
32 #include <media/stagefright/Utils.h>
33
34 namespace android {
35
NuPlayerDriver(pid_t pid)36 NuPlayerDriver::NuPlayerDriver(pid_t pid)
37 : mState(STATE_IDLE),
38 mIsAsyncPrepare(false),
39 mAsyncResult(UNKNOWN_ERROR),
40 mSetSurfaceInProgress(false),
41 mDurationUs(-1),
42 mPositionUs(-1),
43 mSeekInProgress(false),
44 mLooper(new ALooper),
45 mPlayerFlags(0),
46 mAtEOS(false),
47 mLooping(false),
48 mAutoLoop(false) {
49 ALOGV("NuPlayerDriver(%p)", this);
50 mLooper->setName("NuPlayerDriver Looper");
51
52 mLooper->start(
53 false, /* runOnCallingThread */
54 true, /* canCallJava */
55 PRIORITY_AUDIO);
56
57 mPlayer = new NuPlayer(pid);
58 mLooper->registerHandler(mPlayer);
59
60 mPlayer->setDriver(this);
61 }
62
~NuPlayerDriver()63 NuPlayerDriver::~NuPlayerDriver() {
64 ALOGV("~NuPlayerDriver(%p)", this);
65 mLooper->stop();
66 }
67
initCheck()68 status_t NuPlayerDriver::initCheck() {
69 return OK;
70 }
71
setUID(uid_t uid)72 status_t NuPlayerDriver::setUID(uid_t uid) {
73 mPlayer->setUID(uid);
74
75 return OK;
76 }
77
setDataSource(const sp<IMediaHTTPService> & httpService,const char * url,const KeyedVector<String8,String8> * headers)78 status_t NuPlayerDriver::setDataSource(
79 const sp<IMediaHTTPService> &httpService,
80 const char *url,
81 const KeyedVector<String8, String8> *headers) {
82 ALOGV("setDataSource(%p) url(%s)", this, uriDebugString(url, false).c_str());
83 Mutex::Autolock autoLock(mLock);
84
85 if (mState != STATE_IDLE) {
86 return INVALID_OPERATION;
87 }
88
89 mState = STATE_SET_DATASOURCE_PENDING;
90
91 mPlayer->setDataSourceAsync(httpService, url, headers);
92
93 while (mState == STATE_SET_DATASOURCE_PENDING) {
94 mCondition.wait(mLock);
95 }
96
97 return mAsyncResult;
98 }
99
setDataSource(int fd,int64_t offset,int64_t length)100 status_t NuPlayerDriver::setDataSource(int fd, int64_t offset, int64_t length) {
101 ALOGV("setDataSource(%p) file(%d)", this, fd);
102 Mutex::Autolock autoLock(mLock);
103
104 if (mState != STATE_IDLE) {
105 return INVALID_OPERATION;
106 }
107
108 mState = STATE_SET_DATASOURCE_PENDING;
109
110 mPlayer->setDataSourceAsync(fd, offset, length);
111
112 while (mState == STATE_SET_DATASOURCE_PENDING) {
113 mCondition.wait(mLock);
114 }
115
116 return mAsyncResult;
117 }
118
setDataSource(const sp<IStreamSource> & source)119 status_t NuPlayerDriver::setDataSource(const sp<IStreamSource> &source) {
120 ALOGV("setDataSource(%p) stream source", this);
121 Mutex::Autolock autoLock(mLock);
122
123 if (mState != STATE_IDLE) {
124 return INVALID_OPERATION;
125 }
126
127 mState = STATE_SET_DATASOURCE_PENDING;
128
129 mPlayer->setDataSourceAsync(source);
130
131 while (mState == STATE_SET_DATASOURCE_PENDING) {
132 mCondition.wait(mLock);
133 }
134
135 return mAsyncResult;
136 }
137
setDataSource(const sp<DataSource> & source)138 status_t NuPlayerDriver::setDataSource(const sp<DataSource> &source) {
139 ALOGV("setDataSource(%p) callback source", this);
140 Mutex::Autolock autoLock(mLock);
141
142 if (mState != STATE_IDLE) {
143 return INVALID_OPERATION;
144 }
145
146 mState = STATE_SET_DATASOURCE_PENDING;
147
148 mPlayer->setDataSourceAsync(source);
149
150 while (mState == STATE_SET_DATASOURCE_PENDING) {
151 mCondition.wait(mLock);
152 }
153
154 return mAsyncResult;
155 }
156
setVideoSurfaceTexture(const sp<IGraphicBufferProducer> & bufferProducer)157 status_t NuPlayerDriver::setVideoSurfaceTexture(
158 const sp<IGraphicBufferProducer> &bufferProducer) {
159 ALOGV("setVideoSurfaceTexture(%p)", this);
160 Mutex::Autolock autoLock(mLock);
161
162 if (mSetSurfaceInProgress) {
163 return INVALID_OPERATION;
164 }
165
166 switch (mState) {
167 case STATE_SET_DATASOURCE_PENDING:
168 case STATE_RESET_IN_PROGRESS:
169 return INVALID_OPERATION;
170
171 default:
172 break;
173 }
174
175 mSetSurfaceInProgress = true;
176
177 mPlayer->setVideoSurfaceTextureAsync(bufferProducer);
178
179 while (mSetSurfaceInProgress) {
180 mCondition.wait(mLock);
181 }
182
183 return OK;
184 }
185
prepare()186 status_t NuPlayerDriver::prepare() {
187 ALOGV("prepare(%p)", this);
188 Mutex::Autolock autoLock(mLock);
189 return prepare_l();
190 }
191
prepare_l()192 status_t NuPlayerDriver::prepare_l() {
193 switch (mState) {
194 case STATE_UNPREPARED:
195 mState = STATE_PREPARING;
196
197 // Make sure we're not posting any notifications, success or
198 // failure information is only communicated through our result
199 // code.
200 mIsAsyncPrepare = false;
201 mPlayer->prepareAsync();
202 while (mState == STATE_PREPARING) {
203 mCondition.wait(mLock);
204 }
205 return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR;
206 case STATE_STOPPED:
207 // this is really just paused. handle as seek to start
208 mAtEOS = false;
209 mState = STATE_STOPPED_AND_PREPARING;
210 mIsAsyncPrepare = false;
211 mPlayer->seekToAsync(0, true /* needNotify */);
212 while (mState == STATE_STOPPED_AND_PREPARING) {
213 mCondition.wait(mLock);
214 }
215 return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR;
216 default:
217 return INVALID_OPERATION;
218 };
219 }
220
prepareAsync()221 status_t NuPlayerDriver::prepareAsync() {
222 ALOGV("prepareAsync(%p)", this);
223 Mutex::Autolock autoLock(mLock);
224
225 switch (mState) {
226 case STATE_UNPREPARED:
227 mState = STATE_PREPARING;
228 mIsAsyncPrepare = true;
229 mPlayer->prepareAsync();
230 return OK;
231 case STATE_STOPPED:
232 // this is really just paused. handle as seek to start
233 mAtEOS = false;
234 mState = STATE_STOPPED_AND_PREPARING;
235 mIsAsyncPrepare = true;
236 mPlayer->seekToAsync(0, true /* needNotify */);
237 return OK;
238 default:
239 return INVALID_OPERATION;
240 };
241 }
242
start()243 status_t NuPlayerDriver::start() {
244 ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS);
245 Mutex::Autolock autoLock(mLock);
246 return start_l();
247 }
248
start_l()249 status_t NuPlayerDriver::start_l() {
250 switch (mState) {
251 case STATE_UNPREPARED:
252 {
253 status_t err = prepare_l();
254
255 if (err != OK) {
256 return err;
257 }
258
259 CHECK_EQ(mState, STATE_PREPARED);
260
261 // fall through
262 }
263
264 case STATE_PAUSED:
265 case STATE_STOPPED_AND_PREPARED:
266 case STATE_PREPARED:
267 {
268 mPlayer->start();
269
270 // fall through
271 }
272
273 case STATE_RUNNING:
274 {
275 if (mAtEOS) {
276 mPlayer->seekToAsync(0);
277 mAtEOS = false;
278 mPositionUs = -1;
279 }
280 break;
281 }
282
283 default:
284 return INVALID_OPERATION;
285 }
286
287 mState = STATE_RUNNING;
288
289 return OK;
290 }
291
stop()292 status_t NuPlayerDriver::stop() {
293 ALOGD("stop(%p)", this);
294 Mutex::Autolock autoLock(mLock);
295
296 switch (mState) {
297 case STATE_RUNNING:
298 mPlayer->pause();
299 // fall through
300
301 case STATE_PAUSED:
302 mState = STATE_STOPPED;
303 notifyListener_l(MEDIA_STOPPED);
304 break;
305
306 case STATE_PREPARED:
307 case STATE_STOPPED:
308 case STATE_STOPPED_AND_PREPARING:
309 case STATE_STOPPED_AND_PREPARED:
310 mState = STATE_STOPPED;
311 break;
312
313 default:
314 return INVALID_OPERATION;
315 }
316
317 return OK;
318 }
319
pause()320 status_t NuPlayerDriver::pause() {
321 ALOGD("pause(%p)", this);
322 // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear
323 // down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
324 // current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
325 // getCurrentPosition here.
326 int unused;
327 getCurrentPosition(&unused);
328
329 Mutex::Autolock autoLock(mLock);
330
331 switch (mState) {
332 case STATE_PAUSED:
333 case STATE_PREPARED:
334 return OK;
335
336 case STATE_RUNNING:
337 mState = STATE_PAUSED;
338 notifyListener_l(MEDIA_PAUSED);
339 mPlayer->pause();
340 break;
341
342 default:
343 return INVALID_OPERATION;
344 }
345
346 return OK;
347 }
348
isPlaying()349 bool NuPlayerDriver::isPlaying() {
350 return mState == STATE_RUNNING && !mAtEOS;
351 }
352
setPlaybackSettings(const AudioPlaybackRate & rate)353 status_t NuPlayerDriver::setPlaybackSettings(const AudioPlaybackRate &rate) {
354 status_t err = mPlayer->setPlaybackSettings(rate);
355 if (err == OK) {
356 // try to update position
357 int unused;
358 getCurrentPosition(&unused);
359 Mutex::Autolock autoLock(mLock);
360 if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
361 mState = STATE_PAUSED;
362 notifyListener_l(MEDIA_PAUSED);
363 } else if (rate.mSpeed != 0.f
364 && (mState == STATE_PAUSED
365 || mState == STATE_STOPPED_AND_PREPARED
366 || mState == STATE_PREPARED)) {
367 err = start_l();
368 }
369 }
370 return err;
371 }
372
getPlaybackSettings(AudioPlaybackRate * rate)373 status_t NuPlayerDriver::getPlaybackSettings(AudioPlaybackRate *rate) {
374 return mPlayer->getPlaybackSettings(rate);
375 }
376
setSyncSettings(const AVSyncSettings & sync,float videoFpsHint)377 status_t NuPlayerDriver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
378 return mPlayer->setSyncSettings(sync, videoFpsHint);
379 }
380
getSyncSettings(AVSyncSettings * sync,float * videoFps)381 status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
382 return mPlayer->getSyncSettings(sync, videoFps);
383 }
384
seekTo(int msec)385 status_t NuPlayerDriver::seekTo(int msec) {
386 ALOGD("seekTo(%p) %d ms at state %d", this, msec, mState);
387 Mutex::Autolock autoLock(mLock);
388
389 int64_t seekTimeUs = msec * 1000ll;
390
391 switch (mState) {
392 case STATE_PREPARED:
393 case STATE_STOPPED_AND_PREPARED:
394 case STATE_PAUSED:
395 case STATE_RUNNING:
396 {
397 mAtEOS = false;
398 mSeekInProgress = true;
399 // seeks can take a while, so we essentially paused
400 notifyListener_l(MEDIA_PAUSED);
401 mPlayer->seekToAsync(seekTimeUs, true /* needNotify */);
402 break;
403 }
404
405 default:
406 return INVALID_OPERATION;
407 }
408
409 mPositionUs = seekTimeUs;
410 return OK;
411 }
412
getCurrentPosition(int * msec)413 status_t NuPlayerDriver::getCurrentPosition(int *msec) {
414 int64_t tempUs = 0;
415 {
416 Mutex::Autolock autoLock(mLock);
417 if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
418 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
419 *msec = (int)divRound(tempUs, (int64_t)(1000));
420 return OK;
421 }
422 }
423
424 status_t ret = mPlayer->getCurrentPosition(&tempUs);
425
426 Mutex::Autolock autoLock(mLock);
427 // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which
428 // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a
429 // position value that's different the seek to position.
430 if (ret != OK) {
431 tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
432 } else {
433 mPositionUs = tempUs;
434 }
435 *msec = (int)divRound(tempUs, (int64_t)(1000));
436 return OK;
437 }
438
getDuration(int * msec)439 status_t NuPlayerDriver::getDuration(int *msec) {
440 Mutex::Autolock autoLock(mLock);
441
442 if (mDurationUs < 0) {
443 return UNKNOWN_ERROR;
444 }
445
446 *msec = (mDurationUs + 500ll) / 1000;
447
448 return OK;
449 }
450
reset()451 status_t NuPlayerDriver::reset() {
452 ALOGD("reset(%p) at state %d", this, mState);
453 Mutex::Autolock autoLock(mLock);
454
455 switch (mState) {
456 case STATE_IDLE:
457 return OK;
458
459 case STATE_SET_DATASOURCE_PENDING:
460 case STATE_RESET_IN_PROGRESS:
461 return INVALID_OPERATION;
462
463 case STATE_PREPARING:
464 {
465 CHECK(mIsAsyncPrepare);
466
467 notifyListener_l(MEDIA_PREPARED);
468 break;
469 }
470
471 default:
472 break;
473 }
474
475 if (mState != STATE_STOPPED) {
476 notifyListener_l(MEDIA_STOPPED);
477 }
478
479 char value[PROPERTY_VALUE_MAX];
480 if (property_get("persist.debug.sf.stats", value, NULL) &&
481 (!strcmp("1", value) || !strcasecmp("true", value))) {
482 Vector<String16> args;
483 dump(-1, args);
484 }
485
486 mState = STATE_RESET_IN_PROGRESS;
487 mPlayer->resetAsync();
488
489 while (mState == STATE_RESET_IN_PROGRESS) {
490 mCondition.wait(mLock);
491 }
492
493 mDurationUs = -1;
494 mPositionUs = -1;
495 mLooping = false;
496
497 return OK;
498 }
499
setLooping(int loop)500 status_t NuPlayerDriver::setLooping(int loop) {
501 mLooping = loop != 0;
502 return OK;
503 }
504
playerType()505 player_type NuPlayerDriver::playerType() {
506 return NU_PLAYER;
507 }
508
invoke(const Parcel & request,Parcel * reply)509 status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
510 if (reply == NULL) {
511 ALOGE("reply is a NULL pointer");
512 return BAD_VALUE;
513 }
514
515 int32_t methodId;
516 status_t ret = request.readInt32(&methodId);
517 if (ret != OK) {
518 ALOGE("Failed to retrieve the requested method to invoke");
519 return ret;
520 }
521
522 switch (methodId) {
523 case INVOKE_ID_SET_VIDEO_SCALING_MODE:
524 {
525 int mode = request.readInt32();
526 return mPlayer->setVideoScalingMode(mode);
527 }
528
529 case INVOKE_ID_GET_TRACK_INFO:
530 {
531 return mPlayer->getTrackInfo(reply);
532 }
533
534 case INVOKE_ID_SELECT_TRACK:
535 {
536 int trackIndex = request.readInt32();
537 int msec = 0;
538 // getCurrentPosition should always return OK
539 getCurrentPosition(&msec);
540 return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
541 }
542
543 case INVOKE_ID_UNSELECT_TRACK:
544 {
545 int trackIndex = request.readInt32();
546 return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
547 }
548
549 case INVOKE_ID_GET_SELECTED_TRACK:
550 {
551 int32_t type = request.readInt32();
552 return mPlayer->getSelectedTrack(type, reply);
553 }
554
555 default:
556 {
557 return INVALID_OPERATION;
558 }
559 }
560 }
561
setAudioSink(const sp<AudioSink> & audioSink)562 void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
563 mPlayer->setAudioSink(audioSink);
564 mAudioSink = audioSink;
565 }
566
setParameter(int,const Parcel &)567 status_t NuPlayerDriver::setParameter(
568 int /* key */, const Parcel & /* request */) {
569 return INVALID_OPERATION;
570 }
571
getParameter(int,Parcel *)572 status_t NuPlayerDriver::getParameter(int /* key */, Parcel * /* reply */) {
573 return INVALID_OPERATION;
574 }
575
getMetadata(const media::Metadata::Filter &,Parcel * records)576 status_t NuPlayerDriver::getMetadata(
577 const media::Metadata::Filter& /* ids */, Parcel *records) {
578 Mutex::Autolock autoLock(mLock);
579
580 using media::Metadata;
581
582 Metadata meta(records);
583
584 meta.appendBool(
585 Metadata::kPauseAvailable,
586 mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE);
587
588 meta.appendBool(
589 Metadata::kSeekBackwardAvailable,
590 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD);
591
592 meta.appendBool(
593 Metadata::kSeekForwardAvailable,
594 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD);
595
596 meta.appendBool(
597 Metadata::kSeekAvailable,
598 mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK);
599
600 return OK;
601 }
602
notifyResetComplete()603 void NuPlayerDriver::notifyResetComplete() {
604 ALOGD("notifyResetComplete(%p)", this);
605 Mutex::Autolock autoLock(mLock);
606
607 CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
608 mState = STATE_IDLE;
609 mCondition.broadcast();
610 }
611
notifySetSurfaceComplete()612 void NuPlayerDriver::notifySetSurfaceComplete() {
613 ALOGV("notifySetSurfaceComplete(%p)", this);
614 Mutex::Autolock autoLock(mLock);
615
616 CHECK(mSetSurfaceInProgress);
617 mSetSurfaceInProgress = false;
618
619 mCondition.broadcast();
620 }
621
notifyDuration(int64_t durationUs)622 void NuPlayerDriver::notifyDuration(int64_t durationUs) {
623 Mutex::Autolock autoLock(mLock);
624 mDurationUs = durationUs;
625 }
626
notifySeekComplete()627 void NuPlayerDriver::notifySeekComplete() {
628 ALOGV("notifySeekComplete(%p)", this);
629 Mutex::Autolock autoLock(mLock);
630 mSeekInProgress = false;
631 notifySeekComplete_l();
632 }
633
notifySeekComplete_l()634 void NuPlayerDriver::notifySeekComplete_l() {
635 bool wasSeeking = true;
636 if (mState == STATE_STOPPED_AND_PREPARING) {
637 wasSeeking = false;
638 mState = STATE_STOPPED_AND_PREPARED;
639 mCondition.broadcast();
640 if (!mIsAsyncPrepare) {
641 // if we are preparing synchronously, no need to notify listener
642 return;
643 }
644 } else if (mState == STATE_STOPPED) {
645 // no need to notify listener
646 return;
647 }
648 notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
649 }
650
dump(int fd,const Vector<String16> &) const651 status_t NuPlayerDriver::dump(
652 int fd, const Vector<String16> & /* args */) const {
653
654 Vector<sp<AMessage> > trackStats;
655 mPlayer->getStats(&trackStats);
656
657 AString logString(" NuPlayer\n");
658 char buf[256] = {0};
659
660 for (size_t i = 0; i < trackStats.size(); ++i) {
661 const sp<AMessage> &stats = trackStats.itemAt(i);
662
663 AString mime;
664 if (stats->findString("mime", &mime)) {
665 snprintf(buf, sizeof(buf), " mime(%s)\n", mime.c_str());
666 logString.append(buf);
667 }
668
669 AString name;
670 if (stats->findString("component-name", &name)) {
671 snprintf(buf, sizeof(buf), " decoder(%s)\n", name.c_str());
672 logString.append(buf);
673 }
674
675 if (mime.startsWith("video/")) {
676 int32_t width, height;
677 if (stats->findInt32("width", &width)
678 && stats->findInt32("height", &height)) {
679 snprintf(buf, sizeof(buf), " resolution(%d x %d)\n", width, height);
680 logString.append(buf);
681 }
682
683 int64_t numFramesTotal = 0;
684 int64_t numFramesDropped = 0;
685
686 stats->findInt64("frames-total", &numFramesTotal);
687 stats->findInt64("frames-dropped-output", &numFramesDropped);
688 snprintf(buf, sizeof(buf), " numFramesTotal(%lld), numFramesDropped(%lld), "
689 "percentageDropped(%.2f%%)\n",
690 (long long)numFramesTotal,
691 (long long)numFramesDropped,
692 numFramesTotal == 0
693 ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal);
694 logString.append(buf);
695 }
696 }
697
698 ALOGI("%s", logString.c_str());
699
700 if (fd >= 0) {
701 FILE *out = fdopen(dup(fd), "w");
702 fprintf(out, "%s", logString.c_str());
703 fclose(out);
704 out = NULL;
705 }
706
707 return OK;
708 }
709
notifyListener(int msg,int ext1,int ext2,const Parcel * in)710 void NuPlayerDriver::notifyListener(
711 int msg, int ext1, int ext2, const Parcel *in) {
712 Mutex::Autolock autoLock(mLock);
713 notifyListener_l(msg, ext1, ext2, in);
714 }
715
notifyListener_l(int msg,int ext1,int ext2,const Parcel * in)716 void NuPlayerDriver::notifyListener_l(
717 int msg, int ext1, int ext2, const Parcel *in) {
718 ALOGD("notifyListener_l(%p), (%d, %d, %d), loop setting(%d, %d)",
719 this, msg, ext1, ext2, mAutoLoop, mLooping);
720 switch (msg) {
721 case MEDIA_PLAYBACK_COMPLETE:
722 {
723 if (mState != STATE_RESET_IN_PROGRESS) {
724 if (mAutoLoop) {
725 audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
726 if (mAudioSink != NULL) {
727 streamType = mAudioSink->getAudioStreamType();
728 }
729 if (streamType == AUDIO_STREAM_NOTIFICATION) {
730 ALOGW("disabling auto-loop for notification");
731 mAutoLoop = false;
732 }
733 }
734 if (mLooping || mAutoLoop) {
735 mPlayer->seekToAsync(0);
736 if (mAudioSink != NULL) {
737 // The renderer has stopped the sink at the end in order to play out
738 // the last little bit of audio. If we're looping, we need to restart it.
739 mAudioSink->start();
740 }
741 // don't send completion event when looping
742 return;
743 }
744
745 mPlayer->pause();
746 mState = STATE_PAUSED;
747 }
748 // fall through
749 }
750
751 case MEDIA_ERROR:
752 {
753 mAtEOS = true;
754 break;
755 }
756
757 default:
758 break;
759 }
760
761 mLock.unlock();
762 sendEvent(msg, ext1, ext2, in);
763 mLock.lock();
764 }
765
notifySetDataSourceCompleted(status_t err)766 void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
767 Mutex::Autolock autoLock(mLock);
768
769 CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
770
771 mAsyncResult = err;
772 mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
773 mCondition.broadcast();
774 }
775
notifyPrepareCompleted(status_t err)776 void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
777 Mutex::Autolock autoLock(mLock);
778
779 if (mState != STATE_PREPARING) {
780 // We were preparing asynchronously when the client called
781 // reset(), we sent a premature "prepared" notification and
782 // then initiated the reset. This notification is stale.
783 CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
784 return;
785 }
786
787 CHECK_EQ(mState, STATE_PREPARING);
788
789 mAsyncResult = err;
790
791 if (err == OK) {
792 // update state before notifying client, so that if client calls back into NuPlayerDriver
793 // in response, NuPlayerDriver has the right state
794 mState = STATE_PREPARED;
795 if (mIsAsyncPrepare) {
796 notifyListener_l(MEDIA_PREPARED);
797 }
798 } else {
799 mState = STATE_UNPREPARED;
800 if (mIsAsyncPrepare) {
801 notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
802 }
803 }
804
805 sp<MetaData> meta = mPlayer->getFileMeta();
806 int32_t loop;
807 if (meta != NULL
808 && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
809 mAutoLoop = true;
810 }
811
812 mCondition.broadcast();
813 }
814
notifyFlagsChanged(uint32_t flags)815 void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) {
816 Mutex::Autolock autoLock(mLock);
817
818 mPlayerFlags = flags;
819 }
820
821 } // namespace android
822