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