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