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