NuPlayerDriver.cpp revision cefac14261a32fb856b0d1ab31541787112e306e
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::getDefaultBufferingSettings(BufferingSettings* buffering) {
208    ALOGV("getDefaultBufferingSettings(%p)", this);
209    Mutex::Autolock autoLock(mLock);
210    if (mState == STATE_IDLE) {
211        return INVALID_OPERATION;
212    }
213
214    return mPlayer->getDefaultBufferingSettings(buffering);
215}
216
217status_t NuPlayerDriver::setBufferingSettings(const BufferingSettings& buffering) {
218    ALOGV("setBufferingSettings(%p)", this);
219    Mutex::Autolock autoLock(mLock);
220    if (mState == STATE_IDLE) {
221        return INVALID_OPERATION;
222    }
223
224    return mPlayer->setBufferingSettings(buffering);
225}
226
227status_t NuPlayerDriver::prepare() {
228    ALOGV("prepare(%p)", this);
229    Mutex::Autolock autoLock(mLock);
230    return prepare_l();
231}
232
233status_t NuPlayerDriver::prepare_l() {
234    switch (mState) {
235        case STATE_UNPREPARED:
236            mState = STATE_PREPARING;
237
238            // Make sure we're not posting any notifications, success or
239            // failure information is only communicated through our result
240            // code.
241            mIsAsyncPrepare = false;
242            mPlayer->prepareAsync();
243            while (mState == STATE_PREPARING) {
244                mCondition.wait(mLock);
245            }
246            return (mState == STATE_PREPARED) ? OK : UNKNOWN_ERROR;
247        case STATE_STOPPED:
248            // this is really just paused. handle as seek to start
249            mAtEOS = false;
250            mState = STATE_STOPPED_AND_PREPARING;
251            mIsAsyncPrepare = false;
252            mPlayer->seekToAsync(0, MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */,
253                    true /* needNotify */);
254            while (mState == STATE_STOPPED_AND_PREPARING) {
255                mCondition.wait(mLock);
256            }
257            return (mState == STATE_STOPPED_AND_PREPARED) ? OK : UNKNOWN_ERROR;
258        default:
259            return INVALID_OPERATION;
260    };
261}
262
263status_t NuPlayerDriver::prepareAsync() {
264    ALOGV("prepareAsync(%p)", this);
265    Mutex::Autolock autoLock(mLock);
266
267    switch (mState) {
268        case STATE_UNPREPARED:
269            mState = STATE_PREPARING;
270            mIsAsyncPrepare = true;
271            mPlayer->prepareAsync();
272            return OK;
273        case STATE_STOPPED:
274            // this is really just paused. handle as seek to start
275            mAtEOS = false;
276            mState = STATE_STOPPED_AND_PREPARING;
277            mIsAsyncPrepare = true;
278            mPlayer->seekToAsync(0, MediaPlayerSeekMode::SEEK_PREVIOUS_SYNC /* mode */,
279                    true /* needNotify */);
280            return OK;
281        default:
282            return INVALID_OPERATION;
283    };
284}
285
286status_t NuPlayerDriver::start() {
287    ALOGD("start(%p), state is %d, eos is %d", this, mState, mAtEOS);
288    Mutex::Autolock autoLock(mLock);
289    return start_l();
290}
291
292status_t NuPlayerDriver::start_l() {
293    switch (mState) {
294        case STATE_UNPREPARED:
295        {
296            status_t err = prepare_l();
297
298            if (err != OK) {
299                return err;
300            }
301
302            CHECK_EQ(mState, STATE_PREPARED);
303
304            // fall through
305        }
306
307        case STATE_PAUSED:
308        case STATE_STOPPED_AND_PREPARED:
309        case STATE_PREPARED:
310        {
311            mPlayer->start();
312
313            // fall through
314        }
315
316        case STATE_RUNNING:
317        {
318            if (mAtEOS) {
319                mPlayer->seekToAsync(0);
320                mAtEOS = false;
321                mPositionUs = -1;
322            }
323            break;
324        }
325
326        default:
327            return INVALID_OPERATION;
328    }
329
330    mState = STATE_RUNNING;
331
332    return OK;
333}
334
335status_t NuPlayerDriver::stop() {
336    ALOGD("stop(%p)", this);
337    Mutex::Autolock autoLock(mLock);
338
339    switch (mState) {
340        case STATE_RUNNING:
341            mPlayer->pause();
342            // fall through
343
344        case STATE_PAUSED:
345            mState = STATE_STOPPED;
346            notifyListener_l(MEDIA_STOPPED);
347            break;
348
349        case STATE_PREPARED:
350        case STATE_STOPPED:
351        case STATE_STOPPED_AND_PREPARING:
352        case STATE_STOPPED_AND_PREPARED:
353            mState = STATE_STOPPED;
354            break;
355
356        default:
357            return INVALID_OPERATION;
358    }
359
360    return OK;
361}
362
363status_t NuPlayerDriver::pause() {
364    ALOGD("pause(%p)", this);
365    // The NuPlayerRenderer may get flushed if pause for long enough, e.g. the pause timeout tear
366    // down for audio offload mode. If that happens, the NuPlayerRenderer will no longer know the
367    // current position. So similar to seekTo, update |mPositionUs| to the pause position by calling
368    // getCurrentPosition here.
369    int unused;
370    getCurrentPosition(&unused);
371
372    Mutex::Autolock autoLock(mLock);
373
374    switch (mState) {
375        case STATE_PAUSED:
376        case STATE_PREPARED:
377            return OK;
378
379        case STATE_RUNNING:
380            mState = STATE_PAUSED;
381            notifyListener_l(MEDIA_PAUSED);
382            mPlayer->pause();
383            break;
384
385        default:
386            return INVALID_OPERATION;
387    }
388
389    return OK;
390}
391
392bool NuPlayerDriver::isPlaying() {
393    return mState == STATE_RUNNING && !mAtEOS;
394}
395
396status_t NuPlayerDriver::setPlaybackSettings(const AudioPlaybackRate &rate) {
397    status_t err = mPlayer->setPlaybackSettings(rate);
398    if (err == OK) {
399        // try to update position
400        int unused;
401        getCurrentPosition(&unused);
402        Mutex::Autolock autoLock(mLock);
403        if (rate.mSpeed == 0.f && mState == STATE_RUNNING) {
404            mState = STATE_PAUSED;
405            notifyListener_l(MEDIA_PAUSED);
406        } else if (rate.mSpeed != 0.f
407                && (mState == STATE_PAUSED
408                    || mState == STATE_STOPPED_AND_PREPARED
409                    || mState == STATE_PREPARED)) {
410            err = start_l();
411        }
412    }
413    return err;
414}
415
416status_t NuPlayerDriver::getPlaybackSettings(AudioPlaybackRate *rate) {
417    return mPlayer->getPlaybackSettings(rate);
418}
419
420status_t NuPlayerDriver::setSyncSettings(const AVSyncSettings &sync, float videoFpsHint) {
421    return mPlayer->setSyncSettings(sync, videoFpsHint);
422}
423
424status_t NuPlayerDriver::getSyncSettings(AVSyncSettings *sync, float *videoFps) {
425    return mPlayer->getSyncSettings(sync, videoFps);
426}
427
428status_t NuPlayerDriver::seekTo(int msec, MediaPlayerSeekMode mode) {
429    ALOGD("seekTo(%p) (%d ms, %d) at state %d", this, msec, mode, mState);
430    Mutex::Autolock autoLock(mLock);
431
432    int64_t seekTimeUs = msec * 1000ll;
433
434    switch (mState) {
435        case STATE_PREPARED:
436        case STATE_STOPPED_AND_PREPARED:
437        case STATE_PAUSED:
438        case STATE_RUNNING:
439        {
440            mAtEOS = false;
441            mSeekInProgress = true;
442            // seeks can take a while, so we essentially paused
443            notifyListener_l(MEDIA_PAUSED);
444            mPlayer->seekToAsync(seekTimeUs, mode, true /* needNotify */);
445            break;
446        }
447
448        default:
449            return INVALID_OPERATION;
450    }
451
452    mPositionUs = seekTimeUs;
453    return OK;
454}
455
456status_t NuPlayerDriver::getCurrentPosition(int *msec) {
457    int64_t tempUs = 0;
458    {
459        Mutex::Autolock autoLock(mLock);
460        if (mSeekInProgress || (mState == STATE_PAUSED && !mAtEOS)) {
461            tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
462            *msec = (int)divRound(tempUs, (int64_t)(1000));
463            return OK;
464        }
465    }
466
467    status_t ret = mPlayer->getCurrentPosition(&tempUs);
468
469    Mutex::Autolock autoLock(mLock);
470    // We need to check mSeekInProgress here because mPlayer->seekToAsync is an async call, which
471    // means getCurrentPosition can be called before seek is completed. Iow, renderer may return a
472    // position value that's different the seek to position.
473    if (ret != OK) {
474        tempUs = (mPositionUs <= 0) ? 0 : mPositionUs;
475    } else {
476        mPositionUs = tempUs;
477    }
478    *msec = (int)divRound(tempUs, (int64_t)(1000));
479    return OK;
480}
481
482status_t NuPlayerDriver::getDuration(int *msec) {
483    Mutex::Autolock autoLock(mLock);
484
485    if (mDurationUs < 0) {
486        return UNKNOWN_ERROR;
487    }
488
489    *msec = (mDurationUs + 500ll) / 1000;
490
491    return OK;
492}
493
494void NuPlayerDriver::finalizeMetrics(const char *where) {
495    if (where == NULL) {
496        where = "unknown";
497    }
498    ALOGD("finalizeMetrics(%p) from %s at state %d", this, where, mState);
499
500    // gather the final stats for this record
501    Vector<sp<AMessage>> trackStats;
502    mPlayer->getStats(&trackStats);
503
504    if (trackStats.size() > 0) {
505        for (size_t i = 0; i < trackStats.size(); ++i) {
506            const sp<AMessage> &stats = trackStats.itemAt(i);
507
508            AString mime;
509            stats->findString("mime", &mime);
510
511            AString name;
512            stats->findString("component-name", &name);
513
514            if (mime.startsWith("video/")) {
515                int32_t width, height;
516                mAnalyticsItem->setCString("video/mime", mime.c_str());
517                if (!name.empty()) {
518                    mAnalyticsItem->setCString("video/codec", name.c_str());
519                }
520
521                if (stats->findInt32("width", &width)
522                        && stats->findInt32("height", &height)) {
523                    mAnalyticsItem->setInt32("wid", width);
524                    mAnalyticsItem->setInt32("ht", height);
525                }
526
527                int64_t numFramesTotal = 0;
528                int64_t numFramesDropped = 0;
529                stats->findInt64("frames-total", &numFramesTotal);
530                stats->findInt64("frames-dropped-output", &numFramesDropped);
531
532                mAnalyticsItem->setInt64("frames", numFramesTotal);
533                mAnalyticsItem->setInt64("dropped", numFramesDropped);
534
535
536            } else if (mime.startsWith("audio/")) {
537                mAnalyticsItem->setCString("audio/mime", mime.c_str());
538                if (!name.empty()) {
539                    mAnalyticsItem->setCString("audio/codec", name.c_str());
540                }
541            }
542        }
543
544        // getDuration() uses mLock for mutex -- careful where we use it.
545        int duration_ms = -1;
546        getDuration(&duration_ms);
547        if (duration_ms != -1) {
548            mAnalyticsItem->setInt64("duration", duration_ms);
549        }
550
551        if (mPlayingTimeUs > 0) {
552            mAnalyticsItem->setInt64("playing", (mPlayingTimeUs+500)/1000 );
553        }
554    }
555}
556
557
558void NuPlayerDriver::logMetrics(const char *where) {
559    if (where == NULL) {
560        where = "unknown";
561    }
562    ALOGD("logMetrics(%p) from %s at state %d", this, where, mState);
563
564    if (mAnalyticsItem == NULL || mAnalyticsItem->isEnabled() == false) {
565        return;
566    }
567
568    // only bother to log non-empty records
569    if (mAnalyticsItem->count() > 0) {
570
571        mAnalyticsItem->setFinalized(true);
572        mAnalyticsItem->selfrecord();
573
574        // re-init in case we prepare() and start() again.
575        delete mAnalyticsItem ;
576        mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
577        if (mAnalyticsItem) {
578            mAnalyticsItem->generateSessionID();
579        }
580    } else {
581        ALOGV("did not have anything to record");
582    }
583}
584
585status_t NuPlayerDriver::reset() {
586    ALOGD("reset(%p) at state %d", this, mState);
587
588    finalizeMetrics("reset");
589    logMetrics("reset");
590
591    Mutex::Autolock autoLock(mLock);
592
593    switch (mState) {
594        case STATE_IDLE:
595            return OK;
596
597        case STATE_SET_DATASOURCE_PENDING:
598        case STATE_RESET_IN_PROGRESS:
599            return INVALID_OPERATION;
600
601        case STATE_PREPARING:
602        {
603            CHECK(mIsAsyncPrepare);
604
605            notifyListener_l(MEDIA_PREPARED);
606            break;
607        }
608
609        default:
610            break;
611    }
612
613    if (mState != STATE_STOPPED) {
614        notifyListener_l(MEDIA_STOPPED);
615    }
616
617    if (property_get_bool("persist.debug.sf.stats", false)) {
618        Vector<String16> args;
619        dump(-1, args);
620    }
621
622    mState = STATE_RESET_IN_PROGRESS;
623    mPlayer->resetAsync();
624
625    while (mState == STATE_RESET_IN_PROGRESS) {
626        mCondition.wait(mLock);
627    }
628
629    mDurationUs = -1;
630    mPositionUs = -1;
631    mLooping = false;
632    mPlayingTimeUs = 0;
633
634    return OK;
635}
636
637status_t NuPlayerDriver::setLooping(int loop) {
638    mLooping = loop != 0;
639    return OK;
640}
641
642player_type NuPlayerDriver::playerType() {
643    return NU_PLAYER;
644}
645
646status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
647    if (reply == NULL) {
648        ALOGE("reply is a NULL pointer");
649        return BAD_VALUE;
650    }
651
652    int32_t methodId;
653    status_t ret = request.readInt32(&methodId);
654    if (ret != OK) {
655        ALOGE("Failed to retrieve the requested method to invoke");
656        return ret;
657    }
658
659    switch (methodId) {
660        case INVOKE_ID_SET_VIDEO_SCALING_MODE:
661        {
662            int mode = request.readInt32();
663            return mPlayer->setVideoScalingMode(mode);
664        }
665
666        case INVOKE_ID_GET_TRACK_INFO:
667        {
668            return mPlayer->getTrackInfo(reply);
669        }
670
671        case INVOKE_ID_SELECT_TRACK:
672        {
673            int trackIndex = request.readInt32();
674            int msec = 0;
675            // getCurrentPosition should always return OK
676            getCurrentPosition(&msec);
677            return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
678        }
679
680        case INVOKE_ID_UNSELECT_TRACK:
681        {
682            int trackIndex = request.readInt32();
683            return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
684        }
685
686        case INVOKE_ID_GET_SELECTED_TRACK:
687        {
688            int32_t type = request.readInt32();
689            return mPlayer->getSelectedTrack(type, reply);
690        }
691
692        default:
693        {
694            return INVALID_OPERATION;
695        }
696    }
697}
698
699void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
700    mPlayer->setAudioSink(audioSink);
701    mAudioSink = audioSink;
702}
703
704status_t NuPlayerDriver::setParameter(
705        int /* key */, const Parcel & /* request */) {
706    return INVALID_OPERATION;
707}
708
709status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
710
711    if (key == FOURCC('m','t','r','X')) {
712        // mtrX -- a play on 'metrics' (not matrix)
713        // gather current info all together, parcel it, and send it back
714        finalizeMetrics("api");
715        mAnalyticsItem->writeToParcel(reply);
716        return OK;
717    }
718
719    return INVALID_OPERATION;
720}
721
722status_t NuPlayerDriver::getMetadata(
723        const media::Metadata::Filter& /* ids */, Parcel *records) {
724    Mutex::Autolock autoLock(mLock);
725
726    using media::Metadata;
727
728    Metadata meta(records);
729
730    meta.appendBool(
731            Metadata::kPauseAvailable,
732            mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE);
733
734    meta.appendBool(
735            Metadata::kSeekBackwardAvailable,
736            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD);
737
738    meta.appendBool(
739            Metadata::kSeekForwardAvailable,
740            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD);
741
742    meta.appendBool(
743            Metadata::kSeekAvailable,
744            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK);
745
746    return OK;
747}
748
749void NuPlayerDriver::notifyResetComplete() {
750    ALOGD("notifyResetComplete(%p)", this);
751    Mutex::Autolock autoLock(mLock);
752
753    CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
754    mState = STATE_IDLE;
755    mCondition.broadcast();
756}
757
758void NuPlayerDriver::notifySetSurfaceComplete() {
759    ALOGV("notifySetSurfaceComplete(%p)", this);
760    Mutex::Autolock autoLock(mLock);
761
762    CHECK(mSetSurfaceInProgress);
763    mSetSurfaceInProgress = false;
764
765    mCondition.broadcast();
766}
767
768void NuPlayerDriver::notifyDuration(int64_t durationUs) {
769    Mutex::Autolock autoLock(mLock);
770    mDurationUs = durationUs;
771}
772
773void NuPlayerDriver::notifyMorePlayingTimeUs(int64_t playingUs) {
774    Mutex::Autolock autoLock(mLock);
775    mPlayingTimeUs += playingUs;
776}
777
778void NuPlayerDriver::notifySeekComplete() {
779    ALOGV("notifySeekComplete(%p)", this);
780    Mutex::Autolock autoLock(mLock);
781    mSeekInProgress = false;
782    notifySeekComplete_l();
783}
784
785void NuPlayerDriver::notifySeekComplete_l() {
786    bool wasSeeking = true;
787    if (mState == STATE_STOPPED_AND_PREPARING) {
788        wasSeeking = false;
789        mState = STATE_STOPPED_AND_PREPARED;
790        mCondition.broadcast();
791        if (!mIsAsyncPrepare) {
792            // if we are preparing synchronously, no need to notify listener
793            return;
794        }
795    } else if (mState == STATE_STOPPED) {
796        // no need to notify listener
797        return;
798    }
799    notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
800}
801
802status_t NuPlayerDriver::dump(
803        int fd, const Vector<String16> & /* args */) const {
804
805    Vector<sp<AMessage> > trackStats;
806    mPlayer->getStats(&trackStats);
807
808    AString logString(" NuPlayer\n");
809    char buf[256] = {0};
810
811    bool locked = false;
812    for (int i = 0; i < kDumpLockRetries; ++i) {
813        if (mLock.tryLock() == NO_ERROR) {
814            locked = true;
815            break;
816        }
817        usleep(kDumpLockSleepUs);
818    }
819
820    if (locked) {
821        snprintf(buf, sizeof(buf), "  state(%d), atEOS(%d), looping(%d), autoLoop(%d)\n",
822                mState, mAtEOS, mLooping, mAutoLoop);
823        mLock.unlock();
824    } else {
825        snprintf(buf, sizeof(buf), "  NPD(%p) lock is taken\n", this);
826    }
827    logString.append(buf);
828
829    for (size_t i = 0; i < trackStats.size(); ++i) {
830        const sp<AMessage> &stats = trackStats.itemAt(i);
831
832        AString mime;
833        if (stats->findString("mime", &mime)) {
834            snprintf(buf, sizeof(buf), "  mime(%s)\n", mime.c_str());
835            logString.append(buf);
836        }
837
838        AString name;
839        if (stats->findString("component-name", &name)) {
840            snprintf(buf, sizeof(buf), "    decoder(%s)\n", name.c_str());
841            logString.append(buf);
842        }
843
844        if (mime.startsWith("video/")) {
845            int32_t width, height;
846            if (stats->findInt32("width", &width)
847                    && stats->findInt32("height", &height)) {
848                snprintf(buf, sizeof(buf), "    resolution(%d x %d)\n", width, height);
849                logString.append(buf);
850            }
851
852            int64_t numFramesTotal = 0;
853            int64_t numFramesDropped = 0;
854
855            stats->findInt64("frames-total", &numFramesTotal);
856            stats->findInt64("frames-dropped-output", &numFramesDropped);
857            snprintf(buf, sizeof(buf), "    numFramesTotal(%lld), numFramesDropped(%lld), "
858                     "percentageDropped(%.2f%%)\n",
859                     (long long)numFramesTotal,
860                     (long long)numFramesDropped,
861                     numFramesTotal == 0
862                            ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal);
863            logString.append(buf);
864        }
865    }
866
867    ALOGI("%s", logString.c_str());
868
869    if (fd >= 0) {
870        FILE *out = fdopen(dup(fd), "w");
871        fprintf(out, "%s", logString.c_str());
872        fclose(out);
873        out = NULL;
874    }
875
876    return OK;
877}
878
879void NuPlayerDriver::notifyListener(
880        int msg, int ext1, int ext2, const Parcel *in) {
881    Mutex::Autolock autoLock(mLock);
882    notifyListener_l(msg, ext1, ext2, in);
883}
884
885void NuPlayerDriver::notifyListener_l(
886        int msg, int ext1, int ext2, const Parcel *in) {
887    ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)",
888            this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
889    switch (msg) {
890        case MEDIA_PLAYBACK_COMPLETE:
891        {
892            if (mState != STATE_RESET_IN_PROGRESS) {
893                if (mAutoLoop) {
894                    audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
895                    if (mAudioSink != NULL) {
896                        streamType = mAudioSink->getAudioStreamType();
897                    }
898                    if (streamType == AUDIO_STREAM_NOTIFICATION) {
899                        ALOGW("disabling auto-loop for notification");
900                        mAutoLoop = false;
901                    }
902                }
903                if (mLooping || mAutoLoop) {
904                    mPlayer->seekToAsync(0);
905                    if (mAudioSink != NULL) {
906                        // The renderer has stopped the sink at the end in order to play out
907                        // the last little bit of audio. If we're looping, we need to restart it.
908                        mAudioSink->start();
909                    }
910                    // don't send completion event when looping
911                    return;
912                }
913
914                mPlayer->pause();
915                mState = STATE_PAUSED;
916            }
917            // fall through
918        }
919
920        case MEDIA_ERROR:
921        {
922            mAtEOS = true;
923            break;
924        }
925
926        default:
927            break;
928    }
929
930    mLock.unlock();
931    sendEvent(msg, ext1, ext2, in);
932    mLock.lock();
933}
934
935void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
936    Mutex::Autolock autoLock(mLock);
937
938    CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
939
940    mAsyncResult = err;
941    mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
942    mCondition.broadcast();
943}
944
945void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
946    ALOGV("notifyPrepareCompleted %d", err);
947
948    Mutex::Autolock autoLock(mLock);
949
950    if (mState != STATE_PREPARING) {
951        // We were preparing asynchronously when the client called
952        // reset(), we sent a premature "prepared" notification and
953        // then initiated the reset. This notification is stale.
954        CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
955        return;
956    }
957
958    CHECK_EQ(mState, STATE_PREPARING);
959
960    mAsyncResult = err;
961
962    if (err == OK) {
963        // update state before notifying client, so that if client calls back into NuPlayerDriver
964        // in response, NuPlayerDriver has the right state
965        mState = STATE_PREPARED;
966        if (mIsAsyncPrepare) {
967            notifyListener_l(MEDIA_PREPARED);
968        }
969    } else {
970        mState = STATE_UNPREPARED;
971        if (mIsAsyncPrepare) {
972            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
973        }
974    }
975
976    sp<MetaData> meta = mPlayer->getFileMeta();
977    int32_t loop;
978    if (meta != NULL
979            && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
980        mAutoLoop = true;
981    }
982
983    mCondition.broadcast();
984}
985
986void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) {
987    Mutex::Autolock autoLock(mLock);
988
989    mPlayerFlags = flags;
990}
991
992// Modular DRM
993status_t NuPlayerDriver::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
994{
995    ALOGV("prepareDrm(%p) state: %d", this, mState);
996
997    Mutex::Autolock autoLock(mLock);
998
999    // leaving the state verification for mediaplayer.cpp
1000    status_t ret = mPlayer->prepareDrm(uuid, drmSessionId);
1001
1002    ALOGV("prepareDrm ret: %d", ret);
1003
1004    return ret;
1005}
1006
1007status_t NuPlayerDriver::releaseDrm()
1008{
1009    ALOGV("releaseDrm(%p) state: %d", this, mState);
1010
1011    Mutex::Autolock autoLock(mLock);
1012
1013    // leaving the state verification for mediaplayer.cpp
1014    status_t ret = mPlayer->releaseDrm();
1015
1016    ALOGV("releaseDrm ret: %d", ret);
1017
1018    return ret;
1019}
1020
1021}  // namespace android
1022