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