blob: d15d9c5c09667edc48e025d4a51199f9f17b2bc2 [file] [log] [blame]
Andreas Huber7a747b82010-06-07 15:19:40 -07001/*
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
Andreas Huber6e3fa442010-09-21 13:13:15 -070021//#define LOG_NDEBUG 0
22#define LOG_TAG "MyHandler"
23#include <utils/Log.h>
24
Andreas Huber7a747b82010-06-07 15:19:40 -070025#include "APacketSource.h"
26#include "ARTPConnection.h"
27#include "ARTSPConnection.h"
28#include "ASessionDescription.h"
29
Andreas Hubereeb97d92010-08-27 13:29:08 -070030#include <ctype.h>
Andreas Huber0dc64032010-10-11 11:05:52 -070031#include <cutils/properties.h>
Andreas Hubereeb97d92010-08-27 13:29:08 -070032
Andreas Huber7a747b82010-06-07 15:19:40 -070033#include <media/stagefright/foundation/ABuffer.h>
34#include <media/stagefright/foundation/ADebug.h>
35#include <media/stagefright/foundation/ALooper.h>
36#include <media/stagefright/foundation/AMessage.h>
37#include <media/stagefright/MediaErrors.h>
38
Andreas Huber0dc64032010-10-11 11:05:52 -070039#include <arpa/inet.h>
40#include <sys/socket.h>
Andreas Huber27db53d2011-02-15 13:32:42 -080041#include <netdb.h>
Andreas Huber0dc64032010-10-11 11:05:52 -070042
Andreas Huberb2934b12011-02-08 10:18:41 -080043// If no access units are received within 5 secs, assume that the rtp
Andreas Huberf6639c42010-08-30 09:53:52 -070044// stream has ended and signal end of stream.
Andreas Huberb2934b12011-02-08 10:18:41 -080045static int64_t kAccessUnitTimeoutUs = 5000000ll;
Andreas Huberf6639c42010-08-30 09:53:52 -070046
47// If no access units arrive for the first 10 secs after starting the
48// stream, assume none ever will and signal EOS or switch transports.
49static int64_t kStartupTimeoutUs = 10000000ll;
50
Andreas Huber7a747b82010-06-07 15:19:40 -070051namespace android {
52
Andreas Huber0dc64032010-10-11 11:05:52 -070053static void MakeUserAgentString(AString *s) {
54 s->setTo("stagefright/1.1 (Linux;Android ");
55
56#if (PROPERTY_VALUE_MAX < 8)
57#error "PROPERTY_VALUE_MAX must be at least 8"
58#endif
59
60 char value[PROPERTY_VALUE_MAX];
61 property_get("ro.build.version.release", value, "Unknown");
62 s->append(value);
63 s->append(")");
64}
65
Andreas Hubereeb97d92010-08-27 13:29:08 -070066static bool GetAttribute(const char *s, const char *key, AString *value) {
67 value->clear();
68
69 size_t keyLen = strlen(key);
70
71 for (;;) {
72 while (isspace(*s)) {
73 ++s;
74 }
75
76 const char *colonPos = strchr(s, ';');
77
78 size_t len =
79 (colonPos == NULL) ? strlen(s) : colonPos - s;
80
81 if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
82 value->setTo(&s[keyLen + 1], len - keyLen - 1);
83 return true;
84 }
85
86 if (colonPos == NULL) {
87 return false;
88 }
89
90 s = colonPos + 1;
91 }
92}
93
Andreas Huber7a747b82010-06-07 15:19:40 -070094struct MyHandler : public AHandler {
95 MyHandler(const char *url, const sp<ALooper> &looper)
96 : mLooper(looper),
Andreas Huber4e4173b2010-07-22 09:20:13 -070097 mNetLooper(new ALooper),
Andreas Huber7a747b82010-06-07 15:19:40 -070098 mConn(new ARTSPConnection),
99 mRTPConn(new ARTPConnection),
Andreas Hubera0b442e2010-10-20 15:00:34 -0700100 mOriginalSessionURL(url),
Andreas Huber7a747b82010-06-07 15:19:40 -0700101 mSessionURL(url),
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700102 mSetupTracksSuccessful(false),
103 mSeekPending(false),
104 mFirstAccessUnit(true),
Andreas Huberb2934b12011-02-08 10:18:41 -0800105 mNTPAnchorUs(-1),
106 mMediaAnchorUs(-1),
107 mLastMediaTimeUs(0),
Andreas Hubereeb97d92010-08-27 13:29:08 -0700108 mNumAccessUnitsReceived(0),
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700109 mCheckPending(false),
Andreas Hubere51e8092010-10-08 09:04:25 -0700110 mCheckGeneration(0),
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700111 mTryTCPInterleaving(false),
Andreas Hubercc5fb1d2010-10-13 12:15:03 -0700112 mTryFakeRTCP(false),
Andreas Huber0c46b692010-10-08 15:21:08 -0700113 mReceivedFirstRTCPPacket(false),
Andreas Hubercc5fb1d2010-10-13 12:15:03 -0700114 mReceivedFirstRTPPacket(false),
Andreas Huber0c46b692010-10-08 15:21:08 -0700115 mSeekable(false) {
Andreas Huberc4e0b702010-08-27 15:21:07 -0700116 mNetLooper->setName("rtsp net");
Andreas Huber4e4173b2010-07-22 09:20:13 -0700117 mNetLooper->start(false /* runOnCallingThread */,
118 false /* canCallJava */,
119 PRIORITY_HIGHEST);
Andreas Hubera0b442e2010-10-20 15:00:34 -0700120
121 // Strip any authentication info from the session url, we don't
122 // want to transmit user/pass in cleartext.
123 AString host, path, user, pass;
124 unsigned port;
Andreas Huber27db53d2011-02-15 13:32:42 -0800125 CHECK(ARTSPConnection::ParseURL(
126 mSessionURL.c_str(), &host, &port, &path, &user, &pass));
127
128 if (user.size() > 0) {
Andreas Hubera0b442e2010-10-20 15:00:34 -0700129 mSessionURL.clear();
130 mSessionURL.append("rtsp://");
131 mSessionURL.append(host);
132 mSessionURL.append(":");
133 mSessionURL.append(StringPrintf("%u", port));
134 mSessionURL.append(path);
135
136 LOGI("rewritten session url: '%s'", mSessionURL.c_str());
137 }
Andreas Huber27db53d2011-02-15 13:32:42 -0800138
139 mSessionHost = host;
Andreas Huber4e4173b2010-07-22 09:20:13 -0700140 }
141
Andreas Huber8370be12010-08-23 11:28:34 -0700142 void connect(const sp<AMessage> &doneMsg) {
143 mDoneMsg = doneMsg;
144
Andreas Huber7a747b82010-06-07 15:19:40 -0700145 mLooper->registerHandler(this);
146 mLooper->registerHandler(mConn);
Andreas Huber4e4173b2010-07-22 09:20:13 -0700147 (1 ? mNetLooper : mLooper)->registerHandler(mRTPConn);
Andreas Huber4e4173b2010-07-22 09:20:13 -0700148
Andreas Huber0416da72010-08-26 11:17:32 -0700149 sp<AMessage> notify = new AMessage('biny', id());
150 mConn->observeBinaryData(notify);
151
Andreas Huber8370be12010-08-23 11:28:34 -0700152 sp<AMessage> reply = new AMessage('conn', id());
Andreas Hubera0b442e2010-10-20 15:00:34 -0700153 mConn->connect(mOriginalSessionURL.c_str(), reply);
Andreas Huber7a747b82010-06-07 15:19:40 -0700154 }
155
Andreas Huber8370be12010-08-23 11:28:34 -0700156 void disconnect(const sp<AMessage> &doneMsg) {
157 mDoneMsg = doneMsg;
158
159 (new AMessage('abor', id()))->post();
Andreas Huber4e4173b2010-07-22 09:20:13 -0700160 }
161
Andreas Huber0c46b692010-10-08 15:21:08 -0700162 void seek(int64_t timeUs, const sp<AMessage> &doneMsg) {
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700163 sp<AMessage> msg = new AMessage('seek', id());
164 msg->setInt64("time", timeUs);
Andreas Huber0c46b692010-10-08 15:21:08 -0700165 msg->setMessage("doneMsg", doneMsg);
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700166 msg->post();
167 }
168
Andreas Hubereeb97d92010-08-27 13:29:08 -0700169 int64_t getNormalPlayTimeUs() {
170 int64_t maxTimeUs = 0;
171 for (size_t i = 0; i < mTracks.size(); ++i) {
172 int64_t timeUs = mTracks.editItemAt(i).mPacketSource
173 ->getNormalPlayTimeUs();
174
175 if (i == 0 || timeUs > maxTimeUs) {
176 maxTimeUs = timeUs;
177 }
178 }
179
180 return maxTimeUs;
181 }
182
Andreas Huber0dc64032010-10-11 11:05:52 -0700183 static void addRR(const sp<ABuffer> &buf) {
184 uint8_t *ptr = buf->data() + buf->size();
185 ptr[0] = 0x80 | 0;
186 ptr[1] = 201; // RR
187 ptr[2] = 0;
188 ptr[3] = 1;
189 ptr[4] = 0xde; // SSRC
190 ptr[5] = 0xad;
191 ptr[6] = 0xbe;
192 ptr[7] = 0xef;
193
194 buf->setRange(0, buf->size() + 8);
195 }
196
197 static void addSDES(int s, const sp<ABuffer> &buffer) {
198 struct sockaddr_in addr;
199 socklen_t addrSize = sizeof(addr);
200 CHECK_EQ(0, getsockname(s, (sockaddr *)&addr, &addrSize));
201
202 uint8_t *data = buffer->data() + buffer->size();
203 data[0] = 0x80 | 1;
204 data[1] = 202; // SDES
205 data[4] = 0xde; // SSRC
206 data[5] = 0xad;
207 data[6] = 0xbe;
208 data[7] = 0xef;
209
210 size_t offset = 8;
211
212 data[offset++] = 1; // CNAME
213
214 AString cname = "stagefright@";
215 cname.append(inet_ntoa(addr.sin_addr));
216 data[offset++] = cname.size();
217
218 memcpy(&data[offset], cname.c_str(), cname.size());
219 offset += cname.size();
220
221 data[offset++] = 6; // TOOL
222
223 AString tool;
224 MakeUserAgentString(&tool);
225
226 data[offset++] = tool.size();
227
228 memcpy(&data[offset], tool.c_str(), tool.size());
229 offset += tool.size();
230
231 data[offset++] = 0;
232
233 if ((offset % 4) > 0) {
234 size_t count = 4 - (offset % 4);
235 switch (count) {
236 case 3:
237 data[offset++] = 0;
238 case 2:
239 data[offset++] = 0;
240 case 1:
241 data[offset++] = 0;
242 }
243 }
244
245 size_t numWords = (offset / 4) - 1;
246 data[2] = numWords >> 8;
247 data[3] = numWords & 0xff;
248
249 buffer->setRange(buffer->offset(), buffer->size() + offset);
250 }
251
252 // In case we're behind NAT, fire off two UDP packets to the remote
253 // rtp/rtcp ports to poke a hole into the firewall for future incoming
254 // packets. We're going to send an RR/SDES RTCP packet to both of them.
Andreas Huber04072692011-02-15 10:39:48 -0800255 bool pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
Andreas Huber27db53d2011-02-15 13:32:42 -0800256 struct sockaddr_in addr;
257 memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
258 addr.sin_family = AF_INET;
259
Andreas Huber0dc64032010-10-11 11:05:52 -0700260 AString source;
261 AString server_port;
262 if (!GetAttribute(transport.c_str(),
263 "source",
Andreas Huber27db53d2011-02-15 13:32:42 -0800264 &source)) {
265 LOGW("Missing 'source' field in Transport response. Using "
266 "RTSP endpoint address.");
267
268 struct hostent *ent = gethostbyname(mSessionHost.c_str());
269 if (ent == NULL) {
270 LOGE("Failed to look up address of session host '%s'",
271 mSessionHost.c_str());
272
273 return false;
274 }
275
276 addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
277 } else {
278 addr.sin_addr.s_addr = inet_addr(source.c_str());
279 }
280
281 if (!GetAttribute(transport.c_str(),
Andreas Huber0dc64032010-10-11 11:05:52 -0700282 "server_port",
283 &server_port)) {
Andreas Huber27db53d2011-02-15 13:32:42 -0800284 LOGI("Missing 'server_port' field in Transport response.");
Andreas Huber04072692011-02-15 10:39:48 -0800285 return false;
Andreas Huber0dc64032010-10-11 11:05:52 -0700286 }
287
288 int rtpPort, rtcpPort;
289 if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
290 || rtpPort <= 0 || rtpPort > 65535
291 || rtcpPort <=0 || rtcpPort > 65535
Andreas Huber04072692011-02-15 10:39:48 -0800292 || rtcpPort != rtpPort + 1) {
293 LOGE("Server picked invalid RTP/RTCP port pair %s,"
294 " RTP port must be even, RTCP port must be one higher.",
295 server_port.c_str());
296
297 return false;
298 }
299
300 if (rtpPort & 1) {
301 LOGW("Server picked an odd RTP port, it should've picked an "
302 "even one, we'll let it pass for now, but this may break "
303 "in the future.");
Andreas Huber0dc64032010-10-11 11:05:52 -0700304 }
305
Andreas Huber0dc64032010-10-11 11:05:52 -0700306 if (addr.sin_addr.s_addr == INADDR_NONE) {
Andreas Huber04072692011-02-15 10:39:48 -0800307 return true;
308 }
309
310 if (IN_LOOPBACK(ntohl(addr.sin_addr.s_addr))) {
311 // No firewalls to traverse on the loopback interface.
312 return true;
Andreas Huber0dc64032010-10-11 11:05:52 -0700313 }
314
315 // Make up an RR/SDES RTCP packet.
316 sp<ABuffer> buf = new ABuffer(65536);
317 buf->setRange(0, 0);
318 addRR(buf);
319 addSDES(rtpSocket, buf);
320
321 addr.sin_port = htons(rtpPort);
322
323 ssize_t n = sendto(
324 rtpSocket, buf->data(), buf->size(), 0,
325 (const sockaddr *)&addr, sizeof(addr));
Andreas Huber04072692011-02-15 10:39:48 -0800326
327 if (n < (ssize_t)buf->size()) {
328 LOGE("failed to poke a hole for RTP packets");
329 return false;
330 }
Andreas Huber0dc64032010-10-11 11:05:52 -0700331
332 addr.sin_port = htons(rtcpPort);
333
334 n = sendto(
335 rtcpSocket, buf->data(), buf->size(), 0,
336 (const sockaddr *)&addr, sizeof(addr));
Andreas Huber04072692011-02-15 10:39:48 -0800337
338 if (n < (ssize_t)buf->size()) {
339 LOGE("failed to poke a hole for RTCP packets");
340 return false;
341 }
Andreas Huber0dc64032010-10-11 11:05:52 -0700342
343 LOGV("successfully poked holes.");
Andreas Huber04072692011-02-15 10:39:48 -0800344
345 return true;
Andreas Huber0dc64032010-10-11 11:05:52 -0700346 }
347
Andreas Huber7a747b82010-06-07 15:19:40 -0700348 virtual void onMessageReceived(const sp<AMessage> &msg) {
349 switch (msg->what()) {
350 case 'conn':
351 {
352 int32_t result;
353 CHECK(msg->findInt32("result", &result));
354
Andreas Huber6e3fa442010-09-21 13:13:15 -0700355 LOGI("connection request completed with result %d (%s)",
356 result, strerror(-result));
Andreas Huber7a747b82010-06-07 15:19:40 -0700357
358 if (result == OK) {
359 AString request;
360 request = "DESCRIBE ";
361 request.append(mSessionURL);
362 request.append(" RTSP/1.0\r\n");
363 request.append("Accept: application/sdp\r\n");
364 request.append("\r\n");
365
366 sp<AMessage> reply = new AMessage('desc', id());
367 mConn->sendRequest(request.c_str(), reply);
Andreas Huber0416da72010-08-26 11:17:32 -0700368 } else {
369 (new AMessage('disc', id()))->post();
Andreas Huber7a747b82010-06-07 15:19:40 -0700370 }
371 break;
372 }
373
374 case 'disc':
375 {
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700376 int32_t reconnect;
377 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
378 sp<AMessage> reply = new AMessage('conn', id());
Andreas Hubera0b442e2010-10-20 15:00:34 -0700379 mConn->connect(mOriginalSessionURL.c_str(), reply);
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700380 } else {
381 (new AMessage('quit', id()))->post();
382 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700383 break;
384 }
385
386 case 'desc':
387 {
388 int32_t result;
389 CHECK(msg->findInt32("result", &result));
390
Andreas Huber6e3fa442010-09-21 13:13:15 -0700391 LOGI("DESCRIBE completed with result %d (%s)",
392 result, strerror(-result));
Andreas Huber7a747b82010-06-07 15:19:40 -0700393
394 if (result == OK) {
395 sp<RefBase> obj;
396 CHECK(msg->findObject("response", &obj));
397 sp<ARTSPResponse> response =
398 static_cast<ARTSPResponse *>(obj.get());
399
400 if (response->mStatusCode == 302) {
401 ssize_t i = response->mHeaders.indexOfKey("location");
402 CHECK_GE(i, 0);
403
404 mSessionURL = response->mHeaders.valueAt(i);
405
406 AString request;
407 request = "DESCRIBE ";
408 request.append(mSessionURL);
409 request.append(" RTSP/1.0\r\n");
410 request.append("Accept: application/sdp\r\n");
411 request.append("\r\n");
412
413 sp<AMessage> reply = new AMessage('desc', id());
414 mConn->sendRequest(request.c_str(), reply);
415 break;
416 }
417
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700418 if (response->mStatusCode != 200) {
419 result = UNKNOWN_ERROR;
Andreas Huber7a747b82010-06-07 15:19:40 -0700420 } else {
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700421 mSessionDesc = new ASessionDescription;
422
423 mSessionDesc->setTo(
424 response->mContent->data(),
425 response->mContent->size());
426
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700427 if (!mSessionDesc->isValid()) {
Andreas Huber04072692011-02-15 10:39:48 -0800428 LOGE("Failed to parse session description.");
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700429 result = ERROR_MALFORMED;
Andreas Huber7a747b82010-06-07 15:19:40 -0700430 } else {
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700431 ssize_t i = response->mHeaders.indexOfKey("content-base");
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700432 if (i >= 0) {
433 mBaseURL = response->mHeaders.valueAt(i);
434 } else {
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700435 i = response->mHeaders.indexOfKey("content-location");
436 if (i >= 0) {
437 mBaseURL = response->mHeaders.valueAt(i);
438 } else {
439 mBaseURL = mSessionURL;
440 }
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700441 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700442
Andreas Huber04072692011-02-15 10:39:48 -0800443 if (!mBaseURL.startsWith("rtsp://")) {
444 // Some misbehaving servers specify a relative
445 // URL in one of the locations above, combine
446 // it with the absolute session URL to get
447 // something usable...
448
449 LOGW("Server specified a non-absolute base URL"
450 ", combining it with the session URL to "
451 "get something usable...");
452
453 AString tmp;
454 CHECK(MakeURL(
455 mSessionURL.c_str(),
456 mBaseURL.c_str(),
457 &tmp));
458
459 mBaseURL = tmp;
460 }
461
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700462 CHECK_GT(mSessionDesc->countTracks(), 1u);
463 setupTrack(1);
464 }
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700465 }
466 }
467
468 if (result != OK) {
Andreas Huber7a747b82010-06-07 15:19:40 -0700469 sp<AMessage> reply = new AMessage('disc', id());
470 mConn->disconnect(reply);
471 }
472 break;
473 }
474
475 case 'setu':
476 {
477 size_t index;
478 CHECK(msg->findSize("index", &index));
479
Andreas Huber57648e42010-08-04 10:14:30 -0700480 TrackInfo *track = NULL;
Andreas Huber7a747b82010-06-07 15:19:40 -0700481 size_t trackIndex;
Andreas Huber57648e42010-08-04 10:14:30 -0700482 if (msg->findSize("track-index", &trackIndex)) {
483 track = &mTracks.editItemAt(trackIndex);
484 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700485
486 int32_t result;
487 CHECK(msg->findInt32("result", &result));
488
Andreas Huber6e3fa442010-09-21 13:13:15 -0700489 LOGI("SETUP(%d) completed with result %d (%s)",
490 index, result, strerror(-result));
Andreas Huber7a747b82010-06-07 15:19:40 -0700491
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700492 if (result == OK) {
493 CHECK(track != NULL);
494
495 sp<RefBase> obj;
496 CHECK(msg->findObject("response", &obj));
497 sp<ARTSPResponse> response =
498 static_cast<ARTSPResponse *>(obj.get());
499
500 if (response->mStatusCode != 200) {
501 result = UNKNOWN_ERROR;
502 } else {
503 ssize_t i = response->mHeaders.indexOfKey("session");
504 CHECK_GE(i, 0);
505
506 mSessionID = response->mHeaders.valueAt(i);
507 i = mSessionID.find(";");
508 if (i >= 0) {
509 // Remove options, i.e. ";timeout=90"
510 mSessionID.erase(i, mSessionID.size() - i);
511 }
512
513 sp<AMessage> notify = new AMessage('accu', id());
514 notify->setSize("track-index", trackIndex);
515
Andreas Huber0dc64032010-10-11 11:05:52 -0700516 i = response->mHeaders.indexOfKey("transport");
517 CHECK_GE(i, 0);
518
519 if (!track->mUsingInterleavedTCP) {
520 AString transport = response->mHeaders.valueAt(i);
521
Andreas Huber27db53d2011-02-15 13:32:42 -0800522 // We are going to continue even if we were
523 // unable to poke a hole into the firewall...
524 pokeAHole(
525 track->mRTPSocket,
526 track->mRTCPSocket,
527 transport);
Andreas Huber0dc64032010-10-11 11:05:52 -0700528 }
529
Andreas Huber27db53d2011-02-15 13:32:42 -0800530 mRTPConn->addStream(
531 track->mRTPSocket, track->mRTCPSocket,
532 mSessionDesc, index,
533 notify, track->mUsingInterleavedTCP);
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700534
Andreas Huber27db53d2011-02-15 13:32:42 -0800535 mSetupTracksSuccessful = true;
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700536 }
537 }
538
Andreas Huber57648e42010-08-04 10:14:30 -0700539 if (result != OK) {
540 if (track) {
Andreas Huber0416da72010-08-26 11:17:32 -0700541 if (!track->mUsingInterleavedTCP) {
542 close(track->mRTPSocket);
543 close(track->mRTCPSocket);
544 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700545
Andreas Huber57648e42010-08-04 10:14:30 -0700546 mTracks.removeItemsAt(trackIndex);
547 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700548 }
549
Andreas Huber57648e42010-08-04 10:14:30 -0700550 ++index;
551 if (index < mSessionDesc->countTracks()) {
552 setupTrack(index);
553 } else if (mSetupTracksSuccessful) {
Andreas Huber7a747b82010-06-07 15:19:40 -0700554 AString request = "PLAY ";
555 request.append(mSessionURL);
556 request.append(" RTSP/1.0\r\n");
557
558 request.append("Session: ");
559 request.append(mSessionID);
560 request.append("\r\n");
561
562 request.append("\r\n");
563
564 sp<AMessage> reply = new AMessage('play', id());
565 mConn->sendRequest(request.c_str(), reply);
566 } else {
567 sp<AMessage> reply = new AMessage('disc', id());
568 mConn->disconnect(reply);
569 }
570 break;
571 }
572
573 case 'play':
574 {
575 int32_t result;
576 CHECK(msg->findInt32("result", &result));
577
Andreas Huber6e3fa442010-09-21 13:13:15 -0700578 LOGI("PLAY completed with result %d (%s)",
579 result, strerror(-result));
Andreas Huber7a747b82010-06-07 15:19:40 -0700580
581 if (result == OK) {
582 sp<RefBase> obj;
583 CHECK(msg->findObject("response", &obj));
584 sp<ARTSPResponse> response =
585 static_cast<ARTSPResponse *>(obj.get());
586
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700587 if (response->mStatusCode != 200) {
588 result = UNKNOWN_ERROR;
589 } else {
590 parsePlayResponse(response);
Andreas Huber7a747b82010-06-07 15:19:40 -0700591
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700592 sp<AMessage> timeout = new AMessage('tiou', id());
593 timeout->post(kStartupTimeoutUs);
594 }
595 }
Andreas Hubereeb97d92010-08-27 13:29:08 -0700596
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700597 if (result != OK) {
Andreas Huber7a747b82010-06-07 15:19:40 -0700598 sp<AMessage> reply = new AMessage('disc', id());
599 mConn->disconnect(reply);
600 }
601
602 break;
603 }
604
605 case 'abor':
606 {
607 for (size_t i = 0; i < mTracks.size(); ++i) {
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700608 TrackInfo *info = &mTracks.editItemAt(i);
609
610 info->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
611
612 if (!info->mUsingInterleavedTCP) {
613 mRTPConn->removeStream(info->mRTPSocket, info->mRTCPSocket);
614
615 close(info->mRTPSocket);
616 close(info->mRTCPSocket);
617 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700618 }
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700619 mTracks.clear();
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700620 mSetupTracksSuccessful = false;
621 mSeekPending = false;
622 mFirstAccessUnit = true;
Andreas Huberb2934b12011-02-08 10:18:41 -0800623 mNTPAnchorUs = -1;
624 mMediaAnchorUs = -1;
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700625 mNumAccessUnitsReceived = 0;
626 mReceivedFirstRTCPPacket = false;
Andreas Hubercc5fb1d2010-10-13 12:15:03 -0700627 mReceivedFirstRTPPacket = false;
Andreas Huber0c46b692010-10-08 15:21:08 -0700628 mSeekable = false;
Andreas Huber7a747b82010-06-07 15:19:40 -0700629
630 sp<AMessage> reply = new AMessage('tear', id());
631
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700632 int32_t reconnect;
633 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
634 reply->setInt32("reconnect", true);
635 }
636
Andreas Huber7a747b82010-06-07 15:19:40 -0700637 AString request;
638 request = "TEARDOWN ";
639
640 // XXX should use aggregate url from SDP here...
641 request.append(mSessionURL);
642 request.append(" RTSP/1.0\r\n");
643
644 request.append("Session: ");
645 request.append(mSessionID);
646 request.append("\r\n");
647
648 request.append("\r\n");
649
650 mConn->sendRequest(request.c_str(), reply);
651 break;
652 }
653
654 case 'tear':
655 {
656 int32_t result;
657 CHECK(msg->findInt32("result", &result));
658
Andreas Huber6e3fa442010-09-21 13:13:15 -0700659 LOGI("TEARDOWN completed with result %d (%s)",
660 result, strerror(-result));
Andreas Huber7a747b82010-06-07 15:19:40 -0700661
662 sp<AMessage> reply = new AMessage('disc', id());
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700663
664 int32_t reconnect;
665 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
666 reply->setInt32("reconnect", true);
667 }
668
Andreas Huber7a747b82010-06-07 15:19:40 -0700669 mConn->disconnect(reply);
670 break;
671 }
672
673 case 'quit':
674 {
Andreas Huber8370be12010-08-23 11:28:34 -0700675 if (mDoneMsg != NULL) {
676 mDoneMsg->setInt32("result", UNKNOWN_ERROR);
677 mDoneMsg->post();
678 mDoneMsg = NULL;
679 }
Andreas Huber7a747b82010-06-07 15:19:40 -0700680 break;
681 }
682
Andreas Hubereeb97d92010-08-27 13:29:08 -0700683 case 'chek':
684 {
Andreas Hubere51e8092010-10-08 09:04:25 -0700685 int32_t generation;
686 CHECK(msg->findInt32("generation", &generation));
687 if (generation != mCheckGeneration) {
688 // This is an outdated message. Ignore.
689 break;
690 }
691
Andreas Hubereeb97d92010-08-27 13:29:08 -0700692 if (mNumAccessUnitsReceived == 0) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700693 LOGI("stream ended? aborting.");
Andreas Hubereeb97d92010-08-27 13:29:08 -0700694 (new AMessage('abor', id()))->post();
695 break;
696 }
697
698 mNumAccessUnitsReceived = 0;
Andreas Huberf6639c42010-08-30 09:53:52 -0700699 msg->post(kAccessUnitTimeoutUs);
Andreas Hubereeb97d92010-08-27 13:29:08 -0700700 break;
701 }
702
Andreas Huber7a747b82010-06-07 15:19:40 -0700703 case 'accu':
704 {
Andreas Huberb2934b12011-02-08 10:18:41 -0800705 int32_t timeUpdate;
706 if (msg->findInt32("time-update", &timeUpdate) && timeUpdate) {
707 size_t trackIndex;
708 CHECK(msg->findSize("track-index", &trackIndex));
709
710 uint32_t rtpTime;
711 uint64_t ntpTime;
712 CHECK(msg->findInt32("rtp-time", (int32_t *)&rtpTime));
713 CHECK(msg->findInt64("ntp-time", (int64_t *)&ntpTime));
714
715 onTimeUpdate(trackIndex, rtpTime, ntpTime);
716 break;
717 }
718
Andreas Hubercc5fb1d2010-10-13 12:15:03 -0700719 int32_t first;
720 if (msg->findInt32("first-rtcp", &first)) {
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700721 mReceivedFirstRTCPPacket = true;
722 break;
723 }
724
Andreas Hubercc5fb1d2010-10-13 12:15:03 -0700725 if (msg->findInt32("first-rtp", &first)) {
726 mReceivedFirstRTPPacket = true;
727 break;
728 }
729
Andreas Hubereeb97d92010-08-27 13:29:08 -0700730 ++mNumAccessUnitsReceived;
Andreas Hubere51e8092010-10-08 09:04:25 -0700731 postAccessUnitTimeoutCheck();
Andreas Hubereeb97d92010-08-27 13:29:08 -0700732
Andreas Huber7a747b82010-06-07 15:19:40 -0700733 size_t trackIndex;
734 CHECK(msg->findSize("track-index", &trackIndex));
735
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700736 if (trackIndex >= mTracks.size()) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700737 LOGV("late packets ignored.");
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700738 break;
739 }
740
Andreas Hubereeb97d92010-08-27 13:29:08 -0700741 TrackInfo *track = &mTracks.editItemAt(trackIndex);
742
Andreas Huberaf063a62010-08-18 10:17:18 -0700743 int32_t eos;
744 if (msg->findInt32("eos", &eos)) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700745 LOGI("received BYE on track index %d", trackIndex);
Andreas Huberaf063a62010-08-18 10:17:18 -0700746#if 0
Andreas Huberaf063a62010-08-18 10:17:18 -0700747 track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
748#endif
749 return;
750 }
751
Andreas Huber7a747b82010-06-07 15:19:40 -0700752 sp<RefBase> obj;
753 CHECK(msg->findObject("access-unit", &obj));
754
755 sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
756
Andreas Hubereeb97d92010-08-27 13:29:08 -0700757 uint32_t seqNum = (uint32_t)accessUnit->int32Data();
758
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700759 if (mSeekPending) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700760 LOGV("we're seeking, dropping stale packet.");
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700761 break;
762 }
763
Andreas Hubereeb97d92010-08-27 13:29:08 -0700764 if (seqNum < track->mFirstSeqNumInSegment) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700765 LOGV("dropping stale access-unit (%d < %d)",
766 seqNum, track->mFirstSeqNumInSegment);
Andreas Hubereeb97d92010-08-27 13:29:08 -0700767 break;
768 }
769
Andreas Hubereeb97d92010-08-27 13:29:08 -0700770 if (track->mNewSegment) {
771 track->mNewSegment = false;
Andreas Hubereeb97d92010-08-27 13:29:08 -0700772 }
773
Andreas Huberb2934b12011-02-08 10:18:41 -0800774 onAccessUnitComplete(trackIndex, accessUnit);
Andreas Huber7a747b82010-06-07 15:19:40 -0700775 break;
776 }
777
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700778 case 'seek':
779 {
Andreas Huber0c46b692010-10-08 15:21:08 -0700780 sp<AMessage> doneMsg;
781 CHECK(msg->findMessage("doneMsg", &doneMsg));
782
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700783 if (mSeekPending) {
Andreas Huber0c46b692010-10-08 15:21:08 -0700784 doneMsg->post();
785 break;
786 }
787
788 if (!mSeekable) {
789 LOGW("This is a live stream, ignoring seek request.");
790 doneMsg->post();
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700791 break;
792 }
793
794 int64_t timeUs;
795 CHECK(msg->findInt64("time", &timeUs));
796
797 mSeekPending = true;
798
Andreas Hubere51e8092010-10-08 09:04:25 -0700799 // Disable the access unit timeout until we resumed
800 // playback again.
801 mCheckPending = true;
802 ++mCheckGeneration;
803
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700804 AString request = "PAUSE ";
805 request.append(mSessionURL);
806 request.append(" RTSP/1.0\r\n");
807
808 request.append("Session: ");
809 request.append(mSessionID);
810 request.append("\r\n");
811
812 request.append("\r\n");
813
814 sp<AMessage> reply = new AMessage('see1', id());
815 reply->setInt64("time", timeUs);
Andreas Huber0c46b692010-10-08 15:21:08 -0700816 reply->setMessage("doneMsg", doneMsg);
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700817 mConn->sendRequest(request.c_str(), reply);
818 break;
819 }
820
821 case 'see1':
822 {
Andreas Hubereeb97d92010-08-27 13:29:08 -0700823 // Session is paused now.
824 for (size_t i = 0; i < mTracks.size(); ++i) {
Andreas Huberb2934b12011-02-08 10:18:41 -0800825 TrackInfo *info = &mTracks.editItemAt(i);
826
827 info->mPacketSource->flushQueue();
828 info->mRTPAnchor = 0;
829 info->mNTPAnchorUs = -1;
Andreas Hubereeb97d92010-08-27 13:29:08 -0700830 }
831
Andreas Huberb2934b12011-02-08 10:18:41 -0800832 mNTPAnchorUs = -1;
833
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700834 int64_t timeUs;
835 CHECK(msg->findInt64("time", &timeUs));
836
837 AString request = "PLAY ";
838 request.append(mSessionURL);
839 request.append(" RTSP/1.0\r\n");
840
841 request.append("Session: ");
842 request.append(mSessionID);
843 request.append("\r\n");
844
845 request.append(
846 StringPrintf(
847 "Range: npt=%lld-\r\n", timeUs / 1000000ll));
848
849 request.append("\r\n");
850
Andreas Huber0c46b692010-10-08 15:21:08 -0700851 sp<AMessage> doneMsg;
852 CHECK(msg->findMessage("doneMsg", &doneMsg));
853
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700854 sp<AMessage> reply = new AMessage('see2', id());
Andreas Huber0c46b692010-10-08 15:21:08 -0700855 reply->setMessage("doneMsg", doneMsg);
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700856 mConn->sendRequest(request.c_str(), reply);
857 break;
858 }
859
860 case 'see2':
861 {
862 CHECK(mSeekPending);
863
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700864 int32_t result;
865 CHECK(msg->findInt32("result", &result));
Andreas Hubereeb97d92010-08-27 13:29:08 -0700866
Andreas Huber6e3fa442010-09-21 13:13:15 -0700867 LOGI("PLAY completed with result %d (%s)",
868 result, strerror(-result));
Andreas Hubereeb97d92010-08-27 13:29:08 -0700869
Andreas Hubere51e8092010-10-08 09:04:25 -0700870 mCheckPending = false;
871 postAccessUnitTimeoutCheck();
872
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700873 if (result == OK) {
874 sp<RefBase> obj;
875 CHECK(msg->findObject("response", &obj));
876 sp<ARTSPResponse> response =
877 static_cast<ARTSPResponse *>(obj.get());
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700878
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700879 if (response->mStatusCode != 200) {
880 result = UNKNOWN_ERROR;
881 } else {
882 parsePlayResponse(response);
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700883
Andreas Huberb2934b12011-02-08 10:18:41 -0800884 ssize_t i = response->mHeaders.indexOfKey("rtp-info");
885 CHECK_GE(i, 0);
886
887 LOGV("rtp-info: %s", response->mHeaders.valueAt(i).c_str());
888
Andreas Huber6e3fa442010-09-21 13:13:15 -0700889 LOGI("seek completed.");
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700890 }
891 }
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700892
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700893 if (result != OK) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700894 LOGE("seek failed, aborting.");
Andreas Huberf3d2bdf2010-09-15 11:18:13 -0700895 (new AMessage('abor', id()))->post();
896 }
Andreas Hubereeb97d92010-08-27 13:29:08 -0700897
Andreas Hubereeb97d92010-08-27 13:29:08 -0700898 mSeekPending = false;
Andreas Huber0c46b692010-10-08 15:21:08 -0700899
900 sp<AMessage> doneMsg;
901 CHECK(msg->findMessage("doneMsg", &doneMsg));
902
903 doneMsg->post();
Andreas Hubere0dd7d32010-08-24 14:33:58 -0700904 break;
905 }
906
Andreas Huber0416da72010-08-26 11:17:32 -0700907 case 'biny':
908 {
909 sp<RefBase> obj;
910 CHECK(msg->findObject("buffer", &obj));
911 sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
912
913 int32_t index;
914 CHECK(buffer->meta()->findInt32("index", &index));
915
916 mRTPConn->injectPacket(index, buffer);
917 break;
918 }
919
920 case 'tiou':
921 {
Andreas Huber3a48d4d2010-08-31 10:43:47 -0700922 if (!mReceivedFirstRTCPPacket) {
Andreas Huber04072692011-02-15 10:39:48 -0800923 if (mReceivedFirstRTPPacket && !mTryFakeRTCP) {
Andreas Hubercc5fb1d2010-10-13 12:15:03 -0700924 LOGW("We received RTP packets but no RTCP packets, "
925 "using fake timestamps.");
926
927 mTryFakeRTCP = true;
928
929 mReceivedFirstRTCPPacket = true;
Andreas Huber04072692011-02-15 10:39:48 -0800930
931 fakeTimestamps();
932 } else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) {
Andreas Huber6e3fa442010-09-21 13:13:15 -0700933 LOGW("Never received any data, switching transports.");
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700934
935 mTryTCPInterleaving = true;
936
937 sp<AMessage> msg = new AMessage('abor', id());
938 msg->setInt32("reconnect", true);
939 msg->post();
Andreas Huber04072692011-02-15 10:39:48 -0800940 } else {
941 LOGW("Never received any data, disconnecting.");
942 (new AMessage('abor', id()))->post();
Andreas Huberf88ca7a02010-08-30 15:25:35 -0700943 }
Andreas Huber0416da72010-08-26 11:17:32 -0700944 }
Andreas Huber0416da72010-08-26 11:17:32 -0700945 break;
946 }
947
Andreas Huber7a747b82010-06-07 15:19:40 -0700948 default:
949 TRESPASS();
950 break;
951 }
952 }
953
Andreas Hubere51e8092010-10-08 09:04:25 -0700954 void postAccessUnitTimeoutCheck() {
955 if (mCheckPending) {
956 return;
957 }
958
959 mCheckPending = true;
960 sp<AMessage> check = new AMessage('chek', id());
961 check->setInt32("generation", mCheckGeneration);
962 check->post(kAccessUnitTimeoutUs);
963 }
964
Andreas Hubereeb97d92010-08-27 13:29:08 -0700965 static void SplitString(
966 const AString &s, const char *separator, List<AString> *items) {
967 items->clear();
968 size_t start = 0;
969 while (start < s.size()) {
970 ssize_t offset = s.find(separator, start);
971
972 if (offset < 0) {
973 items->push_back(AString(s, start, s.size() - start));
974 break;
975 }
976
977 items->push_back(AString(s, start, offset - start));
978 start = offset + strlen(separator);
979 }
980 }
981
982 void parsePlayResponse(const sp<ARTSPResponse> &response) {
Andreas Huber0c46b692010-10-08 15:21:08 -0700983 mSeekable = false;
984
Andreas Hubereeb97d92010-08-27 13:29:08 -0700985 ssize_t i = response->mHeaders.indexOfKey("range");
986 if (i < 0) {
987 // Server doesn't even tell use what range it is going to
988 // play, therefore we won't support seeking.
989 return;
990 }
991
992 AString range = response->mHeaders.valueAt(i);
Andreas Huber6e3fa442010-09-21 13:13:15 -0700993 LOGV("Range: %s", range.c_str());
Andreas Hubereeb97d92010-08-27 13:29:08 -0700994
995 AString val;
996 CHECK(GetAttribute(range.c_str(), "npt", &val));
Andreas Hubereeb97d92010-08-27 13:29:08 -0700997
Andreas Hubera2edd7d82011-01-28 09:19:12 -0800998 float npt1, npt2;
999 if (!ASessionDescription::parseNTPRange(val.c_str(), &npt1, &npt2)) {
Andreas Hubereeb97d92010-08-27 13:29:08 -07001000 // This is a live stream and therefore not seekable.
1001 return;
Andreas Hubereeb97d92010-08-27 13:29:08 -07001002 }
1003
1004 i = response->mHeaders.indexOfKey("rtp-info");
1005 CHECK_GE(i, 0);
1006
1007 AString rtpInfo = response->mHeaders.valueAt(i);
1008 List<AString> streamInfos;
1009 SplitString(rtpInfo, ",", &streamInfos);
1010
1011 int n = 1;
1012 for (List<AString>::iterator it = streamInfos.begin();
1013 it != streamInfos.end(); ++it) {
1014 (*it).trim();
Andreas Huber6e3fa442010-09-21 13:13:15 -07001015 LOGV("streamInfo[%d] = %s", n, (*it).c_str());
Andreas Hubereeb97d92010-08-27 13:29:08 -07001016
1017 CHECK(GetAttribute((*it).c_str(), "url", &val));
1018
1019 size_t trackIndex = 0;
1020 while (trackIndex < mTracks.size()
1021 && !(val == mTracks.editItemAt(trackIndex).mURL)) {
1022 ++trackIndex;
1023 }
1024 CHECK_LT(trackIndex, mTracks.size());
1025
1026 CHECK(GetAttribute((*it).c_str(), "seq", &val));
1027
1028 char *end;
1029 unsigned long seq = strtoul(val.c_str(), &end, 10);
1030
1031 TrackInfo *info = &mTracks.editItemAt(trackIndex);
1032 info->mFirstSeqNumInSegment = seq;
1033 info->mNewSegment = true;
1034
1035 CHECK(GetAttribute((*it).c_str(), "rtptime", &val));
1036
1037 uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
1038
Andreas Huberb2934b12011-02-08 10:18:41 -08001039 LOGV("track #%d: rtpTime=%u <=> npt=%.2f", n, rtpTime, npt1);
Andreas Hubereeb97d92010-08-27 13:29:08 -07001040
1041 info->mPacketSource->setNormalPlayTimeMapping(
1042 rtpTime, (int64_t)(npt1 * 1E6));
1043
1044 ++n;
1045 }
Andreas Huber0c46b692010-10-08 15:21:08 -07001046
1047 mSeekable = true;
Andreas Hubereeb97d92010-08-27 13:29:08 -07001048 }
1049
Andreas Huber7a747b82010-06-07 15:19:40 -07001050 sp<APacketSource> getPacketSource(size_t index) {
1051 CHECK_GE(index, 0u);
1052 CHECK_LT(index, mTracks.size());
1053
1054 return mTracks.editItemAt(index).mPacketSource;
1055 }
1056
1057 size_t countTracks() const {
1058 return mTracks.size();
1059 }
1060
1061private:
Andreas Huberb2934b12011-02-08 10:18:41 -08001062 struct TrackInfo {
1063 AString mURL;
1064 int mRTPSocket;
1065 int mRTCPSocket;
1066 bool mUsingInterleavedTCP;
1067 uint32_t mFirstSeqNumInSegment;
1068 bool mNewSegment;
1069
1070 uint32_t mRTPAnchor;
1071 int64_t mNTPAnchorUs;
1072 int32_t mTimeScale;
1073
1074 sp<APacketSource> mPacketSource;
1075
1076 // Stores packets temporarily while no notion of time
1077 // has been established yet.
1078 List<sp<ABuffer> > mPackets;
1079 };
1080
Andreas Huber7a747b82010-06-07 15:19:40 -07001081 sp<ALooper> mLooper;
Andreas Huber4e4173b2010-07-22 09:20:13 -07001082 sp<ALooper> mNetLooper;
Andreas Huber7a747b82010-06-07 15:19:40 -07001083 sp<ARTSPConnection> mConn;
1084 sp<ARTPConnection> mRTPConn;
1085 sp<ASessionDescription> mSessionDesc;
Andreas Hubera0b442e2010-10-20 15:00:34 -07001086 AString mOriginalSessionURL; // This one still has user:pass@
Andreas Huber7a747b82010-06-07 15:19:40 -07001087 AString mSessionURL;
Andreas Huber27db53d2011-02-15 13:32:42 -08001088 AString mSessionHost;
Andreas Huber7a747b82010-06-07 15:19:40 -07001089 AString mBaseURL;
1090 AString mSessionID;
1091 bool mSetupTracksSuccessful;
Andreas Hubere0dd7d32010-08-24 14:33:58 -07001092 bool mSeekPending;
1093 bool mFirstAccessUnit;
Andreas Huberb2934b12011-02-08 10:18:41 -08001094
1095 int64_t mNTPAnchorUs;
1096 int64_t mMediaAnchorUs;
1097 int64_t mLastMediaTimeUs;
1098
Andreas Hubereeb97d92010-08-27 13:29:08 -07001099 int64_t mNumAccessUnitsReceived;
1100 bool mCheckPending;
Andreas Hubere51e8092010-10-08 09:04:25 -07001101 int32_t mCheckGeneration;
Andreas Huberf88ca7a02010-08-30 15:25:35 -07001102 bool mTryTCPInterleaving;
Andreas Hubercc5fb1d2010-10-13 12:15:03 -07001103 bool mTryFakeRTCP;
Andreas Huber3a48d4d2010-08-31 10:43:47 -07001104 bool mReceivedFirstRTCPPacket;
Andreas Hubercc5fb1d2010-10-13 12:15:03 -07001105 bool mReceivedFirstRTPPacket;
Andreas Huber0c46b692010-10-08 15:21:08 -07001106 bool mSeekable;
Andreas Huber7a747b82010-06-07 15:19:40 -07001107
Andreas Huber7a747b82010-06-07 15:19:40 -07001108 Vector<TrackInfo> mTracks;
1109
Andreas Huber8370be12010-08-23 11:28:34 -07001110 sp<AMessage> mDoneMsg;
1111
Andreas Huber7a747b82010-06-07 15:19:40 -07001112 void setupTrack(size_t index) {
Andreas Huber57648e42010-08-04 10:14:30 -07001113 sp<APacketSource> source =
1114 new APacketSource(mSessionDesc, index);
Andreas Huberf88ca7a02010-08-30 15:25:35 -07001115
Andreas Huber57648e42010-08-04 10:14:30 -07001116 if (source->initCheck() != OK) {
Andreas Huber6e3fa442010-09-21 13:13:15 -07001117 LOGW("Unsupported format. Ignoring track #%d.", index);
Andreas Huber57648e42010-08-04 10:14:30 -07001118
1119 sp<AMessage> reply = new AMessage('setu', id());
1120 reply->setSize("index", index);
1121 reply->setInt32("result", ERROR_UNSUPPORTED);
1122 reply->post();
1123 return;
1124 }
1125
Andreas Huber7a747b82010-06-07 15:19:40 -07001126 AString url;
1127 CHECK(mSessionDesc->findAttribute(index, "a=control", &url));
1128
1129 AString trackURL;
1130 CHECK(MakeURL(mBaseURL.c_str(), url.c_str(), &trackURL));
1131
1132 mTracks.push(TrackInfo());
1133 TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
Andreas Hubereeb97d92010-08-27 13:29:08 -07001134 info->mURL = trackURL;
Andreas Huber57648e42010-08-04 10:14:30 -07001135 info->mPacketSource = source;
Andreas Huber0416da72010-08-26 11:17:32 -07001136 info->mUsingInterleavedTCP = false;
Andreas Hubereeb97d92010-08-27 13:29:08 -07001137 info->mFirstSeqNumInSegment = 0;
1138 info->mNewSegment = true;
Andreas Huberb2934b12011-02-08 10:18:41 -08001139 info->mRTPAnchor = 0;
1140 info->mNTPAnchorUs = -1;
1141
1142 unsigned long PT;
1143 AString formatDesc;
1144 AString formatParams;
1145 mSessionDesc->getFormatType(index, &PT, &formatDesc, &formatParams);
1146
1147 int32_t timescale;
1148 int32_t numChannels;
1149 ASessionDescription::ParseFormatDesc(
1150 formatDesc.c_str(), &timescale, &numChannels);
1151
1152 info->mTimeScale = timescale;
Andreas Hubereeb97d92010-08-27 13:29:08 -07001153
Andreas Huber6e3fa442010-09-21 13:13:15 -07001154 LOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str());
Andreas Huber7a747b82010-06-07 15:19:40 -07001155
1156 AString request = "SETUP ";
1157 request.append(trackURL);
1158 request.append(" RTSP/1.0\r\n");
1159
Andreas Huberf88ca7a02010-08-30 15:25:35 -07001160 if (mTryTCPInterleaving) {
1161 size_t interleaveIndex = 2 * (mTracks.size() - 1);
1162 info->mUsingInterleavedTCP = true;
1163 info->mRTPSocket = interleaveIndex;
1164 info->mRTCPSocket = interleaveIndex + 1;
Andreas Huber0416da72010-08-26 11:17:32 -07001165
Andreas Huberf88ca7a02010-08-30 15:25:35 -07001166 request.append("Transport: RTP/AVP/TCP;interleaved=");
1167 request.append(interleaveIndex);
1168 request.append("-");
1169 request.append(interleaveIndex + 1);
1170 } else {
1171 unsigned rtpPort;
1172 ARTPConnection::MakePortPair(
1173 &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
Andreas Huber0416da72010-08-26 11:17:32 -07001174
Andreas Huberf88ca7a02010-08-30 15:25:35 -07001175 request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
1176 request.append(rtpPort);
1177 request.append("-");
1178 request.append(rtpPort + 1);
1179 }
Andreas Huber0416da72010-08-26 11:17:32 -07001180
Andreas Huber7a747b82010-06-07 15:19:40 -07001181 request.append("\r\n");
1182
1183 if (index > 1) {
1184 request.append("Session: ");
1185 request.append(mSessionID);
1186 request.append("\r\n");
1187 }
1188
1189 request.append("\r\n");
1190
1191 sp<AMessage> reply = new AMessage('setu', id());
1192 reply->setSize("index", index);
1193 reply->setSize("track-index", mTracks.size() - 1);
1194 mConn->sendRequest(request.c_str(), reply);
1195 }
1196
1197 static bool MakeURL(const char *baseURL, const char *url, AString *out) {
1198 out->clear();
1199
1200 if (strncasecmp("rtsp://", baseURL, 7)) {
1201 // Base URL must be absolute
1202 return false;
1203 }
1204
1205 if (!strncasecmp("rtsp://", url, 7)) {
1206 // "url" is already an absolute URL, ignore base URL.
1207 out->setTo(url);
1208 return true;
1209 }
1210
1211 size_t n = strlen(baseURL);
1212 if (baseURL[n - 1] == '/') {
1213 out->setTo(baseURL);
1214 out->append(url);
1215 } else {
Mike Lockwood5a23f8c2010-07-15 14:58:25 -04001216 const char *slashPos = strrchr(baseURL, '/');
Andreas Huber7a747b82010-06-07 15:19:40 -07001217
1218 if (slashPos > &baseURL[6]) {
1219 out->setTo(baseURL, slashPos - baseURL);
1220 } else {
1221 out->setTo(baseURL);
1222 }
1223
1224 out->append("/");
1225 out->append(url);
1226 }
1227
1228 return true;
1229 }
1230
Andreas Huber04072692011-02-15 10:39:48 -08001231 void fakeTimestamps() {
1232 for (size_t i = 0; i < mTracks.size(); ++i) {
1233 onTimeUpdate(i, 0, 0ll);
1234 }
1235 }
1236
Andreas Huberb2934b12011-02-08 10:18:41 -08001237 void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
1238 LOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx",
1239 trackIndex, rtpTime, ntpTime);
1240
1241 int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
1242
1243 TrackInfo *track = &mTracks.editItemAt(trackIndex);
1244
1245 track->mRTPAnchor = rtpTime;
1246 track->mNTPAnchorUs = ntpTimeUs;
1247
1248 if (mNTPAnchorUs < 0) {
1249 mNTPAnchorUs = ntpTimeUs;
1250 mMediaAnchorUs = mLastMediaTimeUs;
1251 }
1252 }
1253
1254 void onAccessUnitComplete(
1255 int32_t trackIndex, const sp<ABuffer> &accessUnit) {
1256 LOGV("onAccessUnitComplete track %d", trackIndex);
1257
1258 if (mFirstAccessUnit) {
1259 mDoneMsg->setInt32("result", OK);
1260 mDoneMsg->post();
1261 mDoneMsg = NULL;
1262
1263 mFirstAccessUnit = false;
1264 }
1265
1266 TrackInfo *track = &mTracks.editItemAt(trackIndex);
1267
1268 if (mNTPAnchorUs < 0 || mMediaAnchorUs < 0 || track->mNTPAnchorUs < 0) {
1269 LOGV("storing accessUnit, no time established yet");
1270 track->mPackets.push_back(accessUnit);
1271 return;
1272 }
1273
1274 while (!track->mPackets.empty()) {
1275 sp<ABuffer> accessUnit = *track->mPackets.begin();
1276 track->mPackets.erase(track->mPackets.begin());
1277
1278 if (addMediaTimestamp(trackIndex, track, accessUnit)) {
1279 track->mPacketSource->queueAccessUnit(accessUnit);
1280 }
1281 }
1282
1283 if (addMediaTimestamp(trackIndex, track, accessUnit)) {
1284 track->mPacketSource->queueAccessUnit(accessUnit);
1285 }
1286 }
1287
1288 bool addMediaTimestamp(
1289 int32_t trackIndex, const TrackInfo *track,
1290 const sp<ABuffer> &accessUnit) {
1291 uint32_t rtpTime;
1292 CHECK(accessUnit->meta()->findInt32(
1293 "rtp-time", (int32_t *)&rtpTime));
1294
1295 int64_t relRtpTimeUs =
1296 (((int64_t)rtpTime - (int64_t)track->mRTPAnchor) * 1000000ll)
1297 / track->mTimeScale;
1298
1299 int64_t ntpTimeUs = track->mNTPAnchorUs + relRtpTimeUs;
1300
1301 int64_t mediaTimeUs = mMediaAnchorUs + ntpTimeUs - mNTPAnchorUs;
1302
1303 if (mediaTimeUs > mLastMediaTimeUs) {
1304 mLastMediaTimeUs = mediaTimeUs;
1305 }
1306
1307 if (mediaTimeUs < 0) {
1308 LOGV("dropping early accessUnit.");
1309 return false;
1310 }
1311
1312 LOGV("track %d rtpTime=%d mediaTimeUs = %lld us (%.2f secs)",
1313 trackIndex, rtpTime, mediaTimeUs, mediaTimeUs / 1E6);
1314
1315 accessUnit->meta()->setInt64("timeUs", mediaTimeUs);
1316
1317 return true;
1318 }
1319
1320
Andreas Huber7a747b82010-06-07 15:19:40 -07001321 DISALLOW_EVIL_CONSTRUCTORS(MyHandler);
1322};
1323
1324} // namespace android
1325
1326#endif // MY_HANDLER_H_