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