NuPlayer.cpp revision 3fe62150fa3dd6d25cb84aad80bc9e27ddd16c45
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                mRenderer->queueEOS(audio, ERROR_END_OF_STREAM);
276            } else if (what == ACodec::kWhatFlushCompleted) {
277                bool needShutdown;
278
279                if (audio) {
280                    CHECK(IsFlushingState(mFlushingAudio, &needShutdown));
281                    mFlushingAudio = FLUSHED;
282                } else {
283                    CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
284                    mFlushingVideo = FLUSHED;
285
286                    mVideoLateByUs = 0;
287                }
288
289                LOGV("decoder %s flush completed", audio ? "audio" : "video");
290
291                if (needShutdown) {
292                    LOGV("initiating %s decoder shutdown",
293                         audio ? "audio" : "video");
294
295                    (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
296
297                    if (audio) {
298                        mFlushingAudio = SHUTTING_DOWN_DECODER;
299                    } else {
300                        mFlushingVideo = SHUTTING_DOWN_DECODER;
301                    }
302                }
303
304                finishFlushIfPossible();
305            } else if (what == ACodec::kWhatOutputFormatChanged) {
306                if (audio) {
307                    int32_t numChannels;
308                    CHECK(codecRequest->findInt32("channel-count", &numChannels));
309
310                    int32_t sampleRate;
311                    CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
312
313                    LOGV("Audio output format changed to %d Hz, %d channels",
314                         sampleRate, numChannels);
315
316                    mAudioSink->close();
317                    CHECK_EQ(mAudioSink->open(
318                                sampleRate,
319                                numChannels,
320                                AUDIO_FORMAT_PCM_16_BIT,
321                                8 /* bufferCount */),
322                             (status_t)OK);
323                    mAudioSink->start();
324
325                    mRenderer->signalAudioSinkChanged();
326                } else {
327                    // video
328
329                    int32_t width, height;
330                    CHECK(codecRequest->findInt32("width", &width));
331                    CHECK(codecRequest->findInt32("height", &height));
332
333                    int32_t cropLeft, cropTop, cropRight, cropBottom;
334                    CHECK(codecRequest->findRect(
335                                "crop",
336                                &cropLeft, &cropTop, &cropRight, &cropBottom));
337
338                    LOGV("Video output format changed to %d x %d "
339                         "(crop: %d x %d @ (%d, %d))",
340                         width, height,
341                         (cropRight - cropLeft + 1),
342                         (cropBottom - cropTop + 1),
343                         cropLeft, cropTop);
344
345                    notifyListener(
346                            MEDIA_SET_VIDEO_SIZE,
347                            cropRight - cropLeft + 1,
348                            cropBottom - cropTop + 1);
349                }
350            } else if (what == ACodec::kWhatShutdownCompleted) {
351                LOGV("%s shutdown completed", audio ? "audio" : "video");
352                if (audio) {
353                    mAudioDecoder.clear();
354
355                    CHECK_EQ((int)mFlushingAudio, (int)SHUTTING_DOWN_DECODER);
356                    mFlushingAudio = SHUT_DOWN;
357                } else {
358                    mVideoDecoder.clear();
359
360                    CHECK_EQ((int)mFlushingVideo, (int)SHUTTING_DOWN_DECODER);
361                    mFlushingVideo = SHUT_DOWN;
362                }
363
364                finishFlushIfPossible();
365            } else if (what == ACodec::kWhatError) {
366                LOGE("Received error from %s decoder, aborting playback.",
367                     audio ? "audio" : "video");
368
369                mRenderer->queueEOS(audio, UNKNOWN_ERROR);
370            } else {
371                CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
372
373                renderBuffer(audio, codecRequest);
374            }
375
376            break;
377        }
378
379        case kWhatRendererNotify:
380        {
381            int32_t what;
382            CHECK(msg->findInt32("what", &what));
383
384            if (what == Renderer::kWhatEOS) {
385                int32_t audio;
386                CHECK(msg->findInt32("audio", &audio));
387
388                int32_t finalResult;
389                CHECK(msg->findInt32("finalResult", &finalResult));
390
391                if (audio) {
392                    mAudioEOS = true;
393                } else {
394                    mVideoEOS = true;
395                }
396
397                if (finalResult == ERROR_END_OF_STREAM) {
398                    LOGV("reached %s EOS", audio ? "audio" : "video");
399                } else {
400                    LOGE("%s track encountered an error (0x%08x)",
401                         audio ? "audio" : "video", finalResult);
402
403                    notifyListener(
404                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, finalResult);
405                }
406
407                if ((mAudioEOS || mAudioDecoder == NULL)
408                        && (mVideoEOS || mVideoDecoder == NULL)) {
409                    notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
410                }
411            } else if (what == Renderer::kWhatPosition) {
412                int64_t positionUs;
413                CHECK(msg->findInt64("positionUs", &positionUs));
414
415                CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs));
416
417                if (mDriver != NULL) {
418                    sp<NuPlayerDriver> driver = mDriver.promote();
419                    if (driver != NULL) {
420                        driver->notifyPosition(positionUs);
421
422                        driver->notifyFrameStats(
423                                mNumFramesTotal, mNumFramesDropped);
424                    }
425                }
426            } else if (what == Renderer::kWhatFlushComplete) {
427                CHECK_EQ(what, (int32_t)Renderer::kWhatFlushComplete);
428
429                int32_t audio;
430                CHECK(msg->findInt32("audio", &audio));
431
432                LOGV("renderer %s flush completed.", audio ? "audio" : "video");
433            }
434            break;
435        }
436
437        case kWhatMoreDataQueued:
438        {
439            break;
440        }
441
442        case kWhatReset:
443        {
444            LOGV("kWhatReset");
445
446            if (mFlushingAudio != NONE || mFlushingVideo != NONE) {
447                // We're currently flushing, postpone the reset until that's
448                // completed.
449
450                LOGV("postponing reset");
451
452                mResetPostponed = true;
453                break;
454            }
455
456            if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
457                finishReset();
458                break;
459            }
460
461            if (mAudioDecoder != NULL) {
462                flushDecoder(true /* audio */, true /* needShutdown */);
463            }
464
465            if (mVideoDecoder != NULL) {
466                flushDecoder(false /* audio */, true /* needShutdown */);
467            }
468
469            mResetInProgress = true;
470            break;
471        }
472
473        case kWhatSeek:
474        {
475            int64_t seekTimeUs;
476            CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
477
478            LOGV("kWhatSeek seekTimeUs=%lld us (%.2f secs)",
479                 seekTimeUs, seekTimeUs / 1E6);
480
481            mSource->seekTo(seekTimeUs);
482
483            if (mDriver != NULL) {
484                sp<NuPlayerDriver> driver = mDriver.promote();
485                if (driver != NULL) {
486                    driver->notifySeekComplete();
487                }
488            }
489
490            break;
491        }
492
493        case kWhatPause:
494        {
495            CHECK(mRenderer != NULL);
496            mRenderer->pause();
497            break;
498        }
499
500        case kWhatResume:
501        {
502            CHECK(mRenderer != NULL);
503            mRenderer->resume();
504            break;
505        }
506
507        default:
508            TRESPASS();
509            break;
510    }
511}
512
513void NuPlayer::finishFlushIfPossible() {
514    if (mFlushingAudio != FLUSHED && mFlushingAudio != SHUT_DOWN) {
515        return;
516    }
517
518    if (mFlushingVideo != FLUSHED && mFlushingVideo != SHUT_DOWN) {
519        return;
520    }
521
522    LOGV("both audio and video are flushed now.");
523
524    mRenderer->signalTimeDiscontinuity();
525
526    if (mAudioDecoder != NULL) {
527        mAudioDecoder->signalResume();
528    }
529
530    if (mVideoDecoder != NULL) {
531        mVideoDecoder->signalResume();
532    }
533
534    mFlushingAudio = NONE;
535    mFlushingVideo = NONE;
536
537    if (mResetInProgress) {
538        LOGV("reset completed");
539
540        mResetInProgress = false;
541        finishReset();
542    } else if (mResetPostponed) {
543        (new AMessage(kWhatReset, id()))->post();
544        mResetPostponed = false;
545    } else if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
546        postScanSources();
547    }
548}
549
550void NuPlayer::finishReset() {
551    CHECK(mAudioDecoder == NULL);
552    CHECK(mVideoDecoder == NULL);
553
554    mRenderer.clear();
555    mSource.clear();
556
557    if (mDriver != NULL) {
558        sp<NuPlayerDriver> driver = mDriver.promote();
559        if (driver != NULL) {
560            driver->notifyResetComplete();
561        }
562    }
563}
564
565void NuPlayer::postScanSources() {
566    if (mScanSourcesPending) {
567        return;
568    }
569
570    sp<AMessage> msg = new AMessage(kWhatScanSources, id());
571    msg->setInt32("generation", mScanSourcesGeneration);
572    msg->post();
573
574    mScanSourcesPending = true;
575}
576
577status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
578    if (*decoder != NULL) {
579        return OK;
580    }
581
582    sp<MetaData> meta = mSource->getFormat(audio);
583
584    if (meta == NULL) {
585        return -EWOULDBLOCK;
586    }
587
588    if (!audio) {
589        const char *mime;
590        CHECK(meta->findCString(kKeyMIMEType, &mime));
591        mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime);
592    }
593
594    sp<AMessage> notify =
595        new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
596                     id());
597
598    *decoder = audio ? new Decoder(notify) :
599                       new Decoder(notify, mNativeWindow);
600    looper()->registerHandler(*decoder);
601
602    (*decoder)->configure(meta);
603
604    int64_t durationUs;
605    if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
606        sp<NuPlayerDriver> driver = mDriver.promote();
607        if (driver != NULL) {
608            driver->notifyDuration(durationUs);
609        }
610    }
611
612    return OK;
613}
614
615status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
616    sp<AMessage> reply;
617    CHECK(msg->findMessage("reply", &reply));
618
619    if ((audio && IsFlushingState(mFlushingAudio))
620            || (!audio && IsFlushingState(mFlushingVideo))) {
621        reply->setInt32("err", INFO_DISCONTINUITY);
622        reply->post();
623        return OK;
624    }
625
626    sp<ABuffer> accessUnit;
627
628    bool dropAccessUnit;
629    do {
630        status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
631
632        if (err == -EWOULDBLOCK) {
633            return err;
634        } else if (err != OK) {
635            if (err == INFO_DISCONTINUITY) {
636                int32_t type;
637                CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
638
639                bool formatChange =
640                    type == ATSParser::DISCONTINUITY_FORMATCHANGE;
641
642                LOGV("%s discontinuity (formatChange=%d)",
643                     audio ? "audio" : "video", formatChange);
644
645                if (audio) {
646                    mSkipRenderingAudioUntilMediaTimeUs = -1;
647                } else {
648                    mSkipRenderingVideoUntilMediaTimeUs = -1;
649                }
650
651                sp<AMessage> extra;
652                if (accessUnit->meta()->findMessage("extra", &extra)
653                        && extra != NULL) {
654                    int64_t resumeAtMediaTimeUs;
655                    if (extra->findInt64(
656                                "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
657                        LOGI("suppressing rendering of %s until %lld us",
658                                audio ? "audio" : "video", resumeAtMediaTimeUs);
659
660                        if (audio) {
661                            mSkipRenderingAudioUntilMediaTimeUs =
662                                resumeAtMediaTimeUs;
663                        } else {
664                            mSkipRenderingVideoUntilMediaTimeUs =
665                                resumeAtMediaTimeUs;
666                        }
667                    }
668                }
669
670                flushDecoder(audio, formatChange);
671            }
672
673            reply->setInt32("err", err);
674            reply->post();
675            return OK;
676        }
677
678        if (!audio) {
679            ++mNumFramesTotal;
680        }
681
682        dropAccessUnit = false;
683        if (!audio
684                && mVideoLateByUs > 100000ll
685                && mVideoIsAVC
686                && !IsAVCReferenceFrame(accessUnit)) {
687            dropAccessUnit = true;
688            ++mNumFramesDropped;
689        }
690    } while (dropAccessUnit);
691
692    // LOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
693
694#if 0
695    int64_t mediaTimeUs;
696    CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
697    LOGV("feeding %s input buffer at media time %.2f secs",
698         audio ? "audio" : "video",
699         mediaTimeUs / 1E6);
700#endif
701
702    reply->setObject("buffer", accessUnit);
703    reply->post();
704
705    return OK;
706}
707
708void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
709    // LOGV("renderBuffer %s", audio ? "audio" : "video");
710
711    sp<AMessage> reply;
712    CHECK(msg->findMessage("reply", &reply));
713
714    if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) {
715        // We're currently attempting to flush the decoder, in order
716        // to complete this, the decoder wants all its buffers back,
717        // so we don't want any output buffers it sent us (from before
718        // we initiated the flush) to be stuck in the renderer's queue.
719
720        LOGV("we're still flushing the %s decoder, sending its output buffer"
721             " right back.", audio ? "audio" : "video");
722
723        reply->post();
724        return;
725    }
726
727    sp<RefBase> obj;
728    CHECK(msg->findObject("buffer", &obj));
729
730    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
731
732    int64_t &skipUntilMediaTimeUs =
733        audio
734            ? mSkipRenderingAudioUntilMediaTimeUs
735            : mSkipRenderingVideoUntilMediaTimeUs;
736
737    if (skipUntilMediaTimeUs >= 0) {
738        int64_t mediaTimeUs;
739        CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs));
740
741        if (mediaTimeUs < skipUntilMediaTimeUs) {
742            LOGV("dropping %s buffer at time %lld as requested.",
743                 audio ? "audio" : "video",
744                 mediaTimeUs);
745
746            reply->post();
747            return;
748        }
749
750        skipUntilMediaTimeUs = -1;
751    }
752
753    mRenderer->queueBuffer(audio, buffer, reply);
754}
755
756void NuPlayer::notifyListener(int msg, int ext1, int ext2) {
757    if (mDriver == NULL) {
758        return;
759    }
760
761    sp<NuPlayerDriver> driver = mDriver.promote();
762
763    if (driver == NULL) {
764        return;
765    }
766
767    driver->sendEvent(msg, ext1, ext2);
768}
769
770void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
771    // Make sure we don't continue to scan sources until we finish flushing.
772    ++mScanSourcesGeneration;
773    mScanSourcesPending = false;
774
775    (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
776    mRenderer->flush(audio);
777
778    FlushStatus newStatus =
779        needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER;
780
781    if (audio) {
782        CHECK(mFlushingAudio == NONE
783                || mFlushingAudio == AWAITING_DISCONTINUITY);
784
785        mFlushingAudio = newStatus;
786
787        if (mFlushingVideo == NONE) {
788            mFlushingVideo = (mVideoDecoder != NULL)
789                ? AWAITING_DISCONTINUITY
790                : FLUSHED;
791        }
792    } else {
793        CHECK(mFlushingVideo == NONE
794                || mFlushingVideo == AWAITING_DISCONTINUITY);
795
796        mFlushingVideo = newStatus;
797
798        if (mFlushingAudio == NONE) {
799            mFlushingAudio = (mAudioDecoder != NULL)
800                ? AWAITING_DISCONTINUITY
801                : FLUSHED;
802        }
803    }
804}
805
806}  // namespace android
807