RTSPSource.cpp revision 91ea571836401ba77854d7b3aefb3ccee9c20eb3
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 "RTSPSource"
19#include <utils/Log.h>
20
21#include "RTSPSource.h"
22
23#include "AnotherPacketSource.h"
24#include "MyHandler.h"
25#include "SDPLoader.h"
26
27#include <media/IMediaHTTPService.h>
28#include <media/stagefright/MediaDefs.h>
29#include <media/stagefright/MetaData.h>
30
31namespace android {
32
33const int64_t kNearEOSTimeoutUs = 2000000ll; // 2 secs
34
35// Buffer Underflow/Prepare/StartServer/Overflow Marks
36const int64_t NuPlayer::RTSPSource::kUnderflowMarkUs   =  1000000ll;
37const int64_t NuPlayer::RTSPSource::kPrepareMarkUs     =  3000000ll;
38const int64_t NuPlayer::RTSPSource::kStartServerMarkUs =  5000000ll;
39const int64_t NuPlayer::RTSPSource::kOverflowMarkUs    = 10000000ll;
40
41NuPlayer::RTSPSource::RTSPSource(
42        const sp<AMessage> &notify,
43        const sp<IMediaHTTPService> &httpService,
44        const char *url,
45        const KeyedVector<String8, String8> *headers,
46        bool uidValid,
47        uid_t uid,
48        bool isSDP)
49    : Source(notify),
50      mHTTPService(httpService),
51      mURL(url),
52      mUIDValid(uidValid),
53      mUID(uid),
54      mFlags(0),
55      mIsSDP(isSDP),
56      mState(DISCONNECTED),
57      mFinalResult(OK),
58      mDisconnectReplyID(0),
59      mBuffering(false),
60      mInPreparationPhase(true),
61      mSeekGeneration(0),
62      mEOSTimeoutAudio(0),
63      mEOSTimeoutVideo(0) {
64    if (headers) {
65        mExtraHeaders = *headers;
66
67        ssize_t index =
68            mExtraHeaders.indexOfKey(String8("x-hide-urls-from-log"));
69
70        if (index >= 0) {
71            mFlags |= kFlagIncognito;
72
73            mExtraHeaders.removeItemsAt(index);
74        }
75    }
76}
77
78NuPlayer::RTSPSource::~RTSPSource() {
79    if (mLooper != NULL) {
80        mLooper->unregisterHandler(id());
81        mLooper->stop();
82    }
83}
84
85void NuPlayer::RTSPSource::prepareAsync() {
86    if (mIsSDP && mHTTPService == NULL) {
87        notifyPrepared(BAD_VALUE);
88        return;
89    }
90
91    if (mLooper == NULL) {
92        mLooper = new ALooper;
93        mLooper->setName("rtsp");
94        mLooper->start();
95
96        mLooper->registerHandler(this);
97    }
98
99    CHECK(mHandler == NULL);
100    CHECK(mSDPLoader == NULL);
101
102    sp<AMessage> notify = new AMessage(kWhatNotify, this);
103
104    CHECK_EQ(mState, (int)DISCONNECTED);
105    mState = CONNECTING;
106
107    if (mIsSDP) {
108        mSDPLoader = new SDPLoader(notify,
109                (mFlags & kFlagIncognito) ? SDPLoader::kFlagIncognito : 0,
110                mHTTPService);
111
112        mSDPLoader->load(
113                mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
114    } else {
115        mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID);
116        mLooper->registerHandler(mHandler);
117
118        mHandler->connect();
119    }
120
121    startBufferingIfNecessary();
122}
123
124void NuPlayer::RTSPSource::start() {
125}
126
127void NuPlayer::RTSPSource::stop() {
128    if (mLooper == NULL) {
129        return;
130    }
131    sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
132
133    sp<AMessage> dummy;
134    msg->postAndAwaitResponse(&dummy);
135}
136
137status_t NuPlayer::RTSPSource::feedMoreTSData() {
138    Mutex::Autolock _l(mBufferingLock);
139    return mFinalResult;
140}
141
142sp<MetaData> NuPlayer::RTSPSource::getFormatMeta(bool audio) {
143    sp<AnotherPacketSource> source = getSource(audio);
144
145    if (source == NULL) {
146        return NULL;
147    }
148
149    return source->getFormat();
150}
151
152bool NuPlayer::RTSPSource::haveSufficientDataOnAllTracks() {
153    // We're going to buffer at least 2 secs worth data on all tracks before
154    // starting playback (both at startup and after a seek).
155
156    static const int64_t kMinDurationUs = 2000000ll;
157
158    int64_t mediaDurationUs = 0;
159    getDuration(&mediaDurationUs);
160    if ((mAudioTrack != NULL && mAudioTrack->isFinished(mediaDurationUs))
161            || (mVideoTrack != NULL && mVideoTrack->isFinished(mediaDurationUs))) {
162        return true;
163    }
164
165    status_t err;
166    int64_t durationUs;
167    if (mAudioTrack != NULL
168            && (durationUs = mAudioTrack->getBufferedDurationUs(&err))
169                    < kMinDurationUs
170            && err == OK) {
171        ALOGV("audio track doesn't have enough data yet. (%.2f secs buffered)",
172              durationUs / 1E6);
173        return false;
174    }
175
176    if (mVideoTrack != NULL
177            && (durationUs = mVideoTrack->getBufferedDurationUs(&err))
178                    < kMinDurationUs
179            && err == OK) {
180        ALOGV("video track doesn't have enough data yet. (%.2f secs buffered)",
181              durationUs / 1E6);
182        return false;
183    }
184
185    return true;
186}
187
188status_t NuPlayer::RTSPSource::dequeueAccessUnit(
189        bool audio, sp<ABuffer> *accessUnit) {
190    if (!stopBufferingIfNecessary()) {
191        return -EWOULDBLOCK;
192    }
193
194    sp<AnotherPacketSource> source = getSource(audio);
195
196    if (source == NULL) {
197        return -EWOULDBLOCK;
198    }
199
200    status_t finalResult;
201    if (!source->hasBufferAvailable(&finalResult)) {
202        if (finalResult == OK) {
203            int64_t mediaDurationUs = 0;
204            getDuration(&mediaDurationUs);
205            sp<AnotherPacketSource> otherSource = getSource(!audio);
206            status_t otherFinalResult;
207
208            // If other source already signaled EOS, this source should also signal EOS
209            if (otherSource != NULL &&
210                    !otherSource->hasBufferAvailable(&otherFinalResult) &&
211                    otherFinalResult == ERROR_END_OF_STREAM) {
212                source->signalEOS(ERROR_END_OF_STREAM);
213                return ERROR_END_OF_STREAM;
214            }
215
216            // If this source has detected near end, give it some time to retrieve more
217            // data before signaling EOS
218            if (source->isFinished(mediaDurationUs)) {
219                int64_t eosTimeout = audio ? mEOSTimeoutAudio : mEOSTimeoutVideo;
220                if (eosTimeout == 0) {
221                    setEOSTimeout(audio, ALooper::GetNowUs());
222                } else if ((ALooper::GetNowUs() - eosTimeout) > kNearEOSTimeoutUs) {
223                    setEOSTimeout(audio, 0);
224                    source->signalEOS(ERROR_END_OF_STREAM);
225                    return ERROR_END_OF_STREAM;
226                }
227                return -EWOULDBLOCK;
228            }
229
230            if (!(otherSource != NULL && otherSource->isFinished(mediaDurationUs))) {
231                // We should not enter buffering mode
232                // if any of the sources already have detected EOS.
233                startBufferingIfNecessary();
234            }
235
236            return -EWOULDBLOCK;
237        }
238        return finalResult;
239    }
240
241    setEOSTimeout(audio, 0);
242
243    return source->dequeueAccessUnit(accessUnit);
244}
245
246sp<AnotherPacketSource> NuPlayer::RTSPSource::getSource(bool audio) {
247    if (mTSParser != NULL) {
248        sp<MediaSource> source = mTSParser->getSource(
249                audio ? ATSParser::AUDIO : ATSParser::VIDEO);
250
251        return static_cast<AnotherPacketSource *>(source.get());
252    }
253
254    return audio ? mAudioTrack : mVideoTrack;
255}
256
257void NuPlayer::RTSPSource::setEOSTimeout(bool audio, int64_t timeout) {
258    if (audio) {
259        mEOSTimeoutAudio = timeout;
260    } else {
261        mEOSTimeoutVideo = timeout;
262    }
263}
264
265status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) {
266    *durationUs = 0ll;
267
268    int64_t audioDurationUs;
269    if (mAudioTrack != NULL
270            && mAudioTrack->getFormat()->findInt64(
271                kKeyDuration, &audioDurationUs)
272            && audioDurationUs > *durationUs) {
273        *durationUs = audioDurationUs;
274    }
275
276    int64_t videoDurationUs;
277    if (mVideoTrack != NULL
278            && mVideoTrack->getFormat()->findInt64(
279                kKeyDuration, &videoDurationUs)
280            && videoDurationUs > *durationUs) {
281        *durationUs = videoDurationUs;
282    }
283
284    return OK;
285}
286
287status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) {
288    sp<AMessage> msg = new AMessage(kWhatPerformSeek, this);
289    msg->setInt32("generation", ++mSeekGeneration);
290    msg->setInt64("timeUs", seekTimeUs);
291
292    sp<AMessage> response;
293    status_t err = msg->postAndAwaitResponse(&response);
294    if (err == OK && response != NULL) {
295        CHECK(response->findInt32("err", &err));
296    }
297
298    return err;
299}
300
301void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) {
302    if (mState != CONNECTED) {
303        finishSeek(INVALID_OPERATION);
304        return;
305    }
306
307    mState = SEEKING;
308    mHandler->seek(seekTimeUs);
309}
310
311void NuPlayer::RTSPSource::schedulePollBuffering() {
312    sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
313    msg->post(1000000ll); // 1 second intervals
314}
315
316void NuPlayer::RTSPSource::checkBuffering(
317        bool *prepared, bool *underflow, bool *overflow, bool *startServer) {
318    size_t numTracks = mTracks.size();
319    size_t preparedCount, underflowCount, overflowCount, startCount;
320    preparedCount = underflowCount = overflowCount = startCount = 0;
321
322    size_t count = numTracks;
323    for (size_t i = 0; i < count; ++i) {
324        status_t finalResult;
325        TrackInfo *info = &mTracks.editItemAt(i);
326        sp<AnotherPacketSource> src = info->mSource;
327        if (src == NULL) {
328            --numTracks;
329            continue;
330        }
331        int64_t bufferedDurationUs = src->getBufferedDurationUs(&finalResult);
332
333        // isFinished when duration is 0 checks for EOS result only
334        if (bufferedDurationUs > kPrepareMarkUs || src->isFinished(/* duration */ 0)) {
335            ++preparedCount;
336        }
337
338        if (src->isFinished(/* duration */ 0)) {
339            ++overflowCount;
340        } else {
341            if (bufferedDurationUs < kUnderflowMarkUs) {
342                ++underflowCount;
343            }
344            if (bufferedDurationUs > kOverflowMarkUs) {
345                ++overflowCount;
346            }
347            if (bufferedDurationUs < kStartServerMarkUs) {
348                ++startCount;
349            }
350        }
351    }
352
353    *prepared    = (preparedCount == numTracks);
354    *underflow   = (underflowCount > 0);
355    *overflow    = (overflowCount == numTracks);
356    *startServer = (startCount > 0);
357}
358
359void NuPlayer::RTSPSource::onPollBuffering() {
360    bool prepared, underflow, overflow, startServer;
361    checkBuffering(&prepared, &underflow, &overflow, &startServer);
362
363    if (prepared && mInPreparationPhase) {
364        mInPreparationPhase = false;
365        notifyPrepared();
366    }
367
368    if (!mInPreparationPhase && underflow) {
369        startBufferingIfNecessary();
370    }
371
372    if (haveSufficientDataOnAllTracks()) {
373        stopBufferingIfNecessary();
374    }
375
376    if (overflow && mHandler != NULL) {
377        mHandler->pause();
378    }
379
380    if (startServer && mHandler != NULL) {
381        mHandler->resume();
382    }
383
384    schedulePollBuffering();
385}
386
387void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
388    if (msg->what() == kWhatDisconnect) {
389        sp<AReplyToken> replyID;
390        CHECK(msg->senderAwaitsResponse(&replyID));
391
392        mDisconnectReplyID = replyID;
393        finishDisconnectIfPossible();
394        return;
395    } else if (msg->what() == kWhatPerformSeek) {
396        int32_t generation;
397        CHECK(msg->findInt32("generation", &generation));
398        CHECK(msg->senderAwaitsResponse(&mSeekReplyID));
399
400        if (generation != mSeekGeneration) {
401            // obsolete.
402            finishSeek(OK);
403            return;
404        }
405
406        int64_t seekTimeUs;
407        CHECK(msg->findInt64("timeUs", &seekTimeUs));
408
409        performSeek(seekTimeUs);
410        return;
411    } else if (msg->what() == kWhatPollBuffering) {
412        onPollBuffering();
413        return;
414    }
415
416    CHECK_EQ(msg->what(), (int)kWhatNotify);
417
418    int32_t what;
419    CHECK(msg->findInt32("what", &what));
420
421    switch (what) {
422        case MyHandler::kWhatConnected:
423        {
424            onConnected();
425
426            notifyVideoSizeChanged();
427
428            uint32_t flags = 0;
429
430            if (mHandler->isSeekable()) {
431                flags = FLAG_CAN_PAUSE
432                        | FLAG_CAN_SEEK
433                        | FLAG_CAN_SEEK_BACKWARD
434                        | FLAG_CAN_SEEK_FORWARD;
435            }
436
437            notifyFlagsChanged(flags);
438            schedulePollBuffering();
439            break;
440        }
441
442        case MyHandler::kWhatDisconnected:
443        {
444            onDisconnected(msg);
445            break;
446        }
447
448        case MyHandler::kWhatSeekDone:
449        {
450            mState = CONNECTED;
451            // Unblock seekTo here in case we attempted to seek in a live stream
452            finishSeek(OK);
453            break;
454        }
455
456        case MyHandler::kWhatSeekPaused:
457        {
458            sp<AnotherPacketSource> source = getSource(true /* audio */);
459            if (source != NULL) {
460                source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE,
461                        /* extra */ NULL,
462                        /* discard */ true);
463            }
464            source = getSource(false /* video */);
465            if (source != NULL) {
466                source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE,
467                        /* extra */ NULL,
468                        /* discard */ true);
469            };
470
471            status_t err = OK;
472            msg->findInt32("err", &err);
473
474            if (err == OK) {
475                int64_t timeUs;
476                CHECK(msg->findInt64("time", &timeUs));
477                mHandler->continueSeekAfterPause(timeUs);
478            } else {
479                finishSeek(err);
480            }
481            break;
482        }
483
484        case MyHandler::kWhatAccessUnit:
485        {
486            size_t trackIndex;
487            CHECK(msg->findSize("trackIndex", &trackIndex));
488
489            if (mTSParser == NULL) {
490                CHECK_LT(trackIndex, mTracks.size());
491            } else {
492                CHECK_EQ(trackIndex, 0u);
493            }
494
495            sp<ABuffer> accessUnit;
496            CHECK(msg->findBuffer("accessUnit", &accessUnit));
497
498            int32_t damaged;
499            if (accessUnit->meta()->findInt32("damaged", &damaged)
500                    && damaged) {
501                ALOGI("dropping damaged access unit.");
502                break;
503            }
504
505            if (mTSParser != NULL) {
506                size_t offset = 0;
507                status_t err = OK;
508                while (offset + 188 <= accessUnit->size()) {
509                    err = mTSParser->feedTSPacket(
510                            accessUnit->data() + offset, 188);
511                    if (err != OK) {
512                        break;
513                    }
514
515                    offset += 188;
516                }
517
518                if (offset < accessUnit->size()) {
519                    err = ERROR_MALFORMED;
520                }
521
522                if (err != OK) {
523                    sp<AnotherPacketSource> source = getSource(false /* audio */);
524                    if (source != NULL) {
525                        source->signalEOS(err);
526                    }
527
528                    source = getSource(true /* audio */);
529                    if (source != NULL) {
530                        source->signalEOS(err);
531                    }
532                }
533                break;
534            }
535
536            TrackInfo *info = &mTracks.editItemAt(trackIndex);
537
538            sp<AnotherPacketSource> source = info->mSource;
539            if (source != NULL) {
540                uint32_t rtpTime;
541                CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
542
543                if (!info->mNPTMappingValid) {
544                    // This is a live stream, we didn't receive any normal
545                    // playtime mapping. We won't map to npt time.
546                    source->queueAccessUnit(accessUnit);
547                    break;
548                }
549
550                int64_t nptUs =
551                    ((double)rtpTime - (double)info->mRTPTime)
552                        / info->mTimeScale
553                        * 1000000ll
554                        + info->mNormalPlaytimeUs;
555
556                accessUnit->meta()->setInt64("timeUs", nptUs);
557
558                source->queueAccessUnit(accessUnit);
559            }
560            break;
561        }
562
563        case MyHandler::kWhatEOS:
564        {
565            int32_t finalResult;
566            CHECK(msg->findInt32("finalResult", &finalResult));
567            CHECK_NE(finalResult, (status_t)OK);
568
569            if (mTSParser != NULL) {
570                sp<AnotherPacketSource> source = getSource(false /* audio */);
571                if (source != NULL) {
572                    source->signalEOS(finalResult);
573                }
574
575                source = getSource(true /* audio */);
576                if (source != NULL) {
577                    source->signalEOS(finalResult);
578                }
579
580                return;
581            }
582
583            size_t trackIndex;
584            CHECK(msg->findSize("trackIndex", &trackIndex));
585            CHECK_LT(trackIndex, mTracks.size());
586
587            TrackInfo *info = &mTracks.editItemAt(trackIndex);
588            sp<AnotherPacketSource> source = info->mSource;
589            if (source != NULL) {
590                source->signalEOS(finalResult);
591            }
592
593            break;
594        }
595
596        case MyHandler::kWhatSeekDiscontinuity:
597        {
598            size_t trackIndex;
599            CHECK(msg->findSize("trackIndex", &trackIndex));
600            CHECK_LT(trackIndex, mTracks.size());
601
602            TrackInfo *info = &mTracks.editItemAt(trackIndex);
603            sp<AnotherPacketSource> source = info->mSource;
604            if (source != NULL) {
605                source->queueDiscontinuity(
606                        ATSParser::DISCONTINUITY_TIME,
607                        NULL,
608                        true /* discard */);
609            }
610
611            break;
612        }
613
614        case MyHandler::kWhatNormalPlayTimeMapping:
615        {
616            size_t trackIndex;
617            CHECK(msg->findSize("trackIndex", &trackIndex));
618            CHECK_LT(trackIndex, mTracks.size());
619
620            uint32_t rtpTime;
621            CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime));
622
623            int64_t nptUs;
624            CHECK(msg->findInt64("nptUs", &nptUs));
625
626            TrackInfo *info = &mTracks.editItemAt(trackIndex);
627            info->mRTPTime = rtpTime;
628            info->mNormalPlaytimeUs = nptUs;
629            info->mNPTMappingValid = true;
630            break;
631        }
632
633        case SDPLoader::kWhatSDPLoaded:
634        {
635            onSDPLoaded(msg);
636            break;
637        }
638
639        default:
640            TRESPASS();
641    }
642}
643
644void NuPlayer::RTSPSource::onConnected() {
645    CHECK(mAudioTrack == NULL);
646    CHECK(mVideoTrack == NULL);
647
648    size_t numTracks = mHandler->countTracks();
649    for (size_t i = 0; i < numTracks; ++i) {
650        int32_t timeScale;
651        sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale);
652
653        const char *mime;
654        CHECK(format->findCString(kKeyMIMEType, &mime));
655
656        if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
657            // Very special case for MPEG2 Transport Streams.
658            CHECK_EQ(numTracks, 1u);
659
660            mTSParser = new ATSParser;
661            return;
662        }
663
664        bool isAudio = !strncasecmp(mime, "audio/", 6);
665        bool isVideo = !strncasecmp(mime, "video/", 6);
666
667        TrackInfo info;
668        info.mTimeScale = timeScale;
669        info.mRTPTime = 0;
670        info.mNormalPlaytimeUs = 0ll;
671        info.mNPTMappingValid = false;
672
673        if ((isAudio && mAudioTrack == NULL)
674                || (isVideo && mVideoTrack == NULL)) {
675            sp<AnotherPacketSource> source = new AnotherPacketSource(format);
676
677            if (isAudio) {
678                mAudioTrack = source;
679            } else {
680                mVideoTrack = source;
681            }
682
683            info.mSource = source;
684        }
685
686        mTracks.push(info);
687    }
688
689    mState = CONNECTED;
690}
691
692void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) {
693    status_t err;
694    CHECK(msg->findInt32("result", &err));
695
696    mSDPLoader.clear();
697
698    if (mDisconnectReplyID != 0) {
699        err = UNKNOWN_ERROR;
700    }
701
702    if (err == OK) {
703        sp<ASessionDescription> desc;
704        sp<RefBase> obj;
705        CHECK(msg->findObject("description", &obj));
706        desc = static_cast<ASessionDescription *>(obj.get());
707
708        AString rtspUri;
709        if (!desc->findAttribute(0, "a=control", &rtspUri)) {
710            ALOGE("Unable to find url in SDP");
711            err = UNKNOWN_ERROR;
712        } else {
713            sp<AMessage> notify = new AMessage(kWhatNotify, this);
714
715            mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID);
716            mLooper->registerHandler(mHandler);
717
718            mHandler->loadSDP(desc);
719        }
720    }
721
722    if (err != OK) {
723        if (mState == CONNECTING) {
724            // We're still in the preparation phase, signal that it
725            // failed.
726            notifyPrepared(err);
727        }
728
729        mState = DISCONNECTED;
730        setError(err);
731
732        if (mDisconnectReplyID != 0) {
733            finishDisconnectIfPossible();
734        }
735    }
736}
737
738void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) {
739    if (mState == DISCONNECTED) {
740        return;
741    }
742
743    status_t err;
744    CHECK(msg->findInt32("result", &err));
745    CHECK_NE(err, (status_t)OK);
746
747    mLooper->unregisterHandler(mHandler->id());
748    mHandler.clear();
749
750    if (mState == CONNECTING) {
751        // We're still in the preparation phase, signal that it
752        // failed.
753        notifyPrepared(err);
754    }
755
756    mState = DISCONNECTED;
757    setError(err);
758
759    if (mDisconnectReplyID != 0) {
760        finishDisconnectIfPossible();
761    }
762}
763
764void NuPlayer::RTSPSource::finishDisconnectIfPossible() {
765    if (mState != DISCONNECTED) {
766        if (mHandler != NULL) {
767            mHandler->disconnect();
768        } else if (mSDPLoader != NULL) {
769            mSDPLoader->cancel();
770        }
771        return;
772    }
773
774    (new AMessage)->postReply(mDisconnectReplyID);
775    mDisconnectReplyID = 0;
776}
777
778void NuPlayer::RTSPSource::setError(status_t err) {
779    Mutex::Autolock _l(mBufferingLock);
780    mFinalResult = err;
781}
782
783void NuPlayer::RTSPSource::startBufferingIfNecessary() {
784    Mutex::Autolock _l(mBufferingLock);
785
786    if (!mBuffering) {
787        mBuffering = true;
788
789        sp<AMessage> notify = dupNotify();
790        notify->setInt32("what", kWhatPauseOnBufferingStart);
791        notify->post();
792    }
793}
794
795bool NuPlayer::RTSPSource::stopBufferingIfNecessary() {
796    Mutex::Autolock _l(mBufferingLock);
797
798    if (mBuffering) {
799        if (!haveSufficientDataOnAllTracks()) {
800            return false;
801        }
802
803        mBuffering = false;
804
805        sp<AMessage> notify = dupNotify();
806        notify->setInt32("what", kWhatResumeOnBufferingEnd);
807        notify->post();
808    }
809
810    return true;
811}
812
813void NuPlayer::RTSPSource::finishSeek(status_t err) {
814    if (mSeekReplyID == NULL) {
815        return;
816    }
817    sp<AMessage> seekReply = new AMessage;
818    seekReply->setInt32("err", err);
819    seekReply->postReply(mSeekReplyID);
820    mSeekReplyID = NULL;
821}
822
823}  // namespace android
824