NuPlayer.cpp revision dc9bacd838442a524585887e6ea6696836be2eda
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 "NuPlayer"
19#include <utils/Log.h>
20
21#include "NuPlayer.h"
22
23#include "HTTPLiveSource.h"
24#include "NuPlayerDecoder.h"
25#include "NuPlayerDriver.h"
26#include "NuPlayerRenderer.h"
27#include "NuPlayerSource.h"
28#include "StreamingSource.h"
29
30#include "ATSParser.h"
31
32#include <media/stagefright/foundation/hexdump.h>
33#include <media/stagefright/foundation/ABuffer.h>
34#include <media/stagefright/foundation/ADebug.h>
35#include <media/stagefright/foundation/AMessage.h>
36#include <media/stagefright/ACodec.h>
37#include <media/stagefright/MediaDefs.h>
38#include <media/stagefright/MediaErrors.h>
39#include <media/stagefright/MetaData.h>
40#include <surfaceflinger/Surface.h>
41#include <gui/ISurfaceTexture.h>
42
43#include "avc_utils.h"
44
45namespace android {
46
47////////////////////////////////////////////////////////////////////////////////
48
49NuPlayer::NuPlayer()
50    : mUIDValid(false),
51      mVideoIsAVC(false),
52      mAudioEOS(false),
53      mVideoEOS(false),
54      mScanSourcesPending(false),
55      mScanSourcesGeneration(0),
56      mFlushingAudio(NONE),
57      mFlushingVideo(NONE),
58      mResetInProgress(false),
59      mResetPostponed(false),
60      mSkipRenderingAudioUntilMediaTimeUs(-1ll),
61      mSkipRenderingVideoUntilMediaTimeUs(-1ll),
62      mVideoLateByUs(0ll),
63      mNumFramesTotal(0ll),
64      mNumFramesDropped(0ll) {
65}
66
67NuPlayer::~NuPlayer() {
68}
69
70void NuPlayer::setUID(uid_t uid) {
71    mUIDValid = true;
72    mUID = uid;
73}
74
75void NuPlayer::setDriver(const wp<NuPlayerDriver> &driver) {
76    mDriver = driver;
77}
78
79void NuPlayer::setDataSource(const sp<IStreamSource> &source) {
80    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
81
82    msg->setObject("source", new StreamingSource(source));
83    msg->post();
84}
85
86void NuPlayer::setDataSource(
87        const char *url, const KeyedVector<String8, String8> *headers) {
88    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
89
90    msg->setObject("source", new HTTPLiveSource(url, headers, mUIDValid, mUID));
91    msg->post();
92}
93
94void NuPlayer::setVideoSurface(const sp<Surface> &surface) {
95    sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
96    msg->setObject("native-window", new NativeWindowWrapper(surface));
97    msg->post();
98}
99
100void NuPlayer::setVideoSurfaceTexture(const sp<ISurfaceTexture> &surfaceTexture) {
101    sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
102    sp<SurfaceTextureClient> surfaceTextureClient(surfaceTexture != NULL ?
103                new SurfaceTextureClient(surfaceTexture) : NULL);
104    msg->setObject("native-window", new NativeWindowWrapper(surfaceTextureClient));
105    msg->post();
106}
107
108void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
109    sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
110    msg->setObject("sink", sink);
111    msg->post();
112}
113
114void NuPlayer::start() {
115    (new AMessage(kWhatStart, id()))->post();
116}
117
118void NuPlayer::pause() {
119    (new AMessage(kWhatPause, id()))->post();
120}
121
122void NuPlayer::resume() {
123    (new AMessage(kWhatResume, id()))->post();
124}
125
126void NuPlayer::resetAsync() {
127    (new AMessage(kWhatReset, id()))->post();
128}
129
130void NuPlayer::seekToAsync(int64_t seekTimeUs) {
131    sp<AMessage> msg = new AMessage(kWhatSeek, id());
132    msg->setInt64("seekTimeUs", seekTimeUs);
133    msg->post();
134}
135
136// static
137bool NuPlayer::IsFlushingState(FlushStatus state, bool *needShutdown) {
138    switch (state) {
139        case FLUSHING_DECODER:
140            if (needShutdown != NULL) {
141                *needShutdown = false;
142            }
143            return true;
144
145        case FLUSHING_DECODER_SHUTDOWN:
146            if (needShutdown != NULL) {
147                *needShutdown = true;
148            }
149            return true;
150
151        default:
152            return false;
153    }
154}
155
156void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
157    switch (msg->what()) {
158        case kWhatSetDataSource:
159        {
160            LOGV("kWhatSetDataSource");
161
162            CHECK(mSource == NULL);
163
164            sp<RefBase> obj;
165            CHECK(msg->findObject("source", &obj));
166
167            mSource = static_cast<Source *>(obj.get());
168            break;
169        }
170
171        case kWhatSetVideoNativeWindow:
172        {
173            LOGV("kWhatSetVideoNativeWindow");
174
175            sp<RefBase> obj;
176            CHECK(msg->findObject("native-window", &obj));
177
178            mNativeWindow = static_cast<NativeWindowWrapper *>(obj.get());
179            break;
180        }
181
182        case kWhatSetAudioSink:
183        {
184            LOGV("kWhatSetAudioSink");
185
186            sp<RefBase> obj;
187            CHECK(msg->findObject("sink", &obj));
188
189            mAudioSink = static_cast<MediaPlayerBase::AudioSink *>(obj.get());
190            break;
191        }
192
193        case kWhatStart:
194        {
195            LOGV("kWhatStart");
196
197            mVideoIsAVC = false;
198            mAudioEOS = false;
199            mVideoEOS = false;
200            mSkipRenderingAudioUntilMediaTimeUs = -1;
201            mSkipRenderingVideoUntilMediaTimeUs = -1;
202            mVideoLateByUs = 0;
203            mNumFramesTotal = 0;
204            mNumFramesDropped = 0;
205
206            mSource->start();
207
208            mRenderer = new Renderer(
209                    mAudioSink,
210                    new AMessage(kWhatRendererNotify, id()));
211
212            looper()->registerHandler(mRenderer);
213
214            postScanSources();
215            break;
216        }
217
218        case kWhatScanSources:
219        {
220            int32_t generation;
221            CHECK(msg->findInt32("generation", &generation));
222            if (generation != mScanSourcesGeneration) {
223                // Drop obsolete msg.
224                break;
225            }
226
227            mScanSourcesPending = false;
228
229            LOGV("scanning sources haveAudio=%d, haveVideo=%d",
230                 mAudioDecoder != NULL, mVideoDecoder != NULL);
231
232            instantiateDecoder(false, &mVideoDecoder);
233
234            if (mAudioSink != NULL) {
235                instantiateDecoder(true, &mAudioDecoder);
236            }
237
238            if (!mSource->feedMoreTSData()) {
239                if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
240                    // We're not currently decoding anything (no audio or
241                    // video tracks found) and we just ran out of input data.
242                    notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
243                }
244                break;
245            }
246
247            if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
248                msg->post(100000ll);
249                mScanSourcesPending = true;
250            }
251            break;
252        }
253
254        case kWhatVideoNotify:
255        case kWhatAudioNotify:
256        {
257            bool audio = msg->what() == kWhatAudioNotify;
258
259            sp<AMessage> codecRequest;
260            CHECK(msg->findMessage("codec-request", &codecRequest));
261
262            int32_t what;
263            CHECK(codecRequest->findInt32("what", &what));
264
265            if (what == ACodec::kWhatFillThisBuffer) {
266                status_t err = feedDecoderInputData(
267                        audio, codecRequest);
268
269                if (err == -EWOULDBLOCK) {
270                    if (mSource->feedMoreTSData()) {
271                        msg->post();
272                    }
273                }
274            } else if (what == ACodec::kWhatEOS) {
275                int32_t err;
276                CHECK(codecRequest->findInt32("err", &err));
277
278                if (err == ERROR_END_OF_STREAM) {
279                    LOGV("got %s decoder EOS", audio ? "audio" : "video");
280                } else {
281                    LOGV("got %s decoder EOS w/ error %d",
282                         audio ? "audio" : "video",
283                         err);
284                }
285
286                mRenderer->queueEOS(audio, err);
287            } else if (what == ACodec::kWhatFlushCompleted) {
288                bool needShutdown;
289
290                if (audio) {
291                    CHECK(IsFlushingState(mFlushingAudio, &needShutdown));
292                    mFlushingAudio = FLUSHED;
293                } else {
294                    CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
295                    mFlushingVideo = FLUSHED;
296
297                    mVideoLateByUs = 0;
298                }
299
300                LOGV("decoder %s flush completed", audio ? "audio" : "video");
301
302                if (needShutdown) {
303                    LOGV("initiating %s decoder shutdown",
304                         audio ? "audio" : "video");
305
306                    (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
307
308                    if (audio) {
309                        mFlushingAudio = SHUTTING_DOWN_DECODER;
310                    } else {
311                        mFlushingVideo = SHUTTING_DOWN_DECODER;
312                    }
313                }
314
315                finishFlushIfPossible();
316            } else if (what == ACodec::kWhatOutputFormatChanged) {
317                if (audio) {
318                    int32_t numChannels;
319                    CHECK(codecRequest->findInt32("channel-count", &numChannels));
320
321                    int32_t sampleRate;
322                    CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
323
324                    LOGV("Audio output format changed to %d Hz, %d channels",
325                         sampleRate, numChannels);
326
327                    mAudioSink->close();
328                    CHECK_EQ(mAudioSink->open(
329                                sampleRate,
330                                numChannels,
331                                AUDIO_FORMAT_PCM_16_BIT,
332                                8 /* bufferCount */),
333                             (status_t)OK);
334                    mAudioSink->start();
335
336                    mRenderer->signalAudioSinkChanged();
337                } else {
338                    // video
339
340                    int32_t width, height;
341                    CHECK(codecRequest->findInt32("width", &width));
342                    CHECK(codecRequest->findInt32("height", &height));
343
344                    int32_t cropLeft, cropTop, cropRight, cropBottom;
345                    CHECK(codecRequest->findRect(
346                                "crop",
347                                &cropLeft, &cropTop, &cropRight, &cropBottom));
348
349                    LOGV("Video output format changed to %d x %d "
350                         "(crop: %d x %d @ (%d, %d))",
351                         width, height,
352                         (cropRight - cropLeft + 1),
353                         (cropBottom - cropTop + 1),
354                         cropLeft, cropTop);
355
356                    notifyListener(
357                            MEDIA_SET_VIDEO_SIZE,
358                            cropRight - cropLeft + 1,
359                            cropBottom - cropTop + 1);
360                }
361            } else if (what == ACodec::kWhatShutdownCompleted) {
362                LOGV("%s shutdown completed", audio ? "audio" : "video");
363                if (audio) {
364                    mAudioDecoder.clear();
365
366                    CHECK_EQ((int)mFlushingAudio, (int)SHUTTING_DOWN_DECODER);
367                    mFlushingAudio = SHUT_DOWN;
368                } else {
369                    mVideoDecoder.clear();
370
371                    CHECK_EQ((int)mFlushingVideo, (int)SHUTTING_DOWN_DECODER);
372                    mFlushingVideo = SHUT_DOWN;
373                }
374
375                finishFlushIfPossible();
376            } else if (what == ACodec::kWhatError) {
377                LOGE("Received error from %s decoder, aborting playback.",
378                     audio ? "audio" : "video");
379
380                mRenderer->queueEOS(audio, UNKNOWN_ERROR);
381            } else {
382                CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
383
384                renderBuffer(audio, codecRequest);
385            }
386
387            break;
388        }
389
390        case kWhatRendererNotify:
391        {
392            int32_t what;
393            CHECK(msg->findInt32("what", &what));
394
395            if (what == Renderer::kWhatEOS) {
396                int32_t audio;
397                CHECK(msg->findInt32("audio", &audio));
398
399                int32_t finalResult;
400                CHECK(msg->findInt32("finalResult", &finalResult));
401
402                if (audio) {
403                    mAudioEOS = true;
404                } else {
405                    mVideoEOS = true;
406                }
407
408                if (finalResult == ERROR_END_OF_STREAM) {
409                    LOGV("reached %s EOS", audio ? "audio" : "video");
410                } else {
411                    LOGE("%s track encountered an error (%d)",
412                         audio ? "audio" : "video", finalResult);
413
414                    notifyListener(
415                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, finalResult);
416                }
417
418                if ((mAudioEOS || mAudioDecoder == NULL)
419                        && (mVideoEOS || mVideoDecoder == NULL)) {
420                    notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
421                }
422            } else if (what == Renderer::kWhatPosition) {
423                int64_t positionUs;
424                CHECK(msg->findInt64("positionUs", &positionUs));
425
426                CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs));
427
428                if (mDriver != NULL) {
429                    sp<NuPlayerDriver> driver = mDriver.promote();
430                    if (driver != NULL) {
431                        driver->notifyPosition(positionUs);
432
433                        driver->notifyFrameStats(
434                                mNumFramesTotal, mNumFramesDropped);
435                    }
436                }
437            } else if (what == Renderer::kWhatFlushComplete) {
438                CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
439
440                int32_t audio;
441                CHECK(msg->findInt32("audio", &audio));
442
443                LOGV("renderer %s flush completed.", audio ? "audio" : "video");
444            }
445            break;
446        }
447
448        case kWhatMoreDataQueued:
449        {
450            break;
451        }
452
453        case kWhatReset:
454        {
455            LOGV("kWhatReset");
456
457            if (mFlushingAudio != NONE || mFlushingVideo != NONE) {
458                // We're currently flushing, postpone the reset until that's
459                // completed.
460
461                LOGV("postponing reset");
462
463                mResetPostponed = true;
464                break;
465            }
466
467            if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
468                finishReset();
469                break;
470            }
471
472            if (mAudioDecoder != NULL) {
473                flushDecoder(true /* audio */, true /* needShutdown */);
474            }
475
476            if (mVideoDecoder != NULL) {
477                flushDecoder(false /* audio */, true /* needShutdown */);
478            }
479
480            mResetInProgress = true;
481            break;
482        }
483
484        case kWhatSeek:
485        {
486            int64_t seekTimeUs;
487            CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
488
489            LOGV("kWhatSeek seekTimeUs=%lld us (%.2f secs)",
490                 seekTimeUs, seekTimeUs / 1E6);
491
492            mSource->seekTo(seekTimeUs);
493
494            if (mDriver != NULL) {
495                sp<NuPlayerDriver> driver = mDriver.promote();
496                if (driver != NULL) {
497                    driver->notifySeekComplete();
498                }
499            }
500
501            break;
502        }
503
504        case kWhatPause:
505        {
506            CHECK(mRenderer != NULL);
507            mRenderer->pause();
508            break;
509        }
510
511        case kWhatResume:
512        {
513            CHECK(mRenderer != NULL);
514            mRenderer->resume();
515            break;
516        }
517
518        default:
519            TRESPASS();
520            break;
521    }
522}
523
524void NuPlayer::finishFlushIfPossible() {
525    if (mFlushingAudio != FLUSHED && mFlushingAudio != SHUT_DOWN) {
526        return;
527    }
528
529    if (mFlushingVideo != FLUSHED && mFlushingVideo != SHUT_DOWN) {
530        return;
531    }
532
533    LOGV("both audio and video are flushed now.");
534
535    mRenderer->signalTimeDiscontinuity();
536
537    if (mAudioDecoder != NULL) {
538        mAudioDecoder->signalResume();
539    }
540
541    if (mVideoDecoder != NULL) {
542        mVideoDecoder->signalResume();
543    }
544
545    mFlushingAudio = NONE;
546    mFlushingVideo = NONE;
547
548    if (mResetInProgress) {
549        LOGV("reset completed");
550
551        mResetInProgress = false;
552        finishReset();
553    } else if (mResetPostponed) {
554        (new AMessage(kWhatReset, id()))->post();
555        mResetPostponed = false;
556    } else if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
557        postScanSources();
558    }
559}
560
561void NuPlayer::finishReset() {
562    CHECK(mAudioDecoder == NULL);
563    CHECK(mVideoDecoder == NULL);
564
565    mRenderer.clear();
566    mSource.clear();
567
568    if (mDriver != NULL) {
569        sp<NuPlayerDriver> driver = mDriver.promote();
570        if (driver != NULL) {
571            driver->notifyResetComplete();
572        }
573    }
574}
575
576void NuPlayer::postScanSources() {
577    if (mScanSourcesPending) {
578        return;
579    }
580
581    sp<AMessage> msg = new AMessage(kWhatScanSources, id());
582    msg->setInt32("generation", mScanSourcesGeneration);
583    msg->post();
584
585    mScanSourcesPending = true;
586}
587
588status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
589    if (*decoder != NULL) {
590        return OK;
591    }
592
593    sp<MetaData> meta = mSource->getFormat(audio);
594
595    if (meta == NULL) {
596        return -EWOULDBLOCK;
597    }
598
599    if (!audio) {
600        const char *mime;
601        CHECK(meta->findCString(kKeyMIMEType, &mime));
602        mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime);
603    }
604
605    sp<AMessage> notify =
606        new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
607                     id());
608
609    *decoder = audio ? new Decoder(notify) :
610                       new Decoder(notify, mNativeWindow);
611    looper()->registerHandler(*decoder);
612
613    (*decoder)->configure(meta);
614
615    int64_t durationUs;
616    if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
617        sp<NuPlayerDriver> driver = mDriver.promote();
618        if (driver != NULL) {
619            driver->notifyDuration(durationUs);
620        }
621    }
622
623    return OK;
624}
625
626status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
627    sp<AMessage> reply;
628    CHECK(msg->findMessage("reply", &reply));
629
630    if ((audio && IsFlushingState(mFlushingAudio))
631            || (!audio && IsFlushingState(mFlushingVideo))) {
632        reply->setInt32("err", INFO_DISCONTINUITY);
633        reply->post();
634        return OK;
635    }
636
637    sp<ABuffer> accessUnit;
638
639    bool dropAccessUnit;
640    do {
641        status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
642
643        if (err == -EWOULDBLOCK) {
644            return err;
645        } else if (err != OK) {
646            if (err == INFO_DISCONTINUITY) {
647                int32_t type;
648                CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
649
650                bool formatChange =
651                    type == ATSParser::DISCONTINUITY_FORMATCHANGE;
652
653                LOGV("%s discontinuity (formatChange=%d)",
654                     audio ? "audio" : "video", formatChange);
655
656                if (audio) {
657                    mSkipRenderingAudioUntilMediaTimeUs = -1;
658                } else {
659                    mSkipRenderingVideoUntilMediaTimeUs = -1;
660                }
661
662                sp<AMessage> extra;
663                if (accessUnit->meta()->findMessage("extra", &extra)
664                        && extra != NULL) {
665                    int64_t resumeAtMediaTimeUs;
666                    if (extra->findInt64(
667                                "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
668                        LOGI("suppressing rendering of %s until %lld us",
669                                audio ? "audio" : "video", resumeAtMediaTimeUs);
670
671                        if (audio) {
672                            mSkipRenderingAudioUntilMediaTimeUs =
673                                resumeAtMediaTimeUs;
674                        } else {
675                            mSkipRenderingVideoUntilMediaTimeUs =
676                                resumeAtMediaTimeUs;
677                        }
678                    }
679                }
680
681                flushDecoder(audio, formatChange);
682            }
683
684            reply->setInt32("err", err);
685            reply->post();
686            return OK;
687        }
688
689        if (!audio) {
690            ++mNumFramesTotal;
691        }
692
693        dropAccessUnit = false;
694        if (!audio
695                && mVideoLateByUs > 100000ll
696                && mVideoIsAVC
697                && !IsAVCReferenceFrame(accessUnit)) {
698            dropAccessUnit = true;
699            ++mNumFramesDropped;
700        }
701    } while (dropAccessUnit);
702
703    // LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
704
705#if 0
706    int64_t mediaTimeUs;
707    CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
708    LOGV("feeding %s input buffer at media time %.2f secs",
709         audio ? "audio" : "video",
710         mediaTimeUs / 1E6);
711#endif
712
713    reply->setObject("buffer", accessUnit);
714    reply->post();
715
716    return OK;
717}
718
719void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
720    // LOGV("renderBuffer %s", audio ? "audio" : "video");
721
722    sp<AMessage> reply;
723    CHECK(msg->findMessage("reply", &reply));
724
725    if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) {
726        // We're currently attempting to flush the decoder, in order
727        // to complete this, the decoder wants all its buffers back,
728        // so we don't want any output buffers it sent us (from before
729        // we initiated the flush) to be stuck in the renderer's queue.
730
731        LOGV("we're still flushing the %s decoder, sending its output buffer"
732             " right back.", audio ? "audio" : "video");
733
734        reply->post();
735        return;
736    }
737
738    sp<RefBase> obj;
739    CHECK(msg->findObject("buffer", &obj));
740
741    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
742
743    int64_t &skipUntilMediaTimeUs =
744        audio
745            ? mSkipRenderingAudioUntilMediaTimeUs
746            : mSkipRenderingVideoUntilMediaTimeUs;
747
748    if (skipUntilMediaTimeUs >= 0) {
749        int64_t mediaTimeUs;
750        CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs));
751
752        if (mediaTimeUs < skipUntilMediaTimeUs) {
753            LOGV("dropping %s buffer at time %lld as requested.",
754                 audio ? "audio" : "video",
755                 mediaTimeUs);
756
757            reply->post();
758            return;
759        }
760
761        skipUntilMediaTimeUs = -1;
762    }
763
764    mRenderer->queueBuffer(audio, buffer, reply);
765}
766
767void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
768    if (mDriver == NULL) {
769        return;
770    }
771
772    sp<NuPlayerDriver> driver = mDriver.promote();
773
774    if (driver == NULL) {
775        return;
776    }
777
778    driver->sendEvent(msg, ext1, ext2);
779}
780
781void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
782    // Make sure we don't continue to scan sources until we finish flushing.
783    ++mScanSourcesGeneration;
784    mScanSourcesPending = false;
785
786    (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
787    mRenderer->flush(audio);
788
789    FlushStatus newStatus =
790        needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER;
791
792    if (audio) {
793        CHECK(mFlushingAudio == NONE
794                || mFlushingAudio == AWAITING_DISCONTINUITY);
795
796        mFlushingAudio = newStatus;
797
798        if (mFlushingVideo == NONE) {
799            mFlushingVideo = (mVideoDecoder != NULL)
800                ? AWAITING_DISCONTINUITY
801                : FLUSHED;
802        }
803    } else {
804        CHECK(mFlushingVideo == NONE
805                || mFlushingVideo == AWAITING_DISCONTINUITY);
806
807        mFlushingVideo = newStatus;
808
809        if (mFlushingAudio == NONE) {
810            mFlushingAudio = (mAudioDecoder != NULL)
811                ? AWAITING_DISCONTINUITY
812                : FLUSHED;
813        }
814    }
815}
816
817}  // namespace android
818