Merge "Better support for rtsp (normal play-)time display. Better seek support, timeout if no packets arrive for too long." into gingerbread

This commit is contained in:
Andreas Huber
2010-08-27 13:44:14 -07:00
committed by Android (Google) Code Review
14 changed files with 302 additions and 65 deletions

View File

@@ -196,7 +196,6 @@ AwesomePlayer::AwesomePlayer()
mExtractorFlags(0), mExtractorFlags(0),
mLastVideoBuffer(NULL), mLastVideoBuffer(NULL),
mVideoBuffer(NULL), mVideoBuffer(NULL),
mRTSPTimeOffset(0),
mSuspensionState(NULL) { mSuspensionState(NULL) {
CHECK_EQ(mClient.connect(), OK); CHECK_EQ(mClient.connect(), OK);
@@ -739,7 +738,10 @@ status_t AwesomePlayer::getDuration(int64_t *durationUs) {
} }
status_t AwesomePlayer::getPosition(int64_t *positionUs) { status_t AwesomePlayer::getPosition(int64_t *positionUs) {
if (mSeeking) { if (mRTSPController != NULL) {
*positionUs = mRTSPController->getNormalPlayTimeUs();
}
else if (mSeeking) {
*positionUs = mSeekTimeUs; *positionUs = mSeekTimeUs;
} else if (mVideoSource != NULL) { } else if (mVideoSource != NULL) {
Mutex::Autolock autoLock(mMiscStateLock); Mutex::Autolock autoLock(mMiscStateLock);
@@ -750,10 +752,6 @@ status_t AwesomePlayer::getPosition(int64_t *positionUs) {
*positionUs = 0; *positionUs = 0;
} }
if (mRTSPController != NULL) {
*positionUs += mRTSPTimeOffset;
}
return OK; return OK;
} }
@@ -770,13 +768,10 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) {
status_t AwesomePlayer::seekTo_l(int64_t timeUs) { status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
if (mRTSPController != NULL) { if (mRTSPController != NULL) {
pause_l();
mRTSPController->seek(timeUs); mRTSPController->seek(timeUs);
play_l();
notifyListener_l(MEDIA_SEEK_COMPLETE); notifyListener_l(MEDIA_SEEK_COMPLETE);
mSeekNotificationSent = true; mSeekNotificationSent = true;
mRTSPTimeOffset = timeUs;
return OK; return OK;
} }

View File

@@ -41,6 +41,8 @@ struct ARTSPController : public MediaExtractor {
virtual sp<MetaData> getTrackMetaData( virtual sp<MetaData> getTrackMetaData(
size_t index, uint32_t flags); size_t index, uint32_t flags);
int64_t getNormalPlayTimeUs();
void onMessageReceived(const sp<AMessage> &msg); void onMessageReceived(const sp<AMessage> &msg);
protected: protected:

View File

@@ -180,7 +180,6 @@ private:
sp<ALooper> mLooper; sp<ALooper> mLooper;
sp<ARTSPController> mRTSPController; sp<ARTSPController> mRTSPController;
int64_t mRTSPTimeOffset;
sp<ARTPSession> mRTPSession; sp<ARTPSession> mRTPSession;
sp<UDPPusher> mRTPPusher, mRTCPPusher; sp<UDPPusher> mRTPPusher, mRTCPPusher;

View File

@@ -178,12 +178,8 @@ ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket(
} }
} }
uint64_t ntpTime;
CHECK(buffer->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
sp<ABuffer> accessUnit = new ABuffer(totalSize); sp<ABuffer> accessUnit = new ABuffer(totalSize);
accessUnit->meta()->setInt64("ntp-time", ntpTime); CopyTimes(accessUnit, buffer);
size_t dstOffset = 0; size_t dstOffset = 0;
for (size_t i = 0; i < tableOfContents.size(); ++i) { for (size_t i = 0; i < tableOfContents.size(); ++i) {

View File

@@ -155,7 +155,7 @@ bool AAVCAssembler::addSingleTimeAggregationPacket(const sp<ABuffer> &buffer) {
sp<ABuffer> unit = new ABuffer(nalSize); sp<ABuffer> unit = new ABuffer(nalSize);
memcpy(unit->data(), &data[2], nalSize); memcpy(unit->data(), &data[2], nalSize);
PropagateTimes(buffer, unit); CopyTimes(unit, buffer);
addSingleNALUnit(unit); addSingleNALUnit(unit);
@@ -287,7 +287,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit(
++totalSize; ++totalSize;
sp<ABuffer> unit = new ABuffer(totalSize); sp<ABuffer> unit = new ABuffer(totalSize);
PropagateTimes(buffer, unit); CopyTimes(unit, *queue->begin());
unit->data()[0] = (nri << 5) | nalType; unit->data()[0] = (nri << 5) | nalType;
@@ -325,10 +325,6 @@ void AAVCAssembler::submitAccessUnit() {
LOG(VERBOSE) << "Access unit complete (" << mNALUnits.size() << " nal units)"; LOG(VERBOSE) << "Access unit complete (" << mNALUnits.size() << " nal units)";
#endif #endif
uint64_t ntpTime;
CHECK((*mNALUnits.begin())->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
size_t totalSize = 0; size_t totalSize = 0;
for (List<sp<ABuffer> >::iterator it = mNALUnits.begin(); for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
it != mNALUnits.end(); ++it) { it != mNALUnits.end(); ++it) {
@@ -347,7 +343,7 @@ void AAVCAssembler::submitAccessUnit() {
offset += nal->size(); offset += nal->size();
} }
accessUnit->meta()->setInt64("ntp-time", ntpTime); CopyTimes(accessUnit, *mNALUnits.begin());
#if 0 #if 0
printf(mAccessUnitDamaged ? "X" : "."); printf(mAccessUnitDamaged ? "X" : ".");

View File

@@ -128,10 +128,6 @@ void AH263Assembler::submitAccessUnit() {
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)"; LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
#endif #endif
uint64_t ntpTime;
CHECK((*mPackets.begin())->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
size_t totalSize = 0; size_t totalSize = 0;
List<sp<ABuffer> >::iterator it = mPackets.begin(); List<sp<ABuffer> >::iterator it = mPackets.begin();
while (it != mPackets.end()) { while (it != mPackets.end()) {
@@ -155,7 +151,7 @@ void AH263Assembler::submitAccessUnit() {
++it; ++it;
} }
accessUnit->meta()->setInt64("ntp-time", ntpTime); CopyTimes(accessUnit, *mPackets.begin());
#if 0 #if 0
printf(mAccessUnitDamaged ? "X" : "."); printf(mAccessUnitDamaged ? "X" : ".");

View File

@@ -103,10 +103,6 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)"; LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
#endif #endif
uint64_t ntpTime;
CHECK((*mPackets.begin())->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
size_t totalSize = 0; size_t totalSize = 0;
List<sp<ABuffer> >::iterator it = mPackets.begin(); List<sp<ABuffer> >::iterator it = mPackets.begin();
while (it != mPackets.end()) { while (it != mPackets.end()) {
@@ -142,7 +138,7 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
++it; ++it;
} }
accessUnit->meta()->setInt64("ntp-time", ntpTime); CopyTimes(accessUnit, *mPackets.begin());
#if 0 #if 0
printf(mAccessUnitDamaged ? "X" : "."); printf(mAccessUnitDamaged ? "X" : ".");

View File

@@ -101,10 +101,6 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() {
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)"; LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
#endif #endif
uint64_t ntpTime;
CHECK((*mPackets.begin())->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime));
size_t totalSize = 0; size_t totalSize = 0;
for (List<sp<ABuffer> >::iterator it = mPackets.begin(); for (List<sp<ABuffer> >::iterator it = mPackets.begin();
it != mPackets.end(); ++it) { it != mPackets.end(); ++it) {
@@ -120,7 +116,7 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() {
offset += nal->size(); offset += nal->size();
} }
accessUnit->meta()->setInt64("ntp-time", ntpTime); CopyTimes(accessUnit, *mPackets.begin());
#if 0 #if 0
printf(mAccessUnitDamaged ? "X" : "."); printf(mAccessUnitDamaged ? "X" : ".");

View File

@@ -402,16 +402,41 @@ sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
return csd; return csd;
} }
static bool GetClockRate(const AString &desc, uint32_t *clockRate) {
ssize_t slashPos = desc.find("/");
if (slashPos < 0) {
return false;
}
const char *s = desc.c_str() + slashPos + 1;
char *end;
unsigned long x = strtoul(s, &end, 10);
if (end == s || (*end != '\0' && *end != '/')) {
return false;
}
*clockRate = x;
return true;
}
APacketSource::APacketSource( APacketSource::APacketSource(
const sp<ASessionDescription> &sessionDesc, size_t index) const sp<ASessionDescription> &sessionDesc, size_t index)
: mInitCheck(NO_INIT), : mInitCheck(NO_INIT),
mFormat(new MetaData), mFormat(new MetaData),
mEOSResult(OK) { mEOSResult(OK),
mRTPTimeBase(0),
mNormalPlayTimeBaseUs(0),
mLastNormalPlayTimeUs(0) {
unsigned long PT; unsigned long PT;
AString desc; AString desc;
AString params; AString params;
sessionDesc->getFormatType(index, &PT, &desc, &params); sessionDesc->getFormatType(index, &PT, &desc, &params);
CHECK(GetClockRate(desc, &mClockRate));
int64_t durationUs; int64_t durationUs;
if (sessionDesc->getDurationUs(&durationUs)) { if (sessionDesc->getDurationUs(&durationUs)) {
mFormat->setInt64(kKeyDuration, durationUs); mFormat->setInt64(kKeyDuration, durationUs);
@@ -571,6 +596,8 @@ status_t APacketSource::read(
if (!mBuffers.empty()) { if (!mBuffers.empty()) {
const sp<ABuffer> buffer = *mBuffers.begin(); const sp<ABuffer> buffer = *mBuffers.begin();
updateNormalPlayTime_l(buffer);
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size()); MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
int64_t timeUs; int64_t timeUs;
@@ -588,6 +615,16 @@ status_t APacketSource::read(
return mEOSResult; return mEOSResult;
} }
void APacketSource::updateNormalPlayTime_l(const sp<ABuffer> &buffer) {
uint32_t rtpTime;
CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
mLastNormalPlayTimeUs =
(((double)rtpTime - (double)mRTPTimeBase) / mClockRate)
* 1000000ll
+ mNormalPlayTimeBaseUs;
}
void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged; int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) { if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
@@ -613,4 +650,17 @@ void APacketSource::flushQueue() {
mBuffers.clear(); mBuffers.clear();
} }
int64_t APacketSource::getNormalPlayTimeUs() {
Mutex::Autolock autoLock(mLock);
return mLastNormalPlayTimeUs;
}
void APacketSource::setNormalPlayTimeMapping(
uint32_t rtpTime, int64_t normalPlayTimeUs) {
Mutex::Autolock autoLock(mLock);
mRTPTimeBase = rtpTime;
mNormalPlayTimeBaseUs = normalPlayTimeUs;
}
} // namespace android } // namespace android

View File

@@ -45,6 +45,11 @@ struct APacketSource : public MediaSource {
void flushQueue(); void flushQueue();
int64_t getNormalPlayTimeUs();
void setNormalPlayTimeMapping(
uint32_t rtpTime, int64_t normalPlayTimeUs);
protected: protected:
virtual ~APacketSource(); virtual ~APacketSource();
@@ -58,6 +63,15 @@ private:
List<sp<ABuffer> > mBuffers; List<sp<ABuffer> > mBuffers;
status_t mEOSResult; status_t mEOSResult;
uint32_t mClockRate;
uint32_t mRTPTimeBase;
int64_t mNormalPlayTimeBaseUs;
int64_t mLastNormalPlayTimeUs;
void updateNormalPlayTime_l(const sp<ABuffer> &buffer);
DISALLOW_EVIL_CONSTRUCTORS(APacketSource); DISALLOW_EVIL_CONSTRUCTORS(APacketSource);
}; };

View File

@@ -35,18 +35,6 @@ ARTPAssembler::ARTPAssembler()
: mFirstFailureTimeUs(-1) { : mFirstFailureTimeUs(-1) {
} }
void ARTPAssembler::PropagateTimes(
const sp<ABuffer> &from, const sp<ABuffer> &to) {
uint32_t rtpTime;
CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
uint64_t ntpTime = 0;
CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime));
to->meta()->setInt32("rtp-time", rtpTime);
to->meta()->setInt64("ntp-time", ntpTime);
}
void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) { void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) {
AssemblyStatus status; AssemblyStatus status;
for (;;) { for (;;) {
@@ -75,4 +63,19 @@ void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) {
} }
} }
// static
void ARTPAssembler::CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from) {
uint64_t ntpTime;
CHECK(from->meta()->findInt64("ntp-time", (int64_t *)&ntpTime));
uint32_t rtpTime;
CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
to->meta()->setInt64("ntp-time", ntpTime);
to->meta()->setInt32("rtp-time", rtpTime);
// Copy the seq number.
to->setInt32Data(from->int32Data());
}
} // namespace android } // namespace android

View File

@@ -40,12 +40,11 @@ struct ARTPAssembler : public RefBase {
virtual void onByeReceived() = 0; virtual void onByeReceived() = 0;
protected: protected:
static void PropagateTimes(
const sp<ABuffer> &from, const sp<ABuffer> &to);
virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source) = 0; virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source) = 0;
virtual void packetLost() = 0; virtual void packetLost() = 0;
static void CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from);
private: private:
int64_t mFirstFailureTimeUs; int64_t mFirstFailureTimeUs;

View File

@@ -138,4 +138,9 @@ void ARTSPController::onMessageReceived(const sp<AMessage> &msg) {
} }
} }
int64_t ARTSPController::getNormalPlayTimeUs() {
CHECK(mHandler != NULL);
return mHandler->getNormalPlayTimeUs();
}
} // namespace android } // namespace android

