blob: b2f0e5ebda56fad7a317fe24d4b99335ebbd1efb [file] [log] [blame]
Andreas Hubercf7b9c72010-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 Huber6e4c5c42010-09-21 13:13:15 -070021//#define LOG_NDEBUG 0
22#define LOG_TAG "MyHandler"
23#include <utils/Log.h>
24
Andreas Hubercf7b9c72010-06-07 15:19:40 -070025#include "APacketSource.h"
26#include "ARTPConnection.h"
27#include "ARTSPConnection.h"
28#include "ASessionDescription.h"
29
Andreas Huber8d342972010-08-27 13:29:08 -070030#include <ctype.h>
Andreas Huber2bc940b2010-10-11 11:05:52 -070031#include <cutils/properties.h>
Andreas Huber8d342972010-08-27 13:29:08 -070032
Andreas Hubercf7b9c72010-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 Huber2bc940b2010-10-11 11:05:52 -070039#include <arpa/inet.h>
40#include <sys/socket.h>
Andreas Huberde9a20c2011-02-15 13:32:42 -080041#include <netdb.h>
Andreas Huber2bc940b2010-10-11 11:05:52 -070042
Andreas Huberdab718b2011-07-13 15:45:01 -070043#include "HTTPBase.h"
Andreas Huber9b80c2b2011-06-30 15:47:02 -070044
Andreas Huber100a4402011-02-08 10:18:41 -080045// If no access units are received within 5 secs, assume that the rtp
Andreas Hubere56121b2010-08-30 09:53:52 -070046// stream has ended and signal end of stream.
Andreas Huber908dbde2011-11-08 15:31:23 -080047static int64_t kAccessUnitTimeoutUs = 10000000ll;
Andreas Hubere56121b2010-08-30 09:53:52 -070048
49// If no access units arrive for the first 10 secs after starting the
50// stream, assume none ever will and signal EOS or switch transports.
51static int64_t kStartupTimeoutUs = 10000000ll;
52
Andreas Huber908dbde2011-11-08 15:31:23 -080053static int64_t kDefaultKeepAliveTimeoutUs = 60000000ll;
54
Andreas Hubercf7b9c72010-06-07 15:19:40 -070055namespace android {
56
Andreas Huber2bc940b2010-10-11 11:05:52 -070057static void MakeUserAgentString(AString *s) {
58 s->setTo("stagefright/1.1 (Linux;Android ");
59
60#if (PROPERTY_VALUE_MAX < 8)
61#error "PROPERTY_VALUE_MAX must be at least 8"
62#endif
63
64 char value[PROPERTY_VALUE_MAX];
65 property_get("ro.build.version.release", value, "Unknown");
66 s->append(value);
67 s->append(")");
68}
69
Andreas Huber8d342972010-08-27 13:29:08 -070070static bool GetAttribute(const char *s, const char *key, AString *value) {
71 value->clear();
72
73 size_t keyLen = strlen(key);
74
75 for (;;) {
76 while (isspace(*s)) {
77 ++s;
78 }
79
80 const char *colonPos = strchr(s, ';');
81
82 size_t len =
83 (colonPos == NULL) ? strlen(s) : colonPos - s;
84
85 if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
86 value->setTo(&s[keyLen + 1], len - keyLen - 1);
87 return true;
88 }
89
90 if (colonPos == NULL) {
91 return false;
92 }
93
94 s = colonPos + 1;
95 }
96}
97
Andreas Hubercf7b9c72010-06-07 15:19:40 -070098struct MyHandler : public AHandler {
Andreas Huber2bfdd422011-10-11 15:24:07 -070099 enum {
100 kWhatConnected = 'conn',
101 kWhatDisconnected = 'disc',
102 kWhatSeekDone = 'sdon',
103
104 kWhatAccessUnit = 'accU',
105 kWhatEOS = 'eos!',
106 kWhatSeekDiscontinuity = 'seeD',
107 kWhatNormalPlayTimeMapping = 'nptM',
108 };
109
Andreas Huber9b80c2b2011-06-30 15:47:02 -0700110 MyHandler(
Andreas Huber2bfdd422011-10-11 15:24:07 -0700111 const char *url,
112 const sp<AMessage> &notify,
Andreas Huber9b80c2b2011-06-30 15:47:02 -0700113 bool uidValid = false, uid_t uid = 0)
Andreas Huber2bfdd422011-10-11 15:24:07 -0700114 : mNotify(notify),
115 mUIDValid(uidValid),
Andreas Huber9b80c2b2011-06-30 15:47:02 -0700116 mUID(uid),
Andreas Huber348a8ea2010-07-22 09:20:13 -0700117 mNetLooper(new ALooper),
Andreas Huber9b80c2b2011-06-30 15:47:02 -0700118 mConn(new ARTSPConnection(mUIDValid, mUID)),
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700119 mRTPConn(new ARTPConnection),
Andreas Huber4579b7d2010-10-20 15:00:34 -0700120 mOriginalSessionURL(url),
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700121 mSessionURL(url),
Andreas Hubercce326f2010-08-24 14:33:58 -0700122 mSetupTracksSuccessful(false),
123 mSeekPending(false),
124 mFirstAccessUnit(true),
Andreas Huber7e73e442012-01-20 13:30:03 -0800125 mAllTracksHaveTime(false),
Andreas Huber100a4402011-02-08 10:18:41 -0800126 mNTPAnchorUs(-1),
127 mMediaAnchorUs(-1),
128 mLastMediaTimeUs(0),
Andreas Huber8d342972010-08-27 13:29:08 -0700129 mNumAccessUnitsReceived(0),
Andreas Huber7aef0332010-08-30 15:25:35 -0700130 mCheckPending(false),
Andreas Hubera9d9dd22010-10-08 09:04:25 -0700131 mCheckGeneration(0),
Andreas Hubere7d3e902010-08-31 10:43:47 -0700132 mTryTCPInterleaving(false),
Andreas Huberf61551f2010-10-13 12:15:03 -0700133 mTryFakeRTCP(false),
Andreas Huber0dcd8372010-10-08 15:21:08 -0700134 mReceivedFirstRTCPPacket(false),
Andreas Huberf61551f2010-10-13 12:15:03 -0700135 mReceivedFirstRTPPacket(false),
Andreas Huber908dbde2011-11-08 15:31:23 -0800136 mSeekable(false),
137 mKeepAliveTimeoutUs(kDefaultKeepAliveTimeoutUs),
138 mKeepAliveGeneration(0) {
Andreas Hubera814c1f2010-08-27 15:21:07 -0700139 mNetLooper->setName("rtsp net");
Andreas Huber348a8ea2010-07-22 09:20:13 -0700140 mNetLooper->start(false /* runOnCallingThread */,
141 false /* canCallJava */,
142 PRIORITY_HIGHEST);
Andreas Huber4579b7d2010-10-20 15:00:34 -0700143
144 // Strip any authentication info from the session url, we don't
145 // want to transmit user/pass in cleartext.
146 AString host, path, user, pass;
147 unsigned port;
Andreas Huberde9a20c2011-02-15 13:32:42 -0800148 CHECK(ARTSPConnection::ParseURL(
149 mSessionURL.c_str(), &host, &port, &path, &user, &pass));
150
151 if (user.size() > 0) {
Andreas Huber4579b7d2010-10-20 15:00:34 -0700152 mSessionURL.clear();
153 mSessionURL.append("rtsp://");
154 mSessionURL.append(host);
155 mSessionURL.append(":");
156 mSessionURL.append(StringPrintf("%u", port));
157 mSessionURL.append(path);
158
Steve Blockdf64d152012-01-04 20:05:49 +0000159 ALOGI("rewritten session url: '%s'", mSessionURL.c_str());
Andreas Huber4579b7d2010-10-20 15:00:34 -0700160 }
Andreas Huberde9a20c2011-02-15 13:32:42 -0800161
162 mSessionHost = host;
Andreas Huber348a8ea2010-07-22 09:20:13 -0700163 }
164
Andreas Huber2bfdd422011-10-11 15:24:07 -0700165 void connect() {
166 looper()->registerHandler(mConn);
167 (1 ? mNetLooper : looper())->registerHandler(mRTPConn);
Andreas Huber348a8ea2010-07-22 09:20:13 -0700168
Andreas Huber0792ce72010-08-26 11:17:32 -0700169 sp<AMessage> notify = new AMessage('biny', id());
170 mConn->observeBinaryData(notify);
171
Andreas Huber1b543242010-08-23 11:28:34 -0700172 sp<AMessage> reply = new AMessage('conn', id());
Andreas Huber4579b7d2010-10-20 15:00:34 -0700173 mConn->connect(mOriginalSessionURL.c_str(), reply);
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700174 }
175
Andreas Huber2bfdd422011-10-11 15:24:07 -0700176 void disconnect() {
Andreas Huber1b543242010-08-23 11:28:34 -0700177 (new AMessage('abor', id()))->post();
Andreas Huber348a8ea2010-07-22 09:20:13 -0700178 }
179
Andreas Huber2bfdd422011-10-11 15:24:07 -0700180 void seek(int64_t timeUs) {
Andreas Hubercce326f2010-08-24 14:33:58 -0700181 sp<AMessage> msg = new AMessage('seek', id());
182 msg->setInt64("time", timeUs);
183 msg->post();
184 }
185
Andreas Huber2bc940b2010-10-11 11:05:52 -0700186 static void addRR(const sp<ABuffer> &buf) {
187 uint8_t *ptr = buf->data() + buf->size();
188 ptr[0] = 0x80 | 0;
189 ptr[1] = 201; // RR
190 ptr[2] = 0;
191 ptr[3] = 1;
192 ptr[4] = 0xde; // SSRC
193 ptr[5] = 0xad;
194 ptr[6] = 0xbe;
195 ptr[7] = 0xef;
196
197 buf->setRange(0, buf->size() + 8);
198 }
199
200 static void addSDES(int s, const sp<ABuffer> &buffer) {
201 struct sockaddr_in addr;
202 socklen_t addrSize = sizeof(addr);
203 CHECK_EQ(0, getsockname(s, (sockaddr *)&addr, &addrSize));
204
205 uint8_t *data = buffer->data() + buffer->size();
206 data[0] = 0x80 | 1;
207 data[1] = 202; // SDES
208 data[4] = 0xde; // SSRC
209 data[5] = 0xad;
210 data[6] = 0xbe;
211 data[7] = 0xef;
212
213 size_t offset = 8;
214
215 data[offset++] = 1; // CNAME
216
217 AString cname = "stagefright@";
218 cname.append(inet_ntoa(addr.sin_addr));
219 data[offset++] = cname.size();
220
221 memcpy(&data[offset], cname.c_str(), cname.size());
222 offset += cname.size();
223
224 data[offset++] = 6; // TOOL
225
226 AString tool;
227 MakeUserAgentString(&tool);
228
229 data[offset++] = tool.size();
230
231 memcpy(&data[offset], tool.c_str(), tool.size());
232 offset += tool.size();
233
234 data[offset++] = 0;
235
236 if ((offset % 4) > 0) {
237 size_t count = 4 - (offset % 4);
238 switch (count) {
239 case 3:
240 data[offset++] = 0;
241 case 2:
242 data[offset++] = 0;
243 case 1:
244 data[offset++] = 0;
245 }
246 }
247
248 size_t numWords = (offset / 4) - 1;
249 data[2] = numWords >> 8;
250 data[3] = numWords & 0xff;
251
252 buffer->setRange(buffer->offset(), buffer->size() + offset);
253 }
254
255 // In case we're behind NAT, fire off two UDP packets to the remote
256 // rtp/rtcp ports to poke a hole into the firewall for future incoming
257 // packets. We're going to send an RR/SDES RTCP packet to both of them.
Andreas Huberdc468c52011-02-15 10:39:48 -0800258 bool pokeAHole(int rtpSocket, int rtcpSocket, const AString &transport) {
Andreas Huberde9a20c2011-02-15 13:32:42 -0800259 struct sockaddr_in addr;
260 memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
261 addr.sin_family = AF_INET;
262
Andreas Huber2bc940b2010-10-11 11:05:52 -0700263 AString source;
264 AString server_port;
265 if (!GetAttribute(transport.c_str(),
266 "source",
Andreas Huberde9a20c2011-02-15 13:32:42 -0800267 &source)) {
Steve Block5ff1dd52012-01-05 23:22:43 +0000268 ALOGW("Missing 'source' field in Transport response. Using "
Andreas Huberde9a20c2011-02-15 13:32:42 -0800269 "RTSP endpoint address.");
270
271 struct hostent *ent = gethostbyname(mSessionHost.c_str());
272 if (ent == NULL) {
Steve Block29357bc2012-01-06 19:20:56 +0000273 ALOGE("Failed to look up address of session host '%s'",
Andreas Huberde9a20c2011-02-15 13:32:42 -0800274 mSessionHost.c_str());
275
276 return false;
277 }
278
279 addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
280 } else {
281 addr.sin_addr.s_addr = inet_addr(source.c_str());
282 }
283
284 if (!GetAttribute(transport.c_str(),
Andreas Huber2bc940b2010-10-11 11:05:52 -0700285 "server_port",
286 &server_port)) {
Steve Blockdf64d152012-01-04 20:05:49 +0000287 ALOGI("Missing 'server_port' field in Transport response.");
Andreas Huberdc468c52011-02-15 10:39:48 -0800288 return false;
Andreas Huber2bc940b2010-10-11 11:05:52 -0700289 }
290
291 int rtpPort, rtcpPort;
292 if (sscanf(server_port.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
293 || rtpPort <= 0 || rtpPort > 65535
294 || rtcpPort <=0 || rtcpPort > 65535
Andreas Huberdc468c52011-02-15 10:39:48 -0800295 || rtcpPort != rtpPort + 1) {
Steve Block29357bc2012-01-06 19:20:56 +0000296 ALOGE("Server picked invalid RTP/RTCP port pair %s,"
Andreas Huberdc468c52011-02-15 10:39:48 -0800297 " RTP port must be even, RTCP port must be one higher.",
298 server_port.c_str());
299
300 return false;
301 }
302
303 if (rtpPort & 1) {
Steve Block5ff1dd52012-01-05 23:22:43 +0000304 ALOGW("Server picked an odd RTP port, it should've picked an "
Andreas Huberdc468c52011-02-15 10:39:48 -0800305 "even one, we'll let it pass for now, but this may break "
306 "in the future.");
Andreas Huber2bc940b2010-10-11 11:05:52 -0700307 }
308
Andreas Huber2bc940b2010-10-11 11:05:52 -0700309 if (addr.sin_addr.s_addr == INADDR_NONE) {
Andreas Huberdc468c52011-02-15 10:39:48 -0800310 return true;
311 }
312
313 if (IN_LOOPBACK(ntohl(addr.sin_addr.s_addr))) {
314 // No firewalls to traverse on the loopback interface.
315 return true;
Andreas Huber2bc940b2010-10-11 11:05:52 -0700316 }
317
318 // Make up an RR/SDES RTCP packet.
319 sp<ABuffer> buf = new ABuffer(65536);
320 buf->setRange(0, 0);
321 addRR(buf);
322 addSDES(rtpSocket, buf);
323
324 addr.sin_port = htons(rtpPort);
325
326 ssize_t n = sendto(
327 rtpSocket, buf->data(), buf->size(), 0,
328 (const sockaddr *)&addr, sizeof(addr));
Andreas Huberdc468c52011-02-15 10:39:48 -0800329
330 if (n < (ssize_t)buf->size()) {
Steve Block29357bc2012-01-06 19:20:56 +0000331 ALOGE("failed to poke a hole for RTP packets");
Andreas Huberdc468c52011-02-15 10:39:48 -0800332 return false;
333 }
Andreas Huber2bc940b2010-10-11 11:05:52 -0700334
335 addr.sin_port = htons(rtcpPort);
336
337 n = sendto(
338 rtcpSocket, buf->data(), buf->size(), 0,
339 (const sockaddr *)&addr, sizeof(addr));
Andreas Huberdc468c52011-02-15 10:39:48 -0800340
341 if (n < (ssize_t)buf->size()) {
Steve Block29357bc2012-01-06 19:20:56 +0000342 ALOGE("failed to poke a hole for RTCP packets");
Andreas Huberdc468c52011-02-15 10:39:48 -0800343 return false;
344 }
Andreas Huber2bc940b2010-10-11 11:05:52 -0700345
Steve Block3856b092011-10-20 11:56:00 +0100346 ALOGV("successfully poked holes.");
Andreas Huberdc468c52011-02-15 10:39:48 -0800347
348 return true;
Andreas Huber2bc940b2010-10-11 11:05:52 -0700349 }
350
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700351 virtual void onMessageReceived(const sp<AMessage> &msg) {
352 switch (msg->what()) {
353 case 'conn':
354 {
355 int32_t result;
356 CHECK(msg->findInt32("result", &result));
357
Steve Blockdf64d152012-01-04 20:05:49 +0000358 ALOGI("connection request completed with result %d (%s)",
Andreas Huber6e4c5c42010-09-21 13:13:15 -0700359 result, strerror(-result));
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700360
361 if (result == OK) {
362 AString request;
363 request = "DESCRIBE ";
364 request.append(mSessionURL);
365 request.append(" RTSP/1.0\r\n");
366 request.append("Accept: application/sdp\r\n");
367 request.append("\r\n");
368
369 sp<AMessage> reply = new AMessage('desc', id());
370 mConn->sendRequest(request.c_str(), reply);
Andreas Huber0792ce72010-08-26 11:17:32 -0700371 } else {
372 (new AMessage('disc', id()))->post();
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700373 }
374 break;
375 }
376
377 case 'disc':
378 {
Andreas Huber908dbde2011-11-08 15:31:23 -0800379 ++mKeepAliveGeneration;
380
Andreas Huber7aef0332010-08-30 15:25:35 -0700381 int32_t reconnect;
382 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
383 sp<AMessage> reply = new AMessage('conn', id());
Andreas Huber4579b7d2010-10-20 15:00:34 -0700384 mConn->connect(mOriginalSessionURL.c_str(), reply);
Andreas Huber7aef0332010-08-30 15:25:35 -0700385 } else {
386 (new AMessage('quit', id()))->post();
387 }
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700388 break;
389 }
390
391 case 'desc':
392 {
393 int32_t result;
394 CHECK(msg->findInt32("result", &result));
395
Steve Blockdf64d152012-01-04 20:05:49 +0000396 ALOGI("DESCRIBE completed with result %d (%s)",
Andreas Huber6e4c5c42010-09-21 13:13:15 -0700397 result, strerror(-result));
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700398
399 if (result == OK) {
400 sp<RefBase> obj;
401 CHECK(msg->findObject("response", &obj));
402 sp<ARTSPResponse> response =
403 static_cast<ARTSPResponse *>(obj.get());
404
405 if (response->mStatusCode == 302) {
406 ssize_t i = response->mHeaders.indexOfKey("location");
407 CHECK_GE(i, 0);
408
409 mSessionURL = response->mHeaders.valueAt(i);
410
411 AString request;
412 request = "DESCRIBE ";
413 request.append(mSessionURL);
414 request.append(" RTSP/1.0\r\n");
415 request.append("Accept: application/sdp\r\n");
416 request.append("\r\n");
417
418 sp<AMessage> reply = new AMessage('desc', id());
419 mConn->sendRequest(request.c_str(), reply);
420 break;
421 }
422
Andreas Hubere7d3e902010-08-31 10:43:47 -0700423 if (response->mStatusCode != 200) {
424 result = UNKNOWN_ERROR;
Xuefei Chen5f189752013-01-07 16:43:13 +0800425 } else if (response->mContent == NULL) {
426 result = ERROR_MALFORMED;
427 ALOGE("The response has no content.");
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700428 } else {
Andreas Hubere7d3e902010-08-31 10:43:47 -0700429 mSessionDesc = new ASessionDescription;
430
431 mSessionDesc->setTo(
432 response->mContent->data(),
433 response->mContent->size());
434
Andreas Huber6f85dba2010-09-15 11:18:13 -0700435 if (!mSessionDesc->isValid()) {
Steve Block29357bc2012-01-06 19:20:56 +0000436 ALOGE("Failed to parse session description.");
Andreas Huber6f85dba2010-09-15 11:18:13 -0700437 result = ERROR_MALFORMED;
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700438 } else {
Andreas Huber6f85dba2010-09-15 11:18:13 -0700439 ssize_t i = response->mHeaders.indexOfKey("content-base");
Andreas Hubere7d3e902010-08-31 10:43:47 -0700440 if (i >= 0) {
441 mBaseURL = response->mHeaders.valueAt(i);
442 } else {
Andreas Huber6f85dba2010-09-15 11:18:13 -0700443 i = response->mHeaders.indexOfKey("content-location");
444 if (i >= 0) {
445 mBaseURL = response->mHeaders.valueAt(i);
446 } else {
447 mBaseURL = mSessionURL;
448 }
Andreas Hubere7d3e902010-08-31 10:43:47 -0700449 }
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700450
Andreas Huberdc468c52011-02-15 10:39:48 -0800451 if (!mBaseURL.startsWith("rtsp://")) {
452 // Some misbehaving servers specify a relative
453 // URL in one of the locations above, combine
454 // it with the absolute session URL to get
455 // something usable...
456
Steve Block5ff1dd52012-01-05 23:22:43 +0000457 ALOGW("Server specified a non-absolute base URL"
Andreas Huberdc468c52011-02-15 10:39:48 -0800458 ", combining it with the session URL to "
459 "get something usable...");
460
461 AString tmp;
462 CHECK(MakeURL(
463 mSessionURL.c_str(),
464 mBaseURL.c_str(),
465 &tmp));
466
467 mBaseURL = tmp;
468 }
469
Andreas Huberf0c86a82011-11-10 12:40:30 -0800470 if (mSessionDesc->countTracks() < 2) {
471 // There's no actual tracks in this session.
472 // The first "track" is merely session meta
473 // data.
474
Steve Block5ff1dd52012-01-05 23:22:43 +0000475 ALOGW("Session doesn't contain any playable "
Andreas Huberf0c86a82011-11-10 12:40:30 -0800476 "tracks. Aborting.");
477 result = ERROR_UNSUPPORTED;
478 } else {
479 setupTrack(1);
480 }
Andreas Huber6f85dba2010-09-15 11:18:13 -0700481 }
Andreas Hubere7d3e902010-08-31 10:43:47 -0700482 }
483 }
484
485 if (result != OK) {
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700486 sp<AMessage> reply = new AMessage('disc', id());
487 mConn->disconnect(reply);
488 }
489 break;
490 }
491
492 case 'setu':
493 {
494 size_t index;
495 CHECK(msg->findSize("index", &index));
496
Andreas Huber39ddf8e2010-08-04 10:14:30 -0700497 TrackInfo *track = NULL;
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700498 size_t trackIndex;
Andreas Huber39ddf8e2010-08-04 10:14:30 -0700499 if (msg->findSize("track-index", &trackIndex)) {
500 track = &mTracks.editItemAt(trackIndex);
501 }
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700502
503 int32_t result;
504 CHECK(msg->findInt32("result", &result));
505
Steve Blockdf64d152012-01-04 20:05:49 +0000506 ALOGI("SETUP(%d) completed with result %d (%s)",
Andreas Huber6e4c5c42010-09-21 13:13:15 -0700507 index, result, strerror(-result));
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700508
Andreas Hubere7d3e902010-08-31 10:43:47 -0700509 if (result == OK) {
510 CHECK(track != NULL);
511
512 sp<RefBase> obj;
513 CHECK(msg->findObject("response", &obj));
514 sp<ARTSPResponse> response =
515 static_cast<ARTSPResponse *>(obj.get());
516
517 if (response->mStatusCode != 200) {
518 result = UNKNOWN_ERROR;
519 } else {
520 ssize_t i = response->mHeaders.indexOfKey("session");
521 CHECK_GE(i, 0);
522
523 mSessionID = response->mHeaders.valueAt(i);
Andreas Huber908dbde2011-11-08 15:31:23 -0800524
525 mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
526 AString timeoutStr;
527 if (GetAttribute(
528 mSessionID.c_str(), "timeout", &timeoutStr)) {
529 char *end;
530 unsigned long timeoutSecs =
531 strtoul(timeoutStr.c_str(), &end, 10);
532
533 if (end == timeoutStr.c_str() || *end != '\0') {
Steve Block5ff1dd52012-01-05 23:22:43 +0000534 ALOGW("server specified malformed timeout '%s'",
Andreas Huber908dbde2011-11-08 15:31:23 -0800535 timeoutStr.c_str());
536
537 mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
538 } else if (timeoutSecs < 15) {
Steve Block5ff1dd52012-01-05 23:22:43 +0000539 ALOGW("server specified too short a timeout "
Andreas Huber908dbde2011-11-08 15:31:23 -0800540 "(%lu secs), using default.",
541 timeoutSecs);
542
543 mKeepAliveTimeoutUs = kDefaultKeepAliveTimeoutUs;
544 } else {
545 mKeepAliveTimeoutUs = timeoutSecs * 1000000ll;
546
Steve Blockdf64d152012-01-04 20:05:49 +0000547 ALOGI("server specified timeout of %lu secs.",
Andreas Huber908dbde2011-11-08 15:31:23 -0800548 timeoutSecs);
549 }
550 }
551
Andreas Hubere7d3e902010-08-31 10:43:47 -0700552 i = mSessionID.find(";");
553 if (i >= 0) {
554 // Remove options, i.e. ";timeout=90"
555 mSessionID.erase(i, mSessionID.size() - i);
556 }
557
558 sp<AMessage> notify = new AMessage('accu', id());
559 notify->setSize("track-index", trackIndex);
560
Andreas Huber2bc940b2010-10-11 11:05:52 -0700561 i = response->mHeaders.indexOfKey("transport");
562 CHECK_GE(i, 0);
563
564 if (!track->mUsingInterleavedTCP) {
565 AString transport = response->mHeaders.valueAt(i);
566
Andreas Huberde9a20c2011-02-15 13:32:42 -0800567 // We are going to continue even if we were
568 // unable to poke a hole into the firewall...
569 pokeAHole(
570 track->mRTPSocket,
571 track->mRTCPSocket,
572 transport);
Andreas Huber2bc940b2010-10-11 11:05:52 -0700573 }
574
Andreas Huberde9a20c2011-02-15 13:32:42 -0800575 mRTPConn->addStream(
576 track->mRTPSocket, track->mRTCPSocket,
577 mSessionDesc, index,
578 notify, track->mUsingInterleavedTCP);
Andreas Hubere7d3e902010-08-31 10:43:47 -0700579
Andreas Huberde9a20c2011-02-15 13:32:42 -0800580 mSetupTracksSuccessful = true;
Andreas Hubere7d3e902010-08-31 10:43:47 -0700581 }
582 }
583
Andreas Huber39ddf8e2010-08-04 10:14:30 -0700584 if (result != OK) {
585 if (track) {
Andreas Huber0792ce72010-08-26 11:17:32 -0700586 if (!track->mUsingInterleavedTCP) {
Ashish Sharmaa23456b2011-07-07 17:57:05 -0700587 // Clear the tag
588 if (mUIDValid) {
589 HTTPBase::UnRegisterSocketUserTag(track->mRTPSocket);
590 HTTPBase::UnRegisterSocketUserTag(track->mRTCPSocket);
591 }
592
Andreas Huber0792ce72010-08-26 11:17:32 -0700593 close(track->mRTPSocket);
594 close(track->mRTCPSocket);
595 }
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700596
Andreas Huber39ddf8e2010-08-04 10:14:30 -0700597 mTracks.removeItemsAt(trackIndex);
598 }
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700599 }
600
Andreas Huber39ddf8e2010-08-04 10:14:30 -0700601 ++index;
602 if (index < mSessionDesc->countTracks()) {
603 setupTrack(index);
604 } else if (mSetupTracksSuccessful) {
Andreas Huber908dbde2011-11-08 15:31:23 -0800605 ++mKeepAliveGeneration;
606 postKeepAlive();
607
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700608 AString request = "PLAY ";
609 request.append(mSessionURL);
610 request.append(" RTSP/1.0\r\n");
611
612 request.append("Session: ");
613 request.append(mSessionID);
614 request.append("\r\n");
615
616 request.append("\r\n");
617
618 sp<AMessage> reply = new AMessage('play', id());
619 mConn->sendRequest(request.c_str(), reply);
620 } else {
621 sp<AMessage> reply = new AMessage('disc', id());
622 mConn->disconnect(reply);
623 }
624 break;
625 }
626
627 case 'play':
628 {
629 int32_t result;
630 CHECK(msg->findInt32("result", &result));
631
Steve Blockdf64d152012-01-04 20:05:49 +0000632 ALOGI("PLAY completed with result %d (%s)",
Andreas Huber6e4c5c42010-09-21 13:13:15 -0700633 result, strerror(-result));
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700634
635 if (result == OK) {
636 sp<RefBase> obj;
637 CHECK(msg->findObject("response", &obj));
638 sp<ARTSPResponse> response =
639 static_cast<ARTSPResponse *>(obj.get());
640
Andreas Huber6f85dba2010-09-15 11:18:13 -0700641 if (response->mStatusCode != 200) {
642 result = UNKNOWN_ERROR;
643 } else {
644 parsePlayResponse(response);
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700645
Andreas Huber6f85dba2010-09-15 11:18:13 -0700646 sp<AMessage> timeout = new AMessage('tiou', id());
647 timeout->post(kStartupTimeoutUs);
648 }
649 }
Andreas Huber8d342972010-08-27 13:29:08 -0700650
Andreas Huber6f85dba2010-09-15 11:18:13 -0700651 if (result != OK) {
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700652 sp<AMessage> reply = new AMessage('disc', id());
653 mConn->disconnect(reply);
654 }
655
656 break;
657 }
658
Andreas Huber908dbde2011-11-08 15:31:23 -0800659 case 'aliv':
660 {
661 int32_t generation;
662 CHECK(msg->findInt32("generation", &generation));
663
664 if (generation != mKeepAliveGeneration) {
665 // obsolete event.
666 break;
667 }
668
669 AString request;
670 request.append("OPTIONS ");
671 request.append(mSessionURL);
672 request.append(" RTSP/1.0\r\n");
673 request.append("Session: ");
674 request.append(mSessionID);
675 request.append("\r\n");
676 request.append("\r\n");
677
678 sp<AMessage> reply = new AMessage('opts', id());
679 reply->setInt32("generation", mKeepAliveGeneration);
680 mConn->sendRequest(request.c_str(), reply);
681 break;
682 }
683
684 case 'opts':
685 {
686 int32_t result;
687 CHECK(msg->findInt32("result", &result));
688
Steve Blockdf64d152012-01-04 20:05:49 +0000689 ALOGI("OPTIONS completed with result %d (%s)",
Andreas Huber908dbde2011-11-08 15:31:23 -0800690 result, strerror(-result));
691
692 int32_t generation;
693 CHECK(msg->findInt32("generation", &generation));
694
695 if (generation != mKeepAliveGeneration) {
696 // obsolete event.
697 break;
698 }
699
700 postKeepAlive();
701 break;
702 }
703
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700704 case 'abor':
705 {
706 for (size_t i = 0; i < mTracks.size(); ++i) {
Andreas Huber7aef0332010-08-30 15:25:35 -0700707 TrackInfo *info = &mTracks.editItemAt(i);
708
Andreas Huber2bfdd422011-10-11 15:24:07 -0700709 if (!mFirstAccessUnit) {
710 postQueueEOS(i, ERROR_END_OF_STREAM);
711 }
Andreas Huber7aef0332010-08-30 15:25:35 -0700712
713 if (!info->mUsingInterleavedTCP) {
714 mRTPConn->removeStream(info->mRTPSocket, info->mRTCPSocket);
715
Ashish Sharmaa23456b2011-07-07 17:57:05 -0700716 // Clear the tag
717 if (mUIDValid) {
718 HTTPBase::UnRegisterSocketUserTag(info->mRTPSocket);
719 HTTPBase::UnRegisterSocketUserTag(info->mRTCPSocket);
720 }
721
Andreas Huber7aef0332010-08-30 15:25:35 -0700722 close(info->mRTPSocket);
723 close(info->mRTCPSocket);
724 }
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700725 }
Andreas Huber7aef0332010-08-30 15:25:35 -0700726 mTracks.clear();
Andreas Hubere7d3e902010-08-31 10:43:47 -0700727 mSetupTracksSuccessful = false;
728 mSeekPending = false;
729 mFirstAccessUnit = true;
Andreas Huber7e73e442012-01-20 13:30:03 -0800730 mAllTracksHaveTime = false;
Andreas Huber100a4402011-02-08 10:18:41 -0800731 mNTPAnchorUs = -1;
732 mMediaAnchorUs = -1;
Andreas Hubere7d3e902010-08-31 10:43:47 -0700733 mNumAccessUnitsReceived = 0;
734 mReceivedFirstRTCPPacket = false;
Andreas Huberf61551f2010-10-13 12:15:03 -0700735 mReceivedFirstRTPPacket = false;
Andreas Huber0dcd8372010-10-08 15:21:08 -0700736 mSeekable = false;
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700737
738 sp<AMessage> reply = new AMessage('tear', id());
739
Andreas Huber7aef0332010-08-30 15:25:35 -0700740 int32_t reconnect;
741 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
742 reply->setInt32("reconnect", true);
743 }
744
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700745 AString request;
746 request = "TEARDOWN ";
747
748 // XXX should use aggregate url from SDP here...
749 request.append(mSessionURL);
750 request.append(" RTSP/1.0\r\n");
751
752 request.append("Session: ");
753 request.append(mSessionID);
754 request.append("\r\n");
755
756 request.append("\r\n");
757
758 mConn->sendRequest(request.c_str(), reply);
759 break;
760 }
761
762 case 'tear':
763 {
764 int32_t result;
765 CHECK(msg->findInt32("result", &result));
766
Steve Blockdf64d152012-01-04 20:05:49 +0000767 ALOGI("TEARDOWN completed with result %d (%s)",
Andreas Huber6e4c5c42010-09-21 13:13:15 -0700768 result, strerror(-result));
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700769
770 sp<AMessage> reply = new AMessage('disc', id());
Andreas Huber7aef0332010-08-30 15:25:35 -0700771
772 int32_t reconnect;
773 if (msg->findInt32("reconnect", &reconnect) && reconnect) {
774 reply->setInt32("reconnect", true);
775 }
776
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700777 mConn->disconnect(reply);
778 break;
779 }
780
781 case 'quit':
782 {
Andreas Huber2bfdd422011-10-11 15:24:07 -0700783 sp<AMessage> msg = mNotify->dup();
784 msg->setInt32("what", kWhatDisconnected);
785 msg->setInt32("result", UNKNOWN_ERROR);
786 msg->post();
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700787 break;
788 }
789
Andreas Huber8d342972010-08-27 13:29:08 -0700790 case 'chek':
791 {
Andreas Hubera9d9dd22010-10-08 09:04:25 -0700792 int32_t generation;
793 CHECK(msg->findInt32("generation", &generation));
794 if (generation != mCheckGeneration) {
795 // This is an outdated message. Ignore.
796 break;
797 }
798
Andreas Huber8d342972010-08-27 13:29:08 -0700799 if (mNumAccessUnitsReceived == 0) {
Andreas Huber91f23042011-11-11 10:17:00 -0800800#if 1
Steve Blockdf64d152012-01-04 20:05:49 +0000801 ALOGI("stream ended? aborting.");
Andreas Huber8d342972010-08-27 13:29:08 -0700802 (new AMessage('abor', id()))->post();
803 break;
Andreas Huberf0c86a82011-11-10 12:40:30 -0800804#else
Steve Blockdf64d152012-01-04 20:05:49 +0000805 ALOGI("haven't seen an AU in a looong time.");
Andreas Huberf0c86a82011-11-10 12:40:30 -0800806#endif
Andreas Huber8d342972010-08-27 13:29:08 -0700807 }
808
809 mNumAccessUnitsReceived = 0;
Andreas Hubere56121b2010-08-30 09:53:52 -0700810 msg->post(kAccessUnitTimeoutUs);
Andreas Huber8d342972010-08-27 13:29:08 -0700811 break;
812 }
813
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700814 case 'accu':
815 {
Andreas Huber100a4402011-02-08 10:18:41 -0800816 int32_t timeUpdate;
817 if (msg->findInt32("time-update", &timeUpdate) && timeUpdate) {
818 size_t trackIndex;
819 CHECK(msg->findSize("track-index", &trackIndex));
820
821 uint32_t rtpTime;
822 uint64_t ntpTime;
823 CHECK(msg->findInt32("rtp-time", (int32_t *)&rtpTime));
824 CHECK(msg->findInt64("ntp-time", (int64_t *)&ntpTime));
825
826 onTimeUpdate(trackIndex, rtpTime, ntpTime);
827 break;
828 }
829
Andreas Huberf61551f2010-10-13 12:15:03 -0700830 int32_t first;
831 if (msg->findInt32("first-rtcp", &first)) {
Andreas Hubere7d3e902010-08-31 10:43:47 -0700832 mReceivedFirstRTCPPacket = true;
833 break;
834 }
835
Andreas Huberf61551f2010-10-13 12:15:03 -0700836 if (msg->findInt32("first-rtp", &first)) {
837 mReceivedFirstRTPPacket = true;
838 break;
839 }
840
Andreas Huber8d342972010-08-27 13:29:08 -0700841 ++mNumAccessUnitsReceived;
Andreas Hubera9d9dd22010-10-08 09:04:25 -0700842 postAccessUnitTimeoutCheck();
Andreas Huber8d342972010-08-27 13:29:08 -0700843
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700844 size_t trackIndex;
845 CHECK(msg->findSize("track-index", &trackIndex));
846
Andreas Huber7aef0332010-08-30 15:25:35 -0700847 if (trackIndex >= mTracks.size()) {
Steve Block3856b092011-10-20 11:56:00 +0100848 ALOGV("late packets ignored.");
Andreas Huber7aef0332010-08-30 15:25:35 -0700849 break;
850 }
851
Andreas Huber8d342972010-08-27 13:29:08 -0700852 TrackInfo *track = &mTracks.editItemAt(trackIndex);
853
Andreas Huberef7af7f2010-08-18 10:17:18 -0700854 int32_t eos;
855 if (msg->findInt32("eos", &eos)) {
Steve Blockdf64d152012-01-04 20:05:49 +0000856 ALOGI("received BYE on track index %d", trackIndex);
Andreas Huberef7af7f2010-08-18 10:17:18 -0700857#if 0
Andreas Huberef7af7f2010-08-18 10:17:18 -0700858 track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
859#endif
860 return;
861 }
862
Andreas Huber2d8bedd2012-02-21 14:38:23 -0800863 sp<ABuffer> accessUnit;
864 CHECK(msg->findBuffer("access-unit", &accessUnit));
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700865
Andreas Huber8d342972010-08-27 13:29:08 -0700866 uint32_t seqNum = (uint32_t)accessUnit->int32Data();
867
Andreas Huber6f85dba2010-09-15 11:18:13 -0700868 if (mSeekPending) {
Steve Block3856b092011-10-20 11:56:00 +0100869 ALOGV("we're seeking, dropping stale packet.");
Andreas Huber6f85dba2010-09-15 11:18:13 -0700870 break;
871 }
872
Andreas Huber8d342972010-08-27 13:29:08 -0700873 if (seqNum < track->mFirstSeqNumInSegment) {
Steve Block3856b092011-10-20 11:56:00 +0100874 ALOGV("dropping stale access-unit (%d < %d)",
Andreas Huber6e4c5c42010-09-21 13:13:15 -0700875 seqNum, track->mFirstSeqNumInSegment);
Andreas Huber8d342972010-08-27 13:29:08 -0700876 break;
877 }
878
Andreas Huber8d342972010-08-27 13:29:08 -0700879 if (track->mNewSegment) {
880 track->mNewSegment = false;
Andreas Huber8d342972010-08-27 13:29:08 -0700881 }
882
Andreas Huber100a4402011-02-08 10:18:41 -0800883 onAccessUnitComplete(trackIndex, accessUnit);
Andreas Hubercf7b9c72010-06-07 15:19:40 -0700884 break;
885 }
886
Andreas Hubercce326f2010-08-24 14:33:58 -0700887 case 'seek':
888 {
Andreas Huber0dcd8372010-10-08 15:21:08 -0700889 if (!mSeekable) {
Steve Block5ff1dd52012-01-05 23:22:43 +0000890 ALOGW("This is a live stream, ignoring seek request.");
Andreas Huber2bfdd422011-10-11 15:24:07 -0700891
892 sp<AMessage> msg = mNotify->dup();
893 msg->setInt32("what", kWhatSeekDone);
894 msg->post();
Andreas Hubercce326f2010-08-24 14:33:58 -0700895 break;
896 }
897
898 int64_t timeUs;
899 CHECK(msg->findInt64("time", &timeUs));
900
901 mSeekPending = true;
902
Andreas Hubera9d9dd22010-10-08 09:04:25 -0700903 // Disable the access unit timeout until we resumed
904 // playback again.
905 mCheckPending = true;
906 ++mCheckGeneration;
907
Andreas Hubercce326f2010-08-24 14:33:58 -0700908 AString request = "PAUSE ";
909 request.append(mSessionURL);
910 request.append(" RTSP/1.0\r\n");
911
912 request.append("Session: ");
913 request.append(mSessionID);
914 request.append("\r\n");
915
916 request.append("\r\n");
917
918 sp<AMessage> reply = new AMessage('see1', id());
919 reply->setInt64("time", timeUs);
920 mConn->sendRequest(request.c_str(), reply);
921 break;
922 }
923
924 case 'see1':
925 {
Andreas Huber8d342972010-08-27 13:29:08 -0700926 // Session is paused now.
927 for (size_t i = 0; i < mTracks.size(); ++i) {
Andreas Huber100a4402011-02-08 10:18:41 -0800928 TrackInfo *info = &mTracks.editItemAt(i);
929
Andreas Huber2bfdd422011-10-11 15:24:07 -0700930 postQueueSeekDiscontinuity(i);
931
Andreas Huber100a4402011-02-08 10:18:41 -0800932 info->mRTPAnchor = 0;
933 info->mNTPAnchorUs = -1;
Andreas Huber8d342972010-08-27 13:29:08 -0700934 }
935
Andreas Huber7e73e442012-01-20 13:30:03 -0800936 mAllTracksHaveTime = false;
Andreas Huber100a4402011-02-08 10:18:41 -0800937 mNTPAnchorUs = -1;
938
Andreas Hubercce326f2010-08-24 14:33:58 -0700939 int64_t timeUs;
940 CHECK(msg->findInt64("time", &timeUs));
941
942 AString request = "PLAY ";
943 request.append(mSessionURL);
944 request.append(" RTSP/1.0\r\n");
945
946 request.append("Session: ");
947 request.append(mSessionID);
948 request.append("\r\n");
949
950 request.append(
951 StringPrintf(
952 "Range: npt=%lld-\r\n", timeUs / 1000000ll));
953
954 request.append("\r\n");
955
956 sp<AMessage> reply = new AMessage('see2', id());
957 mConn->sendRequest(request.c_str(), reply);
958 break;
959 }
960
961 case 'see2':
962 {
963 CHECK(mSeekPending);
964
Andreas Hubercce326f2010-08-24 14:33:58 -0700965 int32_t result;
966 CHECK(msg->findInt32("result", &result));
Andreas Huber8d342972010-08-27 13:29:08 -0700967
Steve Blockdf64d152012-01-04 20:05:49 +0000968 ALOGI("PLAY completed with result %d (%s)",
Andreas Huber6e4c5c42010-09-21 13:13:15 -0700969 result, strerror(-result));
Andreas Huber8d342972010-08-27 13:29:08 -0700970
Andreas Hubera9d9dd22010-10-08 09:04:25 -0700971 mCheckPending = false;
972 postAccessUnitTimeoutCheck();
973
Andreas Huber6f85dba2010-09-15 11:18:13 -0700974 if (result == OK) {
975 sp<RefBase> obj;
976 CHECK(msg->findObject("response", &obj));
977 sp<ARTSPResponse> response =
978 static_cast<ARTSPResponse *>(obj.get());
Andreas Hubercce326f2010-08-24 14:33:58 -0700979
Andreas Huber6f85dba2010-09-15 11:18:13 -0700980 if (response->mStatusCode != 200) {
981 result = UNKNOWN_ERROR;
982 } else {
983 parsePlayResponse(response);
Andreas Hubercce326f2010-08-24 14:33:58 -0700984
Andreas Huber100a4402011-02-08 10:18:41 -0800985 ssize_t i = response->mHeaders.indexOfKey("rtp-info");
986 CHECK_GE(i, 0);
987
Steve Block3856b092011-10-20 11:56:00 +0100988 ALOGV("rtp-info: %s", response->mHeaders.valueAt(i).c_str());
Andreas Huber100a4402011-02-08 10:18:41 -0800989
Steve Blockdf64d152012-01-04 20:05:49 +0000990 ALOGI("seek completed.");
Andreas Huber6f85dba2010-09-15 11:18:13 -0700991 }
992 }
Andreas Hubercce326f2010-08-24 14:33:58 -0700993
Andreas Huber6f85dba2010-09-15 11:18:13 -0700994 if (result != OK) {
Steve Block29357bc2012-01-06 19:20:56 +0000995 ALOGE("seek failed, aborting.");
Andreas Huber6f85dba2010-09-15 11:18:13 -0700996 (new AMessage('abor', id()))->post();
997 }
Andreas Huber8d342972010-08-27 13:29:08 -0700998
Andreas Huber8d342972010-08-27 13:29:08 -0700999 mSeekPending = false;
Andreas Huber0dcd8372010-10-08 15:21:08 -07001000
Andreas Huber2bfdd422011-10-11 15:24:07 -07001001 sp<AMessage> msg = mNotify->dup();
1002 msg->setInt32("what", kWhatSeekDone);
1003 msg->post();
Andreas Hubercce326f2010-08-24 14:33:58 -07001004 break;
1005 }
1006
Andreas Huber0792ce72010-08-26 11:17:32 -07001007 case 'biny':
1008 {
Andreas Huber2d8bedd2012-02-21 14:38:23 -08001009 sp<ABuffer> buffer;
1010 CHECK(msg->findBuffer("buffer", &buffer));
Andreas Huber0792ce72010-08-26 11:17:32 -07001011
1012 int32_t index;
1013 CHECK(buffer->meta()->findInt32("index", &index));
1014
1015 mRTPConn->injectPacket(index, buffer);
1016 break;
1017 }
1018
1019 case 'tiou':
1020 {
Andreas Hubere7d3e902010-08-31 10:43:47 -07001021 if (!mReceivedFirstRTCPPacket) {
Andreas Huberdc468c52011-02-15 10:39:48 -08001022 if (mReceivedFirstRTPPacket && !mTryFakeRTCP) {
Steve Block5ff1dd52012-01-05 23:22:43 +00001023 ALOGW("We received RTP packets but no RTCP packets, "
Andreas Huberf61551f2010-10-13 12:15:03 -07001024 "using fake timestamps.");
1025
1026 mTryFakeRTCP = true;
1027
1028 mReceivedFirstRTCPPacket = true;
Andreas Huberdc468c52011-02-15 10:39:48 -08001029
1030 fakeTimestamps();
1031 } else if (!mReceivedFirstRTPPacket && !mTryTCPInterleaving) {
Steve Block5ff1dd52012-01-05 23:22:43 +00001032 ALOGW("Never received any data, switching transports.");
Andreas Huber7aef0332010-08-30 15:25:35 -07001033
1034 mTryTCPInterleaving = true;
1035
1036 sp<AMessage> msg = new AMessage('abor', id());
1037 msg->setInt32("reconnect", true);
1038 msg->post();
Andreas Huberdc468c52011-02-15 10:39:48 -08001039 } else {
Steve Block5ff1dd52012-01-05 23:22:43 +00001040 ALOGW("Never received any data, disconnecting.");
Andreas Huberdc468c52011-02-15 10:39:48 -08001041 (new AMessage('abor', id()))->post();
Andreas Huber7aef0332010-08-30 15:25:35 -07001042 }
Andreas Huber7e73e442012-01-20 13:30:03 -08001043 } else {
1044 if (!mAllTracksHaveTime) {
1045 ALOGW("We received some RTCP packets, but time "
1046 "could not be established on all tracks, now "
1047 "using fake timestamps");
1048
1049 fakeTimestamps();
1050 }
Andreas Huber0792ce72010-08-26 11:17:32 -07001051 }
Andreas Huber0792ce72010-08-26 11:17:32 -07001052 break;
1053 }
1054
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001055 default:
1056 TRESPASS();
1057 break;
1058 }
1059 }
1060
Andreas Huber908dbde2011-11-08 15:31:23 -08001061 void postKeepAlive() {
1062 sp<AMessage> msg = new AMessage('aliv', id());
1063 msg->setInt32("generation", mKeepAliveGeneration);
1064 msg->post((mKeepAliveTimeoutUs * 9) / 10);
1065 }
1066
Andreas Hubera9d9dd22010-10-08 09:04:25 -07001067 void postAccessUnitTimeoutCheck() {
1068 if (mCheckPending) {
1069 return;
1070 }
1071
1072 mCheckPending = true;
1073 sp<AMessage> check = new AMessage('chek', id());
1074 check->setInt32("generation", mCheckGeneration);
1075 check->post(kAccessUnitTimeoutUs);
1076 }
1077
Andreas Huber8d342972010-08-27 13:29:08 -07001078 static void SplitString(
1079 const AString &s, const char *separator, List<AString> *items) {
1080 items->clear();
1081 size_t start = 0;
1082 while (start < s.size()) {
1083 ssize_t offset = s.find(separator, start);
1084
1085 if (offset < 0) {
1086 items->push_back(AString(s, start, s.size() - start));
1087 break;
1088 }
1089
1090 items->push_back(AString(s, start, offset - start));
1091 start = offset + strlen(separator);
1092 }
1093 }
1094
1095 void parsePlayResponse(const sp<ARTSPResponse> &response) {
Andreas Huber0dcd8372010-10-08 15:21:08 -07001096 mSeekable = false;
Patric Frederiksen738198a2011-09-26 10:51:35 +02001097 if (mTracks.size() == 0) {
1098 ALOGV("parsePlayResponse: late packets ignored.");
1099 return;
1100 }
Andreas Huber0dcd8372010-10-08 15:21:08 -07001101
Andreas Huber8d342972010-08-27 13:29:08 -07001102 ssize_t i = response->mHeaders.indexOfKey("range");
1103 if (i < 0) {
1104 // Server doesn't even tell use what range it is going to
1105 // play, therefore we won't support seeking.
1106 return;
1107 }
1108
1109 AString range = response->mHeaders.valueAt(i);
Steve Block3856b092011-10-20 11:56:00 +01001110 ALOGV("Range: %s", range.c_str());
Andreas Huber8d342972010-08-27 13:29:08 -07001111
1112 AString val;
1113 CHECK(GetAttribute(range.c_str(), "npt", &val));
Andreas Huber8d342972010-08-27 13:29:08 -07001114
Andreas Huber783e5cd2011-01-28 09:19:12 -08001115 float npt1, npt2;
1116 if (!ASessionDescription::parseNTPRange(val.c_str(), &npt1, &npt2)) {
Andreas Huber8d342972010-08-27 13:29:08 -07001117 // This is a live stream and therefore not seekable.
Andreas Huber1906e5c2011-12-08 12:27:47 -08001118
Steve Blockdf64d152012-01-04 20:05:49 +00001119 ALOGI("This is a live stream");
Andreas Huberac5767a2011-06-30 10:59:19 -07001120 return;
Andreas Huber8d342972010-08-27 13:29:08 -07001121 }
1122
1123 i = response->mHeaders.indexOfKey("rtp-info");
1124 CHECK_GE(i, 0);
1125
1126 AString rtpInfo = response->mHeaders.valueAt(i);
1127 List<AString> streamInfos;
1128 SplitString(rtpInfo, ",", &streamInfos);
1129
1130 int n = 1;
1131 for (List<AString>::iterator it = streamInfos.begin();
1132 it != streamInfos.end(); ++it) {
1133 (*it).trim();
Steve Block3856b092011-10-20 11:56:00 +01001134 ALOGV("streamInfo[%d] = %s", n, (*it).c_str());
Andreas Huber8d342972010-08-27 13:29:08 -07001135
1136 CHECK(GetAttribute((*it).c_str(), "url", &val));
1137
1138 size_t trackIndex = 0;
1139 while (trackIndex < mTracks.size()
1140 && !(val == mTracks.editItemAt(trackIndex).mURL)) {
1141 ++trackIndex;
1142 }
1143 CHECK_LT(trackIndex, mTracks.size());
1144
1145 CHECK(GetAttribute((*it).c_str(), "seq", &val));
1146
1147 char *end;
1148 unsigned long seq = strtoul(val.c_str(), &end, 10);
1149
1150 TrackInfo *info = &mTracks.editItemAt(trackIndex);
1151 info->mFirstSeqNumInSegment = seq;
1152 info->mNewSegment = true;
1153
1154 CHECK(GetAttribute((*it).c_str(), "rtptime", &val));
1155
1156 uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
1157
Steve Block3856b092011-10-20 11:56:00 +01001158 ALOGV("track #%d: rtpTime=%u <=> npt=%.2f", n, rtpTime, npt1);
Andreas Huber8d342972010-08-27 13:29:08 -07001159
Andreas Huber2bfdd422011-10-11 15:24:07 -07001160 info->mNormalPlayTimeRTP = rtpTime;
1161 info->mNormalPlayTimeUs = (int64_t)(npt1 * 1E6);
1162
1163 if (!mFirstAccessUnit) {
1164 postNormalPlayTimeMapping(
1165 trackIndex,
1166 info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs);
1167 }
Andreas Huber8d342972010-08-27 13:29:08 -07001168
1169 ++n;
1170 }
Andreas Huber0dcd8372010-10-08 15:21:08 -07001171
Andreas Huberac5767a2011-06-30 10:59:19 -07001172 mSeekable = true;
Andreas Huber8d342972010-08-27 13:29:08 -07001173 }
1174
Andreas Huber2bfdd422011-10-11 15:24:07 -07001175 sp<MetaData> getTrackFormat(size_t index, int32_t *timeScale) {
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001176 CHECK_GE(index, 0u);
1177 CHECK_LT(index, mTracks.size());
1178
Andreas Huber2bfdd422011-10-11 15:24:07 -07001179 const TrackInfo &info = mTracks.itemAt(index);
1180
1181 *timeScale = info.mTimeScale;
1182
1183 return info.mPacketSource->getFormat();
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001184 }
1185
1186 size_t countTracks() const {
1187 return mTracks.size();
1188 }
1189
1190private:
Andreas Huber100a4402011-02-08 10:18:41 -08001191 struct TrackInfo {
1192 AString mURL;
1193 int mRTPSocket;
1194 int mRTCPSocket;
1195 bool mUsingInterleavedTCP;
1196 uint32_t mFirstSeqNumInSegment;
1197 bool mNewSegment;
1198
1199 uint32_t mRTPAnchor;
1200 int64_t mNTPAnchorUs;
1201 int32_t mTimeScale;
1202
Andreas Huber2bfdd422011-10-11 15:24:07 -07001203 uint32_t mNormalPlayTimeRTP;
1204 int64_t mNormalPlayTimeUs;
1205
Andreas Huber100a4402011-02-08 10:18:41 -08001206 sp<APacketSource> mPacketSource;
1207
1208 // Stores packets temporarily while no notion of time
1209 // has been established yet.
1210 List<sp<ABuffer> > mPackets;
1211 };
1212
Andreas Huber2bfdd422011-10-11 15:24:07 -07001213 sp<AMessage> mNotify;
Andreas Huber9b80c2b2011-06-30 15:47:02 -07001214 bool mUIDValid;
1215 uid_t mUID;
Andreas Huber348a8ea2010-07-22 09:20:13 -07001216 sp<ALooper> mNetLooper;
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001217 sp<ARTSPConnection> mConn;
1218 sp<ARTPConnection> mRTPConn;
1219 sp<ASessionDescription> mSessionDesc;
Andreas Huber4579b7d2010-10-20 15:00:34 -07001220 AString mOriginalSessionURL; // This one still has user:pass@
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001221 AString mSessionURL;
Andreas Huberde9a20c2011-02-15 13:32:42 -08001222 AString mSessionHost;
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001223 AString mBaseURL;
1224 AString mSessionID;
1225 bool mSetupTracksSuccessful;
Andreas Hubercce326f2010-08-24 14:33:58 -07001226 bool mSeekPending;
1227 bool mFirstAccessUnit;
Andreas Huber100a4402011-02-08 10:18:41 -08001228
Andreas Huber7e73e442012-01-20 13:30:03 -08001229 bool mAllTracksHaveTime;
Andreas Huber100a4402011-02-08 10:18:41 -08001230 int64_t mNTPAnchorUs;
1231 int64_t mMediaAnchorUs;
1232 int64_t mLastMediaTimeUs;
1233
Andreas Huber8d342972010-08-27 13:29:08 -07001234 int64_t mNumAccessUnitsReceived;
1235 bool mCheckPending;
Andreas Hubera9d9dd22010-10-08 09:04:25 -07001236 int32_t mCheckGeneration;
Andreas Huber7aef0332010-08-30 15:25:35 -07001237 bool mTryTCPInterleaving;
Andreas Huberf61551f2010-10-13 12:15:03 -07001238 bool mTryFakeRTCP;
Andreas Hubere7d3e902010-08-31 10:43:47 -07001239 bool mReceivedFirstRTCPPacket;
Andreas Huberf61551f2010-10-13 12:15:03 -07001240 bool mReceivedFirstRTPPacket;
Andreas Huber0dcd8372010-10-08 15:21:08 -07001241 bool mSeekable;
Andreas Huber908dbde2011-11-08 15:31:23 -08001242 int64_t mKeepAliveTimeoutUs;
1243 int32_t mKeepAliveGeneration;
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001244
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001245 Vector<TrackInfo> mTracks;
1246
1247 void setupTrack(size_t index) {
Andreas Huber39ddf8e2010-08-04 10:14:30 -07001248 sp<APacketSource> source =
1249 new APacketSource(mSessionDesc, index);
Andreas Huber7aef0332010-08-30 15:25:35 -07001250
Andreas Huber39ddf8e2010-08-04 10:14:30 -07001251 if (source->initCheck() != OK) {
Steve Block5ff1dd52012-01-05 23:22:43 +00001252 ALOGW("Unsupported format. Ignoring track #%d.", index);
Andreas Huber39ddf8e2010-08-04 10:14:30 -07001253
1254 sp<AMessage> reply = new AMessage('setu', id());
1255 reply->setSize("index", index);
1256 reply->setInt32("result", ERROR_UNSUPPORTED);
1257 reply->post();
1258 return;
1259 }
1260
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001261 AString url;
1262 CHECK(mSessionDesc->findAttribute(index, "a=control", &url));
1263
1264 AString trackURL;
1265 CHECK(MakeURL(mBaseURL.c_str(), url.c_str(), &trackURL));
1266
1267 mTracks.push(TrackInfo());
1268 TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
Andreas Huber8d342972010-08-27 13:29:08 -07001269 info->mURL = trackURL;
Andreas Huber39ddf8e2010-08-04 10:14:30 -07001270 info->mPacketSource = source;
Andreas Huber0792ce72010-08-26 11:17:32 -07001271 info->mUsingInterleavedTCP = false;
Andreas Huber8d342972010-08-27 13:29:08 -07001272 info->mFirstSeqNumInSegment = 0;
1273 info->mNewSegment = true;
Andreas Huber100a4402011-02-08 10:18:41 -08001274 info->mRTPAnchor = 0;
1275 info->mNTPAnchorUs = -1;
Andreas Huber2bfdd422011-10-11 15:24:07 -07001276 info->mNormalPlayTimeRTP = 0;
1277 info->mNormalPlayTimeUs = 0ll;
Andreas Huber100a4402011-02-08 10:18:41 -08001278
1279 unsigned long PT;
1280 AString formatDesc;
1281 AString formatParams;
1282 mSessionDesc->getFormatType(index, &PT, &formatDesc, &formatParams);
1283
1284 int32_t timescale;
1285 int32_t numChannels;
1286 ASessionDescription::ParseFormatDesc(
1287 formatDesc.c_str(), &timescale, &numChannels);
1288
1289 info->mTimeScale = timescale;
Andreas Huber8d342972010-08-27 13:29:08 -07001290
Steve Block3856b092011-10-20 11:56:00 +01001291 ALOGV("track #%d URL=%s", mTracks.size(), trackURL.c_str());
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001292
1293 AString request = "SETUP ";
1294 request.append(trackURL);
1295 request.append(" RTSP/1.0\r\n");
1296
Andreas Huber7aef0332010-08-30 15:25:35 -07001297 if (mTryTCPInterleaving) {
1298 size_t interleaveIndex = 2 * (mTracks.size() - 1);
1299 info->mUsingInterleavedTCP = true;
1300 info->mRTPSocket = interleaveIndex;
1301 info->mRTCPSocket = interleaveIndex + 1;
Andreas Huber0792ce72010-08-26 11:17:32 -07001302
Andreas Huber7aef0332010-08-30 15:25:35 -07001303 request.append("Transport: RTP/AVP/TCP;interleaved=");
1304 request.append(interleaveIndex);
1305 request.append("-");
1306 request.append(interleaveIndex + 1);
1307 } else {
1308 unsigned rtpPort;
1309 ARTPConnection::MakePortPair(
1310 &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
Andreas Huber0792ce72010-08-26 11:17:32 -07001311
Andreas Huber9b80c2b2011-06-30 15:47:02 -07001312 if (mUIDValid) {
Ashish Sharmaa23456b2011-07-07 17:57:05 -07001313 HTTPBase::RegisterSocketUserTag(info->mRTPSocket, mUID,
1314 (uint32_t)*(uint32_t*) "RTP_");
1315 HTTPBase::RegisterSocketUserTag(info->mRTCPSocket, mUID,
1316 (uint32_t)*(uint32_t*) "RTP_");
Andreas Huber9b80c2b2011-06-30 15:47:02 -07001317 }
1318
Andreas Huber7aef0332010-08-30 15:25:35 -07001319 request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
1320 request.append(rtpPort);
1321 request.append("-");
1322 request.append(rtpPort + 1);
1323 }
Andreas Huber0792ce72010-08-26 11:17:32 -07001324
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001325 request.append("\r\n");
1326
1327 if (index > 1) {
1328 request.append("Session: ");
1329 request.append(mSessionID);
1330 request.append("\r\n");
1331 }
1332
1333 request.append("\r\n");
1334
1335 sp<AMessage> reply = new AMessage('setu', id());
1336 reply->setSize("index", index);
1337 reply->setSize("track-index", mTracks.size() - 1);
1338 mConn->sendRequest(request.c_str(), reply);
1339 }
1340
1341 static bool MakeURL(const char *baseURL, const char *url, AString *out) {
1342 out->clear();
1343
1344 if (strncasecmp("rtsp://", baseURL, 7)) {
1345 // Base URL must be absolute
1346 return false;
1347 }
1348
1349 if (!strncasecmp("rtsp://", url, 7)) {
1350 // "url" is already an absolute URL, ignore base URL.
1351 out->setTo(url);
1352 return true;
1353 }
1354
1355 size_t n = strlen(baseURL);
1356 if (baseURL[n - 1] == '/') {
1357 out->setTo(baseURL);
1358 out->append(url);
1359 } else {
Mike Lockwood4e2ffa42010-07-15 14:58:25 -04001360 const char *slashPos = strrchr(baseURL, '/');
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001361
1362 if (slashPos > &baseURL[6]) {
1363 out->setTo(baseURL, slashPos - baseURL);
1364 } else {
1365 out->setTo(baseURL);
1366 }
1367
1368 out->append("/");
1369 out->append(url);
1370 }
1371
1372 return true;
1373 }
1374
Andreas Huberdc468c52011-02-15 10:39:48 -08001375 void fakeTimestamps() {
Andreas Huber7e73e442012-01-20 13:30:03 -08001376 mNTPAnchorUs = -1ll;
Andreas Huberdc468c52011-02-15 10:39:48 -08001377 for (size_t i = 0; i < mTracks.size(); ++i) {
1378 onTimeUpdate(i, 0, 0ll);
1379 }
1380 }
1381
Andreas Huber100a4402011-02-08 10:18:41 -08001382 void onTimeUpdate(int32_t trackIndex, uint32_t rtpTime, uint64_t ntpTime) {
Steve Block3856b092011-10-20 11:56:00 +01001383 ALOGV("onTimeUpdate track %d, rtpTime = 0x%08x, ntpTime = 0x%016llx",
Andreas Huber100a4402011-02-08 10:18:41 -08001384 trackIndex, rtpTime, ntpTime);
1385
1386 int64_t ntpTimeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32));
1387
1388 TrackInfo *track = &mTracks.editItemAt(trackIndex);
1389
1390 track->mRTPAnchor = rtpTime;
1391 track->mNTPAnchorUs = ntpTimeUs;
1392
1393 if (mNTPAnchorUs < 0) {
1394 mNTPAnchorUs = ntpTimeUs;
1395 mMediaAnchorUs = mLastMediaTimeUs;
1396 }
Andreas Huber7e73e442012-01-20 13:30:03 -08001397
1398 if (!mAllTracksHaveTime) {
1399 bool allTracksHaveTime = true;
1400 for (size_t i = 0; i < mTracks.size(); ++i) {
1401 TrackInfo *track = &mTracks.editItemAt(i);
1402 if (track->mNTPAnchorUs < 0) {
1403 allTracksHaveTime = false;
1404 break;
1405 }
1406 }
1407 if (allTracksHaveTime) {
1408 mAllTracksHaveTime = true;
1409 ALOGI("Time now established for all tracks.");
1410 }
1411 }
Andreas Huber100a4402011-02-08 10:18:41 -08001412 }
1413
1414 void onAccessUnitComplete(
1415 int32_t trackIndex, const sp<ABuffer> &accessUnit) {
Steve Block3856b092011-10-20 11:56:00 +01001416 ALOGV("onAccessUnitComplete track %d", trackIndex);
Andreas Huber100a4402011-02-08 10:18:41 -08001417
1418 if (mFirstAccessUnit) {
Andreas Huber2bfdd422011-10-11 15:24:07 -07001419 sp<AMessage> msg = mNotify->dup();
1420 msg->setInt32("what", kWhatConnected);
1421 msg->post();
1422
Andreas Huber1906e5c2011-12-08 12:27:47 -08001423 if (mSeekable) {
1424 for (size_t i = 0; i < mTracks.size(); ++i) {
1425 TrackInfo *info = &mTracks.editItemAt(i);
Andreas Huber2bfdd422011-10-11 15:24:07 -07001426
Andreas Huber1906e5c2011-12-08 12:27:47 -08001427 postNormalPlayTimeMapping(
1428 i,
1429 info->mNormalPlayTimeRTP, info->mNormalPlayTimeUs);
1430 }
Andreas Huber2bfdd422011-10-11 15:24:07 -07001431 }
Andreas Huber100a4402011-02-08 10:18:41 -08001432
1433 mFirstAccessUnit = false;
1434 }
1435
1436 TrackInfo *track = &mTracks.editItemAt(trackIndex);
1437
Andreas Huber7e73e442012-01-20 13:30:03 -08001438 if (!mAllTracksHaveTime) {
Steve Block3856b092011-10-20 11:56:00 +01001439 ALOGV("storing accessUnit, no time established yet");
Andreas Huber100a4402011-02-08 10:18:41 -08001440 track->mPackets.push_back(accessUnit);
1441 return;
1442 }
1443
1444 while (!track->mPackets.empty()) {
1445 sp<ABuffer> accessUnit = *track->mPackets.begin();
1446 track->mPackets.erase(track->mPackets.begin());
1447
1448 if (addMediaTimestamp(trackIndex, track, accessUnit)) {
Andreas Huber2bfdd422011-10-11 15:24:07 -07001449 postQueueAccessUnit(trackIndex, accessUnit);
Andreas Huber100a4402011-02-08 10:18:41 -08001450 }
1451 }
1452
1453 if (addMediaTimestamp(trackIndex, track, accessUnit)) {
Andreas Huber2bfdd422011-10-11 15:24:07 -07001454 postQueueAccessUnit(trackIndex, accessUnit);
Andreas Huber100a4402011-02-08 10:18:41 -08001455 }
1456 }
1457
1458 bool addMediaTimestamp(
1459 int32_t trackIndex, const TrackInfo *track,
1460 const sp<ABuffer> &accessUnit) {
1461 uint32_t rtpTime;
1462 CHECK(accessUnit->meta()->findInt32(
1463 "rtp-time", (int32_t *)&rtpTime));
1464
1465 int64_t relRtpTimeUs =
1466 (((int64_t)rtpTime - (int64_t)track->mRTPAnchor) * 1000000ll)
1467 / track->mTimeScale;
1468
1469 int64_t ntpTimeUs = track->mNTPAnchorUs + relRtpTimeUs;
1470
1471 int64_t mediaTimeUs = mMediaAnchorUs + ntpTimeUs - mNTPAnchorUs;
1472
1473 if (mediaTimeUs > mLastMediaTimeUs) {
1474 mLastMediaTimeUs = mediaTimeUs;
1475 }
1476
1477 if (mediaTimeUs < 0) {
Steve Block3856b092011-10-20 11:56:00 +01001478 ALOGV("dropping early accessUnit.");
Andreas Huber100a4402011-02-08 10:18:41 -08001479 return false;
1480 }
1481
Steve Block3856b092011-10-20 11:56:00 +01001482 ALOGV("track %d rtpTime=%d mediaTimeUs = %lld us (%.2f secs)",
Andreas Huber100a4402011-02-08 10:18:41 -08001483 trackIndex, rtpTime, mediaTimeUs, mediaTimeUs / 1E6);
1484
1485 accessUnit->meta()->setInt64("timeUs", mediaTimeUs);
1486
1487 return true;
1488 }
1489
Andreas Huber2bfdd422011-10-11 15:24:07 -07001490 void postQueueAccessUnit(
1491 size_t trackIndex, const sp<ABuffer> &accessUnit) {
1492 sp<AMessage> msg = mNotify->dup();
1493 msg->setInt32("what", kWhatAccessUnit);
1494 msg->setSize("trackIndex", trackIndex);
Andreas Huber2d8bedd2012-02-21 14:38:23 -08001495 msg->setBuffer("accessUnit", accessUnit);
Andreas Huber2bfdd422011-10-11 15:24:07 -07001496 msg->post();
1497 }
1498
1499 void postQueueEOS(size_t trackIndex, status_t finalResult) {
1500 sp<AMessage> msg = mNotify->dup();
1501 msg->setInt32("what", kWhatEOS);
1502 msg->setSize("trackIndex", trackIndex);
1503 msg->setInt32("finalResult", finalResult);
1504 msg->post();
1505 }
1506
1507 void postQueueSeekDiscontinuity(size_t trackIndex) {
1508 sp<AMessage> msg = mNotify->dup();
1509 msg->setInt32("what", kWhatSeekDiscontinuity);
1510 msg->setSize("trackIndex", trackIndex);
1511 msg->post();
1512 }
1513
1514 void postNormalPlayTimeMapping(
1515 size_t trackIndex, uint32_t rtpTime, int64_t nptUs) {
1516 sp<AMessage> msg = mNotify->dup();
1517 msg->setInt32("what", kWhatNormalPlayTimeMapping);
1518 msg->setSize("trackIndex", trackIndex);
1519 msg->setInt32("rtpTime", rtpTime);
1520 msg->setInt64("nptUs", nptUs);
1521 msg->post();
1522 }
Andreas Huber100a4402011-02-08 10:18:41 -08001523
Andreas Hubercf7b9c72010-06-07 15:19:40 -07001524 DISALLOW_EVIL_CONSTRUCTORS(MyHandler);
1525};
1526
1527} // namespace android
1528
1529#endif // MY_HANDLER_H_