MyHandler.h revision 6e4c5c499999c04c2477b987f9e64f3ff2bf1a06
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#ifndef MY_HANDLER_H_
18
19#define MY_HANDLER_H_
20
21//#define LOG_NDEBUG 0
22#define LOG_TAG "MyHandler"
23#include <utils/Log.h>
24
25#include "APacketSource.h"
26#include "ARTPConnection.h"
27#include "ARTSPConnection.h"
28#include "ASessionDescription.h"
29
30#include <ctype.h>
31
32#include <media/stagefright/foundation/ABuffer.h>
33#include <media/stagefright/foundation/ADebug.h>
34#include <media/stagefright/foundation/ALooper.h>
35#include <media/stagefright/foundation/AMessage.h>
36#include <media/stagefright/MediaErrors.h>
37
38// If no access units are received within 3 secs, assume that the rtp
39// stream has ended and signal end of stream.
40static int64_t kAccessUnitTimeoutUs = 3000000ll;
41
42// If no access units arrive for the first 10 secs after starting the
43// stream, assume none ever will and signal EOS or switch transports.
44static int64_t kStartupTimeoutUs = 10000000ll;
45
46namespace android {
47
48static bool GetAttribute(const char *s, const char *key, AString *value) {
49    value->clear();
50
51    size_t keyLen = strlen(key);
52
53    for (;;) {
54        while (isspace(*s)) {
55            ++s;
56        }
57
58        const char *colonPos = strchr(s, ';');
59
60        size_t len =
61            (colonPos == NULL) ? strlen(s) : colonPos - s;
62
63        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
64            value->setTo(&s[keyLen + 1], len - keyLen - 1);
65            return true;
66        }
67
68        if (colonPos == NULL) {
69            return false;
70        }
71
72        s = colonPos + 1;
73    }
74}
75
76struct MyHandler : public AHandler {
77    MyHandler(const char *url, const sp<ALooper> &looper)
78        : mLooper(looper),
79          mNetLooper(new ALooper),
80          mConn(new ARTSPConnection),
81          mRTPConn(new ARTPConnection),
82          mSessionURL(url),
83          mSetupTracksSuccessful(false),
84          mSeekPending(false),
85          mFirstAccessUnit(true),
86          mFirstAccessUnitNTP(0),
87          mNumAccessUnitsReceived(0),
88          mCheckPending(false),
89          mTryTCPInterleaving(false),
90          mReceivedFirstRTCPPacket(false) {
91        mNetLooper->setName("rtsp net");
92        mNetLooper->start(false /* runOnCallingThread */,
93                          false /* canCallJava */,
94                          PRIORITY_HIGHEST);
95    }
96
97    void connect(const sp<AMessage> &doneMsg) {
98        mDoneMsg = doneMsg;
99
100        mLooper->registerHandler(this);
101        mLooper->registerHandler(mConn);
102        (1 ? mNetLooper : mLooper)->registerHandler(mRTPConn);
103
104        sp<AMessage> notify = new AMessage('biny', id());
105        mConn->observeBinaryData(notify);
106
107        sp<AMessage> reply = new AMessage('conn', id());
108        mConn->connect(mSessionURL.c_str(), reply);
109    }
110
111    void disconnect(const sp<AMessage> &doneMsg) {
112        mDoneMsg = doneMsg;
113
114        (new AMessage('abor', id()))->post();
115    }
116
117    void seek(int64_t timeUs) {
118        sp<AMessage> msg = new AMessage('seek', id());
119        msg->setInt64("time", timeUs);
120        msg->post();
121    }
122
123    int64_t getNormalPlayTimeUs() {
124        int64_t maxTimeUs = 0;
125        for (size_t i = 0; i < mTracks.size(); ++i) {
126            int64_t timeUs = mTracks.editItemAt(i).mPacketSource
127                ->getNormalPlayTimeUs();
128
129            if (i == 0 || timeUs > maxTimeUs) {
130                maxTimeUs = timeUs;
131            }
132        }
133
134        return maxTimeUs;
135    }
136
137    virtual void onMessageReceived(const sp<AMessage> &msg) {
138        switch (msg->what()) {
139            case 'conn':
140            {
141                int32_t result;
142                CHECK(msg->findInt32("result", &result));
143
144                LOGI("connection request completed with result %d (%s)",
145                     result, strerror(-result));
146
147                if (result == OK) {
148                    AString request;
149                    request = "DESCRIBE ";
150                    request.append(mSessionURL);
151                    request.append(" RTSP/1.0\r\n");
152                    request.append("Accept: application/sdp\r\n");
153                    request.append("\r\n");
154
155                    sp<AMessage> reply = new AMessage('desc', id());
156                    mConn->sendRequest(request.c_str(), reply);
157                } else {
158                    (new AMessage('disc', id()))->post();
159                }
160                break;
161            }
162
163            case 'disc':
164            {
165                int32_t reconnect;
166                if (msg->findInt32("reconnect", &reconnect) && reconnect) {
167                    sp<AMessage> reply = new AMessage('conn', id());
168                    mConn->connect(mSessionURL.c_str(), reply);
169                } else {
170                    (new AMessage('quit', id()))->post();
171                }
172                break;
173            }
174
175            case 'desc':
176            {
177                int32_t result;
178                CHECK(msg->findInt32("result", &result));
179
180                LOGI("DESCRIBE completed with result %d (%s)",
181                     result, strerror(-result));
182
183                if (result == OK) {
184                    sp<RefBase> obj;
185                    CHECK(msg->findObject("response", &obj));
186                    sp<ARTSPResponse> response =
187                        static_cast<ARTSPResponse *>(obj.get());
188
189                    if (response->mStatusCode == 302) {
190                        ssize_t i = response->mHeaders.indexOfKey("location");
191                        CHECK_GE(i, 0);
192
193                        mSessionURL = response->mHeaders.valueAt(i);
194
195                        AString request;
196                        request = "DESCRIBE ";
197                        request.append(mSessionURL);
198                        request.append(" RTSP/1.0\r\n");
199                        request.append("Accept: application/sdp\r\n");
200                        request.append("\r\n");
201
202                        sp<AMessage> reply = new AMessage('desc', id());
203                        mConn->sendRequest(request.c_str(), reply);
204                        break;
205                    }
206
207                    if (response->mStatusCode != 200) {
208                        result = UNKNOWN_ERROR;
209                    } else {
210                        mSessionDesc = new ASessionDescription;
211
212                        mSessionDesc->setTo(
213                                response->mContent->data(),
214                                response->mContent->size());
215
216                        if (!mSessionDesc->isValid()) {
217                            result = ERROR_MALFORMED;
218                        } else {
219                            ssize_t i = response->mHeaders.indexOfKey("content-base");
220                            if (i >= 0) {
221                                mBaseURL = response->mHeaders.valueAt(i);
222                            } else {
223                                i = response->mHeaders.indexOfKey("content-location");
224                                if (i >= 0) {
225                                    mBaseURL = response->mHeaders.valueAt(i);
226                                } else {
227                                    mBaseURL = mSessionURL;
228                                }
229                            }
230
231                            CHECK_GT(mSessionDesc->countTracks(), 1u);
232                            setupTrack(1);
233                        }
234                    }
235                }
236
237                if (result != OK) {
238                    sp<AMessage> reply = new AMessage('disc', id());
239                    mConn->disconnect(reply);
240                }
241                break;
242            }
243
244            case 'setu':
245            {
246                size_t index;
247                CHECK(msg->findSize("index", &index));
248
249                TrackInfo *track = NULL;
250                size_t trackIndex;
251                if (msg->findSize("track-index", &trackIndex)) {
252                    track = &mTracks.editItemAt(trackIndex);
253                }
254
255                int32_t result;
256                CHECK(msg->findInt32("result", &result));
257
258                LOGI("SETUP(%d) completed with result %d (%s)",
259                     index, result, strerror(-result));
260
261                if (result == OK) {
262                    CHECK(track != NULL);
263
264                    sp<RefBase> obj;
265                    CHECK(msg->findObject("response", &obj));
266                    sp<ARTSPResponse> response =
267                        static_cast<ARTSPResponse *>(obj.get());
268
269                    if (response->mStatusCode != 200) {
270                        result = UNKNOWN_ERROR;
271                    } else {
272                        ssize_t i = response->mHeaders.indexOfKey("session");
273                        CHECK_GE(i, 0);
274
275                        mSessionID = response->mHeaders.valueAt(i);
276                        i = mSessionID.find(";");
277                        if (i >= 0) {
278                            // Remove options, i.e. ";timeout=90"
279                            mSessionID.erase(i, mSessionID.size() - i);
280                        }
281
282                        sp<AMessage> notify = new AMessage('accu', id());
283                        notify->setSize("track-index", trackIndex);
284
285                        mRTPConn->addStream(
286                                track->mRTPSocket, track->mRTCPSocket,
287                                mSessionDesc, index,
288                                notify, track->mUsingInterleavedTCP);
289
290                        mSetupTracksSuccessful = true;
291                    }
292                }
293
294                if (result != OK) {
295                    if (track) {
296                        if (!track->mUsingInterleavedTCP) {
297                            close(track->mRTPSocket);
298                            close(track->mRTCPSocket);
299                        }
300
301                        mTracks.removeItemsAt(trackIndex);
302                    }
303                }
304
305                ++index;
306                if (index < mSessionDesc->countTracks()) {
307                    setupTrack(index);
308                } else if (mSetupTracksSuccessful) {
309                    AString request = "PLAY ";
310                    request.append(mSessionURL);
311                    request.append(" RTSP/1.0\r\n");
312
313                    request.append("Session: ");
314                    request.append(mSessionID);
315                    request.append("\r\n");
316
317                    request.append("\r\n");
318
319                    sp<AMessage> reply = new AMessage('play', id());
320                    mConn->sendRequest(request.c_str(), reply);
321                } else {
322                    sp<AMessage> reply = new AMessage('disc', id());
323                    mConn->disconnect(reply);
324                }
325                break;
326            }
327
328            case 'play':
329            {
330                int32_t result;
331                CHECK(msg->findInt32("result", &result));
332
333                LOGI("PLAY completed with result %d (%s)",
334                     result, strerror(-result));
335
336                if (result == OK) {
337                    sp<RefBase> obj;
338                    CHECK(msg->findObject("response", &obj));
339                    sp<ARTSPResponse> response =
340                        static_cast<ARTSPResponse *>(obj.get());
341
342                    if (response->mStatusCode != 200) {
343                        result = UNKNOWN_ERROR;
344                    } else {
345                        parsePlayResponse(response);
346
347                        sp<AMessage> timeout = new AMessage('tiou', id());
348                        timeout->post(kStartupTimeoutUs);
349                    }
350                }
351
352                if (result != OK) {
353                    sp<AMessage> reply = new AMessage('disc', id());
354                    mConn->disconnect(reply);
355                }
356
357                break;
358            }
359
360            case 'abor':
361            {
362                for (size_t i = 0; i < mTracks.size(); ++i) {
363                    TrackInfo *info = &mTracks.editItemAt(i);
364
365                    info->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
366
367                    if (!info->mUsingInterleavedTCP) {
368                        mRTPConn->removeStream(info->mRTPSocket, info->mRTCPSocket);
369
370                        close(info->mRTPSocket);
371                        close(info->mRTCPSocket);
372                    }
373                }
374                mTracks.clear();
375                mSetupTracksSuccessful = false;
376                mSeekPending = false;
377                mFirstAccessUnit = true;
378                mFirstAccessUnitNTP = 0;
379                mNumAccessUnitsReceived = 0;
380                mReceivedFirstRTCPPacket = false;
381
382                sp<AMessage> reply = new AMessage('tear', id());
383
384                int32_t reconnect;
385                if (msg->findInt32("reconnect", &reconnect) && reconnect) {
386                    reply->setInt32("reconnect", true);
387                }
388
389                AString request;
390                request = "TEARDOWN ";
391
392                // XXX should use aggregate url from SDP here...
393                request.append(mSessionURL);
394                request.append(" RTSP/1.0\r\n");
395
396                request.append("Session: ");
397                request.append(mSessionID);
398                request.append("\r\n");
399
400                request.append("\r\n");
401
402                mConn->sendRequest(request.c_str(), reply);
403                break;
404            }
405
406            case 'tear':
407            {
408                int32_t result;
409                CHECK(msg->findInt32("result", &result));
410
411                LOGI("TEARDOWN completed with result %d (%s)",
412                     result, strerror(-result));
413
414                sp<AMessage> reply = new AMessage('disc', id());
415
416                int32_t reconnect;
417                if (msg->findInt32("reconnect", &reconnect) && reconnect) {
418                    reply->setInt32("reconnect", true);
419                }
420
421                mConn->disconnect(reply);
422                break;
423            }
424
425            case 'quit':
426            {
427                if (mDoneMsg != NULL) {
428                    mDoneMsg->setInt32("result", UNKNOWN_ERROR);
429                    mDoneMsg->post();
430                    mDoneMsg = NULL;
431                }
432                break;
433            }
434
435            case 'chek':
436            {
437                if (mNumAccessUnitsReceived == 0) {
438                    LOGI("stream ended? aborting.");
439                    (new AMessage('abor', id()))->post();
440                    break;
441                }
442
443                mNumAccessUnitsReceived = 0;
444                msg->post(kAccessUnitTimeoutUs);
445                break;
446            }
447
448            case 'accu':
449            {
450                int32_t firstRTCP;
451                if (msg->findInt32("first-rtcp", &firstRTCP)) {
452                    mReceivedFirstRTCPPacket = true;
453                    break;
454                }
455
456                ++mNumAccessUnitsReceived;
457
458                if (!mCheckPending) {
459                    mCheckPending = true;
460                    sp<AMessage> check = new AMessage('chek', id());
461                    check->post(kAccessUnitTimeoutUs);
462                }
463
464                size_t trackIndex;
465                CHECK(msg->findSize("track-index", &trackIndex));
466
467                if (trackIndex >= mTracks.size()) {
468                    LOGV("late packets ignored.");
469                    break;
470                }
471
472                TrackInfo *track = &mTracks.editItemAt(trackIndex);
473
474                int32_t eos;
475                if (msg->findInt32("eos", &eos)) {
476                    LOGI("received BYE on track index %d", trackIndex);
477#if 0
478                    track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
479#endif
480                    return;
481                }
482
483                sp<RefBase> obj;
484                CHECK(msg->findObject("access-unit", &obj));
485
486                sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
487
488                uint32_t seqNum = (uint32_t)accessUnit->int32Data();
489
490                if (mSeekPending) {
491                    LOGV("we're seeking, dropping stale packet.");
492                    break;
493                }
494
495                if (seqNum < track->mFirstSeqNumInSegment) {
496                    LOGV("dropping stale access-unit (%d < %d)",
497                         seqNum, track->mFirstSeqNumInSegment);
498                    break;
499                }
500
501                uint64_t ntpTime;
502                CHECK(accessUnit->meta()->findInt64(
503                            "ntp-time", (int64_t *)&ntpTime));
504
505                uint32_t rtpTime;
506                CHECK(accessUnit->meta()->findInt32(
507                            "rtp-time", (int32_t *)&rtpTime));
508
509                if (track->mNewSegment) {
510                    track->mNewSegment = false;
511
512                    LOGV("first segment unit ntpTime=0x%016llx rtpTime=%u seq=%d",
513                         ntpTime, rtpTime, seqNum);
514                }
515
516                if (mFirstAccessUnit) {
517                    mDoneMsg->setInt32("result", OK);
518                    mDoneMsg->post();
519                    mDoneMsg = NULL;
520
521                    mFirstAccessUnit = false;
522                    mFirstAccessUnitNTP = ntpTime;
523                }
524
525                if (ntpTime >= mFirstAccessUnitNTP) {
526                    ntpTime -= mFirstAccessUnitNTP;
527                } else {
528                    ntpTime = 0;
529                }
530
531                int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
532
533                accessUnit->meta()->setInt64("timeUs", timeUs);
534
535#if 0
536                int32_t damaged;
537                if (accessUnit->meta()->findInt32("damaged", &damaged)
538                        && damaged != 0) {
539                    LOGI("ignoring damaged AU");
540                } else
541#endif
542                {
543                    TrackInfo *track = &mTracks.editItemAt(trackIndex);
544                    track->mPacketSource->queueAccessUnit(accessUnit);
545                }
546                break;
547            }
548
549            case 'seek':
550            {
551                if (mSeekPending) {
552                    break;
553                }
554
555                int64_t timeUs;
556                CHECK(msg->findInt64("time", &timeUs));
557
558                mSeekPending = true;
559
560                AString request = "PAUSE ";
561                request.append(mSessionURL);
562                request.append(" RTSP/1.0\r\n");
563
564                request.append("Session: ");
565                request.append(mSessionID);
566                request.append("\r\n");
567
568                request.append("\r\n");
569
570                sp<AMessage> reply = new AMessage('see1', id());
571                reply->setInt64("time", timeUs);
572                mConn->sendRequest(request.c_str(), reply);
573                break;
574            }
575
576            case 'see1':
577            {
578                // Session is paused now.
579                for (size_t i = 0; i < mTracks.size(); ++i) {
580                    mTracks.editItemAt(i).mPacketSource->flushQueue();
581                }
582
583                int64_t timeUs;
584                CHECK(msg->findInt64("time", &timeUs));
585
586                AString request = "PLAY ";
587                request.append(mSessionURL);
588                request.append(" RTSP/1.0\r\n");
589
590                request.append("Session: ");
591                request.append(mSessionID);
592                request.append("\r\n");
593
594                request.append(
595                        StringPrintf(
596                            "Range: npt=%lld-\r\n", timeUs / 1000000ll));
597
598                request.append("\r\n");
599
600                sp<AMessage> reply = new AMessage('see2', id());
601                mConn->sendRequest(request.c_str(), reply);
602                break;
603            }
604
605            case 'see2':
606            {
607                CHECK(mSeekPending);
608
609                int32_t result;
610                CHECK(msg->findInt32("result", &result));
611
612                LOGI("PLAY completed with result %d (%s)",
613                     result, strerror(-result));
614
615                if (result == OK) {
616                    sp<RefBase> obj;
617                    CHECK(msg->findObject("response", &obj));
618                    sp<ARTSPResponse> response =
619                        static_cast<ARTSPResponse *>(obj.get());
620
621                    if (response->mStatusCode != 200) {
622                        result = UNKNOWN_ERROR;
623                    } else {
624                        parsePlayResponse(response);
625
626                        LOGI("seek completed.");
627                    }
628                }
629
630                if (result != OK) {
631                    LOGE("seek failed, aborting.");
632                    (new AMessage('abor', id()))->post();
633                }
634
635                mSeekPending = false;
636                break;
637            }
638
639            case 'biny':
640            {
641                sp<RefBase> obj;
642                CHECK(msg->findObject("buffer", &obj));
643                sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
644
645                int32_t index;
646                CHECK(buffer->meta()->findInt32("index", &index));
647
648                mRTPConn->injectPacket(index, buffer);
649                break;
650            }
651
652            case 'tiou':
653            {
654                if (!mReceivedFirstRTCPPacket) {
655                    if (mTryTCPInterleaving) {
656                        LOGW("Never received any data, disconnecting.");
657                        (new AMessage('abor', id()))->post();
658                    } else {
659                        LOGW("Never received any data, switching transports.");
660
661                        mTryTCPInterleaving = true;
662
663                        sp<AMessage> msg = new AMessage('abor', id());
664                        msg->setInt32("reconnect", true);
665                        msg->post();
666                    }
667                }
668                break;
669            }
670
671            default:
672                TRESPASS();
673                break;
674        }
675    }
676
677    static void SplitString(
678            const AString &s, const char *separator, List<AString> *items) {
679        items->clear();
680        size_t start = 0;
681        while (start < s.size()) {
682            ssize_t offset = s.find(separator, start);
683
684            if (offset < 0) {
685                items->push_back(AString(s, start, s.size() - start));
686                break;
687            }
688
689            items->push_back(AString(s, start, offset - start));
690            start = offset + strlen(separator);
691        }
692    }
693
694    void parsePlayResponse(const sp<ARTSPResponse> &response) {
695        ssize_t i = response->mHeaders.indexOfKey("range");
696        if (i < 0) {
697            // Server doesn't even tell use what range it is going to
698            // play, therefore we won't support seeking.
699            return;
700        }
701
702        AString range = response->mHeaders.valueAt(i);
703        LOGV("Range: %s", range.c_str());
704
705        AString val;
706        CHECK(GetAttribute(range.c_str(), "npt", &val));
707        float npt1, npt2;
708
709        if (val == "now-") {
710            // This is a live stream and therefore not seekable.
711            return;
712        } else {
713            CHECK_EQ(sscanf(val.c_str(), "%f-%f", &npt1, &npt2), 2);
714        }
715
716        i = response->mHeaders.indexOfKey("rtp-info");
717        CHECK_GE(i, 0);
718
719        AString rtpInfo = response->mHeaders.valueAt(i);
720        List<AString> streamInfos;
721        SplitString(rtpInfo, ",", &streamInfos);
722
723        int n = 1;
724        for (List<AString>::iterator it = streamInfos.begin();
725             it != streamInfos.end(); ++it) {
726            (*it).trim();
727            LOGV("streamInfo[%d] = %s", n, (*it).c_str());
728
729            CHECK(GetAttribute((*it).c_str(), "url", &val));
730
731            size_t trackIndex = 0;
732            while (trackIndex < mTracks.size()
733                    && !(val == mTracks.editItemAt(trackIndex).mURL)) {
734                ++trackIndex;
735            }
736            CHECK_LT(trackIndex, mTracks.size());
737
738            CHECK(GetAttribute((*it).c_str(), "seq", &val));
739
740            char *end;
741            unsigned long seq = strtoul(val.c_str(), &end, 10);
742
743            TrackInfo *info = &mTracks.editItemAt(trackIndex);
744            info->mFirstSeqNumInSegment = seq;
745            info->mNewSegment = true;
746
747            CHECK(GetAttribute((*it).c_str(), "rtptime", &val));
748
749            uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
750
751            LOGV("track #%d: rtpTime=%u <=> ntp=%.2f", n, rtpTime, npt1);
752
753            info->mPacketSource->setNormalPlayTimeMapping(
754                    rtpTime, (int64_t)(npt1 * 1E6));
755
756            ++n;
757        }
758    }
759
760    sp<APacketSource> getPacketSource(size_t index) {
761        CHECK_GE(index, 0u);
762        CHECK_LT(index, mTracks.size());
763
764        return mTracks.editItemAt(index).mPacketSource;
765    }
766
767    size_t countTracks() const {
768        return mTracks.size();
769    }
770
771private:
772    sp<ALooper> mLooper;
773    sp<ALooper> mNetLooper;
774    sp<ARTSPConnection> mConn;
775    sp<ARTPConnection> mRTPConn;
776    sp<ASessionDescription> mSessionDesc;
777    AString mSessionURL;
778    AString mBaseURL;
779    AString mSessionID;
780    bool mSetupTracksSuccessful;
781    bool mSeekPending;
782    bool mFirstAccessUnit;
783    uint64_t mFirstAccessUnitNTP;
784    int64_t mNumAccessUnitsReceived;
785    bool mCheckPending;
786    bool mTryTCPInterleaving;
787    bool mReceivedFirstRTCPPacket;
788
789    struct TrackInfo {
790        AString mURL;
791        int mRTPSocket;
792        int mRTCPSocket;
793        bool mUsingInterleavedTCP;
794        uint32_t mFirstSeqNumInSegment;
795        bool mNewSegment;
796
797        sp<APacketSource> mPacketSource;
798    };
799    Vector<TrackInfo> mTracks;
800
801    sp<AMessage> mDoneMsg;
802
803    void setupTrack(size_t index) {
804        sp<APacketSource> source =
805            new APacketSource(mSessionDesc, index);
806
807        if (source->initCheck() != OK) {
808            LOGW("Unsupported format. Ignoring track #%d.", index);
809
810            sp<AMessage> reply = new AMessage('setu', id());
811            reply->setSize("index", index);
812            reply->setInt32("result", ERROR_UNSUPPORTED);
813            reply->post();
814            return;
815        }
816
817        AString url;
818        CHECK(mSessionDesc->findAttribute(index, "a=control", &url));
819
820        AString trackURL;
821        CHECK(MakeURL(mBaseURL.c_str(), url.c_str(), &trackURL));
822
823        mTracks.push(TrackInfo());
824        TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
825        info->mURL = trackURL;
826        info->mPacketSource = source;
827        info->mUsingInterleavedTCP = false;
828        info->mFirstSeqNumInSegment = 0;
829        info->mNewSegment = true;
830
831        LOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str());
832
833        AString request = "SETUP ";
834        request.append(trackURL);
835        request.append(" RTSP/1.0\r\n");
836
837        if (mTryTCPInterleaving) {
838            size_t interleaveIndex = 2 * (mTracks.size() - 1);
839            info->mUsingInterleavedTCP = true;
840            info->mRTPSocket = interleaveIndex;
841            info->mRTCPSocket = interleaveIndex + 1;
842
843            request.append("Transport: RTP/AVP/TCP;interleaved=");
844            request.append(interleaveIndex);
845            request.append("-");
846            request.append(interleaveIndex + 1);
847        } else {
848            unsigned rtpPort;
849            ARTPConnection::MakePortPair(
850                    &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
851
852            request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
853            request.append(rtpPort);
854            request.append("-");
855            request.append(rtpPort + 1);
856        }
857
858        request.append("\r\n");
859
860        if (index > 1) {
861            request.append("Session: ");
862            request.append(mSessionID);
863            request.append("\r\n");
864        }
865
866        request.append("\r\n");
867
868        sp<AMessage> reply = new AMessage('setu', id());
869        reply->setSize("index", index);
870        reply->setSize("track-index", mTracks.size() - 1);
871        mConn->sendRequest(request.c_str(), reply);
872    }
873
874    static bool MakeURL(const char *baseURL, const char *url, AString *out) {
875        out->clear();
876
877        if (strncasecmp("rtsp://", baseURL, 7)) {
878            // Base URL must be absolute
879            return false;
880        }
881
882        if (!strncasecmp("rtsp://", url, 7)) {
883            // "url" is already an absolute URL, ignore base URL.
884            out->setTo(url);
885            return true;
886        }
887
888        size_t n = strlen(baseURL);
889        if (baseURL[n - 1] == '/') {
890            out->setTo(baseURL);
891            out->append(url);
892        } else {
893            char *slashPos = strrchr(baseURL, '/');
894
895            if (slashPos > &baseURL[6]) {
896                out->setTo(baseURL, slashPos - baseURL);
897            } else {
898                out->setTo(baseURL);
899            }
900
901            out->append("/");
902            out->append(url);
903        }
904
905        return true;
906    }
907
908    DISALLOW_EVIL_CONSTRUCTORS(MyHandler);
909};
910
911}  // namespace android
912
913#endif  // MY_HANDLER_H_
914