NuPlayerDriver.cpp revision 03a5653c8f25c0e90f362e252be48f95703b18fe
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    ALOGD("pause(%p)", this);
334    // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear
335    // down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
336    // current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
337    // getCurrentPosition here.
338    int unused;
339    getCurrentPosition(&unused);
340
341    Mutex::Autolock autoLock(mLock);
342
343    switch (mState) {
344        case STATE_PAUSED:
345        case STATE_PREPARED:
346            return OK;
347
348        case STATE_RUNNING:
349            mState = STATE_PAUSED;
350            notifyListener_l(MEDIA_PAUSED);
351            mPlayer->pause();
352            break;
353
354        default:
355            return INVALID_OPERATION;
356    }
357
358    return OK;
359}
360
361bool NuPlayerDriver::isPlaying() {
362    return mState == STATE_RUNNING && !mAtEOS;
363}
364
365status_t NuPlayerDriver::setPlaybackSettings(const AudioPlaybackRate &rate) {
366    status_t err = mPlayer->setPlaybackSettings(rate);
367    if (err == OK) {
368        // try to update position
369        int unused;
370        getCurrentPosition(&unused);
371        Mutex::Autolock autoLock(mLock);
372        if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
373            mState = STATE_PAUSED;
374            notifyListener_l(MEDIA_PAUSED);
375        } else if (rate.mSpeed != 0.f && mState == STATE_PAUSED) {
376            mState = STATE_RUNNING;
377        }
378    }
379    return err;
380}
381
382status_t NuPlayerDriver::getPlaybackSettings(AudioPlaybackRate *rate) {
383    return mPlayer->getPlaybackSettings(rate);
384}
385
386status_t NuPlayerDriver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
387    return mPlayer->setSyncSettings(sync, videoFpsHint);
388}
389
390status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
391    return mPlayer->getSyncSettings(sync, videoFps);
392}
393
394status_t NuPlayerDriver::seekTo(int msec) {
395    ALOGD("seekTo(%p) %d ms at state %d", this, msec, mState);
396    Mutex::Autolock autoLock(mLock);
397
398    int64_t seekTimeUs = msec * 1000ll;
399
400    switch (mState) {
401        case STATE_PREPARED:
402        case STATE_STOPPED_AND_PREPARED:
403        case STATE_PAUSED:
404            mStartupSeekTimeUs = seekTimeUs;
405            // fall through.
406        case STATE_RUNNING:
407        {
408            mAtEOS = false;
409            mSeekInProgress = true;
410            // seeks can take a while, so we essentially paused
411            notifyListener_l(MEDIA_PAUSED);
412            mPlayer->seekToAsync(seekTimeUs, true /* needNotify */);
413            break;
414        }
415
416        default:
417            return INVALID_OPERATION;
418    }
419
420    mPositionUs = seekTimeUs;
421    return OK;
422}
423
424status_t NuPlayerDriver::getCurrentPosition(int *msec) {
425    int64_t tempUs = 0;
426    {
427        Mutex::Autolock autoLock(mLock);
428        if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
429            tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
430            *msec = (int)divRound(tempUs, (int64_t)(1000));
431            return OK;
432        }
433    }
434
435    status_t ret = mPlayer->getCurrentPosition(&tempUs);
436
437    Mutex::Autolock autoLock(mLock);
438    // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which
439    // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a
440    // position value that's different the seek to position.
441    if (ret != OK) {
442        tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
443    } else {
444        mPositionUs = tempUs;
445    }
446    *msec = (int)divRound(tempUs, (int64_t)(1000));
447    return OK;
448}
449
450status_t NuPlayerDriver::getDuration(int *msec) {
451    Mutex::Autolock autoLock(mLock);
452
453    if (mDurationUs < 0) {
454        return UNKNOWN_ERROR;
455    }
456
457    *msec = (mDurationUs + 500ll) / 1000;
458
459    return OK;
460}
461
462status_t NuPlayerDriver::reset() {
463    ALOGD("reset(%p) at state %d", this, mState);
464    Mutex::Autolock autoLock(mLock);
465
466    switch (mState) {
467        case STATE_IDLE:
468            return OK;
469
470        case STATE_SET_DATASOURCE_PENDING:
471        case STATE_RESET_IN_PROGRESS:
472            return INVALID_OPERATION;
473
474        case STATE_PREPARING:
475        {
476            CHECK(mIsAsyncPrepare);
477
478            notifyListener_l(MEDIA_PREPARED);
479            break;
480        }
481
482        default:
483            break;
484    }
485
486    if (mState != STATE_STOPPED) {
487        notifyListener_l(MEDIA_STOPPED);
488    }
489
490    char value[PROPERTY_VALUE_MAX];
491    if (property_get("persist.debug.sf.stats", value, NULL) &&
492            (!strcmp("1", value) || !strcasecmp("true", value))) {
493        Vector<String16> args;
494        dump(-1, args);
495    }
496
497    mState = STATE_RESET_IN_PROGRESS;
498    mPlayer->resetAsync();
499
500    while (mState == STATE_RESET_IN_PROGRESS) {
501        mCondition.wait(mLock);
502    }
503
504    mDurationUs = -1;
505    mPositionUs = -1;
506    mStartupSeekTimeUs = -1;
507    mLooping = false;
508
509    return OK;
510}
511
512status_t NuPlayerDriver::setLooping(int loop) {
513    mLooping = loop != 0;
514    return OK;
515}
516
517player_type NuPlayerDriver::playerType() {
518    return NU_PLAYER;
519}
520
521status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
522    if (reply == NULL) {
523        ALOGE("reply is a NULL pointer");
524        return BAD_VALUE;
525    }
526
527    int32_t methodId;
528    status_t ret = request.readInt32(&methodId);
529    if (ret != OK) {
530        ALOGE("Failed to retrieve the requested method to invoke");
531        return ret;
532    }
533
534    switch (methodId) {
535        case INVOKE_ID_SET_VIDEO_SCALING_MODE:
536        {
537            int mode = request.readInt32();
538            return mPlayer->setVideoScalingMode(mode);
539        }
540
541        case INVOKE_ID_GET_TRACK_INFO:
542        {
543            return mPlayer->getTrackInfo(reply);
544        }
545
546        case INVOKE_ID_SELECT_TRACK:
547        {
548            int trackIndex = request.readInt32();
549            int msec = 0;
550            // getCurrentPosition should always return OK
551            getCurrentPosition(&msec);
552            return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
553        }
554
555        case INVOKE_ID_UNSELECT_TRACK:
556        {
557            int trackIndex = request.readInt32();
558            return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
559        }
560
561        case INVOKE_ID_GET_SELECTED_TRACK:
562        {
563            int32_t type = request.readInt32();
564            return mPlayer->getSelectedTrack(type, reply);
565        }
566
567        default:
568        {
569            return INVALID_OPERATION;
570        }
571    }
572}
573
574void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
575    mPlayer->setAudioSink(audioSink);
576    mAudioSink = audioSink;
577}
578
579status_t NuPlayerDriver::setParameter(
580        int /* key */, const Parcel & /* request */) {
581    return INVALID_OPERATION;
582}
583
584status_t NuPlayerDriver::getParameter(int /* key */, Parcel * /* reply */) {
585    return INVALID_OPERATION;
586}
587
588status_t NuPlayerDriver::getMetadata(
589        const media::Metadata::Filter& /* ids */, Parcel *records) {
590    Mutex::Autolock autoLock(mLock);
591
592    using media::Metadata;
593
594    Metadata meta(records);
595
596    meta.appendBool(
597            Metadata::kPauseAvailable,
598            mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE);
599
600    meta.appendBool(
601            Metadata::kSeekBackwardAvailable,
602            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD);
603
604    meta.appendBool(
605            Metadata::kSeekForwardAvailable,
606            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD);
607
608    meta.appendBool(
609            Metadata::kSeekAvailable,
610            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK);
611
612    return OK;
613}
614
615void NuPlayerDriver::notifyResetComplete() {
616    ALOGD("notifyResetComplete(%p)", this);
617    Mutex::Autolock autoLock(mLock);
618
619    CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
620    mState = STATE_IDLE;
621    mCondition.broadcast();
622}
623
624void NuPlayerDriver::notifySetSurfaceComplete() {
625    ALOGV("notifySetSurfaceComplete(%p)", this);
626    Mutex::Autolock autoLock(mLock);
627
628    CHECK(mSetSurfaceInProgress);
629    mSetSurfaceInProgress = false;
630
631    mCondition.broadcast();
632}
633
634void NuPlayerDriver::notifyDuration(int64_t durationUs) {
635    Mutex::Autolock autoLock(mLock);
636    mDurationUs = durationUs;
637}
638
639void NuPlayerDriver::notifySeekComplete() {
640    ALOGV("notifySeekComplete(%p)", this);
641    Mutex::Autolock autoLock(mLock);
642    mSeekInProgress = false;
643    notifySeekComplete_l();
644}
645
646void NuPlayerDriver::notifySeekComplete_l() {
647    bool wasSeeking = true;
648    if (mState == STATE_STOPPED_AND_PREPARING) {
649        wasSeeking = false;
650        mState = STATE_STOPPED_AND_PREPARED;
651        mCondition.broadcast();
652        if (!mIsAsyncPrepare) {
653            // if we are preparing synchronously, no need to notify listener
654            return;
655        }
656    } else if (mState == STATE_STOPPED) {
657        // no need to notify listener
658        return;
659    }
660    notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
661}
662
663status_t NuPlayerDriver::dump(
664        int fd, const Vector<String16> & /* args */) const {
665
666    Vector<sp<AMessage> > trackStats;
667    mPlayer->getStats(&trackStats);
668
669    AString logString(" NuPlayer\n");
670    char buf[256] = {0};
671
672    for (size_t i = 0; i < trackStats.size(); ++i) {
673        const sp<AMessage> &stats = trackStats.itemAt(i);
674
675        AString mime;
676        if (stats->findString("mime", &mime)) {
677            snprintf(buf, sizeof(buf), "  mime(%s)\n", mime.c_str());
678            logString.append(buf);
679        }
680
681        AString name;
682        if (stats->findString("component-name", &name)) {
683            snprintf(buf, sizeof(buf), "    decoder(%s)\n", name.c_str());
684            logString.append(buf);
685        }
686
687        if (mime.startsWith("video/")) {
688            int32_t width, height;
689            if (stats->findInt32("width", &width)
690                    && stats->findInt32("height", &height)) {
691                snprintf(buf, sizeof(buf), "    resolution(%d x %d)\n", width, height);
692                logString.append(buf);
693            }
694
695            int64_t numFramesTotal = 0;
696            int64_t numFramesDropped = 0;
697
698            stats->findInt64("frames-total", &numFramesTotal);
699            stats->findInt64("frames-dropped-output", &numFramesDropped);
700            snprintf(buf, sizeof(buf), "    numFramesTotal(%lld), numFramesDropped(%lld), "
701                     "percentageDropped(%.2f%%)\n",
702                     (long long)numFramesTotal,
703                     (long long)numFramesDropped,
704                     numFramesTotal == 0
705                            ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal);
706            logString.append(buf);
707        }
708    }
709
710    ALOGI("%s", logString.c_str());
711
712    if (fd >= 0) {
713        FILE *out = fdopen(dup(fd), "w");
714        fprintf(out, "%s", logString.c_str());
715        fclose(out);
716        out = NULL;
717    }
718
719    return OK;
720}
721
722void NuPlayerDriver::notifyListener(
723        int msg, int ext1, int ext2, const Parcel *in) {
724    Mutex::Autolock autoLock(mLock);
725    notifyListener_l(msg, ext1, ext2, in);
726}
727
728void NuPlayerDriver::notifyListener_l(
729        int msg, int ext1, int ext2, const Parcel *in) {
730    ALOGD("notifyListener_l(%p), (%d, %d, %d), loop setting(%d, %d)",
731            this, msg, ext1, ext2, mAutoLoop, mLooping);
732    switch (msg) {
733        case MEDIA_PLAYBACK_COMPLETE:
734        {
735            if (mState != STATE_RESET_IN_PROGRESS) {
736                if (mAutoLoop) {
737                    audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
738                    if (mAudioSink != NULL) {
739                        streamType = mAudioSink->getAudioStreamType();
740                    }
741                    if (streamType == AUDIO_STREAM_NOTIFICATION) {
742                        ALOGW("disabling auto-loop for notification");
743                        mAutoLoop = false;
744                    }
745                }
746                if (mLooping || mAutoLoop) {
747                    mPlayer->seekToAsync(0);
748                    if (mAudioSink != NULL) {
749                        // The renderer has stopped the sink at the end in order to play out
750                        // the last little bit of audio. If we're looping, we need to restart it.
751                        mAudioSink->start();
752                    }
753                    // don't send completion event when looping
754                    return;
755                }
756
757                mPlayer->pause();
758                mState = STATE_PAUSED;
759            }
760            // fall through
761        }
762
763        case MEDIA_ERROR:
764        {
765            mAtEOS = true;
766            break;
767        }
768
769        default:
770            break;
771    }
772
773    mLock.unlock();
774    sendEvent(msg, ext1, ext2, in);
775    mLock.lock();
776}
777
778void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
779    Mutex::Autolock autoLock(mLock);
780
781    CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
782
783    mAsyncResult = err;
784    mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
785    mCondition.broadcast();
786}
787
788void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
789    Mutex::Autolock autoLock(mLock);
790
791    if (mState != STATE_PREPARING) {
792        // We were preparing asynchronously when the client called
793        // reset(), we sent a premature "prepared" notification and
794        // then initiated the reset. This notification is stale.
795        CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
796        return;
797    }
798
799    CHECK_EQ(mState, STATE_PREPARING);
800
801    mAsyncResult = err;
802
803    if (err == OK) {
804        // update state before notifying client, so that if client calls back into NuPlayerDriver
805        // in response, NuPlayerDriver has the right state
806        mState = STATE_PREPARED;
807        if (mIsAsyncPrepare) {
808            notifyListener_l(MEDIA_PREPARED);
809        }
810    } else {
811        mState = STATE_UNPREPARED;
812        if (mIsAsyncPrepare) {
813            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
814        }
815    }
816
817    sp<MetaData> meta = mPlayer->getFileMeta();
818    int32_t loop;
819    if (meta != NULL
820            && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
821        mAutoLoop = true;
822    }
823
824    mCondition.broadcast();
825}
826
827void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) {
828    Mutex::Autolock autoLock(mLock);
829
830    mPlayerFlags = flags;
831}
832
833}  // namespace android
834