netwerk/protocol/rtsp/rtsp/RTSPTransmitter.h

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /*
michael@0 2 * Copyright (C) 2010 The Android Open Source Project
michael@0 3 *
michael@0 4 * Licensed under the Apache License, Version 2.0 (the "License");
michael@0 5 * you may not use this file except in compliance with the License.
michael@0 6 * You may obtain a copy of the License at
michael@0 7 *
michael@0 8 * http://www.apache.org/licenses/LICENSE-2.0
michael@0 9 *
michael@0 10 * Unless required by applicable law or agreed to in writing, software
michael@0 11 * distributed under the License is distributed on an "AS IS" BASIS,
michael@0 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
michael@0 13 * See the License for the specific language governing permissions and
michael@0 14 * limitations under the License.
michael@0 15 */
michael@0 16
michael@0 17 #ifndef RTSP_TRANSMITTER_H_
michael@0 18
michael@0 19 #define RTSP_TRANSMITTER_H_
michael@0 20
michael@0 21 #include "ARTPConnection.h"
michael@0 22
michael@0 23 #include <arpa/inet.h>
michael@0 24 #include <sys/socket.h>
michael@0 25
michael@0 26 #include <openssl/md5.h>
michael@0 27
michael@0 28 #include <media/stagefright/foundation/ADebug.h>
michael@0 29 #include <media/stagefright/foundation/base64.h>
michael@0 30 #include <media/stagefright/foundation/hexdump.h>
michael@0 31
michael@0 32 #ifdef ANDROID
michael@0 33 #include "VideoSource.h"
michael@0 34
michael@0 35 #include <media/stagefright/OMXClient.h>
michael@0 36 #include <media/stagefright/OMXCodec.h>
michael@0 37 #endif
michael@0 38
michael@0 39 namespace android {
michael@0 40
michael@0 41 #define TRACK_SUFFIX "trackid=1"
michael@0 42 #define PT 96
michael@0 43 #define PT_STR "96"
michael@0 44
michael@0 45 #define USERNAME "bcast"
michael@0 46 #define PASSWORD "test"
michael@0 47
michael@0 48 static int uniformRand(int limit) {
michael@0 49 return ((double)rand() * limit) / RAND_MAX;
michael@0 50 }
michael@0 51
michael@0 52 static bool GetAttribute(const char *s, const char *key, AString *value) {
michael@0 53 value->clear();
michael@0 54
michael@0 55 size_t keyLen = strlen(key);
michael@0 56
michael@0 57 for (;;) {
michael@0 58 const char *colonPos = strchr(s, ';');
michael@0 59
michael@0 60 size_t len =
michael@0 61 (colonPos == NULL) ? strlen(s) : colonPos - s;
michael@0 62
michael@0 63 if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
michael@0 64 value->setTo(&s[keyLen + 1], len - keyLen - 1);
michael@0 65 return true;
michael@0 66 }
michael@0 67
michael@0 68 if (colonPos == NULL) {
michael@0 69 return false;
michael@0 70 }
michael@0 71
michael@0 72 s = colonPos + 1;
michael@0 73 }
michael@0 74 }
michael@0 75
michael@0 76 struct MyTransmitter : public AHandler {
michael@0 77 MyTransmitter(const char *url, const sp<ALooper> &looper)
michael@0 78 : mServerURL(url),
michael@0 79 mLooper(looper),
michael@0 80 mConn(new ARTSPConnection),
michael@0 81 mConnected(false),
michael@0 82 mAuthType(NONE),
michael@0 83 mRTPSocket(-1),
michael@0 84 mRTCPSocket(-1),
michael@0 85 mSourceID(rand()),
michael@0 86 mSeqNo(uniformRand(65536)),
michael@0 87 mRTPTimeBase(rand()),
michael@0 88 mNumSamplesSent(0),
michael@0 89 mNumRTPSent(0),
michael@0 90 mNumRTPOctetsSent(0),
michael@0 91 mLastRTPTime(0),
michael@0 92 mLastNTPTime(0) {
michael@0 93 mStreamURL = mServerURL;
michael@0 94 mStreamURL.append("/bazong.sdp");
michael@0 95
michael@0 96 mTrackURL = mStreamURL;
michael@0 97 mTrackURL.append("/");
michael@0 98 mTrackURL.append(TRACK_SUFFIX);
michael@0 99
michael@0 100 mLooper->registerHandler(this);
michael@0 101 mLooper->registerHandler(mConn);
michael@0 102
michael@0 103 sp<AMessage> reply = new AMessage('conn', id());
michael@0 104 mConn->connect(mServerURL.c_str(), reply);
michael@0 105
michael@0 106 #ifdef ANDROID
michael@0 107 int width = 640;
michael@0 108 int height = 480;
michael@0 109
michael@0 110 sp<MediaSource> source = new VideoSource(width, height);
michael@0 111
michael@0 112 sp<MetaData> encMeta = new MetaData;
michael@0 113 encMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
michael@0 114 encMeta->setInt32(kKeyWidth, width);
michael@0 115 encMeta->setInt32(kKeyHeight, height);
michael@0 116
michael@0 117 OMXClient client;
michael@0 118 client.connect();
michael@0 119
michael@0 120 mEncoder = OMXCodec::Create(
michael@0 121 client.interface(), encMeta,
michael@0 122 true /* createEncoder */, source);
michael@0 123
michael@0 124 mEncoder->start();
michael@0 125
michael@0 126 MediaBuffer *buffer;
michael@0 127 CHECK_EQ(mEncoder->read(&buffer), (status_t)OK);
michael@0 128 CHECK(buffer != NULL);
michael@0 129
michael@0 130 makeH264SPropParamSets(buffer);
michael@0 131
michael@0 132 buffer->release();
michael@0 133 buffer = NULL;
michael@0 134 #endif
michael@0 135 }
michael@0 136
michael@0 137 uint64_t ntpTime() {
michael@0 138 struct timeval tv;
michael@0 139 gettimeofday(&tv, NULL);
michael@0 140
michael@0 141 uint64_t nowUs = tv.tv_sec * 1000000ll + tv.tv_usec;
michael@0 142
michael@0 143 nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
michael@0 144
michael@0 145 uint64_t hi = nowUs / 1000000ll;
michael@0 146 uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
michael@0 147
michael@0 148 return (hi << 32) | lo;
michael@0 149 }
michael@0 150
michael@0 151 void issueAnnounce() {
michael@0 152 AString sdp;
michael@0 153 sdp = "v=0\r\n";
michael@0 154
michael@0 155 sdp.append("o=- ");
michael@0 156
michael@0 157 uint64_t ntp = ntpTime();
michael@0 158 sdp.append(ntp);
michael@0 159 sdp.append(" ");
michael@0 160 sdp.append(ntp);
michael@0 161 sdp.append(" IN IP4 127.0.0.0\r\n");
michael@0 162
michael@0 163 sdp.append(
michael@0 164 "s=Sample\r\n"
michael@0 165 "i=Playing around with ANNOUNCE\r\n"
michael@0 166 "c=IN IP4 ");
michael@0 167
michael@0 168 struct in_addr addr;
michael@0 169 addr.s_addr = htonl(mServerIP);
michael@0 170
michael@0 171 sdp.append(inet_ntoa(addr));
michael@0 172
michael@0 173 sdp.append(
michael@0 174 "\r\n"
michael@0 175 "t=0 0\r\n"
michael@0 176 "a=range:npt=now-\r\n");
michael@0 177
michael@0 178 #ifdef ANDROID
michael@0 179 sp<MetaData> meta = mEncoder->getFormat();
michael@0 180 int32_t width, height;
michael@0 181 CHECK(meta->findInt32(kKeyWidth, &width));
michael@0 182 CHECK(meta->findInt32(kKeyHeight, &height));
michael@0 183
michael@0 184 sdp.append(
michael@0 185 "m=video 0 RTP/AVP " PT_STR "\r\n"
michael@0 186 "b=AS 320000\r\n"
michael@0 187 "a=rtpmap:" PT_STR " H264/90000\r\n");
michael@0 188
michael@0 189 sdp.append("a=cliprect 0,0,");
michael@0 190 sdp.append(height);
michael@0 191 sdp.append(",");
michael@0 192 sdp.append(width);
michael@0 193 sdp.append("\r\n");
michael@0 194
michael@0 195 sdp.append(
michael@0 196 "a=framesize:" PT_STR " ");
michael@0 197 sdp.append(width);
michael@0 198 sdp.append("-");
michael@0 199 sdp.append(height);
michael@0 200 sdp.append("\r\n");
michael@0 201
michael@0 202 sdp.append(
michael@0 203 "a=fmtp:" PT_STR " profile-level-id=42C015;sprop-parameter-sets=");
michael@0 204
michael@0 205 sdp.append(mSeqParamSet);
michael@0 206 sdp.append(",");
michael@0 207 sdp.append(mPicParamSet);
michael@0 208 sdp.append(";packetization-mode=1\r\n");
michael@0 209 #else
michael@0 210 sdp.append(
michael@0 211 "m=audio 0 RTP/AVP " PT_STR "\r\n"
michael@0 212 "a=rtpmap:" PT_STR " L8/8000/1\r\n");
michael@0 213 #endif
michael@0 214
michael@0 215 sdp.append("a=control:" TRACK_SUFFIX "\r\n");
michael@0 216
michael@0 217 AString request;
michael@0 218 request.append("ANNOUNCE ");
michael@0 219 request.append(mStreamURL);
michael@0 220 request.append(" RTSP/1.0\r\n");
michael@0 221
michael@0 222 addAuthentication(&request, "ANNOUNCE", mStreamURL.c_str());
michael@0 223
michael@0 224 request.append("Content-Type: application/sdp\r\n");
michael@0 225 request.append("Content-Length: ");
michael@0 226 request.append(sdp.size());
michael@0 227 request.append("\r\n");
michael@0 228
michael@0 229 request.append("\r\n");
michael@0 230 request.append(sdp);
michael@0 231
michael@0 232 sp<AMessage> reply = new AMessage('anno', id());
michael@0 233 mConn->sendRequest(request.c_str(), reply);
michael@0 234 }
michael@0 235
michael@0 236 void H(const AString &s, AString *out) {
michael@0 237 out->clear();
michael@0 238
michael@0 239 MD5_CTX m;
michael@0 240 MD5_Init(&m);
michael@0 241 MD5_Update(&m, s.c_str(), s.size());
michael@0 242
michael@0 243 uint8_t key[16];
michael@0 244 MD5_Final(key, &m);
michael@0 245
michael@0 246 for (size_t i = 0; i < 16; ++i) {
michael@0 247 char nibble = key[i] >> 4;
michael@0 248 if (nibble <= 9) {
michael@0 249 nibble += '0';
michael@0 250 } else {
michael@0 251 nibble += 'a' - 10;
michael@0 252 }
michael@0 253 out->append(&nibble, 1);
michael@0 254
michael@0 255 nibble = key[i] & 0x0f;
michael@0 256 if (nibble <= 9) {
michael@0 257 nibble += '0';
michael@0 258 } else {
michael@0 259 nibble += 'a' - 10;
michael@0 260 }
michael@0 261 out->append(&nibble, 1);
michael@0 262 }
michael@0 263 }
michael@0 264
michael@0 265 void authenticate(const sp<ARTSPResponse> &response) {
michael@0 266 ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
michael@0 267 CHECK_GE(i, 0);
michael@0 268
michael@0 269 AString value = response->mHeaders.valueAt(i);
michael@0 270
michael@0 271 if (!strncmp(value.c_str(), "Basic", 5)) {
michael@0 272 mAuthType = BASIC;
michael@0 273 } else {
michael@0 274 CHECK(!strncmp(value.c_str(), "Digest", 6));
michael@0 275 mAuthType = DIGEST;
michael@0 276
michael@0 277 i = value.find("nonce=");
michael@0 278 CHECK_GE(i, 0);
michael@0 279 CHECK_EQ(value.c_str()[i + 6], '\"');
michael@0 280 ssize_t j = value.find("\"", i + 7);
michael@0 281 CHECK_GE(j, 0);
michael@0 282
michael@0 283 mNonce.setTo(value, i + 7, j - i - 7);
michael@0 284 }
michael@0 285
michael@0 286 issueAnnounce();
michael@0 287 }
michael@0 288
michael@0 289 void addAuthentication(
michael@0 290 AString *request, const char *method, const char *url) {
michael@0 291 if (mAuthType == NONE) {
michael@0 292 return;
michael@0 293 }
michael@0 294
michael@0 295 if (mAuthType == BASIC) {
michael@0 296 request->append("Authorization: Basic YmNhc3Q6dGVzdAo=\r\n");
michael@0 297 return;
michael@0 298 }
michael@0 299
michael@0 300 CHECK_EQ((int)mAuthType, (int)DIGEST);
michael@0 301
michael@0 302 AString A1;
michael@0 303 A1.append(USERNAME);
michael@0 304 A1.append(":");
michael@0 305 A1.append("Streaming Server");
michael@0 306 A1.append(":");
michael@0 307 A1.append(PASSWORD);
michael@0 308
michael@0 309 AString A2;
michael@0 310 A2.append(method);
michael@0 311 A2.append(":");
michael@0 312 A2.append(url);
michael@0 313
michael@0 314 AString HA1, HA2;
michael@0 315 H(A1, &HA1);
michael@0 316 H(A2, &HA2);
michael@0 317
michael@0 318 AString tmp;
michael@0 319 tmp.append(HA1);
michael@0 320 tmp.append(":");
michael@0 321 tmp.append(mNonce);
michael@0 322 tmp.append(":");
michael@0 323 tmp.append(HA2);
michael@0 324
michael@0 325 AString digest;
michael@0 326 H(tmp, &digest);
michael@0 327
michael@0 328 request->append("Authorization: Digest ");
michael@0 329 request->append("nonce=\"");
michael@0 330 request->append(mNonce);
michael@0 331 request->append("\", ");
michael@0 332 request->append("username=\"" USERNAME "\", ");
michael@0 333 request->append("uri=\"");
michael@0 334 request->append(url);
michael@0 335 request->append("\", ");
michael@0 336 request->append("response=\"");
michael@0 337 request->append(digest);
michael@0 338 request->append("\"");
michael@0 339 request->append("\r\n");
michael@0 340 }
michael@0 341
michael@0 342 virtual void onMessageReceived(const sp<AMessage> &msg) {
michael@0 343 switch (msg->what()) {
michael@0 344 case 'conn':
michael@0 345 {
michael@0 346 int32_t result;
michael@0 347 CHECK(msg->findInt32("result", &result));
michael@0 348
michael@0 349 LOG(INFO) << "connection request completed with result "
michael@0 350 << result << " (" << strerror(-result) << ")";
michael@0 351
michael@0 352 if (result != OK) {
michael@0 353 (new AMessage('quit', id()))->post();
michael@0 354 break;
michael@0 355 }
michael@0 356
michael@0 357 mConnected = true;
michael@0 358
michael@0 359 CHECK(msg->findInt32("server-ip", (int32_t *)&mServerIP));
michael@0 360
michael@0 361 issueAnnounce();
michael@0 362 break;
michael@0 363 }
michael@0 364
michael@0 365 case 'anno':
michael@0 366 {
michael@0 367 int32_t result;
michael@0 368 CHECK(msg->findInt32("result", &result));
michael@0 369
michael@0 370 LOG(INFO) << "ANNOUNCE completed with result "
michael@0 371 << result << " (" << strerror(-result) << ")";
michael@0 372
michael@0 373 sp<RefBase> obj;
michael@0 374 CHECK(msg->findObject("response", &obj));
michael@0 375 sp<ARTSPResponse> response;
michael@0 376
michael@0 377 if (result == OK) {
michael@0 378 response = static_cast<ARTSPResponse *>(obj.get());
michael@0 379 CHECK(response != NULL);
michael@0 380
michael@0 381 if (response->mStatusCode == 401) {
michael@0 382 if (mAuthType != NONE) {
michael@0 383 LOG(INFO) << "FAILED to authenticate";
michael@0 384 (new AMessage('quit', id()))->post();
michael@0 385 break;
michael@0 386 }
michael@0 387
michael@0 388 authenticate(response);
michael@0 389 break;
michael@0 390 }
michael@0 391 }
michael@0 392
michael@0 393 if (result != OK || response->mStatusCode != 200) {
michael@0 394 (new AMessage('quit', id()))->post();
michael@0 395 break;
michael@0 396 }
michael@0 397
michael@0 398 unsigned rtpPort;
michael@0 399 ARTPConnection::MakePortPair(&mRTPSocket, &mRTCPSocket, &rtpPort);
michael@0 400
michael@0 401 // (new AMessage('poll', id()))->post();
michael@0 402
michael@0 403 AString request;
michael@0 404 request.append("SETUP ");
michael@0 405 request.append(mTrackURL);
michael@0 406 request.append(" RTSP/1.0\r\n");
michael@0 407
michael@0 408 addAuthentication(&request, "SETUP", mTrackURL.c_str());
michael@0 409
michael@0 410 request.append("Transport: RTP/AVP;unicast;client_port=");
michael@0 411 request.append(rtpPort);
michael@0 412 request.append("-");
michael@0 413 request.append(rtpPort + 1);
michael@0 414 request.append(";mode=record\r\n");
michael@0 415 request.append("\r\n");
michael@0 416
michael@0 417 sp<AMessage> reply = new AMessage('setu', id());
michael@0 418 mConn->sendRequest(request.c_str(), reply);
michael@0 419 break;
michael@0 420 }
michael@0 421
michael@0 422 #if 0
michael@0 423 case 'poll':
michael@0 424 {
michael@0 425 fd_set rs;
michael@0 426 FD_ZERO(&rs);
michael@0 427 FD_SET(mRTCPSocket, &rs);
michael@0 428
michael@0 429 struct timeval tv;
michael@0 430 tv.tv_sec = 0;
michael@0 431 tv.tv_usec = 0;
michael@0 432
michael@0 433 int res = select(mRTCPSocket + 1, &rs, NULL, NULL, &tv);
michael@0 434
michael@0 435 if (res == 1) {
michael@0 436 sp<ABuffer> buffer = new ABuffer(65536);
michael@0 437 ssize_t n = recv(mRTCPSocket, buffer->data(), buffer->size(), 0);
michael@0 438
michael@0 439 if (n <= 0) {
michael@0 440 LOG(ERROR) << "recv returned " << n;
michael@0 441 } else {
michael@0 442 LOG(INFO) << "recv returned " << n << " bytes of data.";
michael@0 443
michael@0 444 hexdump(buffer->data(), n);
michael@0 445 }
michael@0 446 }
michael@0 447
michael@0 448 msg->post(50000);
michael@0 449 break;
michael@0 450 }
michael@0 451 #endif
michael@0 452
michael@0 453 case 'setu':
michael@0 454 {
michael@0 455 int32_t result;
michael@0 456 CHECK(msg->findInt32("result", &result));
michael@0 457
michael@0 458 LOG(INFO) << "SETUP completed with result "
michael@0 459 << result << " (" << strerror(-result) << ")";
michael@0 460
michael@0 461 sp<RefBase> obj;
michael@0 462 CHECK(msg->findObject("response", &obj));
michael@0 463 sp<ARTSPResponse> response;
michael@0 464
michael@0 465 if (result == OK) {
michael@0 466 response = static_cast<ARTSPResponse *>(obj.get());
michael@0 467 CHECK(response != NULL);
michael@0 468 }
michael@0 469
michael@0 470 if (result != OK || response->mStatusCode != 200) {
michael@0 471 (new AMessage('quit', id()))->post();
michael@0 472 break;
michael@0 473 }
michael@0 474
michael@0 475 ssize_t i = response->mHeaders.indexOfKey("session");
michael@0 476 CHECK_GE(i, 0);
michael@0 477 mSessionID = response->mHeaders.valueAt(i);
michael@0 478 i = mSessionID.find(";");
michael@0 479 if (i >= 0) {
michael@0 480 // Remove options, i.e. ";timeout=90"
michael@0 481 mSessionID.erase(i, mSessionID.size() - i);
michael@0 482 }
michael@0 483
michael@0 484 i = response->mHeaders.indexOfKey("transport");
michael@0 485 CHECK_GE(i, 0);
michael@0 486 AString transport = response->mHeaders.valueAt(i);
michael@0 487
michael@0 488 LOG(INFO) << "transport = '" << transport << "'";
michael@0 489
michael@0 490 AString value;
michael@0 491 CHECK(GetAttribute(transport.c_str(), "server_port", &value));
michael@0 492
michael@0 493 unsigned rtpPort, rtcpPort;
michael@0 494 CHECK_EQ(sscanf(value.c_str(), "%u-%u", &rtpPort, &rtcpPort), 2);
michael@0 495
michael@0 496 CHECK(GetAttribute(transport.c_str(), "source", &value));
michael@0 497
michael@0 498 memset(mRemoteAddr.sin_zero, 0, sizeof(mRemoteAddr.sin_zero));
michael@0 499 mRemoteAddr.sin_family = AF_INET;
michael@0 500 mRemoteAddr.sin_addr.s_addr = inet_addr(value.c_str());
michael@0 501 mRemoteAddr.sin_port = htons(rtpPort);
michael@0 502
michael@0 503 mRemoteRTCPAddr = mRemoteAddr;
michael@0 504 mRemoteRTCPAddr.sin_port = htons(rtpPort + 1);
michael@0 505
michael@0 506 CHECK_EQ(0, connect(mRTPSocket,
michael@0 507 (const struct sockaddr *)&mRemoteAddr,
michael@0 508 sizeof(mRemoteAddr)));
michael@0 509
michael@0 510 CHECK_EQ(0, connect(mRTCPSocket,
michael@0 511 (const struct sockaddr *)&mRemoteRTCPAddr,
michael@0 512 sizeof(mRemoteRTCPAddr)));
michael@0 513
michael@0 514 uint32_t x = ntohl(mRemoteAddr.sin_addr.s_addr);
michael@0 515 LOG(INFO) << "sending data to "
michael@0 516 << (x >> 24)
michael@0 517 << "."
michael@0 518 << ((x >> 16) & 0xff)
michael@0 519 << "."
michael@0 520 << ((x >> 8) & 0xff)
michael@0 521 << "."
michael@0 522 << (x & 0xff)
michael@0 523 << ":"
michael@0 524 << rtpPort;
michael@0 525
michael@0 526 AString request;
michael@0 527 request.append("RECORD ");
michael@0 528 request.append(mStreamURL);
michael@0 529 request.append(" RTSP/1.0\r\n");
michael@0 530
michael@0 531 addAuthentication(&request, "RECORD", mStreamURL.c_str());
michael@0 532
michael@0 533 request.append("Session: ");
michael@0 534 request.append(mSessionID);
michael@0 535 request.append("\r\n");
michael@0 536 request.append("\r\n");
michael@0 537
michael@0 538 sp<AMessage> reply = new AMessage('reco', id());
michael@0 539 mConn->sendRequest(request.c_str(), reply);
michael@0 540 break;
michael@0 541 }
michael@0 542
michael@0 543 case 'reco':
michael@0 544 {
michael@0 545 int32_t result;
michael@0 546 CHECK(msg->findInt32("result", &result));
michael@0 547
michael@0 548 LOG(INFO) << "RECORD completed with result "
michael@0 549 << result << " (" << strerror(-result) << ")";
michael@0 550
michael@0 551 sp<RefBase> obj;
michael@0 552 CHECK(msg->findObject("response", &obj));
michael@0 553 sp<ARTSPResponse> response;
michael@0 554
michael@0 555 if (result == OK) {
michael@0 556 response = static_cast<ARTSPResponse *>(obj.get());
michael@0 557 CHECK(response != NULL);
michael@0 558 }
michael@0 559
michael@0 560 if (result != OK) {
michael@0 561 (new AMessage('quit', id()))->post();
michael@0 562 break;
michael@0 563 }
michael@0 564
michael@0 565 (new AMessage('more', id()))->post();
michael@0 566 (new AMessage('sr ', id()))->post();
michael@0 567 (new AMessage('aliv', id()))->post(30000000ll);
michael@0 568 break;
michael@0 569 }
michael@0 570
michael@0 571 case 'aliv':
michael@0 572 {
michael@0 573 if (!mConnected) {
michael@0 574 break;
michael@0 575 }
michael@0 576
michael@0 577 AString request;
michael@0 578 request.append("OPTIONS ");
michael@0 579 request.append(mStreamURL);
michael@0 580 request.append(" RTSP/1.0\r\n");
michael@0 581
michael@0 582 addAuthentication(&request, "RECORD", mStreamURL.c_str());
michael@0 583
michael@0 584 request.append("Session: ");
michael@0 585 request.append(mSessionID);
michael@0 586 request.append("\r\n");
michael@0 587 request.append("\r\n");
michael@0 588
michael@0 589 sp<AMessage> reply = new AMessage('opts', id());
michael@0 590 mConn->sendRequest(request.c_str(), reply);
michael@0 591 break;
michael@0 592 }
michael@0 593
michael@0 594 case 'opts':
michael@0 595 {
michael@0 596 int32_t result;
michael@0 597 CHECK(msg->findInt32("result", &result));
michael@0 598
michael@0 599 LOG(INFO) << "OPTIONS completed with result "
michael@0 600 << result << " (" << strerror(-result) << ")";
michael@0 601
michael@0 602 if (!mConnected) {
michael@0 603 break;
michael@0 604 }
michael@0 605
michael@0 606 (new AMessage('aliv', id()))->post(30000000ll);
michael@0 607 break;
michael@0 608 }
michael@0 609
michael@0 610 case 'more':
michael@0 611 {
michael@0 612 if (!mConnected) {
michael@0 613 break;
michael@0 614 }
michael@0 615
michael@0 616 sp<ABuffer> buffer = new ABuffer(65536);
michael@0 617 uint8_t *data = buffer->data();
michael@0 618 data[0] = 0x80;
michael@0 619 data[1] = (1 << 7) | PT; // M-bit
michael@0 620 data[2] = (mSeqNo >> 8) & 0xff;
michael@0 621 data[3] = mSeqNo & 0xff;
michael@0 622 data[8] = mSourceID >> 24;
michael@0 623 data[9] = (mSourceID >> 16) & 0xff;
michael@0 624 data[10] = (mSourceID >> 8) & 0xff;
michael@0 625 data[11] = mSourceID & 0xff;
michael@0 626
michael@0 627 #ifdef ANDROID
michael@0 628 MediaBuffer *mediaBuf = NULL;
michael@0 629 for (;;) {
michael@0 630 CHECK_EQ(mEncoder->read(&mediaBuf), (status_t)OK);
michael@0 631 if (mediaBuf->range_length() > 0) {
michael@0 632 break;
michael@0 633 }
michael@0 634 mediaBuf->release();
michael@0 635 mediaBuf = NULL;
michael@0 636 }
michael@0 637
michael@0 638 int64_t timeUs;
michael@0 639 CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
michael@0 640
michael@0 641 uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100ll);
michael@0 642
michael@0 643 const uint8_t *mediaData =
michael@0 644 (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
michael@0 645
michael@0 646 CHECK(!memcmp("\x00\x00\x00\x01", mediaData, 4));
michael@0 647
michael@0 648 CHECK_LE(mediaBuf->range_length() - 4 + 12, buffer->size());
michael@0 649
michael@0 650 memcpy(&data[12],
michael@0 651 mediaData + 4, mediaBuf->range_length() - 4);
michael@0 652
michael@0 653 buffer->setRange(0, mediaBuf->range_length() - 4 + 12);
michael@0 654
michael@0 655 mediaBuf->release();
michael@0 656 mediaBuf = NULL;
michael@0 657 #else
michael@0 658 uint32_t rtpTime = mRTPTimeBase + mNumRTPSent * 128;
michael@0 659 memset(&data[12], 0, 128);
michael@0 660 buffer->setRange(0, 12 + 128);
michael@0 661 #endif
michael@0 662
michael@0 663 data[4] = rtpTime >> 24;
michael@0 664 data[5] = (rtpTime >> 16) & 0xff;
michael@0 665 data[6] = (rtpTime >> 8) & 0xff;
michael@0 666 data[7] = rtpTime & 0xff;
michael@0 667
michael@0 668 ssize_t n = send(
michael@0 669 mRTPSocket, data, buffer->size(), 0);
michael@0 670 if (n < 0) {
michael@0 671 LOG(ERROR) << "send failed (" << strerror(errno) << ")";
michael@0 672 }
michael@0 673 CHECK_EQ(n, (ssize_t)buffer->size());
michael@0 674
michael@0 675 ++mSeqNo;
michael@0 676
michael@0 677 ++mNumRTPSent;
michael@0 678 mNumRTPOctetsSent += buffer->size() - 12;
michael@0 679
michael@0 680 mLastRTPTime = rtpTime;
michael@0 681 mLastNTPTime = ntpTime();
michael@0 682
michael@0 683 #ifdef ANDROID
michael@0 684 if (mNumRTPSent < 60 * 25) { // 60 secs worth
michael@0 685 msg->post(40000);
michael@0 686 #else
michael@0 687 if (mNumRTPOctetsSent < 8000 * 60) {
michael@0 688 msg->post(1000000ll * 128 / 8000);
michael@0 689 #endif
michael@0 690 } else {
michael@0 691 LOG(INFO) << "That's enough, pausing.";
michael@0 692
michael@0 693 AString request;
michael@0 694 request.append("PAUSE ");
michael@0 695 request.append(mStreamURL);
michael@0 696 request.append(" RTSP/1.0\r\n");
michael@0 697
michael@0 698 addAuthentication(&request, "PAUSE", mStreamURL.c_str());
michael@0 699
michael@0 700 request.append("Session: ");
michael@0 701 request.append(mSessionID);
michael@0 702 request.append("\r\n");
michael@0 703 request.append("\r\n");
michael@0 704
michael@0 705 sp<AMessage> reply = new AMessage('paus', id());
michael@0 706 mConn->sendRequest(request.c_str(), reply);
michael@0 707 }
michael@0 708 break;
michael@0 709 }
michael@0 710
michael@0 711 case 'sr ':
michael@0 712 {
michael@0 713 if (!mConnected) {
michael@0 714 break;
michael@0 715 }
michael@0 716
michael@0 717 sp<ABuffer> buffer = new ABuffer(65536);
michael@0 718 buffer->setRange(0, 0);
michael@0 719
michael@0 720 addSR(buffer);
michael@0 721 addSDES(buffer);
michael@0 722
michael@0 723 uint8_t *data = buffer->data();
michael@0 724 ssize_t n = send(
michael@0 725 mRTCPSocket, data, buffer->size(), 0);
michael@0 726 CHECK_EQ(n, (ssize_t)buffer->size());
michael@0 727
michael@0 728 msg->post(3000000);
michael@0 729 break;
michael@0 730 }
michael@0 731
michael@0 732 case 'paus':
michael@0 733 {
michael@0 734 int32_t result;
michael@0 735 CHECK(msg->findInt32("result", &result));
michael@0 736
michael@0 737 LOG(INFO) << "PAUSE completed with result "
michael@0 738 << result << " (" << strerror(-result) << ")";
michael@0 739
michael@0 740 sp<RefBase> obj;
michael@0 741 CHECK(msg->findObject("response", &obj));
michael@0 742 sp<ARTSPResponse> response;
michael@0 743
michael@0 744 AString request;
michael@0 745 request.append("TEARDOWN ");
michael@0 746 request.append(mStreamURL);
michael@0 747 request.append(" RTSP/1.0\r\n");
michael@0 748
michael@0 749 addAuthentication(&request, "TEARDOWN", mStreamURL.c_str());
michael@0 750
michael@0 751 request.append("Session: ");
michael@0 752 request.append(mSessionID);
michael@0 753 request.append("\r\n");
michael@0 754 request.append("\r\n");
michael@0 755
michael@0 756 sp<AMessage> reply = new AMessage('tear', id());
michael@0 757 mConn->sendRequest(request.c_str(), reply);
michael@0 758 break;
michael@0 759 }
michael@0 760
michael@0 761 case 'tear':
michael@0 762 {
michael@0 763 int32_t result;
michael@0 764 CHECK(msg->findInt32("result", &result));
michael@0 765
michael@0 766 LOG(INFO) << "TEARDOWN completed with result "
michael@0 767 << result << " (" << strerror(-result) << ")";
michael@0 768
michael@0 769 sp<RefBase> obj;
michael@0 770 CHECK(msg->findObject("response", &obj));
michael@0 771 sp<ARTSPResponse> response;
michael@0 772
michael@0 773 if (result == OK) {
michael@0 774 response = static_cast<ARTSPResponse *>(obj.get());
michael@0 775 CHECK(response != NULL);
michael@0 776 }
michael@0 777
michael@0 778 (new AMessage('quit', id()))->post();
michael@0 779 break;
michael@0 780 }
michael@0 781
michael@0 782 case 'disc':
michael@0 783 {
michael@0 784 LOG(INFO) << "disconnect completed";
michael@0 785
michael@0 786 mConnected = false;
michael@0 787 (new AMessage('quit', id()))->post();
michael@0 788 break;
michael@0 789 }
michael@0 790
michael@0 791 case 'quit':
michael@0 792 {
michael@0 793 if (mConnected) {
michael@0 794 mConn->disconnect(new AMessage('disc', id()));
michael@0 795 break;
michael@0 796 }
michael@0 797
michael@0 798 if (mRTPSocket >= 0) {
michael@0 799 close(mRTPSocket);
michael@0 800 mRTPSocket = -1;
michael@0 801 }
michael@0 802
michael@0 803 if (mRTCPSocket >= 0) {
michael@0 804 close(mRTCPSocket);
michael@0 805 mRTCPSocket = -1;
michael@0 806 }
michael@0 807
michael@0 808 #ifdef ANDROID
michael@0 809 mEncoder->stop();
michael@0 810 mEncoder.clear();
michael@0 811 #endif
michael@0 812
michael@0 813 mLooper->stop();
michael@0 814 break;
michael@0 815 }
michael@0 816
michael@0 817 default:
michael@0 818 TRESPASS();
michael@0 819 }
michael@0 820 }
michael@0 821
michael@0 822 protected:
michael@0 823 virtual ~MyTransmitter() {
michael@0 824 }
michael@0 825
michael@0 826 private:
michael@0 827 enum AuthType {
michael@0 828 NONE,
michael@0 829 BASIC,
michael@0 830 DIGEST
michael@0 831 };
michael@0 832
michael@0 833 AString mServerURL;
michael@0 834 AString mTrackURL;
michael@0 835 AString mStreamURL;
michael@0 836
michael@0 837 sp<ALooper> mLooper;
michael@0 838 sp<ARTSPConnection> mConn;
michael@0 839 bool mConnected;
michael@0 840 uint32_t mServerIP;
michael@0 841 AuthType mAuthType;
michael@0 842 AString mNonce;
michael@0 843 AString mSessionID;
michael@0 844 int mRTPSocket, mRTCPSocket;
michael@0 845 uint32_t mSourceID;
michael@0 846 uint32_t mSeqNo;
michael@0 847 uint32_t mRTPTimeBase;
michael@0 848 struct sockaddr_in mRemoteAddr;
michael@0 849 struct sockaddr_in mRemoteRTCPAddr;
michael@0 850 size_t mNumSamplesSent;
michael@0 851 uint32_t mNumRTPSent;
michael@0 852 uint32_t mNumRTPOctetsSent;
michael@0 853 uint32_t mLastRTPTime;
michael@0 854 uint64_t mLastNTPTime;
michael@0 855
michael@0 856 #ifdef ANDROID
michael@0 857 sp<MediaSource> mEncoder;
michael@0 858 AString mSeqParamSet;
michael@0 859 AString mPicParamSet;
michael@0 860
michael@0 861 void makeH264SPropParamSets(MediaBuffer *buffer) {
michael@0 862 static const char kStartCode[] = "\x00\x00\x00\x01";
michael@0 863
michael@0 864 const uint8_t *data =
michael@0 865 (const uint8_t *)buffer->data() + buffer->range_offset();
michael@0 866 size_t size = buffer->range_length();
michael@0 867
michael@0 868 CHECK_GE(size, 0u);
michael@0 869 CHECK(!memcmp(kStartCode, data, 4));
michael@0 870
michael@0 871 data += 4;
michael@0 872 size -= 4;
michael@0 873
michael@0 874 size_t startCodePos = 0;
michael@0 875 while (startCodePos + 3 < size
michael@0 876 && memcmp(kStartCode, &data[startCodePos], 4)) {
michael@0 877 ++startCodePos;
michael@0 878 }
michael@0 879
michael@0 880 CHECK_LT(startCodePos + 3, size);
michael@0 881
michael@0 882 encodeBase64(data, startCodePos, &mSeqParamSet);
michael@0 883
michael@0 884 encodeBase64(&data[startCodePos + 4], size - startCodePos - 4,
michael@0 885 &mPicParamSet);
michael@0 886 }
michael@0 887 #endif
michael@0 888
michael@0 889 void addSR(const sp<ABuffer> &buffer) {
michael@0 890 uint8_t *data = buffer->data() + buffer->size();
michael@0 891
michael@0 892 data[0] = 0x80 | 0;
michael@0 893 data[1] = 200; // SR
michael@0 894 data[2] = 0;
michael@0 895 data[3] = 6;
michael@0 896 data[4] = mSourceID >> 24;
michael@0 897 data[5] = (mSourceID >> 16) & 0xff;
michael@0 898 data[6] = (mSourceID >> 8) & 0xff;
michael@0 899 data[7] = mSourceID & 0xff;
michael@0 900
michael@0 901 data[8] = mLastNTPTime >> (64 - 8);
michael@0 902 data[9] = (mLastNTPTime >> (64 - 16)) & 0xff;
michael@0 903 data[10] = (mLastNTPTime >> (64 - 24)) & 0xff;
michael@0 904 data[11] = (mLastNTPTime >> 32) & 0xff;
michael@0 905 data[12] = (mLastNTPTime >> 24) & 0xff;
michael@0 906 data[13] = (mLastNTPTime >> 16) & 0xff;
michael@0 907 data[14] = (mLastNTPTime >> 8) & 0xff;
michael@0 908 data[15] = mLastNTPTime & 0xff;
michael@0 909
michael@0 910 data[16] = (mLastRTPTime >> 24) & 0xff;
michael@0 911 data[17] = (mLastRTPTime >> 16) & 0xff;
michael@0 912 data[18] = (mLastRTPTime >> 8) & 0xff;
michael@0 913 data[19] = mLastRTPTime & 0xff;
michael@0 914
michael@0 915 data[20] = mNumRTPSent >> 24;
michael@0 916 data[21] = (mNumRTPSent >> 16) & 0xff;
michael@0 917 data[22] = (mNumRTPSent >> 8) & 0xff;
michael@0 918 data[23] = mNumRTPSent & 0xff;
michael@0 919
michael@0 920 data[24] = mNumRTPOctetsSent >> 24;
michael@0 921 data[25] = (mNumRTPOctetsSent >> 16) & 0xff;
michael@0 922 data[26] = (mNumRTPOctetsSent >> 8) & 0xff;
michael@0 923 data[27] = mNumRTPOctetsSent & 0xff;
michael@0 924
michael@0 925 buffer->setRange(buffer->offset(), buffer->size() + 28);
michael@0 926 }
michael@0 927
michael@0 928 void addSDES(const sp<ABuffer> &buffer) {
michael@0 929 uint8_t *data = buffer->data() + buffer->size();
michael@0 930 data[0] = 0x80 | 1;
michael@0 931 data[1] = 202; // SDES
michael@0 932 data[4] = mSourceID >> 24;
michael@0 933 data[5] = (mSourceID >> 16) & 0xff;
michael@0 934 data[6] = (mSourceID >> 8) & 0xff;
michael@0 935 data[7] = mSourceID & 0xff;
michael@0 936
michael@0 937 size_t offset = 8;
michael@0 938
michael@0 939 data[offset++] = 1; // CNAME
michael@0 940
michael@0 941 static const char *kCNAME = "andih@laptop";
michael@0 942 data[offset++] = strlen(kCNAME);
michael@0 943
michael@0 944 memcpy(&data[offset], kCNAME, strlen(kCNAME));
michael@0 945 offset += strlen(kCNAME);
michael@0 946
michael@0 947 data[offset++] = 7; // NOTE
michael@0 948
michael@0 949 static const char *kNOTE = "Hell's frozen over.";
michael@0 950 data[offset++] = strlen(kNOTE);
michael@0 951
michael@0 952 memcpy(&data[offset], kNOTE, strlen(kNOTE));
michael@0 953 offset += strlen(kNOTE);
michael@0 954
michael@0 955 data[offset++] = 0;
michael@0 956
michael@0 957 if ((offset % 4) > 0) {
michael@0 958 size_t count = 4 - (offset % 4);
michael@0 959 switch (count) {
michael@0 960 case 3:
michael@0 961 data[offset++] = 0;
michael@0 962 case 2:
michael@0 963 data[offset++] = 0;
michael@0 964 case 1:
michael@0 965 data[offset++] = 0;
michael@0 966 }
michael@0 967 }
michael@0 968
michael@0 969 size_t numWords = (offset / 4) - 1;
michael@0 970 data[2] = numWords >> 8;
michael@0 971 data[3] = numWords & 0xff;
michael@0 972
michael@0 973 buffer->setRange(buffer->offset(), buffer->size() + offset);
michael@0 974 }
michael@0 975
michael@0 976 DISALLOW_EVIL_CONSTRUCTORS(MyTransmitter);
michael@0 977 };
michael@0 978
michael@0 979 } // namespace android
michael@0 980
michael@0 981 #endif // RTSP_TRANSMITTER_H_

mercurial