|
40 | 40 | #include <sys/socket.h> |
41 | 41 | #include <netdb.h> |
42 | 42 |
|
43 | | -// If no access units are received within 3 secs, assume that the rtp |
| 43 | +// If no access units are received within 10 secs, assume that the rtp |
44 | 44 | // stream has ended and signal end of stream. |
45 | | -static int64_t kAccessUnitTimeoutUs = 3000000ll; |
| 45 | +static int64_t kAccessUnitTimeoutUs = 10000000ll; |
46 | 46 |
|
47 | 47 | // If no access units arrive for the first 10 secs after starting the |
48 | 48 | // stream, assume none ever will and signal EOS or switch transports. |
49 | 49 | static int64_t kStartupTimeoutUs = 10000000ll; |
50 | 50 |
|
| 51 | +static int64_t kDefaultKeepAliveTimeoutUs = 60000000ll; |
| 52 | + |
51 | 53 | namespace android { |
52 | 54 |
|
53 | 55 | static void MakeUserAgentString(AString *s) { |
@@ -110,7 +112,9 @@ struct MyHandler : public AHandler { |
110 | 112 | mTryFakeRTCP(false), |
111 | 113 | mReceivedFirstRTCPPacket(false), |
112 | 114 | mReceivedFirstRTPPacket(false), |
113 | | - mSeekable(false) { |
| 115 | + mSeekable(false), |
| 116 | + mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs), |
| 117 | + mKeepAliveGeneration(0) { |
114 | 118 | mNetLooper->setName("rtsp net"); |
115 | 119 | mNetLooper->start(false /* runOnCallingThread */, |
116 | 120 | false /* canCallJava */, |
@@ -371,6 +375,8 @@ struct MyHandler : public AHandler { |
371 | 375 |
|
372 | 376 | case 'disc': |
373 | 377 | { |
| 378 | + ++mKeepAliveGeneration; |
| 379 | + |
374 | 380 | int32_t reconnect; |
375 | 381 | if (msg->findInt32("reconnect", &reconnect) && reconnect) { |
376 | 382 | sp<AMessage> reply = new AMessage('conn', id()); |
@@ -502,6 +508,34 @@ struct MyHandler : public AHandler { |
502 | 508 | CHECK_GE(i, 0); |
503 | 509 |
|
504 | 510 | mSessionID = response->mHeaders.valueAt(i); |
| 511 | + |
| 512 | + mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs; |
| 513 | + AString timeoutStr; |
| 514 | + if (GetAttribute( |
| 515 | + mSessionID.c_str(), "timeout", &timeoutStr)) { |
| 516 | + char *end; |
| 517 | + unsigned long timeoutSecs = |
| 518 | + strtoul(timeoutStr.c_str(), &end, 10); |
| 519 | + |
| 520 | + if (end == timeoutStr.c_str() || *end != '\0') { |
| 521 | + LOGW("server specified malformed timeout '%s'", |
| 522 | + timeoutStr.c_str()); |
| 523 | + |
| 524 | + mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs; |
| 525 | + } else if (timeoutSecs < 15) { |
| 526 | + LOGW("server specified too short a timeout " |
| 527 | + "(%lu secs), using default.", |
| 528 | + timeoutSecs); |
| 529 | + |
| 530 | + mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs; |
| 531 | + } else { |
| 532 | + mKeepAliveTimeoutUs = timeoutSecs * 1000000ll; |
| 533 | + |
| 534 | + LOGI("server specified timeout of %lu secs.", |
| 535 | + timeoutSecs); |
| 536 | + } |
| 537 | + } |
| 538 | + |
505 | 539 | i = mSessionID.find(";"); |
506 | 540 | if (i >= 0) { |
507 | 541 | // Remove options, i.e. ";timeout=90" |
@@ -549,6 +583,9 @@ struct MyHandler : public AHandler { |
549 | 583 | if (index < mSessionDesc->countTracks()) { |
550 | 584 | setupTrack(index); |
551 | 585 | } else if (mSetupTracksSuccessful) { |
| 586 | + ++mKeepAliveGeneration; |
| 587 | + postKeepAlive(); |
| 588 | + |
552 | 589 | AString request = "PLAY "; |
553 | 590 | request.append(mSessionURL); |
554 | 591 | request.append(" RTSP/1.0\r\n"); |
@@ -600,6 +637,51 @@ struct MyHandler : public AHandler { |
600 | 637 | break; |
601 | 638 | } |
602 | 639 |
|
| 640 | + case 'aliv': |
| 641 | + { |
| 642 | + int32_t generation; |
| 643 | + CHECK(msg->findInt32("generation", &generation)); |
| 644 | + |
| 645 | + if (generation != mKeepAliveGeneration) { |
| 646 | + // obsolete event. |
| 647 | + break; |
| 648 | + } |
| 649 | + |
| 650 | + AString request; |
| 651 | + request.append("OPTIONS "); |
| 652 | + request.append(mSessionURL); |
| 653 | + request.append(" RTSP/1.0\r\n"); |
| 654 | + request.append("Session: "); |
| 655 | + request.append(mSessionID); |
| 656 | + request.append("\r\n"); |
| 657 | + request.append("\r\n"); |
| 658 | + |
| 659 | + sp<AMessage> reply = new AMessage('opts', id()); |
| 660 | + reply->setInt32("generation", mKeepAliveGeneration); |
| 661 | + mConn->sendRequest(request.c_str(), reply); |
| 662 | + break; |
| 663 | + } |
| 664 | + |
| 665 | + case 'opts': |
| 666 | + { |
| 667 | + int32_t result; |
| 668 | + CHECK(msg->findInt32("result", &result)); |
| 669 | + |
| 670 | + LOGI("OPTIONS completed with result %d (%s)", |
| 671 | + result, strerror(-result)); |
| 672 | + |
| 673 | + int32_t generation; |
| 674 | + CHECK(msg->findInt32("generation", &generation)); |
| 675 | + |
| 676 | + if (generation != mKeepAliveGeneration) { |
| 677 | + // obsolete event. |
| 678 | + break; |
| 679 | + } |
| 680 | + |
| 681 | + postKeepAlive(); |
| 682 | + break; |
| 683 | + } |
| 684 | + |
603 | 685 | case 'abor': |
604 | 686 | { |
605 | 687 | for (size_t i = 0; i < mTracks.size(); ++i) { |
@@ -962,6 +1044,12 @@ struct MyHandler : public AHandler { |
962 | 1044 | } |
963 | 1045 | } |
964 | 1046 |
|
| 1047 | + void postKeepAlive() { |
| 1048 | + sp<AMessage> msg = new AMessage('aliv', id()); |
| 1049 | + msg->setInt32("generation", mKeepAliveGeneration); |
| 1050 | + msg->post((mKeepAliveTimeoutUs * 9) / 10); |
| 1051 | + } |
| 1052 | + |
965 | 1053 | void postAccessUnitTimeoutCheck() { |
966 | 1054 | if (mCheckPending) { |
967 | 1055 | return; |
@@ -1092,6 +1180,8 @@ struct MyHandler : public AHandler { |
1092 | 1180 | bool mReceivedFirstRTCPPacket; |
1093 | 1181 | bool mReceivedFirstRTPPacket; |
1094 | 1182 | bool mSeekable; |
| 1183 | + int64_t mKeepAliveTimeoutUs; |
| 1184 | + int32_t mKeepAliveGeneration; |
1095 | 1185 |
|
1096 | 1186 | struct TrackInfo { |
1097 | 1187 | AString mURL; |
|
0 commit comments