View File

@@ -23,6 +23,8 @@
#include "ARTSPConnection.h" #include "ARTSPConnection.h"
#include "ASessionDescription.h" #include "ASessionDescription.h"
#include <ctype.h>
#include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h> #include <media/stagefright/foundation/ALooper.h>
@@ -33,6 +35,34 @@
namespace android { namespace android {
static bool GetAttribute(const char *s, const char *key, AString *value) {
value->clear();
size_t keyLen = strlen(key);
for (;;) {
while (isspace(*s)) {
++s;
}
const char *colonPos = strchr(s, ';');
size_t len =
(colonPos == NULL) ? strlen(s) : colonPos - s;
if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
value->setTo(&s[keyLen + 1], len - keyLen - 1);
return true;
}
if (colonPos == NULL) {
return false;
}
s = colonPos + 1;
}
}
struct MyHandler : public AHandler { struct MyHandler : public AHandler {
MyHandler(const char *url, const sp<ALooper> &looper) MyHandler(const char *url, const sp<ALooper> &looper)
: mLooper(looper), : mLooper(looper),
@@ -43,7 +73,9 @@ struct MyHandler : public AHandler {
mSetupTracksSuccessful(false), mSetupTracksSuccessful(false),
mSeekPending(false), mSeekPending(false),
mFirstAccessUnit(true), mFirstAccessUnit(true),
mFirstAccessUnitNTP(0) { mFirstAccessUnitNTP(0),
mNumAccessUnitsReceived(0),
mCheckPending(false) {
mNetLooper->start(false /* runOnCallingThread */, mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */, false /* canCallJava */,
@@ -76,6 +108,20 @@ struct MyHandler : public AHandler {
msg->post(); msg->post();
} }
int64_t getNormalPlayTimeUs() {
int64_t maxTimeUs = 0;
for (size_t i = 0; i < mTracks.size(); ++i) {
int64_t timeUs = mTracks.editItemAt(i).mPacketSource
->getNormalPlayTimeUs();
if (i == 0 || timeUs > maxTimeUs) {
maxTimeUs = timeUs;
}
}
return maxTimeUs;
}
virtual void onMessageReceived(const sp<AMessage> &msg) { virtual void onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) { switch (msg->what()) {
case 'conn': case 'conn':
@@ -269,6 +315,8 @@ struct MyHandler : public AHandler {
CHECK_EQ(response->mStatusCode, 200u); CHECK_EQ(response->mStatusCode, 200u);
parsePlayResponse(response);
mDoneMsg->setInt32("result", OK); mDoneMsg->setInt32("result", OK);
mDoneMsg->post(); mDoneMsg->post();
mDoneMsg = NULL; mDoneMsg = NULL;
@@ -332,16 +380,38 @@ struct MyHandler : public AHandler {
break; break;
} }
case 'chek':
{
if (mNumAccessUnitsReceived == 0) {
LOG(INFO) << "stream ended? aborting.";
(new AMessage('abor', id()))->post();
break;
}
mNumAccessUnitsReceived = 0;
msg->post(500000);
break;
}
case 'accu': case 'accu':
{ {
++mNumAccessUnitsReceived;
if (!mCheckPending) {
mCheckPending = true;
sp<AMessage> check = new AMessage('chek', id());
check->post(500000);
}
size_t trackIndex; size_t trackIndex;
CHECK(msg->findSize("track-index", &trackIndex)); CHECK(msg->findSize("track-index", &trackIndex));
TrackInfo *track = &mTracks.editItemAt(trackIndex);
int32_t eos; int32_t eos;
if (msg->findInt32("eos", &eos)) { if (msg->findInt32("eos", &eos)) {
LOG(INFO) << "received BYE on track index " << trackIndex; LOG(INFO) << "received BYE on track index " << trackIndex;
#if 0 #if 0
TrackInfo *track = &mTracks.editItemAt(trackIndex);
track->mPacketSource->signalEOS(ERROR_END_OF_STREAM); track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
#endif #endif
return; return;
@@ -352,10 +422,32 @@ struct MyHandler : public AHandler {
sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get()); sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
uint32_t seqNum = (uint32_t)accessUnit->int32Data();
if (seqNum < track->mFirstSeqNumInSegment) {
LOG(INFO) << "dropping stale access-unit "
<< "(" << seqNum << " < "
<< track->mFirstSeqNumInSegment << ")";
break;
}
uint64_t ntpTime; uint64_t ntpTime;
CHECK(accessUnit->meta()->findInt64( CHECK(accessUnit->meta()->findInt64(
"ntp-time", (int64_t *)&ntpTime)); "ntp-time", (int64_t *)&ntpTime));
uint32_t rtpTime;
CHECK(accessUnit->meta()->findInt32(
"rtp-time", (int32_t *)&rtpTime));
if (track->mNewSegment) {
track->mNewSegment = false;
LOG(VERBOSE) << "first segment unit ntpTime="
<< StringPrintf("0x%016llx", ntpTime)
<< " rtpTime=" << rtpTime
<< " seq=" << seqNum;
}
if (mFirstAccessUnit) { if (mFirstAccessUnit) {
mFirstAccessUnit = false; mFirstAccessUnit = false;
mFirstAccessUnitNTP = ntpTime; mFirstAccessUnitNTP = ntpTime;
@@ -414,6 +506,11 @@ struct MyHandler : public AHandler {
case 'see1': case 'see1':
{ {
// Session is paused now.
for (size_t i = 0; i < mTracks.size(); ++i) {
mTracks.editItemAt(i).mPacketSource->flushQueue();
}
int64_t timeUs; int64_t timeUs;
CHECK(msg->findInt64("time", &timeUs)); CHECK(msg->findInt64("time", &timeUs));
@@ -440,15 +537,13 @@ struct MyHandler : public AHandler {
{ {
CHECK(mSeekPending); CHECK(mSeekPending);
LOG(INFO) << "seek completed.";
mSeekPending = false;
int32_t result; int32_t result;
CHECK(msg->findInt32("result", &result)); CHECK(msg->findInt32("result", &result));
if (result != OK) {
LOG(ERROR) << "seek FAILED"; LOG(INFO) << "PLAY completed with result "
break; << result << " (" << strerror(-result) << ")";
}
CHECK_EQ(result, (status_t)OK);
sp<RefBase> obj; sp<RefBase> obj;
CHECK(msg->findObject("response", &obj)); CHECK(msg->findObject("response", &obj));
@@ -457,9 +552,10 @@ struct MyHandler : public AHandler {
CHECK_EQ(response->mStatusCode, 200u); CHECK_EQ(response->mStatusCode, 200u);
for (size_t i = 0; i < mTracks.size(); ++i) { parsePlayResponse(response);
mTracks.editItemAt(i).mPacketSource->flushQueue();
} LOG(INFO) << "seek completed.";
mSeekPending = false;
break; break;
} }
@@ -491,6 +587,90 @@ struct MyHandler : public AHandler {
} }
} }
static void SplitString(
const AString &s, const char *separator, List<AString> *items) {
items->clear();
size_t start = 0;
while (start < s.size()) {
ssize_t offset = s.find(separator, start);
if (offset < 0) {
items->push_back(AString(s, start, s.size() - start));
break;
}
items->push_back(AString(s, start, offset - start));
start = offset + strlen(separator);
}
}
void parsePlayResponse(const sp<ARTSPResponse> &response) {
ssize_t i = response->mHeaders.indexOfKey("range");
if (i < 0) {
// Server doesn't even tell use what range it is going to
// play, therefore we won't support seeking.
return;
}
AString range = response->mHeaders.valueAt(i);
LOG(VERBOSE) << "Range: " << range;
AString val;
CHECK(GetAttribute(range.c_str(), "npt", &val));
float npt1, npt2;
if (val == "now-") {
// This is a live stream and therefore not seekable.
return;
} else {
CHECK_EQ(sscanf(val.c_str(), "%f-%f", &npt1, &npt2), 2);
}
i = response->mHeaders.indexOfKey("rtp-info");
CHECK_GE(i, 0);
AString rtpInfo = response->mHeaders.valueAt(i);
List<AString> streamInfos;
SplitString(rtpInfo, ",", &streamInfos);
int n = 1;
for (List<AString>::iterator it = streamInfos.begin();
it != streamInfos.end(); ++it) {
(*it).trim();
LOG(VERBOSE) << "streamInfo[" << n << "] = " << *it;
CHECK(GetAttribute((*it).c_str(), "url", &val));
size_t trackIndex = 0;
while (trackIndex < mTracks.size()
&& !(val == mTracks.editItemAt(trackIndex).mURL)) {
++trackIndex;
}
CHECK_LT(trackIndex, mTracks.size());
CHECK(GetAttribute((*it).c_str(), "seq", &val));
char *end;
unsigned long seq = strtoul(val.c_str(), &end, 10);
TrackInfo *info = &mTracks.editItemAt(trackIndex);
info->mFirstSeqNumInSegment = seq;
info->mNewSegment = true;
CHECK(GetAttribute((*it).c_str(), "rtptime", &val));
uint32_t rtpTime = strtoul(val.c_str(), &end, 10);
LOG(VERBOSE) << "track #" << n
<< ": rtpTime=" << rtpTime << " <=> npt=" << npt1;
info->mPacketSource->setNormalPlayTimeMapping(
rtpTime, (int64_t)(npt1 * 1E6));
++n;
}
}
sp<APacketSource> getPacketSource(size_t index) { sp<APacketSource> getPacketSource(size_t index) {
CHECK_GE(index, 0u); CHECK_GE(index, 0u);
CHECK_LT(index, mTracks.size()); CHECK_LT(index, mTracks.size());
@@ -515,11 +695,16 @@ private:
bool mSeekPending; bool mSeekPending;
bool mFirstAccessUnit; bool mFirstAccessUnit;
uint64_t mFirstAccessUnitNTP; uint64_t mFirstAccessUnitNTP;
int64_t mNumAccessUnitsReceived;
bool mCheckPending;
struct TrackInfo { struct TrackInfo {
AString mURL;
int mRTPSocket; int mRTPSocket;
int mRTCPSocket; int mRTCPSocket;
bool mUsingInterleavedTCP; bool mUsingInterleavedTCP;
uint32_t mFirstSeqNumInSegment;
bool mNewSegment;
sp<APacketSource> mPacketSource; sp<APacketSource> mPacketSource;
}; };
@@ -549,8 +734,13 @@ private:
mTracks.push(TrackInfo()); mTracks.push(TrackInfo());
TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1); TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
info->mURL = trackURL;
info->mPacketSource = source; info->mPacketSource = source;
info->mUsingInterleavedTCP = false; info->mUsingInterleavedTCP = false;
info->mFirstSeqNumInSegment = 0;
info->mNewSegment = true;
LOG(VERBOSE) << "track #" << mTracks.size() << " URL=" << trackURL;
AString request = "SETUP "; AString request = "SETUP ";
request.append(trackURL); request.append(trackURL);