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