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