NuPlayer.cpp revision 259f1624cf7b93ba831af10a616267487601c27f
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 "RTSPSource.h"
29#include "StreamingSource.h"
30#include "GenericSource.h"
31
32#include "ATSParser.h"
33
34#include <media/stagefright/foundation/hexdump.h>
35#include <media/stagefright/foundation/ABuffer.h>
36#include <media/stagefright/foundation/ADebug.h>
37#include <media/stagefright/foundation/AMessage.h>
38#include <media/stagefright/ACodec.h>
39#include <media/stagefright/MediaDefs.h>
40#include <media/stagefright/MediaErrors.h>
41#include <media/stagefright/MetaData.h>
42#include <gui/IGraphicBufferProducer.h>
43
44#include "avc_utils.h"
45
46#include "ESDS.h"
47#include <media/stagefright/Utils.h>
48
49namespace android {
50
51struct NuPlayer::Action : public RefBase {
52    Action() {}
53
54    virtual void execute(NuPlayer *player) = 0;
55
56private:
57    DISALLOW_EVIL_CONSTRUCTORS(Action);
58};
59
60struct NuPlayer::SeekAction : public Action {
61    SeekAction(int64_t seekTimeUs)
62        : mSeekTimeUs(seekTimeUs) {
63    }
64
65    virtual void execute(NuPlayer *player) {
66        player->performSeek(mSeekTimeUs);
67    }
68
69private:
70    int64_t mSeekTimeUs;
71
72    DISALLOW_EVIL_CONSTRUCTORS(SeekAction);
73};
74
75struct NuPlayer::SetSurfaceAction : public Action {
76    SetSurfaceAction(const sp<NativeWindowWrapper> &wrapper)
77        : mWrapper(wrapper) {
78    }
79
80    virtual void execute(NuPlayer *player) {
81        player->performSetSurface(mWrapper);
82    }
83
84private:
85    sp<NativeWindowWrapper> mWrapper;
86
87    DISALLOW_EVIL_CONSTRUCTORS(SetSurfaceAction);
88};
89
90struct NuPlayer::ShutdownDecoderAction : public Action {
91    ShutdownDecoderAction(bool audio, bool video)
92        : mAudio(audio),
93          mVideo(video) {
94    }
95
96    virtual void execute(NuPlayer *player) {
97        player->performDecoderShutdown(mAudio, mVideo);
98    }
99
100private:
101    bool mAudio;
102    bool mVideo;
103
104    DISALLOW_EVIL_CONSTRUCTORS(ShutdownDecoderAction);
105};
106
107struct NuPlayer::PostMessageAction : public Action {
108    PostMessageAction(const sp<AMessage> &msg)
109        : mMessage(msg) {
110    }
111
112    virtual void execute(NuPlayer *) {
113        mMessage->post();
114    }
115
116private:
117    sp<AMessage> mMessage;
118
119    DISALLOW_EVIL_CONSTRUCTORS(PostMessageAction);
120};
121
122// Use this if there's no state necessary to save in order to execute
123// the action.
124struct NuPlayer::SimpleAction : public Action {
125    typedef void (NuPlayer::*ActionFunc)();
126
127    SimpleAction(ActionFunc func)
128        : mFunc(func) {
129    }
130
131    virtual void execute(NuPlayer *player) {
132        (player->*mFunc)();
133    }
134
135private:
136    ActionFunc mFunc;
137
138    DISALLOW_EVIL_CONSTRUCTORS(SimpleAction);
139};
140
141////////////////////////////////////////////////////////////////////////////////
142
143NuPlayer::NuPlayer()
144    : mUIDValid(false),
145      mSourceFlags(0),
146      mVideoIsAVC(false),
147      mAudioEOS(false),
148      mVideoEOS(false),
149      mScanSourcesPending(false),
150      mScanSourcesGeneration(0),
151      mPollDurationGeneration(0),
152      mTimeDiscontinuityPending(false),
153      mFlushingAudio(NONE),
154      mFlushingVideo(NONE),
155      mSkipRenderingAudioUntilMediaTimeUs(-1ll),
156      mSkipRenderingVideoUntilMediaTimeUs(-1ll),
157      mVideoLateByUs(0ll),
158      mNumFramesTotal(0ll),
159      mNumFramesDropped(0ll),
160      mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
161      mStarted(false) {
162}
163
164NuPlayer::~NuPlayer() {
165}
166
167void NuPlayer::setUID(uid_t uid) {
168    mUIDValid = true;
169    mUID = uid;
170}
171
172void NuPlayer::setDriver(const wp<NuPlayerDriver> &driver) {
173    mDriver = driver;
174}
175
176void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) {
177    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
178
179    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
180
181    msg->setObject("source", new StreamingSource(notify, source));
182    msg->post();
183}
184
185static bool IsHTTPLiveURL(const char *url) {
186    if (!strncasecmp("http://", url, 7)
187            || !strncasecmp("https://", url, 8)
188            || !strncasecmp("file://", url, 7)) {
189        size_t len = strlen(url);
190        if (len >= 5 && !strcasecmp(".m3u8", &url[len - 5])) {
191            return true;
192        }
193
194        if (strstr(url,"m3u8")) {
195            return true;
196        }
197    }
198
199    return false;
200}
201
202void NuPlayer::setDataSourceAsync(
203        const sp<IMediaHTTPService> &httpService,
204        const char *url,
205        const KeyedVector<String8, String8> *headers) {
206    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
207    size_t len = strlen(url);
208
209    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
210
211    sp<Source> source;
212    if (IsHTTPLiveURL(url)) {
213        source = new HTTPLiveSource(notify, httpService, url, headers);
214    } else if (!strncasecmp(url, "rtsp://", 7)) {
215        source = new RTSPSource(
216                notify, httpService, url, headers, mUIDValid, mUID);
217    } else if ((!strncasecmp(url, "http://", 7)
218                || !strncasecmp(url, "https://", 8))
219                    && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
220                    || strstr(url, ".sdp?"))) {
221        source = new RTSPSource(
222                notify, httpService, url, headers, mUIDValid, mUID, true);
223    } else {
224        source = new GenericSource(notify, httpService, url, headers);
225    }
226
227    msg->setObject("source", source);
228    msg->post();
229}
230
231void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
232    sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
233
234    sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
235
236    sp<Source> source = new GenericSource(notify, fd, offset, length);
237    msg->setObject("source", source);
238    msg->post();
239}
240
241void NuPlayer::prepareAsync() {
242    (new AMessage(kWhatPrepare, id()))->post();
243}
244
245void NuPlayer::setVideoSurfaceTextureAsync(
246        const sp<IGraphicBufferProducer> &bufferProducer) {
247    sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
248
249    if (bufferProducer == NULL) {
250        msg->setObject("native-window", NULL);
251    } else {
252        msg->setObject(
253                "native-window",
254                new NativeWindowWrapper(
255                    new Surface(bufferProducer)));
256    }
257
258    msg->post();
259}
260
261void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
262    sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
263    msg->setObject("sink", sink);
264    msg->post();
265}
266
267void NuPlayer::start() {
268    (new AMessage(kWhatStart, id()))->post();
269}
270
271void NuPlayer::pause() {
272    (new AMessage(kWhatPause, id()))->post();
273}
274
275void NuPlayer::resume() {
276    (new AMessage(kWhatResume, id()))->post();
277}
278
279void NuPlayer::resetAsync() {
280    (new AMessage(kWhatReset, id()))->post();
281}
282
283void NuPlayer::seekToAsync(int64_t seekTimeUs) {
284    sp<AMessage> msg = new AMessage(kWhatSeek, id());
285    msg->setInt64("seekTimeUs", seekTimeUs);
286    msg->post();
287}
288
289// static
290bool NuPlayer::IsFlushingState(FlushStatus state, bool *needShutdown) {
291    switch (state) {
292        case FLUSHING_DECODER:
293            if (needShutdown != NULL) {
294                *needShutdown = false;
295            }
296            return true;
297
298        case FLUSHING_DECODER_SHUTDOWN:
299            if (needShutdown != NULL) {
300                *needShutdown = true;
301            }
302            return true;
303
304        default:
305            return false;
306    }
307}
308
309void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
310    switch (msg->what()) {
311        case kWhatSetDataSource:
312        {
313            ALOGV("kWhatSetDataSource");
314
315            CHECK(mSource == NULL);
316
317            sp<RefBase> obj;
318            CHECK(msg->findObject("source", &obj));
319
320            mSource = static_cast<Source *>(obj.get());
321
322            looper()->registerHandler(mSource);
323
324            CHECK(mDriver != NULL);
325            sp<NuPlayerDriver> driver = mDriver.promote();
326            if (driver != NULL) {
327                driver->notifySetDataSourceCompleted(OK);
328            }
329            break;
330        }
331
332        case kWhatPrepare:
333        {
334            mSource->prepareAsync();
335            break;
336        }
337
338        case kWhatGetTrackInfo:
339        {
340            uint32_t replyID;
341            CHECK(msg->senderAwaitsResponse(&replyID));
342
343            status_t err = INVALID_OPERATION;
344            if (mSource != NULL) {
345                Parcel* reply;
346                CHECK(msg->findPointer("reply", (void**)&reply));
347                err = mSource->getTrackInfo(reply);
348            }
349
350            sp<AMessage> response = new AMessage;
351            response->setInt32("err", err);
352
353            response->postReply(replyID);
354            break;
355        }
356
357        case kWhatSelectTrack:
358        {
359            uint32_t replyID;
360            CHECK(msg->senderAwaitsResponse(&replyID));
361
362            status_t err = INVALID_OPERATION;
363            if (mSource != NULL) {
364                size_t trackIndex;
365                int32_t select;
366                CHECK(msg->findSize("trackIndex", &trackIndex));
367                CHECK(msg->findInt32("select", &select));
368                err = mSource->selectTrack(trackIndex, select);
369            }
370
371            sp<AMessage> response = new AMessage;
372            response->setInt32("err", err);
373
374            response->postReply(replyID);
375            break;
376        }
377
378        case kWhatPollDuration:
379        {
380            int32_t generation;
381            CHECK(msg->findInt32("generation", &generation));
382
383            if (generation != mPollDurationGeneration) {
384                // stale
385                break;
386            }
387
388            int64_t durationUs;
389            if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
390                sp<NuPlayerDriver> driver = mDriver.promote();
391                if (driver != NULL) {
392                    driver->notifyDuration(durationUs);
393                }
394            }
395
396            msg->post(1000000ll);  // poll again in a second.
397            break;
398        }
399
400        case kWhatSetVideoNativeWindow:
401        {
402            ALOGV("kWhatSetVideoNativeWindow");
403
404            mDeferredActions.push_back(
405                    new ShutdownDecoderAction(
406                        false /* audio */, true /* video */));
407
408            sp<RefBase> obj;
409            CHECK(msg->findObject("native-window", &obj));
410
411            mDeferredActions.push_back(
412                    new SetSurfaceAction(
413                        static_cast<NativeWindowWrapper *>(obj.get())));
414
415            if (obj != NULL) {
416                // If there is a new surface texture, instantiate decoders
417                // again if possible.
418                mDeferredActions.push_back(
419                        new SimpleAction(&NuPlayer::performScanSources));
420            }
421
422            processDeferredActions();
423            break;
424        }
425
426        case kWhatSetAudioSink:
427        {
428            ALOGV("kWhatSetAudioSink");
429
430            sp<RefBase> obj;
431            CHECK(msg->findObject("sink", &obj));
432
433            mAudioSink = static_cast<MediaPlayerBase::AudioSink *>(obj.get());
434            break;
435        }
436
437        case kWhatStart:
438        {
439            ALOGV("kWhatStart");
440
441            mVideoIsAVC = false;
442            mAudioEOS = false;
443            mVideoEOS = false;
444            mSkipRenderingAudioUntilMediaTimeUs = -1;
445            mSkipRenderingVideoUntilMediaTimeUs = -1;
446            mVideoLateByUs = 0;
447            mNumFramesTotal = 0;
448            mNumFramesDropped = 0;
449            mStarted = true;
450
451            mSource->start();
452
453            uint32_t flags = 0;
454
455            if (mSource->isRealTime()) {
456                flags |= Renderer::FLAG_REAL_TIME;
457            }
458
459            mRenderer = new Renderer(
460                    mAudioSink,
461                    new AMessage(kWhatRendererNotify, id()),
462                    flags);
463
464            looper()->registerHandler(mRenderer);
465
466            postScanSources();
467            break;
468        }
469
470        case kWhatScanSources:
471        {
472            int32_t generation;
473            CHECK(msg->findInt32("generation", &generation));
474            if (generation != mScanSourcesGeneration) {
475                // Drop obsolete msg.
476                break;
477            }
478
479            mScanSourcesPending = false;
480
481            ALOGV("scanning sources haveAudio=%d, haveVideo=%d",
482                 mAudioDecoder != NULL, mVideoDecoder != NULL);
483
484            bool mHadAnySourcesBefore =
485                (mAudioDecoder != NULL) || (mVideoDecoder != NULL);
486
487            if (mNativeWindow != NULL) {
488                instantiateDecoder(false, &mVideoDecoder);
489            }
490
491            if (mAudioSink != NULL) {
492                instantiateDecoder(true, &mAudioDecoder);
493            }
494
495            if (!mHadAnySourcesBefore
496                    && (mAudioDecoder != NULL || mVideoDecoder != NULL)) {
497                // This is the first time we've found anything playable.
498
499                if (mSourceFlags & Source::FLAG_DYNAMIC_DURATION) {
500                    schedulePollDuration();
501                }
502            }
503
504            status_t err;
505            if ((err = mSource->feedMoreTSData()) != OK) {
506                if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
507                    // We're not currently decoding anything (no audio or
508                    // video tracks found) and we just ran out of input data.
509
510                    if (err == ERROR_END_OF_STREAM) {
511                        notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
512                    } else {
513                        notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
514                    }
515                }
516                break;
517            }
518
519            if ((mAudioDecoder == NULL && mAudioSink != NULL)
520                    || (mVideoDecoder == NULL && mNativeWindow != NULL)) {
521                msg->post(100000ll);
522                mScanSourcesPending = true;
523            }
524            break;
525        }
526
527        case kWhatVideoNotify:
528        case kWhatAudioNotify:
529        {
530            bool audio = msg->what() == kWhatAudioNotify;
531
532            sp<AMessage> codecRequest;
533            CHECK(msg->findMessage("codec-request", &codecRequest));
534
535            int32_t what;
536            CHECK(codecRequest->findInt32("what", &what));
537
538            if (what == ACodec::kWhatFillThisBuffer) {
539                status_t err = feedDecoderInputData(
540                        audio, codecRequest);
541
542                if (err == -EWOULDBLOCK) {
543                    if (mSource->feedMoreTSData() == OK) {
544                        msg->post(10000ll);
545                    }
546                }
547            } else if (what == ACodec::kWhatEOS) {
548                int32_t err;
549                CHECK(codecRequest->findInt32("err", &err));
550
551                if (err == ERROR_END_OF_STREAM) {
552                    ALOGV("got %s decoder EOS", audio ? "audio" : "video");
553                } else {
554                    ALOGV("got %s decoder EOS w/ error %d",
555                         audio ? "audio" : "video",
556                         err);
557                }
558
559                mRenderer->queueEOS(audio, err);
560            } else if (what == ACodec::kWhatFlushCompleted) {
561                bool needShutdown;
562
563                if (audio) {
564                    CHECK(IsFlushingState(mFlushingAudio, &needShutdown));
565                    mFlushingAudio = FLUSHED;
566                } else {
567                    CHECK(IsFlushingState(mFlushingVideo, &needShutdown));
568                    mFlushingVideo = FLUSHED;
569
570                    mVideoLateByUs = 0;
571                }
572
573                ALOGV("decoder %s flush completed", audio ? "audio" : "video");
574
575                if (needShutdown) {
576                    ALOGV("initiating %s decoder shutdown",
577                         audio ? "audio" : "video");
578
579                    (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
580
581                    if (audio) {
582                        mFlushingAudio = SHUTTING_DOWN_DECODER;
583                    } else {
584                        mFlushingVideo = SHUTTING_DOWN_DECODER;
585                    }
586                }
587
588                finishFlushIfPossible();
589            } else if (what == ACodec::kWhatOutputFormatChanged) {
590                if (audio) {
591                    int32_t numChannels;
592                    CHECK(codecRequest->findInt32(
593                                "channel-count", &numChannels));
594
595                    int32_t sampleRate;
596                    CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
597
598                    ALOGV("Audio output format changed to %d Hz, %d channels",
599                         sampleRate, numChannels);
600
601                    mAudioSink->close();
602
603                    audio_output_flags_t flags;
604                    int64_t durationUs;
605                    // FIXME: we should handle the case where the video decoder
606                    // is created after we receive the format change indication.
607                    // Current code will just make that we select deep buffer
608                    // with video which should not be a problem as it should
609                    // not prevent from keeping A/V sync.
610                    if (mVideoDecoder == NULL &&
611                            mSource->getDuration(&durationUs) == OK &&
612                            durationUs
613                                > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
614                        flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
615                    } else {
616                        flags = AUDIO_OUTPUT_FLAG_NONE;
617                    }
618
619                    int32_t channelMask;
620                    if (!codecRequest->findInt32("channel-mask", &channelMask)) {
621                        channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
622                    }
623
624                    CHECK_EQ(mAudioSink->open(
625                                sampleRate,
626                                numChannels,
627                                (audio_channel_mask_t)channelMask,
628                                AUDIO_FORMAT_PCM_16_BIT,
629                                8 /* bufferCount */,
630                                NULL,
631                                NULL,
632                                flags),
633                             (status_t)OK);
634                    mAudioSink->start();
635
636                    mRenderer->signalAudioSinkChanged();
637                } else {
638                    // video
639
640                    int32_t width, height;
641                    CHECK(codecRequest->findInt32("width", &width));
642                    CHECK(codecRequest->findInt32("height", &height));
643
644                    int32_t cropLeft, cropTop, cropRight, cropBottom;
645                    CHECK(codecRequest->findRect(
646                                "crop",
647                                &cropLeft, &cropTop, &cropRight, &cropBottom));
648
649                    int32_t displayWidth = cropRight - cropLeft + 1;
650                    int32_t displayHeight = cropBottom - cropTop + 1;
651
652                    ALOGV("Video output format changed to %d x %d "
653                         "(crop: %d x %d @ (%d, %d))",
654                         width, height,
655                         displayWidth,
656                         displayHeight,
657                         cropLeft, cropTop);
658
659                    sp<AMessage> videoInputFormat =
660                        mSource->getFormat(false /* audio */);
661
662                    // Take into account sample aspect ratio if necessary:
663                    int32_t sarWidth, sarHeight;
664                    if (videoInputFormat->findInt32("sar-width", &sarWidth)
665                            && videoInputFormat->findInt32(
666                                "sar-height", &sarHeight)) {
667                        ALOGV("Sample aspect ratio %d : %d",
668                              sarWidth, sarHeight);
669
670                        displayWidth = (displayWidth * sarWidth) / sarHeight;
671
672                        ALOGV("display dimensions %d x %d",
673                              displayWidth, displayHeight);
674                    }
675
676                    notifyListener(
677                            MEDIA_SET_VIDEO_SIZE, displayWidth, displayHeight);
678                }
679            } else if (what == ACodec::kWhatShutdownCompleted) {
680                ALOGV("%s shutdown completed", audio ? "audio" : "video");
681                if (audio) {
682                    mAudioDecoder.clear();
683
684                    CHECK_EQ((int)mFlushingAudio, (int)SHUTTING_DOWN_DECODER);
685                    mFlushingAudio = SHUT_DOWN;
686                } else {
687                    mVideoDecoder.clear();
688
689                    CHECK_EQ((int)mFlushingVideo, (int)SHUTTING_DOWN_DECODER);
690                    mFlushingVideo = SHUT_DOWN;
691                }
692
693                finishFlushIfPossible();
694            } else if (what == ACodec::kWhatError) {
695                ALOGE("Received error from %s decoder, aborting playback.",
696                     audio ? "audio" : "video");
697
698                mRenderer->queueEOS(audio, UNKNOWN_ERROR);
699            } else if (what == ACodec::kWhatDrainThisBuffer) {
700                renderBuffer(audio, codecRequest);
701            } else if (what != ACodec::kWhatComponentAllocated
702                    && what != ACodec::kWhatComponentConfigured
703                    && what != ACodec::kWhatBuffersAllocated) {
704                ALOGV("Unhandled codec notification %d '%c%c%c%c'.",
705                      what,
706                      what >> 24,
707                      (what >> 16) & 0xff,
708                      (what >> 8) & 0xff,
709                      what & 0xff);
710            }
711
712            break;
713        }
714
715        case kWhatRendererNotify:
716        {
717            int32_t what;
718            CHECK(msg->findInt32("what", &what));
719
720            if (what == Renderer::kWhatEOS) {
721                int32_t audio;
722                CHECK(msg->findInt32("audio", &audio));
723
724                int32_t finalResult;
725                CHECK(msg->findInt32("finalResult", &finalResult));
726
727                if (audio) {
728                    mAudioEOS = true;
729                } else {
730                    mVideoEOS = true;
731                }
732
733                if (finalResult == ERROR_END_OF_STREAM) {
734                    ALOGV("reached %s EOS", audio ? "audio" : "video");
735                } else {
736                    ALOGE("%s track encountered an error (%d)",
737                         audio ? "audio" : "video", finalResult);
738
739                    notifyListener(
740                            MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, finalResult);
741                }
742
743                if ((mAudioEOS || mAudioDecoder == NULL)
744                        && (mVideoEOS || mVideoDecoder == NULL)) {
745                    notifyListener(MEDIA_PLAYBACK_COMPLETE, 0, 0);
746                }
747            } else if (what == Renderer::kWhatPosition) {
748                int64_t positionUs;
749                CHECK(msg->findInt64("positionUs", &positionUs));
750
751                CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs));
752
753                if (mDriver != NULL) {
754                    sp<NuPlayerDriver> driver = mDriver.promote();
755                    if (driver != NULL) {
756                        driver->notifyPosition(positionUs);
757
758                        driver->notifyFrameStats(
759                                mNumFramesTotal, mNumFramesDropped);
760                    }
761                }
762            } else if (what == Renderer::kWhatFlushComplete) {
763                int32_t audio;
764                CHECK(msg->findInt32("audio", &audio));
765
766                ALOGV("renderer %s flush completed.", audio ? "audio" : "video");
767            } else if (what == Renderer::kWhatVideoRenderingStart) {
768                notifyListener(MEDIA_INFO, MEDIA_INFO_RENDERING_START, 0);
769            } else if (what == Renderer::kWhatMediaRenderingStart) {
770                ALOGV("media rendering started");
771                notifyListener(MEDIA_STARTED, 0, 0);
772            }
773            break;
774        }
775
776        case kWhatMoreDataQueued:
777        {
778            break;
779        }
780
781        case kWhatReset:
782        {
783            ALOGV("kWhatReset");
784
785            mDeferredActions.push_back(
786                    new ShutdownDecoderAction(
787                        true /* audio */, true /* video */));
788
789            mDeferredActions.push_back(
790                    new SimpleAction(&NuPlayer::performReset));
791
792            processDeferredActions();
793            break;
794        }
795
796        case kWhatSeek:
797        {
798            int64_t seekTimeUs;
799            CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
800
801            ALOGV("kWhatSeek seekTimeUs=%lld us", seekTimeUs);
802
803            mDeferredActions.push_back(
804                    new SimpleAction(&NuPlayer::performDecoderFlush));
805
806            mDeferredActions.push_back(new SeekAction(seekTimeUs));
807
808            processDeferredActions();
809            break;
810        }
811
812        case kWhatPause:
813        {
814            CHECK(mRenderer != NULL);
815            mSource->pause();
816            mRenderer->pause();
817            break;
818        }
819
820        case kWhatResume:
821        {
822            CHECK(mRenderer != NULL);
823            mSource->resume();
824            mRenderer->resume();
825            break;
826        }
827
828        case kWhatSourceNotify:
829        {
830            onSourceNotify(msg);
831            break;
832        }
833
834        default:
835            TRESPASS();
836            break;
837    }
838}
839
840void NuPlayer::finishFlushIfPossible() {
841    if (mFlushingAudio != FLUSHED && mFlushingAudio != SHUT_DOWN) {
842        return;
843    }
844
845    if (mFlushingVideo != FLUSHED && mFlushingVideo != SHUT_DOWN) {
846        return;
847    }
848
849    ALOGV("both audio and video are flushed now.");
850
851    if (mTimeDiscontinuityPending) {
852        mRenderer->signalTimeDiscontinuity();
853        mTimeDiscontinuityPending = false;
854    }
855
856    if (mAudioDecoder != NULL) {
857        mAudioDecoder->signalResume();
858    }
859
860    if (mVideoDecoder != NULL) {
861        mVideoDecoder->signalResume();
862    }
863
864    mFlushingAudio = NONE;
865    mFlushingVideo = NONE;
866
867    processDeferredActions();
868}
869
870void NuPlayer::postScanSources() {
871    if (mScanSourcesPending) {
872        return;
873    }
874
875    sp<AMessage> msg = new AMessage(kWhatScanSources, id());
876    msg->setInt32("generation", mScanSourcesGeneration);
877    msg->post();
878
879    mScanSourcesPending = true;
880}
881
882status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
883    if (*decoder != NULL) {
884        return OK;
885    }
886
887    sp<AMessage> format = mSource->getFormat(audio);
888
889    if (format == NULL) {
890        return -EWOULDBLOCK;
891    }
892
893    if (!audio) {
894        AString mime;
895        CHECK(format->findString("mime", &mime));
896        mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str());
897    }
898
899    sp<AMessage> notify =
900        new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify,
901                     id());
902
903    *decoder = audio ? new Decoder(notify) :
904                       new Decoder(notify, mNativeWindow);
905    looper()->registerHandler(*decoder);
906
907    (*decoder)->configure(format);
908
909    return OK;
910}
911
912status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
913    sp<AMessage> reply;
914    CHECK(msg->findMessage("reply", &reply));
915
916    if ((audio && IsFlushingState(mFlushingAudio))
917            || (!audio && IsFlushingState(mFlushingVideo))) {
918        reply->setInt32("err", INFO_DISCONTINUITY);
919        reply->post();
920        return OK;
921    }
922
923    sp<ABuffer> accessUnit;
924
925    bool dropAccessUnit;
926    do {
927        status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
928
929        if (err == -EWOULDBLOCK) {
930            return err;
931        } else if (err != OK) {
932            if (err == INFO_DISCONTINUITY) {
933                int32_t type;
934                CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
935
936                bool formatChange =
937                    (audio &&
938                     (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT))
939                    || (!audio &&
940                            (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT));
941
942                bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0;
943
944                ALOGI("%s discontinuity (formatChange=%d, time=%d)",
945                     audio ? "audio" : "video", formatChange, timeChange);
946
947                if (audio) {
948                    mSkipRenderingAudioUntilMediaTimeUs = -1;
949                } else {
950                    mSkipRenderingVideoUntilMediaTimeUs = -1;
951                }
952
953                if (timeChange) {
954                    sp<AMessage> extra;
955                    if (accessUnit->meta()->findMessage("extra", &extra)
956                            && extra != NULL) {
957                        int64_t resumeAtMediaTimeUs;
958                        if (extra->findInt64(
959                                    "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
960                            ALOGI("suppressing rendering of %s until %lld us",
961                                    audio ? "audio" : "video", resumeAtMediaTimeUs);
962
963                            if (audio) {
964                                mSkipRenderingAudioUntilMediaTimeUs =
965                                    resumeAtMediaTimeUs;
966                            } else {
967                                mSkipRenderingVideoUntilMediaTimeUs =
968                                    resumeAtMediaTimeUs;
969                            }
970                        }
971                    }
972                }
973
974                mTimeDiscontinuityPending =
975                    mTimeDiscontinuityPending || timeChange;
976
977                if (formatChange || timeChange) {
978                    if (mFlushingAudio == NONE && mFlushingVideo == NONE) {
979                        // And we'll resume scanning sources once we're done
980                        // flushing.
981                        mDeferredActions.push_front(
982                                new SimpleAction(
983                                    &NuPlayer::performScanSources));
984                    }
985
986                    sp<AMessage> newFormat = mSource->getFormat(audio);
987                    sp<Decoder> &decoder = audio ? mAudioDecoder : mVideoDecoder;
988                    if (formatChange && !decoder->supportsSeamlessFormatChange(newFormat)) {
989                        flushDecoder(audio, /* needShutdown = */ true);
990                    } else {
991                        flushDecoder(audio, /* needShutdown = */ false);
992                        err = OK;
993                    }
994                } else {
995                    // This stream is unaffected by the discontinuity
996
997                    if (audio) {
998                        mFlushingAudio = FLUSHED;
999                    } else {
1000                        mFlushingVideo = FLUSHED;
1001                    }
1002
1003                    finishFlushIfPossible();
1004
1005                    return -EWOULDBLOCK;
1006                }
1007            }
1008
1009            reply->setInt32("err", err);
1010            reply->post();
1011            return OK;
1012        }
1013
1014        if (!audio) {
1015            ++mNumFramesTotal;
1016        }
1017
1018        dropAccessUnit = false;
1019        if (!audio
1020                && mVideoLateByUs > 100000ll
1021                && mVideoIsAVC
1022                && !IsAVCReferenceFrame(accessUnit)) {
1023            dropAccessUnit = true;
1024            ++mNumFramesDropped;
1025        }
1026    } while (dropAccessUnit);
1027
1028    // ALOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
1029
1030#if 0
1031    int64_t mediaTimeUs;
1032    CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
1033    ALOGV("feeding %s input buffer at media time %.2f secs",
1034         audio ? "audio" : "video",
1035         mediaTimeUs / 1E6);
1036#endif
1037
1038    reply->setBuffer("buffer", accessUnit);
1039    reply->post();
1040
1041    return OK;
1042}
1043
1044void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
1045    // ALOGV("renderBuffer %s", audio ? "audio" : "video");
1046
1047    sp<AMessage> reply;
1048    CHECK(msg->findMessage("reply", &reply));
1049
1050    if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) {
1051        // We're currently attempting to flush the decoder, in order
1052        // to complete this, the decoder wants all its buffers back,
1053        // so we don't want any output buffers it sent us (from before
1054        // we initiated the flush) to be stuck in the renderer's queue.
1055
1056        ALOGV("we're still flushing the %s decoder, sending its output buffer"
1057             " right back.", audio ? "audio" : "video");
1058
1059        reply->post();
1060        return;
1061    }
1062
1063    sp<ABuffer> buffer;
1064    CHECK(msg->findBuffer("buffer", &buffer));
1065
1066    int64_t &skipUntilMediaTimeUs =
1067        audio
1068            ? mSkipRenderingAudioUntilMediaTimeUs
1069            : mSkipRenderingVideoUntilMediaTimeUs;
1070
1071    if (skipUntilMediaTimeUs >= 0) {
1072        int64_t mediaTimeUs;
1073        CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs));
1074
1075        if (mediaTimeUs < skipUntilMediaTimeUs) {
1076            ALOGV("dropping %s buffer at time %lld as requested.",
1077                 audio ? "audio" : "video",
1078                 mediaTimeUs);
1079
1080            reply->post();
1081            return;
1082        }
1083
1084        skipUntilMediaTimeUs = -1;
1085    }
1086
1087    mRenderer->queueBuffer(audio, buffer, reply);
1088}
1089
1090void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) {
1091    if (mDriver == NULL) {
1092        return;
1093    }
1094
1095    sp<NuPlayerDriver> driver = mDriver.promote();
1096
1097    if (driver == NULL) {
1098        return;
1099    }
1100
1101    driver->notifyListener(msg, ext1, ext2, in);
1102}
1103
1104void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
1105    ALOGV("[%s] flushDecoder needShutdown=%d",
1106          audio ? "audio" : "video", needShutdown);
1107
1108    if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) {
1109        ALOGI("flushDecoder %s without decoder present",
1110             audio ? "audio" : "video");
1111    }
1112
1113    // Make sure we don't continue to scan sources until we finish flushing.
1114    ++mScanSourcesGeneration;
1115    mScanSourcesPending = false;
1116
1117    (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
1118    mRenderer->flush(audio);
1119
1120    FlushStatus newStatus =
1121        needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER;
1122
1123    if (audio) {
1124        CHECK(mFlushingAudio == NONE
1125                || mFlushingAudio == AWAITING_DISCONTINUITY);
1126
1127        mFlushingAudio = newStatus;
1128
1129        if (mFlushingVideo == NONE) {
1130            mFlushingVideo = (mVideoDecoder != NULL)
1131                ? AWAITING_DISCONTINUITY
1132                : FLUSHED;
1133        }
1134    } else {
1135        CHECK(mFlushingVideo == NONE
1136                || mFlushingVideo == AWAITING_DISCONTINUITY);
1137
1138        mFlushingVideo = newStatus;
1139
1140        if (mFlushingAudio == NONE) {
1141            mFlushingAudio = (mAudioDecoder != NULL)
1142                ? AWAITING_DISCONTINUITY
1143                : FLUSHED;
1144        }
1145    }
1146}
1147
1148sp<AMessage> NuPlayer::Source::getFormat(bool audio) {
1149    sp<MetaData> meta = getFormatMeta(audio);
1150
1151    if (meta == NULL) {
1152        return NULL;
1153    }
1154
1155    sp<AMessage> msg = new AMessage;
1156
1157    if(convertMetaDataToMessage(meta, &msg) == OK) {
1158        return msg;
1159    }
1160    return NULL;
1161}
1162
1163status_t NuPlayer::setVideoScalingMode(int32_t mode) {
1164    mVideoScalingMode = mode;
1165    if (mNativeWindow != NULL) {
1166        status_t ret = native_window_set_scaling_mode(
1167                mNativeWindow->getNativeWindow().get(), mVideoScalingMode);
1168        if (ret != OK) {
1169            ALOGE("Failed to set scaling mode (%d): %s",
1170                -ret, strerror(-ret));
1171            return ret;
1172        }
1173    }
1174    return OK;
1175}
1176
1177status_t NuPlayer::getTrackInfo(Parcel* reply) const {
1178    sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, id());
1179    msg->setPointer("reply", reply);
1180
1181    sp<AMessage> response;
1182    status_t err = msg->postAndAwaitResponse(&response);
1183    return err;
1184}
1185
1186status_t NuPlayer::selectTrack(size_t trackIndex, bool select) {
1187    sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
1188    msg->setSize("trackIndex", trackIndex);
1189    msg->setInt32("select", select);
1190
1191    sp<AMessage> response;
1192    status_t err = msg->postAndAwaitResponse(&response);
1193
1194    return err;
1195}
1196
1197void NuPlayer::schedulePollDuration() {
1198    sp<AMessage> msg = new AMessage(kWhatPollDuration, id());
1199    msg->setInt32("generation", mPollDurationGeneration);
1200    msg->post();
1201}
1202
1203void NuPlayer::cancelPollDuration() {
1204    ++mPollDurationGeneration;
1205}
1206
1207void NuPlayer::processDeferredActions() {
1208    while (!mDeferredActions.empty()) {
1209        // We won't execute any deferred actions until we're no longer in
1210        // an intermediate state, i.e. one more more decoders are currently
1211        // flushing or shutting down.
1212
1213        if (mRenderer != NULL) {
1214            // There's an edge case where the renderer owns all output
1215            // buffers and is paused, therefore the decoder will not read
1216            // more input data and will never encounter the matching
1217            // discontinuity. To avoid this, we resume the renderer.
1218
1219            if (mFlushingAudio == AWAITING_DISCONTINUITY
1220                    || mFlushingVideo == AWAITING_DISCONTINUITY) {
1221                mRenderer->resume();
1222            }
1223        }
1224
1225        if (mFlushingAudio != NONE || mFlushingVideo != NONE) {
1226            // We're currently flushing, postpone the reset until that's
1227            // completed.
1228
1229            ALOGV("postponing action mFlushingAudio=%d, mFlushingVideo=%d",
1230                  mFlushingAudio, mFlushingVideo);
1231
1232            break;
1233        }
1234
1235        sp<Action> action = *mDeferredActions.begin();
1236        mDeferredActions.erase(mDeferredActions.begin());
1237
1238        action->execute(this);
1239    }
1240}
1241
1242void NuPlayer::performSeek(int64_t seekTimeUs) {
1243    ALOGV("performSeek seekTimeUs=%lld us (%.2f secs)",
1244          seekTimeUs,
1245          seekTimeUs / 1E6);
1246
1247    mSource->seekTo(seekTimeUs);
1248
1249    if (mDriver != NULL) {
1250        sp<NuPlayerDriver> driver = mDriver.promote();
1251        if (driver != NULL) {
1252            driver->notifyPosition(seekTimeUs);
1253            driver->notifySeekComplete();
1254        }
1255    }
1256
1257    // everything's flushed, continue playback.
1258}
1259
1260void NuPlayer::performDecoderFlush() {
1261    ALOGV("performDecoderFlush");
1262
1263    if (mAudioDecoder == NULL && mVideoDecoder == NULL) {
1264        return;
1265    }
1266
1267    mTimeDiscontinuityPending = true;
1268
1269    if (mAudioDecoder != NULL) {
1270        flushDecoder(true /* audio */, false /* needShutdown */);
1271    }
1272
1273    if (mVideoDecoder != NULL) {
1274        flushDecoder(false /* audio */, false /* needShutdown */);
1275    }
1276}
1277
1278void NuPlayer::performDecoderShutdown(bool audio, bool video) {
1279    ALOGV("performDecoderShutdown audio=%d, video=%d", audio, video);
1280
1281    if ((!audio || mAudioDecoder == NULL)
1282            && (!video || mVideoDecoder == NULL)) {
1283        return;
1284    }
1285
1286    mTimeDiscontinuityPending = true;
1287
1288    if (mFlushingAudio == NONE && (!audio || mAudioDecoder == NULL)) {
1289        mFlushingAudio = FLUSHED;
1290    }
1291
1292    if (mFlushingVideo == NONE && (!video || mVideoDecoder == NULL)) {
1293        mFlushingVideo = FLUSHED;
1294    }
1295
1296    if (audio && mAudioDecoder != NULL) {
1297        flushDecoder(true /* audio */, true /* needShutdown */);
1298    }
1299
1300    if (video && mVideoDecoder != NULL) {
1301        flushDecoder(false /* audio */, true /* needShutdown */);
1302    }
1303}
1304
1305void NuPlayer::performReset() {
1306    ALOGV("performReset");
1307
1308    CHECK(mAudioDecoder == NULL);
1309    CHECK(mVideoDecoder == NULL);
1310
1311    cancelPollDuration();
1312
1313    ++mScanSourcesGeneration;
1314    mScanSourcesPending = false;
1315
1316    mRenderer.clear();
1317
1318    if (mSource != NULL) {
1319        mSource->stop();
1320
1321        looper()->unregisterHandler(mSource->id());
1322
1323        mSource.clear();
1324    }
1325
1326    if (mDriver != NULL) {
1327        sp<NuPlayerDriver> driver = mDriver.promote();
1328        if (driver != NULL) {
1329            driver->notifyResetComplete();
1330        }
1331    }
1332
1333    mStarted = false;
1334}
1335
1336void NuPlayer::performScanSources() {
1337    ALOGV("performScanSources");
1338
1339    if (!mStarted) {
1340        return;
1341    }
1342
1343    if (mAudioDecoder == NULL || mVideoDecoder == NULL) {
1344        postScanSources();
1345    }
1346}
1347
1348void NuPlayer::performSetSurface(const sp<NativeWindowWrapper> &wrapper) {
1349    ALOGV("performSetSurface");
1350
1351    mNativeWindow = wrapper;
1352
1353    // XXX - ignore error from setVideoScalingMode for now
1354    setVideoScalingMode(mVideoScalingMode);
1355
1356    if (mDriver != NULL) {
1357        sp<NuPlayerDriver> driver = mDriver.promote();
1358        if (driver != NULL) {
1359            driver->notifySetSurfaceComplete();
1360        }
1361    }
1362}
1363
1364void NuPlayer::onSourceNotify(const sp<AMessage> &msg) {
1365    int32_t what;
1366    CHECK(msg->findInt32("what", &what));
1367
1368    switch (what) {
1369        case Source::kWhatPrepared:
1370        {
1371            if (mSource == NULL) {
1372                // This is a stale notification from a source that was
1373                // asynchronously preparing when the client called reset().
1374                // We handled the reset, the source is gone.
1375                break;
1376            }
1377
1378            int32_t err;
1379            CHECK(msg->findInt32("err", &err));
1380
1381            sp<NuPlayerDriver> driver = mDriver.promote();
1382            if (driver != NULL) {
1383                driver->notifyPrepareCompleted(err);
1384            }
1385
1386            int64_t durationUs;
1387            if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) {
1388                sp<NuPlayerDriver> driver = mDriver.promote();
1389                if (driver != NULL) {
1390                    driver->notifyDuration(durationUs);
1391                }
1392            }
1393            break;
1394        }
1395
1396        case Source::kWhatFlagsChanged:
1397        {
1398            uint32_t flags;
1399            CHECK(msg->findInt32("flags", (int32_t *)&flags));
1400
1401            sp<NuPlayerDriver> driver = mDriver.promote();
1402            if (driver != NULL) {
1403                driver->notifyFlagsChanged(flags);
1404            }
1405
1406            if ((mSourceFlags & Source::FLAG_DYNAMIC_DURATION)
1407                    && (!(flags & Source::FLAG_DYNAMIC_DURATION))) {
1408                cancelPollDuration();
1409            } else if (!(mSourceFlags & Source::FLAG_DYNAMIC_DURATION)
1410                    && (flags & Source::FLAG_DYNAMIC_DURATION)
1411                    && (mAudioDecoder != NULL || mVideoDecoder != NULL)) {
1412                schedulePollDuration();
1413            }
1414
1415            mSourceFlags = flags;
1416            break;
1417        }
1418
1419        case Source::kWhatVideoSizeChanged:
1420        {
1421            int32_t width, height;
1422            CHECK(msg->findInt32("width", &width));
1423            CHECK(msg->findInt32("height", &height));
1424
1425            notifyListener(MEDIA_SET_VIDEO_SIZE, width, height);
1426            break;
1427        }
1428
1429        case Source::kWhatBufferingStart:
1430        {
1431            notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0);
1432            break;
1433        }
1434
1435        case Source::kWhatBufferingEnd:
1436        {
1437            notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_END, 0);
1438            break;
1439        }
1440
1441        case Source::kWhatSubtitleData:
1442        {
1443            sp<ABuffer> buffer;
1444            CHECK(msg->findBuffer("buffer", &buffer));
1445
1446            int32_t trackIndex;
1447            int64_t timeUs, durationUs;
1448            CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex));
1449            CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
1450            CHECK(buffer->meta()->findInt64("durationUs", &durationUs));
1451
1452            Parcel in;
1453            in.writeInt32(trackIndex);
1454            in.writeInt64(timeUs);
1455            in.writeInt64(durationUs);
1456            in.writeInt32(buffer->size());
1457            in.writeInt32(buffer->size());
1458            in.write(buffer->data(), buffer->size());
1459
1460            notifyListener(MEDIA_SUBTITLE_DATA, 0, 0, &in);
1461            break;
1462        }
1463
1464        case Source::kWhatQueueDecoderShutdown:
1465        {
1466            int32_t audio, video;
1467            CHECK(msg->findInt32("audio", &audio));
1468            CHECK(msg->findInt32("video", &video));
1469
1470            sp<AMessage> reply;
1471            CHECK(msg->findMessage("reply", &reply));
1472
1473            queueDecoderShutdown(audio, video, reply);
1474            break;
1475        }
1476
1477        default:
1478            TRESPASS();
1479    }
1480}
1481
1482////////////////////////////////////////////////////////////////////////////////
1483
1484void NuPlayer::Source::notifyFlagsChanged(uint32_t flags) {
1485    sp<AMessage> notify = dupNotify();
1486    notify->setInt32("what", kWhatFlagsChanged);
1487    notify->setInt32("flags", flags);
1488    notify->post();
1489}
1490
1491void NuPlayer::Source::notifyVideoSizeChanged(int32_t width, int32_t height) {
1492    sp<AMessage> notify = dupNotify();
1493    notify->setInt32("what", kWhatVideoSizeChanged);
1494    notify->setInt32("width", width);
1495    notify->setInt32("height", height);
1496    notify->post();
1497}
1498
1499void NuPlayer::Source::notifyPrepared(status_t err) {
1500    sp<AMessage> notify = dupNotify();
1501    notify->setInt32("what", kWhatPrepared);
1502    notify->setInt32("err", err);
1503    notify->post();
1504}
1505
1506void NuPlayer::Source::onMessageReceived(const sp<AMessage> & /* msg */) {
1507    TRESPASS();
1508}
1509
1510void NuPlayer::queueDecoderShutdown(
1511        bool audio, bool video, const sp<AMessage> &reply) {
1512    ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video);
1513
1514    mDeferredActions.push_back(
1515            new ShutdownDecoderAction(audio, video));
1516
1517    mDeferredActions.push_back(
1518            new SimpleAction(&NuPlayer::performScanSources));
1519
1520    mDeferredActions.push_back(new PostMessageAction(reply));
1521
1522    processDeferredActions();
1523}
1524
1525}  // namespace android
1526