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