NuPlayerRenderer.cpp revision 28a8a9ff2a2bfd5edbdbbadde50c6d804335ffdc
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 "NuPlayerRenderer"
19#include <utils/Log.h>
20
21#include "NuPlayerRenderer.h"
22
23#include <media/stagefright/foundation/ABuffer.h>
24#include <media/stagefright/foundation/ADebug.h>
25#include <media/stagefright/foundation/AMessage.h>
26#include <media/stagefright/MediaErrors.h>
27#include <media/stagefright/MetaData.h>
28
29#include <inttypes.h>
30
31namespace android {
32
33// static
34const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll;
35
36NuPlayer::Renderer::Renderer(
37        const sp<MediaPlayerBase::AudioSink> &sink,
38        const sp<AMessage> &notify,
39        uint32_t flags)
40    : mAudioSink(sink),
41      mNotify(notify),
42      mFlags(flags),
43      mNumFramesWritten(0),
44      mDrainAudioQueuePending(false),
45      mDrainVideoQueuePending(false),
46      mAudioQueueGeneration(0),
47      mVideoQueueGeneration(0),
48      mFirstAudioTimeUs(-1),
49      mAnchorTimeMediaUs(-1),
50      mAnchorTimeRealUs(-1),
51      mFlushingAudio(false),
52      mFlushingVideo(false),
53      mHasAudio(false),
54      mHasVideo(false),
55      mSyncQueues(false),
56      mPaused(false),
57      mVideoRenderingStarted(false),
58      mVideoRenderingStartGeneration(0),
59      mAudioRenderingStartGeneration(0),
60      mLastPositionUpdateUs(-1ll),
61      mVideoLateByUs(0ll) {
62}
63
64NuPlayer::Renderer::~Renderer() {
65    if (offloadingAudio()) {
66        mAudioSink->stop();
67        mAudioSink->flush();
68        mAudioSink->close();
69    }
70}
71
72void NuPlayer::Renderer::queueBuffer(
73        bool audio,
74        const sp<ABuffer> &buffer,
75        const sp<AMessage> &notifyConsumed) {
76    sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
77    msg->setInt32("audio", static_cast<int32_t>(audio));
78    msg->setBuffer("buffer", buffer);
79    msg->setMessage("notifyConsumed", notifyConsumed);
80    msg->post();
81}
82
83void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
84    CHECK_NE(finalResult, (status_t)OK);
85
86    sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
87    msg->setInt32("audio", static_cast<int32_t>(audio));
88    msg->setInt32("finalResult", finalResult);
89    msg->post();
90}
91
92void NuPlayer::Renderer::flush(bool audio) {
93    {
94        Mutex::Autolock autoLock(mFlushLock);
95        if (audio) {
96            if (mFlushingAudio) {
97                return;
98            }
99            mFlushingAudio = true;
100        } else {
101            if (mFlushingVideo) {
102                return;
103            }
104            mFlushingVideo = true;
105        }
106    }
107
108    sp<AMessage> msg = new AMessage(kWhatFlush, id());
109    msg->setInt32("audio", static_cast<int32_t>(audio));
110    msg->post();
111}
112
113void NuPlayer::Renderer::signalTimeDiscontinuity() {
114    Mutex::Autolock autoLock(mLock);
115    // CHECK(mAudioQueue.empty());
116    // CHECK(mVideoQueue.empty());
117    mAnchorTimeMediaUs = -1;
118    mAnchorTimeRealUs = -1;
119    mSyncQueues = false;
120}
121
122void NuPlayer::Renderer::signalAudioSinkChanged() {
123    (new AMessage(kWhatAudioSinkChanged, id()))->post();
124}
125
126void NuPlayer::Renderer::signalDisableOffloadAudio() {
127    (new AMessage(kWhatDisableOffloadAudio, id()))->post();
128}
129
130void NuPlayer::Renderer::pause() {
131    (new AMessage(kWhatPause, id()))->post();
132}
133
134void NuPlayer::Renderer::resume() {
135    (new AMessage(kWhatResume, id()))->post();
136}
137
138void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
139    switch (msg->what()) {
140        case kWhatStopAudioSink:
141        {
142            mAudioSink->stop();
143            break;
144        }
145
146        case kWhatDrainAudioQueue:
147        {
148            int32_t generation;
149            CHECK(msg->findInt32("generation", &generation));
150            if (generation != mAudioQueueGeneration) {
151                break;
152            }
153
154            mDrainAudioQueuePending = false;
155
156            if (onDrainAudioQueue()) {
157                uint32_t numFramesPlayed;
158                CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed),
159                         (status_t)OK);
160
161                uint32_t numFramesPendingPlayout =
162                    mNumFramesWritten - numFramesPlayed;
163
164                // This is how long the audio sink will have data to
165                // play back.
166                int64_t delayUs =
167                    mAudioSink->msecsPerFrame()
168                        * numFramesPendingPlayout * 1000ll;
169
170                // Let's give it more data after about half that time
171                // has elapsed.
172                // kWhatDrainAudioQueue is used for non-offloading mode,
173                // and mLock is used only for offloading mode. Therefore,
174                // no need to acquire mLock here.
175                postDrainAudioQueue_l(delayUs / 2);
176            }
177            break;
178        }
179
180        case kWhatDrainVideoQueue:
181        {
182            int32_t generation;
183            CHECK(msg->findInt32("generation", &generation));
184            if (generation != mVideoQueueGeneration) {
185                break;
186            }
187
188            mDrainVideoQueuePending = false;
189
190            onDrainVideoQueue();
191
192            postDrainVideoQueue();
193            break;
194        }
195
196        case kWhatQueueBuffer:
197        {
198            onQueueBuffer(msg);
199            break;
200        }
201
202        case kWhatQueueEOS:
203        {
204            onQueueEOS(msg);
205            break;
206        }
207
208        case kWhatFlush:
209        {
210            onFlush(msg);
211            break;
212        }
213
214        case kWhatAudioSinkChanged:
215        {
216            onAudioSinkChanged();
217            break;
218        }
219
220        case kWhatDisableOffloadAudio:
221        {
222            onDisableOffloadAudio();
223            break;
224        }
225
226        case kWhatPause:
227        {
228            onPause();
229            break;
230        }
231
232        case kWhatResume:
233        {
234            onResume();
235            break;
236        }
237
238        case kWhatAudioOffloadTearDown:
239        {
240            onAudioOffloadTearDown();
241            break;
242        }
243
244        default:
245            TRESPASS();
246            break;
247    }
248}
249
250void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) {
251    if (mDrainAudioQueuePending || mSyncQueues || mPaused
252            || offloadingAudio()) {
253        return;
254    }
255
256    if (mAudioQueue.empty()) {
257        return;
258    }
259
260    mDrainAudioQueuePending = true;
261    sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
262    msg->setInt32("generation", mAudioQueueGeneration);
263    msg->post(delayUs);
264}
265
266void NuPlayer::Renderer::prepareForMediaRenderingStart() {
267    mAudioRenderingStartGeneration = mAudioQueueGeneration;
268    mVideoRenderingStartGeneration = mVideoQueueGeneration;
269}
270
271void NuPlayer::Renderer::notifyIfMediaRenderingStarted() {
272    if (mVideoRenderingStartGeneration == mVideoQueueGeneration &&
273        mAudioRenderingStartGeneration == mAudioQueueGeneration) {
274        mVideoRenderingStartGeneration = -1;
275        mAudioRenderingStartGeneration = -1;
276
277        sp<AMessage> notify = mNotify->dup();
278        notify->setInt32("what", kWhatMediaRenderingStart);
279        notify->post();
280    }
281}
282
283// static
284size_t NuPlayer::Renderer::AudioSinkCallback(
285        MediaPlayerBase::AudioSink * /* audioSink */,
286        void *buffer,
287        size_t size,
288        void *cookie,
289        MediaPlayerBase::AudioSink::cb_event_t event) {
290    NuPlayer::Renderer *me = (NuPlayer::Renderer *)cookie;
291
292    switch (event) {
293        case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
294        {
295            return me->fillAudioBuffer(buffer, size);
296            break;
297        }
298
299        case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END:
300        {
301            me->notifyEOS(true /* audio */, ERROR_END_OF_STREAM);
302            break;
303        }
304
305        case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN:
306        {
307            me->notifyAudioOffloadTearDown();
308            break;
309        }
310    }
311
312    return 0;
313}
314
315size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
316    Mutex::Autolock autoLock(mLock);
317
318    if (!offloadingAudio()) {
319        return 0;
320    }
321
322    bool hasEOS = false;
323
324    size_t sizeCopied = 0;
325    while (sizeCopied < size && !mAudioQueue.empty()) {
326        QueueEntry *entry = &*mAudioQueue.begin();
327
328        if (entry->mBuffer == NULL) { // EOS
329            hasEOS = true;
330            mAudioQueue.erase(mAudioQueue.begin());
331            entry = NULL;
332            break;
333        }
334
335        if (entry->mOffset == 0) {
336            int64_t mediaTimeUs;
337            CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
338            ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
339            if (mFirstAudioTimeUs == -1) {
340                mFirstAudioTimeUs = mediaTimeUs;
341            }
342            mAnchorTimeMediaUs = mediaTimeUs;
343
344            uint32_t numFramesPlayed;
345            CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
346
347            // TODO: figure out how to calculate initial latency.
348            // Otherwise, the initial time is not correct till the first sample
349            // is played.
350            mAnchorTimeMediaUs = mFirstAudioTimeUs
351                    + (numFramesPlayed * mAudioSink->msecsPerFrame()) * 1000ll;
352            mAnchorTimeRealUs = ALooper::GetNowUs();
353        }
354
355        size_t copy = entry->mBuffer->size() - entry->mOffset;
356        size_t sizeRemaining = size - sizeCopied;
357        if (copy > sizeRemaining) {
358            copy = sizeRemaining;
359        }
360
361        memcpy((char *)buffer + sizeCopied,
362               entry->mBuffer->data() + entry->mOffset,
363               copy);
364
365        entry->mOffset += copy;
366        if (entry->mOffset == entry->mBuffer->size()) {
367            entry->mNotifyConsumed->post();
368            mAudioQueue.erase(mAudioQueue.begin());
369            entry = NULL;
370        }
371        sizeCopied += copy;
372        notifyIfMediaRenderingStarted();
373    }
374
375    if (sizeCopied != 0) {
376        notifyPosition();
377    }
378
379    if (hasEOS) {
380        (new AMessage(kWhatStopAudioSink, id()))->post();
381    }
382
383    return sizeCopied;
384}
385
386bool NuPlayer::Renderer::onDrainAudioQueue() {
387    uint32_t numFramesPlayed;
388    if (mAudioSink->getPosition(&numFramesPlayed) != OK) {
389        return false;
390    }
391
392    ssize_t numFramesAvailableToWrite =
393        mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
394
395#if 0
396    if (numFramesAvailableToWrite == mAudioSink->frameCount()) {
397        ALOGI("audio sink underrun");
398    } else {
399        ALOGV("audio queue has %d frames left to play",
400             mAudioSink->frameCount() - numFramesAvailableToWrite);
401    }
402#endif
403
404    size_t numBytesAvailableToWrite =
405        numFramesAvailableToWrite * mAudioSink->frameSize();
406
407    while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) {
408        QueueEntry *entry = &*mAudioQueue.begin();
409
410        if (entry->mBuffer == NULL) {
411            // EOS
412
413            notifyEOS(true /* audio */, entry->mFinalResult);
414
415            mAudioQueue.erase(mAudioQueue.begin());
416            entry = NULL;
417            return false;
418        }
419
420        if (entry->mOffset == 0) {
421            int64_t mediaTimeUs;
422            CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
423
424            ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
425
426            mAnchorTimeMediaUs = mediaTimeUs;
427
428            uint32_t numFramesPlayed;
429            CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
430
431            uint32_t numFramesPendingPlayout =
432                mNumFramesWritten - numFramesPlayed;
433
434            int64_t realTimeOffsetUs =
435                (mAudioSink->latency() / 2  /* XXX */
436                    + numFramesPendingPlayout
437                        * mAudioSink->msecsPerFrame()) * 1000ll;
438
439            // ALOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs);
440
441            mAnchorTimeRealUs =
442                ALooper::GetNowUs() + realTimeOffsetUs;
443        }
444
445        size_t copy = entry->mBuffer->size() - entry->mOffset;
446        if (copy > numBytesAvailableToWrite) {
447            copy = numBytesAvailableToWrite;
448        }
449
450        CHECK_EQ(mAudioSink->write(
451                    entry->mBuffer->data() + entry->mOffset, copy),
452                 (ssize_t)copy);
453
454        entry->mOffset += copy;
455        if (entry->mOffset == entry->mBuffer->size()) {
456            entry->mNotifyConsumed->post();
457            mAudioQueue.erase(mAudioQueue.begin());
458
459            entry = NULL;
460        }
461
462        numBytesAvailableToWrite -= copy;
463        size_t copiedFrames = copy / mAudioSink->frameSize();
464        mNumFramesWritten += copiedFrames;
465
466        notifyIfMediaRenderingStarted();
467    }
468
469    notifyPosition();
470
471    return !mAudioQueue.empty();
472}
473
474void NuPlayer::Renderer::postDrainVideoQueue() {
475    if (mDrainVideoQueuePending || mSyncQueues || mPaused) {
476        return;
477    }
478
479    if (mVideoQueue.empty()) {
480        return;
481    }
482
483    QueueEntry &entry = *mVideoQueue.begin();
484
485    sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
486    msg->setInt32("generation", mVideoQueueGeneration);
487
488    int64_t delayUs;
489
490    if (entry.mBuffer == NULL) {
491        // EOS doesn't carry a timestamp.
492        delayUs = 0;
493    } else if (mFlags & FLAG_REAL_TIME) {
494        int64_t mediaTimeUs;
495        CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
496
497        delayUs = mediaTimeUs - ALooper::GetNowUs();
498    } else {
499        int64_t mediaTimeUs;
500        CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
501
502        if (mAnchorTimeMediaUs < 0) {
503            delayUs = 0;
504
505            if (!mHasAudio) {
506                mAnchorTimeMediaUs = mediaTimeUs;
507                mAnchorTimeRealUs = ALooper::GetNowUs();
508            }
509        } else {
510            int64_t realTimeUs =
511                (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
512
513            delayUs = realTimeUs - ALooper::GetNowUs();
514        }
515    }
516
517    ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
518    msg->post(delayUs);
519
520    mDrainVideoQueuePending = true;
521}
522
523void NuPlayer::Renderer::onDrainVideoQueue() {
524    if (mVideoQueue.empty()) {
525        return;
526    }
527
528    QueueEntry *entry = &*mVideoQueue.begin();
529
530    if (entry->mBuffer == NULL) {
531        // EOS
532
533        notifyEOS(false /* audio */, entry->mFinalResult);
534
535        mVideoQueue.erase(mVideoQueue.begin());
536        entry = NULL;
537
538        mVideoLateByUs = 0ll;
539
540        notifyPosition();
541        return;
542    }
543
544    int64_t realTimeUs;
545    if (mFlags & FLAG_REAL_TIME) {
546        CHECK(entry->mBuffer->meta()->findInt64("timeUs", &realTimeUs));
547    } else {
548        int64_t mediaTimeUs;
549        CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
550
551        realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs;
552    }
553
554    mVideoLateByUs = ALooper::GetNowUs() - realTimeUs;
555    bool tooLate = (mVideoLateByUs > 40000);
556
557    if (tooLate) {
558        ALOGV("video late by %lld us (%.2f secs)",
559             mVideoLateByUs, mVideoLateByUs / 1E6);
560    } else {
561        ALOGV("rendering video at media time %.2f secs",
562                (mFlags & FLAG_REAL_TIME ? realTimeUs :
563                (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6);
564    }
565
566    entry->mNotifyConsumed->setInt32("render", !tooLate);
567    entry->mNotifyConsumed->post();
568    mVideoQueue.erase(mVideoQueue.begin());
569    entry = NULL;
570
571    if (!mVideoRenderingStarted) {
572        mVideoRenderingStarted = true;
573        notifyVideoRenderingStart();
574    }
575
576    notifyIfMediaRenderingStarted();
577
578    notifyPosition();
579}
580
581void NuPlayer::Renderer::notifyVideoRenderingStart() {
582    sp<AMessage> notify = mNotify->dup();
583    notify->setInt32("what", kWhatVideoRenderingStart);
584    notify->post();
585}
586
587void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult) {
588    sp<AMessage> notify = mNotify->dup();
589    notify->setInt32("what", kWhatEOS);
590    notify->setInt32("audio", static_cast<int32_t>(audio));
591    notify->setInt32("finalResult", finalResult);
592    notify->post();
593}
594
595void NuPlayer::Renderer::notifyAudioOffloadTearDown() {
596    (new AMessage(kWhatAudioOffloadTearDown, id()))->post();
597}
598
599void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
600    int32_t audio;
601    CHECK(msg->findInt32("audio", &audio));
602
603    if (audio) {
604        mHasAudio = true;
605    } else {
606        mHasVideo = true;
607    }
608
609    if (dropBufferWhileFlushing(audio, msg)) {
610        return;
611    }
612
613    sp<ABuffer> buffer;
614    CHECK(msg->findBuffer("buffer", &buffer));
615
616    sp<AMessage> notifyConsumed;
617    CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
618
619    QueueEntry entry;
620    entry.mBuffer = buffer;
621    entry.mNotifyConsumed = notifyConsumed;
622    entry.mOffset = 0;
623    entry.mFinalResult = OK;
624
625    if (audio) {
626        Mutex::Autolock autoLock(mLock);
627        mAudioQueue.push_back(entry);
628        postDrainAudioQueue_l();
629    } else {
630        mVideoQueue.push_back(entry);
631        postDrainVideoQueue();
632    }
633
634    Mutex::Autolock autoLock(mLock);
635    if (!mSyncQueues || mAudioQueue.empty() || mVideoQueue.empty()) {
636        return;
637    }
638
639    sp<ABuffer> firstAudioBuffer = (*mAudioQueue.begin()).mBuffer;
640    sp<ABuffer> firstVideoBuffer = (*mVideoQueue.begin()).mBuffer;
641
642    if (firstAudioBuffer == NULL || firstVideoBuffer == NULL) {
643        // EOS signalled on either queue.
644        syncQueuesDone_l();
645        return;
646    }
647
648    int64_t firstAudioTimeUs;
649    int64_t firstVideoTimeUs;
650    CHECK(firstAudioBuffer->meta()
651            ->findInt64("timeUs", &firstAudioTimeUs));
652    CHECK(firstVideoBuffer->meta()
653            ->findInt64("timeUs", &firstVideoTimeUs));
654
655    int64_t diff = firstVideoTimeUs - firstAudioTimeUs;
656
657    ALOGV("queueDiff = %.2f secs", diff / 1E6);
658
659    if (diff > 100000ll) {
660        // Audio data starts More than 0.1 secs before video.
661        // Drop some audio.
662
663        (*mAudioQueue.begin()).mNotifyConsumed->post();
664        mAudioQueue.erase(mAudioQueue.begin());
665        return;
666    }
667
668    syncQueuesDone_l();
669}
670
671void NuPlayer::Renderer::syncQueuesDone_l() {
672    if (!mSyncQueues) {
673        return;
674    }
675
676    mSyncQueues = false;
677
678    if (!mAudioQueue.empty()) {
679        postDrainAudioQueue_l();
680    }
681
682    if (!mVideoQueue.empty()) {
683        postDrainVideoQueue();
684    }
685}
686
687void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
688    int32_t audio;
689    CHECK(msg->findInt32("audio", &audio));
690
691    if (dropBufferWhileFlushing(audio, msg)) {
692        return;
693    }
694
695    int32_t finalResult;
696    CHECK(msg->findInt32("finalResult", &finalResult));
697
698    QueueEntry entry;
699    entry.mOffset = 0;
700    entry.mFinalResult = finalResult;
701
702    if (audio) {
703        Mutex::Autolock autoLock(mLock);
704        if (mAudioQueue.empty() && mSyncQueues) {
705            syncQueuesDone_l();
706        }
707        mAudioQueue.push_back(entry);
708        postDrainAudioQueue_l();
709    } else {
710        if (mVideoQueue.empty() && mSyncQueues) {
711            Mutex::Autolock autoLock(mLock);
712            syncQueuesDone_l();
713        }
714        mVideoQueue.push_back(entry);
715        postDrainVideoQueue();
716    }
717}
718
719void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
720    int32_t audio;
721    CHECK(msg->findInt32("audio", &audio));
722
723    {
724        Mutex::Autolock autoLock(mFlushLock);
725        if (audio) {
726            mFlushingAudio = false;
727        } else {
728            mFlushingVideo = false;
729        }
730    }
731
732    // If we're currently syncing the queues, i.e. dropping audio while
733    // aligning the first audio/video buffer times and only one of the
734    // two queues has data, we may starve that queue by not requesting
735    // more buffers from the decoder. If the other source then encounters
736    // a discontinuity that leads to flushing, we'll never find the
737    // corresponding discontinuity on the other queue.
738    // Therefore we'll stop syncing the queues if at least one of them
739    // is flushed.
740    {
741         Mutex::Autolock autoLock(mLock);
742         syncQueuesDone_l();
743    }
744
745    ALOGV("flushing %s", audio ? "audio" : "video");
746    if (audio) {
747        {
748            Mutex::Autolock autoLock(mLock);
749            flushQueue(&mAudioQueue);
750
751            ++mAudioQueueGeneration;
752            prepareForMediaRenderingStart();
753
754            if (offloadingAudio()) {
755                mFirstAudioTimeUs = -1;
756            }
757        }
758
759        mDrainAudioQueuePending = false;
760
761        if (offloadingAudio()) {
762            mAudioSink->pause();
763            mAudioSink->flush();
764            mAudioSink->start();
765        }
766    } else {
767        flushQueue(&mVideoQueue);
768
769        mDrainVideoQueuePending = false;
770        ++mVideoQueueGeneration;
771
772        prepareForMediaRenderingStart();
773    }
774
775    notifyFlushComplete(audio);
776}
777
778void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) {
779    while (!queue->empty()) {
780        QueueEntry *entry = &*queue->begin();
781
782        if (entry->mBuffer != NULL) {
783            entry->mNotifyConsumed->post();
784        }
785
786        queue->erase(queue->begin());
787        entry = NULL;
788    }
789}
790
791void NuPlayer::Renderer::notifyFlushComplete(bool audio) {
792    sp<AMessage> notify = mNotify->dup();
793    notify->setInt32("what", kWhatFlushComplete);
794    notify->setInt32("audio", static_cast<int32_t>(audio));
795    notify->post();
796}
797
798bool NuPlayer::Renderer::dropBufferWhileFlushing(
799        bool audio, const sp<AMessage> &msg) {
800    bool flushing = false;
801
802    {
803        Mutex::Autolock autoLock(mFlushLock);
804        if (audio) {
805            flushing = mFlushingAudio;
806        } else {
807            flushing = mFlushingVideo;
808        }
809    }
810
811    if (!flushing) {
812        return false;
813    }
814
815    sp<AMessage> notifyConsumed;
816    if (msg->findMessage("notifyConsumed", &notifyConsumed)) {
817        notifyConsumed->post();
818    }
819
820    return true;
821}
822
823void NuPlayer::Renderer::onAudioSinkChanged() {
824    if (offloadingAudio()) {
825        return;
826    }
827    CHECK(!mDrainAudioQueuePending);
828    mNumFramesWritten = 0;
829    uint32_t written;
830    if (mAudioSink->getFramesWritten(&written) == OK) {
831        mNumFramesWritten = written;
832    }
833}
834
835void NuPlayer::Renderer::onDisableOffloadAudio() {
836    Mutex::Autolock autoLock(mLock);
837    mFlags &= ~FLAG_OFFLOAD_AUDIO;
838    ++mAudioQueueGeneration;
839}
840
841void NuPlayer::Renderer::notifyPosition() {
842    if (mAnchorTimeRealUs < 0 || mAnchorTimeMediaUs < 0) {
843        return;
844    }
845
846    int64_t nowUs = ALooper::GetNowUs();
847
848    if (mLastPositionUpdateUs >= 0
849            && nowUs < mLastPositionUpdateUs + kMinPositionUpdateDelayUs) {
850        return;
851    }
852    mLastPositionUpdateUs = nowUs;
853
854    int64_t positionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs;
855
856    sp<AMessage> notify = mNotify->dup();
857    notify->setInt32("what", kWhatPosition);
858    notify->setInt64("positionUs", positionUs);
859    notify->setInt64("videoLateByUs", mVideoLateByUs);
860    notify->post();
861}
862
863void NuPlayer::Renderer::onPause() {
864    CHECK(!mPaused);
865
866    {
867        Mutex::Autolock autoLock(mLock);
868        ++mAudioQueueGeneration;
869        ++mVideoQueueGeneration;
870        prepareForMediaRenderingStart();
871    }
872
873    mDrainAudioQueuePending = false;
874    mDrainVideoQueuePending = false;
875
876    if (mHasAudio) {
877        mAudioSink->pause();
878    }
879
880    ALOGV("now paused audio queue has %d entries, video has %d entries",
881          mAudioQueue.size(), mVideoQueue.size());
882
883    mPaused = true;
884}
885
886void NuPlayer::Renderer::onResume() {
887    if (!mPaused) {
888        return;
889    }
890
891    if (mHasAudio) {
892        mAudioSink->start();
893    }
894
895    mPaused = false;
896
897    Mutex::Autolock autoLock(mLock);
898    if (!mAudioQueue.empty()) {
899        postDrainAudioQueue_l();
900    }
901
902    if (!mVideoQueue.empty()) {
903        postDrainVideoQueue();
904    }
905}
906
907void NuPlayer::Renderer::onAudioOffloadTearDown() {
908    uint32_t numFramesPlayed;
909    CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
910
911    int64_t firstAudioTimeUs;
912    {
913        Mutex::Autolock autoLock(mLock);
914        firstAudioTimeUs = mFirstAudioTimeUs;
915    }
916    int64_t currentPositionUs = firstAudioTimeUs
917            + (numFramesPlayed * mAudioSink->msecsPerFrame()) * 1000ll;
918
919    mAudioSink->stop();
920    mAudioSink->flush();
921
922    sp<AMessage> notify = mNotify->dup();
923    notify->setInt32("what", kWhatAudioOffloadTearDown);
924    notify->setInt64("positionUs", currentPositionUs);
925    notify->post();
926}
927
928}  // namespace android
929
930