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