NuPlayerDriver.cpp revision 5409770c9d3765279a063e828ec0cf55797fd686
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    updateMetrics("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::updateMetrics(const char *where) {
515    if (where == NULL) {
516        where = "unknown";
517    }
518    ALOGV("updateMetrics(%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
565    // always provide duration and playing time, even if they have 0/unknown values.
566
567    // getDuration() uses mLock for mutex -- careful where we use it.
568    int duration_ms = -1;
569    getDuration(&duration_ms);
570    mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
571
572    mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
573}
574
575
576void NuPlayerDriver::logMetrics(const char *where) {
577    if (where == NULL) {
578        where = "unknown";
579    }
580    ALOGV("logMetrics(%p) from %s at state %d", this, where, mState);
581
582    if (mAnalyticsItem == NULL || mAnalyticsItem->isEnabled() == false) {
583        return;
584    }
585
586    // only bother to log non-empty records
587    if (mAnalyticsItem->count() > 0) {
588
589        mAnalyticsItem->setFinalized(true);
590        mAnalyticsItem->selfrecord();
591
592        // re-init in case we prepare() and start() again.
593        delete mAnalyticsItem ;
594        mAnalyticsItem = new MediaAnalyticsItem("nuplayer");
595        if (mAnalyticsItem) {
596            mAnalyticsItem->generateSessionID();
597        }
598    } else {
599        ALOGV("did not have anything to record");
600    }
601}
602
603status_t NuPlayerDriver::reset() {
604    ALOGD("reset(%p) at state %d", this, mState);
605
606    updateMetrics("reset");
607    logMetrics("reset");
608
609    Mutex::Autolock autoLock(mLock);
610
611    switch (mState) {
612        case STATE_IDLE:
613            return OK;
614
615        case STATE_SET_DATASOURCE_PENDING:
616        case STATE_RESET_IN_PROGRESS:
617            return INVALID_OPERATION;
618
619        case STATE_PREPARING:
620        {
621            CHECK(mIsAsyncPrepare);
622
623            notifyListener_l(MEDIA_PREPARED);
624            break;
625        }
626
627        default:
628            break;
629    }
630
631    if (mState != STATE_STOPPED) {
632        notifyListener_l(MEDIA_STOPPED);
633    }
634
635    if (property_get_bool("persist.debug.sf.stats", false)) {
636        Vector<String16> args;
637        dump(-1, args);
638    }
639
640    mState = STATE_RESET_IN_PROGRESS;
641    mPlayer->resetAsync();
642
643    while (mState == STATE_RESET_IN_PROGRESS) {
644        mCondition.wait(mLock);
645    }
646
647    mDurationUs = -1;
648    mPositionUs = -1;
649    mLooping = false;
650    mPlayingTimeUs = 0;
651
652    return OK;
653}
654
655status_t NuPlayerDriver::setLooping(int loop) {
656    mLooping = loop != 0;
657    return OK;
658}
659
660player_type NuPlayerDriver::playerType() {
661    return NU_PLAYER;
662}
663
664status_t NuPlayerDriver::invoke(const Parcel &request, Parcel *reply) {
665    if (reply == NULL) {
666        ALOGE("reply is a NULL pointer");
667        return BAD_VALUE;
668    }
669
670    int32_t methodId;
671    status_t ret = request.readInt32(&methodId);
672    if (ret != OK) {
673        ALOGE("Failed to retrieve the requested method to invoke");
674        return ret;
675    }
676
677    switch (methodId) {
678        case INVOKE_ID_SET_VIDEO_SCALING_MODE:
679        {
680            int mode = request.readInt32();
681            return mPlayer->setVideoScalingMode(mode);
682        }
683
684        case INVOKE_ID_GET_TRACK_INFO:
685        {
686            return mPlayer->getTrackInfo(reply);
687        }
688
689        case INVOKE_ID_SELECT_TRACK:
690        {
691            int trackIndex = request.readInt32();
692            int msec = 0;
693            // getCurrentPosition should always return OK
694            getCurrentPosition(&msec);
695            return mPlayer->selectTrack(trackIndex, true /* select */, msec * 1000ll);
696        }
697
698        case INVOKE_ID_UNSELECT_TRACK:
699        {
700            int trackIndex = request.readInt32();
701            return mPlayer->selectTrack(trackIndex, false /* select */, 0xdeadbeef /* not used */);
702        }
703
704        case INVOKE_ID_GET_SELECTED_TRACK:
705        {
706            int32_t type = request.readInt32();
707            return mPlayer->getSelectedTrack(type, reply);
708        }
709
710        default:
711        {
712            return INVALID_OPERATION;
713        }
714    }
715}
716
717void NuPlayerDriver::setAudioSink(const sp<AudioSink> &audioSink) {
718    mPlayer->setAudioSink(audioSink);
719    mAudioSink = audioSink;
720}
721
722status_t NuPlayerDriver::setParameter(
723        int /* key */, const Parcel & /* request */) {
724    return INVALID_OPERATION;
725}
726
727status_t NuPlayerDriver::getParameter(int key, Parcel *reply) {
728
729    if (key == FOURCC('m','t','r','X')) {
730        // mtrX -- a play on 'metrics' (not matrix)
731        // gather current info all together, parcel it, and send it back
732        updateMetrics("api");
733        mAnalyticsItem->writeToParcel(reply);
734        return OK;
735    }
736
737    return INVALID_OPERATION;
738}
739
740status_t NuPlayerDriver::getMetadata(
741        const media::Metadata::Filter& /* ids */, Parcel *records) {
742    Mutex::Autolock autoLock(mLock);
743
744    using media::Metadata;
745
746    Metadata meta(records);
747
748    meta.appendBool(
749            Metadata::kPauseAvailable,
750            mPlayerFlags & NuPlayer::Source::FLAG_CAN_PAUSE);
751
752    meta.appendBool(
753            Metadata::kSeekBackwardAvailable,
754            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_BACKWARD);
755
756    meta.appendBool(
757            Metadata::kSeekForwardAvailable,
758            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK_FORWARD);
759
760    meta.appendBool(
761            Metadata::kSeekAvailable,
762            mPlayerFlags & NuPlayer::Source::FLAG_CAN_SEEK);
763
764    return OK;
765}
766
767void NuPlayerDriver::notifyResetComplete() {
768    ALOGD("notifyResetComplete(%p)", this);
769    Mutex::Autolock autoLock(mLock);
770
771    CHECK_EQ(mState, STATE_RESET_IN_PROGRESS);
772    mState = STATE_IDLE;
773    mCondition.broadcast();
774}
775
776void NuPlayerDriver::notifySetSurfaceComplete() {
777    ALOGV("notifySetSurfaceComplete(%p)", this);
778    Mutex::Autolock autoLock(mLock);
779
780    CHECK(mSetSurfaceInProgress);
781    mSetSurfaceInProgress = false;
782
783    mCondition.broadcast();
784}
785
786void NuPlayerDriver::notifyDuration(int64_t durationUs) {
787    Mutex::Autolock autoLock(mLock);
788    mDurationUs = durationUs;
789}
790
791void NuPlayerDriver::notifyMorePlayingTimeUs(int64_t playingUs) {
792    Mutex::Autolock autoLock(mLock);
793    mPlayingTimeUs += playingUs;
794}
795
796void NuPlayerDriver::notifySeekComplete() {
797    ALOGV("notifySeekComplete(%p)", this);
798    Mutex::Autolock autoLock(mLock);
799    mSeekInProgress = false;
800    notifySeekComplete_l();
801}
802
803void NuPlayerDriver::notifySeekComplete_l() {
804    bool wasSeeking = true;
805    if (mState == STATE_STOPPED_AND_PREPARING) {
806        wasSeeking = false;
807        mState = STATE_STOPPED_AND_PREPARED;
808        mCondition.broadcast();
809        if (!mIsAsyncPrepare) {
810            // if we are preparing synchronously, no need to notify listener
811            return;
812        }
813    } else if (mState == STATE_STOPPED) {
814        // no need to notify listener
815        return;
816    }
817    notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
818}
819
820status_t NuPlayerDriver::dump(
821        int fd, const Vector<String16> & /* args */) const {
822
823    Vector<sp<AMessage> > trackStats;
824    mPlayer->getStats(&trackStats);
825
826    AString logString(" NuPlayer\n");
827    char buf[256] = {0};
828
829    bool locked = false;
830    for (int i = 0; i < kDumpLockRetries; ++i) {
831        if (mLock.tryLock() == NO_ERROR) {
832            locked = true;
833            break;
834        }
835        usleep(kDumpLockSleepUs);
836    }
837
838    if (locked) {
839        snprintf(buf, sizeof(buf), "  state(%d), atEOS(%d), looping(%d), autoLoop(%d)\n",
840                mState, mAtEOS, mLooping, mAutoLoop);
841        mLock.unlock();
842    } else {
843        snprintf(buf, sizeof(buf), "  NPD(%p) lock is taken\n", this);
844    }
845    logString.append(buf);
846
847    for (size_t i = 0; i < trackStats.size(); ++i) {
848        const sp<AMessage> &stats = trackStats.itemAt(i);
849
850        AString mime;
851        if (stats->findString("mime", &mime)) {
852            snprintf(buf, sizeof(buf), "  mime(%s)\n", mime.c_str());
853            logString.append(buf);
854        }
855
856        AString name;
857        if (stats->findString("component-name", &name)) {
858            snprintf(buf, sizeof(buf), "    decoder(%s)\n", name.c_str());
859            logString.append(buf);
860        }
861
862        if (mime.startsWith("video/")) {
863            int32_t width, height;
864            if (stats->findInt32("width", &width)
865                    && stats->findInt32("height", &height)) {
866                snprintf(buf, sizeof(buf), "    resolution(%d x %d)\n", width, height);
867                logString.append(buf);
868            }
869
870            int64_t numFramesTotal = 0;
871            int64_t numFramesDropped = 0;
872
873            stats->findInt64("frames-total", &numFramesTotal);
874            stats->findInt64("frames-dropped-output", &numFramesDropped);
875            snprintf(buf, sizeof(buf), "    numFramesTotal(%lld), numFramesDropped(%lld), "
876                     "percentageDropped(%.2f%%)\n",
877                     (long long)numFramesTotal,
878                     (long long)numFramesDropped,
879                     numFramesTotal == 0
880                            ? 0.0 : (double)(numFramesDropped * 100) / numFramesTotal);
881            logString.append(buf);
882        }
883    }
884
885    ALOGI("%s", logString.c_str());
886
887    if (fd >= 0) {
888        FILE *out = fdopen(dup(fd), "w");
889        fprintf(out, "%s", logString.c_str());
890        fclose(out);
891        out = NULL;
892    }
893
894    return OK;
895}
896
897void NuPlayerDriver::notifyListener(
898        int msg, int ext1, int ext2, const Parcel *in) {
899    Mutex::Autolock autoLock(mLock);
900    notifyListener_l(msg, ext1, ext2, in);
901}
902
903void NuPlayerDriver::notifyListener_l(
904        int msg, int ext1, int ext2, const Parcel *in) {
905    ALOGD("notifyListener_l(%p), (%d, %d, %d, %d), loop setting(%d, %d)",
906            this, msg, ext1, ext2, (in == NULL ? -1 : (int)in->dataSize()), mAutoLoop, mLooping);
907    switch (msg) {
908        case MEDIA_PLAYBACK_COMPLETE:
909        {
910            if (mState != STATE_RESET_IN_PROGRESS) {
911                if (mAutoLoop) {
912                    audio_stream_type_t streamType = AUDIO_STREAM_MUSIC;
913                    if (mAudioSink != NULL) {
914                        streamType = mAudioSink->getAudioStreamType();
915                    }
916                    if (streamType == AUDIO_STREAM_NOTIFICATION) {
917                        ALOGW("disabling auto-loop for notification");
918                        mAutoLoop = false;
919                    }
920                }
921                if (mLooping || mAutoLoop) {
922                    mPlayer->seekToAsync(0);
923                    if (mAudioSink != NULL) {
924                        // The renderer has stopped the sink at the end in order to play out
925                        // the last little bit of audio. If we're looping, we need to restart it.
926                        mAudioSink->start();
927                    }
928                    // don't send completion event when looping
929                    return;
930                }
931
932                mPlayer->pause();
933                mState = STATE_PAUSED;
934            }
935            // fall through
936        }
937
938        case MEDIA_ERROR:
939        {
940            // when we have an error, add it to the analytics for this playback.
941            // ext1 is our primary 'error type' value. Only add ext2 when non-zero.
942            // [test against msg is due to fall through from previous switch value]
943            if (msg == MEDIA_ERROR) {
944                mAnalyticsItem->setInt32(kPlayerError, ext1);
945                if (ext2 != 0) {
946                    mAnalyticsItem->setInt32(kPlayerErrorCode, ext2);
947                }
948            }
949            mAtEOS = true;
950            break;
951        }
952
953        default:
954            break;
955    }
956
957    mLock.unlock();
958    sendEvent(msg, ext1, ext2, in);
959    mLock.lock();
960}
961
962void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
963    Mutex::Autolock autoLock(mLock);
964
965    CHECK_EQ(mState, STATE_SET_DATASOURCE_PENDING);
966
967    mAsyncResult = err;
968    mState = (err == OK) ? STATE_UNPREPARED : STATE_IDLE;
969    mCondition.broadcast();
970}
971
972void NuPlayerDriver::notifyPrepareCompleted(status_t err) {
973    ALOGV("notifyPrepareCompleted %d", err);
974
975    Mutex::Autolock autoLock(mLock);
976
977    if (mState != STATE_PREPARING) {
978        // We were preparing asynchronously when the client called
979        // reset(), we sent a premature "prepared" notification and
980        // then initiated the reset. This notification is stale.
981        CHECK(mState == STATE_RESET_IN_PROGRESS || mState == STATE_IDLE);
982        return;
983    }
984
985    CHECK_EQ(mState, STATE_PREPARING);
986
987    mAsyncResult = err;
988
989    if (err == OK) {
990        // update state before notifying client, so that if client calls back into NuPlayerDriver
991        // in response, NuPlayerDriver has the right state
992        mState = STATE_PREPARED;
993        if (mIsAsyncPrepare) {
994            notifyListener_l(MEDIA_PREPARED);
995        }
996    } else {
997        mState = STATE_UNPREPARED;
998        if (mIsAsyncPrepare) {
999            notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
1000        }
1001    }
1002
1003    sp<MetaData> meta = mPlayer->getFileMeta();
1004    int32_t loop;
1005    if (meta != NULL
1006            && meta->findInt32(kKeyAutoLoop, &loop) && loop != 0) {
1007        mAutoLoop = true;
1008    }
1009
1010    mCondition.broadcast();
1011}
1012
1013void NuPlayerDriver::notifyFlagsChanged(uint32_t flags) {
1014    Mutex::Autolock autoLock(mLock);
1015
1016    mPlayerFlags = flags;
1017}
1018
1019// Modular DRM
1020status_t NuPlayerDriver::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
1021{
1022    ALOGV("prepareDrm(%p) state: %d", this, mState);
1023
1024    // leaving the state verification for mediaplayer.cpp
1025    status_t ret = mPlayer->prepareDrm(uuid, drmSessionId);
1026
1027    ALOGV("prepareDrm ret: %d", ret);
1028
1029    return ret;
1030}
1031
1032status_t NuPlayerDriver::releaseDrm()
1033{
1034    ALOGV("releaseDrm(%p) state: %d", this, mState);
1035
1036    // leaving the state verification for mediaplayer.cpp
1037    status_t ret = mPlayer->releaseDrm();
1038
1039    ALOGV("releaseDrm ret: %d", ret);
1040
1041    return ret;
1042}
1043
1044}  // namespace android
1045