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