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
31 namespace android {
32
33 const int64_t kNearEOSTimeoutUs = 2000000ll; // 2 secs
34
35 // Buffer Underflow/Prepare/StartServer/Overflow Marks
36 const int64_t NuPlayer::RTSPSource::kUnderflowMarkUs = 1000000ll;
37 const int64_t NuPlayer::RTSPSource::kPrepareMarkUs = 3000000ll;
38 const int64_t NuPlayer::RTSPSource::kStartServerMarkUs = 5000000ll;
39 const int64_t NuPlayer::RTSPSource::kOverflowMarkUs = 10000000ll;
40
RTSPSource(const sp<AMessage> & notify,const sp<IMediaHTTPService> & httpService,const char * url,const KeyedVector<String8,String8> * headers,bool uidValid,uid_t uid,bool isSDP)41 NuPlayer::RTSPSource::RTSPSource(
42 const sp<AMessage> ¬ify,
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
~RTSPSource()78 NuPlayer::RTSPSource::~RTSPSource() {
79 if (mLooper != NULL) {
80 mLooper->unregisterHandler(id());
81 mLooper->stop();
82 }
83 }
84
prepareAsync()85 void 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
start()124 void NuPlayer::RTSPSource::start() {
125 }
126
stop()127 void 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
feedMoreTSData()137 status_t NuPlayer::RTSPSource::feedMoreTSData() {
138 Mutex::Autolock _l(mBufferingLock);
139 return mFinalResult;
140 }
141
getFormatMeta(bool audio)142 sp<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
haveSufficientDataOnAllTracks()152 bool 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
dequeueAccessUnit(bool audio,sp<ABuffer> * accessUnit)188 status_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
getSource(bool audio)246 sp<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
setEOSTimeout(bool audio,int64_t timeout)257 void NuPlayer::RTSPSource::setEOSTimeout(bool audio, int64_t timeout) {
258 if (audio) {
259 mEOSTimeoutAudio = timeout;
260 } else {
261 mEOSTimeoutVideo = timeout;
262 }
263 }
264
getDuration(int64_t * durationUs)265 status_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
seekTo(int64_t seekTimeUs)287 status_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
performSeek(int64_t seekTimeUs)301 void 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
schedulePollBuffering()311 void NuPlayer::RTSPSource::schedulePollBuffering() {
312 sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
313 msg->post(1000000ll); // 1 second intervals
314 }
315
checkBuffering(bool * prepared,bool * underflow,bool * overflow,bool * startServer)316 void 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
onPollBuffering()359 void 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 (overflow && mHandler != NULL) {
373 stopBufferingIfNecessary();
374 mHandler->pause();
375 }
376
377 if (startServer && mHandler != NULL) {
378 mHandler->resume();
379 }
380
381 schedulePollBuffering();
382 }
383
onMessageReceived(const sp<AMessage> & msg)384 void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
385 if (msg->what() == kWhatDisconnect) {
386 sp<AReplyToken> replyID;
387 CHECK(msg->senderAwaitsResponse(&replyID));
388
389 mDisconnectReplyID = replyID;
390 finishDisconnectIfPossible();
391 return;
392 } else if (msg->what() == kWhatPerformSeek) {
393 int32_t generation;
394 CHECK(msg->findInt32("generation", &generation));
395 CHECK(msg->senderAwaitsResponse(&mSeekReplyID));
396
397 if (generation != mSeekGeneration) {
398 // obsolete.
399 finishSeek(OK);
400 return;
401 }
402
403 int64_t seekTimeUs;
404 CHECK(msg->findInt64("timeUs", &seekTimeUs));
405
406 performSeek(seekTimeUs);
407 return;
408 } else if (msg->what() == kWhatPollBuffering) {
409 onPollBuffering();
410 return;
411 }
412
413 CHECK_EQ(msg->what(), (int)kWhatNotify);
414
415 int32_t what;
416 CHECK(msg->findInt32("what", &what));
417
418 switch (what) {
419 case MyHandler::kWhatConnected:
420 {
421 onConnected();
422
423 notifyVideoSizeChanged();
424
425 uint32_t flags = 0;
426
427 if (mHandler->isSeekable()) {
428 flags = FLAG_CAN_PAUSE
429 | FLAG_CAN_SEEK
430 | FLAG_CAN_SEEK_BACKWARD
431 | FLAG_CAN_SEEK_FORWARD;
432 }
433
434 notifyFlagsChanged(flags);
435 schedulePollBuffering();
436 break;
437 }
438
439 case MyHandler::kWhatDisconnected:
440 {
441 onDisconnected(msg);
442 break;
443 }
444
445 case MyHandler::kWhatSeekDone:
446 {
447 mState = CONNECTED;
448 // Unblock seekTo here in case we attempted to seek in a live stream
449 finishSeek(OK);
450 break;
451 }
452
453 case MyHandler::kWhatSeekPaused:
454 {
455 sp<AnotherPacketSource> source = getSource(true /* audio */);
456 if (source != NULL) {
457 source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE,
458 /* extra */ NULL,
459 /* discard */ true);
460 }
461 source = getSource(false /* video */);
462 if (source != NULL) {
463 source->queueDiscontinuity(ATSParser::DISCONTINUITY_NONE,
464 /* extra */ NULL,
465 /* discard */ true);
466 };
467
468 status_t err = OK;
469 msg->findInt32("err", &err);
470
471 if (err == OK) {
472 int64_t timeUs;
473 CHECK(msg->findInt64("time", &timeUs));
474 mHandler->continueSeekAfterPause(timeUs);
475 } else {
476 finishSeek(err);
477 }
478 break;
479 }
480
481 case MyHandler::kWhatAccessUnit:
482 {
483 size_t trackIndex;
484 CHECK(msg->findSize("trackIndex", &trackIndex));
485
486 if (mTSParser == NULL) {
487 CHECK_LT(trackIndex, mTracks.size());
488 } else {
489 CHECK_EQ(trackIndex, 0u);
490 }
491
492 sp<ABuffer> accessUnit;
493 CHECK(msg->findBuffer("accessUnit", &accessUnit));
494
495 int32_t damaged;
496 if (accessUnit->meta()->findInt32("damaged", &damaged)
497 && damaged) {
498 ALOGI("dropping damaged access unit.");
499 break;
500 }
501
502 if (mTSParser != NULL) {
503 size_t offset = 0;
504 status_t err = OK;
505 while (offset + 188 <= accessUnit->size()) {
506 err = mTSParser->feedTSPacket(
507 accessUnit->data() + offset, 188);
508 if (err != OK) {
509 break;
510 }
511
512 offset += 188;
513 }
514
515 if (offset < accessUnit->size()) {
516 err = ERROR_MALFORMED;
517 }
518
519 if (err != OK) {
520 sp<AnotherPacketSource> source = getSource(false /* audio */);
521 if (source != NULL) {
522 source->signalEOS(err);
523 }
524
525 source = getSource(true /* audio */);
526 if (source != NULL) {
527 source->signalEOS(err);
528 }
529 }
530 break;
531 }
532
533 TrackInfo *info = &mTracks.editItemAt(trackIndex);
534
535 sp<AnotherPacketSource> source = info->mSource;
536 if (source != NULL) {
537 uint32_t rtpTime;
538 CHECK(accessUnit->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
539
540 if (!info->mNPTMappingValid) {
541 // This is a live stream, we didn't receive any normal
542 // playtime mapping. We won't map to npt time.
543 source->queueAccessUnit(accessUnit);
544 break;
545 }
546
547 int64_t nptUs =
548 ((double)rtpTime - (double)info->mRTPTime)
549 / info->mTimeScale
550 * 1000000ll
551 + info->mNormalPlaytimeUs;
552
553 accessUnit->meta()->setInt64("timeUs", nptUs);
554
555 source->queueAccessUnit(accessUnit);
556 }
557 break;
558 }
559
560 case MyHandler::kWhatEOS:
561 {
562 int32_t finalResult;
563 CHECK(msg->findInt32("finalResult", &finalResult));
564 CHECK_NE(finalResult, (status_t)OK);
565
566 if (mTSParser != NULL) {
567 sp<AnotherPacketSource> source = getSource(false /* audio */);
568 if (source != NULL) {
569 source->signalEOS(finalResult);
570 }
571
572 source = getSource(true /* audio */);
573 if (source != NULL) {
574 source->signalEOS(finalResult);
575 }
576
577 return;
578 }
579
580 size_t trackIndex;
581 CHECK(msg->findSize("trackIndex", &trackIndex));
582 CHECK_LT(trackIndex, mTracks.size());
583
584 TrackInfo *info = &mTracks.editItemAt(trackIndex);
585 sp<AnotherPacketSource> source = info->mSource;
586 if (source != NULL) {
587 source->signalEOS(finalResult);
588 }
589
590 break;
591 }
592
593 case MyHandler::kWhatSeekDiscontinuity:
594 {
595 size_t trackIndex;
596 CHECK(msg->findSize("trackIndex", &trackIndex));
597 CHECK_LT(trackIndex, mTracks.size());
598
599 TrackInfo *info = &mTracks.editItemAt(trackIndex);
600 sp<AnotherPacketSource> source = info->mSource;
601 if (source != NULL) {
602 source->queueDiscontinuity(
603 ATSParser::DISCONTINUITY_TIME,
604 NULL,
605 true /* discard */);
606 }
607
608 break;
609 }
610
611 case MyHandler::kWhatNormalPlayTimeMapping:
612 {
613 size_t trackIndex;
614 CHECK(msg->findSize("trackIndex", &trackIndex));
615 CHECK_LT(trackIndex, mTracks.size());
616
617 uint32_t rtpTime;
618 CHECK(msg->findInt32("rtpTime", (int32_t *)&rtpTime));
619
620 int64_t nptUs;
621 CHECK(msg->findInt64("nptUs", &nptUs));
622
623 TrackInfo *info = &mTracks.editItemAt(trackIndex);
624 info->mRTPTime = rtpTime;
625 info->mNormalPlaytimeUs = nptUs;
626 info->mNPTMappingValid = true;
627 break;
628 }
629
630 case SDPLoader::kWhatSDPLoaded:
631 {
632 onSDPLoaded(msg);
633 break;
634 }
635
636 default:
637 TRESPASS();
638 }
639 }
640
onConnected()641 void NuPlayer::RTSPSource::onConnected() {
642 CHECK(mAudioTrack == NULL);
643 CHECK(mVideoTrack == NULL);
644
645 size_t numTracks = mHandler->countTracks();
646 for (size_t i = 0; i < numTracks; ++i) {
647 int32_t timeScale;
648 sp<MetaData> format = mHandler->getTrackFormat(i, &timeScale);
649
650 const char *mime;
651 CHECK(format->findCString(kKeyMIMEType, &mime));
652
653 if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
654 // Very special case for MPEG2 Transport Streams.
655 CHECK_EQ(numTracks, 1u);
656
657 mTSParser = new ATSParser;
658 return;
659 }
660
661 bool isAudio = !strncasecmp(mime, "audio/", 6);
662 bool isVideo = !strncasecmp(mime, "video/", 6);
663
664 TrackInfo info;
665 info.mTimeScale = timeScale;
666 info.mRTPTime = 0;
667 info.mNormalPlaytimeUs = 0ll;
668 info.mNPTMappingValid = false;
669
670 if ((isAudio && mAudioTrack == NULL)
671 || (isVideo && mVideoTrack == NULL)) {
672 sp<AnotherPacketSource> source = new AnotherPacketSource(format);
673
674 if (isAudio) {
675 mAudioTrack = source;
676 } else {
677 mVideoTrack = source;
678 }
679
680 info.mSource = source;
681 }
682
683 mTracks.push(info);
684 }
685
686 mState = CONNECTED;
687 }
688
onSDPLoaded(const sp<AMessage> & msg)689 void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) {
690 status_t err;
691 CHECK(msg->findInt32("result", &err));
692
693 mSDPLoader.clear();
694
695 if (mDisconnectReplyID != 0) {
696 err = UNKNOWN_ERROR;
697 }
698
699 if (err == OK) {
700 sp<ASessionDescription> desc;
701 sp<RefBase> obj;
702 CHECK(msg->findObject("description", &obj));
703 desc = static_cast<ASessionDescription *>(obj.get());
704
705 AString rtspUri;
706 if (!desc->findAttribute(0, "a=control", &rtspUri)) {
707 ALOGE("Unable to find url in SDP");
708 err = UNKNOWN_ERROR;
709 } else {
710 sp<AMessage> notify = new AMessage(kWhatNotify, this);
711
712 mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID);
713 mLooper->registerHandler(mHandler);
714
715 mHandler->loadSDP(desc);
716 }
717 }
718
719 if (err != OK) {
720 if (mState == CONNECTING) {
721 // We're still in the preparation phase, signal that it
722 // failed.
723 notifyPrepared(err);
724 }
725
726 mState = DISCONNECTED;
727 setError(err);
728
729 if (mDisconnectReplyID != 0) {
730 finishDisconnectIfPossible();
731 }
732 }
733 }
734
onDisconnected(const sp<AMessage> & msg)735 void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) {
736 if (mState == DISCONNECTED) {
737 return;
738 }
739
740 status_t err;
741 CHECK(msg->findInt32("result", &err));
742 CHECK_NE(err, (status_t)OK);
743
744 mLooper->unregisterHandler(mHandler->id());
745 mHandler.clear();
746
747 if (mState == CONNECTING) {
748 // We're still in the preparation phase, signal that it
749 // failed.
750 notifyPrepared(err);
751 }
752
753 mState = DISCONNECTED;
754 setError(err);
755
756 if (mDisconnectReplyID != 0) {
757 finishDisconnectIfPossible();
758 }
759 }
760
finishDisconnectIfPossible()761 void NuPlayer::RTSPSource::finishDisconnectIfPossible() {
762 if (mState != DISCONNECTED) {
763 if (mHandler != NULL) {
764 mHandler->disconnect();
765 } else if (mSDPLoader != NULL) {
766 mSDPLoader->cancel();
767 }
768 return;
769 }
770
771 (new AMessage)->postReply(mDisconnectReplyID);
772 mDisconnectReplyID = 0;
773 }
774
setError(status_t err)775 void NuPlayer::RTSPSource::setError(status_t err) {
776 Mutex::Autolock _l(mBufferingLock);
777 mFinalResult = err;
778 }
779
startBufferingIfNecessary()780 void NuPlayer::RTSPSource::startBufferingIfNecessary() {
781 Mutex::Autolock _l(mBufferingLock);
782
783 if (!mBuffering) {
784 mBuffering = true;
785
786 sp<AMessage> notify = dupNotify();
787 notify->setInt32("what", kWhatPauseOnBufferingStart);
788 notify->post();
789 }
790 }
791
stopBufferingIfNecessary()792 bool NuPlayer::RTSPSource::stopBufferingIfNecessary() {
793 Mutex::Autolock _l(mBufferingLock);
794
795 if (mBuffering) {
796 if (!haveSufficientDataOnAllTracks()) {
797 return false;
798 }
799
800 mBuffering = false;
801
802 sp<AMessage> notify = dupNotify();
803 notify->setInt32("what", kWhatResumeOnBufferingEnd);
804 notify->post();
805 }
806
807 return true;
808 }
809
finishSeek(status_t err)810 void NuPlayer::RTSPSource::finishSeek(status_t err) {
811 if (mSeekReplyID == NULL) {
812 return;
813 }
814 sp<AMessage> seekReply = new AMessage;
815 seekReply->setInt32("err", err);
816 seekReply->postReply(mSeekReplyID);
817 mSeekReplyID = NULL;
818 }
819
820 } // namespace android
821