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