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