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