Better support for rtsp (normal play-)time display. Better seek support, timeout if no packets arrive for too long.
Change-Id: Id491541a6ae501604cda815f8e961a3bfe26db7d related-to-bug: 2556656
This commit is contained in:
@@ -196,7 +196,6 @@ AwesomePlayer::AwesomePlayer()
|
||||
mExtractorFlags(0),
|
||||
mLastVideoBuffer(NULL),
|
||||
mVideoBuffer(NULL),
|
||||
mRTSPTimeOffset(0),
|
||||
mSuspensionState(NULL) {
|
||||
CHECK_EQ(mClient.connect(), OK);
|
||||
|
||||
@@ -739,7 +738,10 @@ status_t AwesomePlayer::getDuration(int64_t *durationUs) {
|
||||
}
|
||||
|
||||
status_t AwesomePlayer::getPosition(int64_t *positionUs) {
|
||||
if (mSeeking) {
|
||||
if (mRTSPController != NULL) {
|
||||
*positionUs = mRTSPController->getNormalPlayTimeUs();
|
||||
}
|
||||
else if (mSeeking) {
|
||||
*positionUs = mSeekTimeUs;
|
||||
} else if (mVideoSource != NULL) {
|
||||
Mutex::Autolock autoLock(mMiscStateLock);
|
||||
@@ -750,10 +752,6 @@ status_t AwesomePlayer::getPosition(int64_t *positionUs) {
|
||||
*positionUs = 0;
|
||||
}
|
||||
|
||||
if (mRTSPController != NULL) {
|
||||
*positionUs += mRTSPTimeOffset;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -770,13 +768,10 @@ status_t AwesomePlayer::seekTo(int64_t timeUs) {
|
||||
|
||||
status_t AwesomePlayer::seekTo_l(int64_t timeUs) {
|
||||
if (mRTSPController != NULL) {
|
||||
pause_l();
|
||||
mRTSPController->seek(timeUs);
|
||||
play_l();
|
||||
|
||||
notifyListener_l(MEDIA_SEEK_COMPLETE);
|
||||
mSeekNotificationSent = true;
|
||||
mRTSPTimeOffset = timeUs;
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ struct ARTSPController : public MediaExtractor {
|
||||
virtual sp<MetaData> getTrackMetaData(
|
||||
size_t index, uint32_t flags);
|
||||
|
||||
int64_t getNormalPlayTimeUs();
|
||||
|
||||
void onMessageReceived(const sp<AMessage> &msg);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -180,7 +180,6 @@ private:
|
||||
|
||||
sp<ALooper> mLooper;
|
||||
sp<ARTSPController> mRTSPController;
|
||||
int64_t mRTSPTimeOffset;
|
||||
sp<ARTPSession> mRTPSession;
|
||||
sp<UDPPusher> mRTPPusher, mRTCPPusher;
|
||||
|
||||
|
||||
@@ -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);
|
||||
accessUnit->meta()->setInt64("ntp-time", ntpTime);
|
||||
CopyTimes(accessUnit, buffer);
|
||||
|
||||
size_t dstOffset = 0;
|
||||
for (size_t i = 0; i < tableOfContents.size(); ++i) {
|
||||
|
||||
@@ -155,7 +155,7 @@ bool AAVCAssembler::addSingleTimeAggregationPacket(const sp<ABuffer> &buffer) {
|
||||
sp<ABuffer> unit = new ABuffer(nalSize);
|
||||
memcpy(unit->data(), &data[2], nalSize);
|
||||
|
||||
PropagateTimes(buffer, unit);
|
||||
CopyTimes(unit, buffer);
|
||||
|
||||
addSingleNALUnit(unit);
|
||||
|
||||
@@ -287,7 +287,7 @@ ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit(
|
||||
++totalSize;
|
||||
|
||||
sp<ABuffer> unit = new ABuffer(totalSize);
|
||||
PropagateTimes(buffer, unit);
|
||||
CopyTimes(unit, *queue->begin());
|
||||
|
||||
unit->data()[0] = (nri << 5) | nalType;
|
||||
|
||||
@@ -325,10 +325,6 @@ void AAVCAssembler::submitAccessUnit() {
|
||||
LOG(VERBOSE) << "Access unit complete (" << mNALUnits.size() << " nal units)";
|
||||
#endif
|
||||
|
||||
uint64_t ntpTime;
|
||||
CHECK((*mNALUnits.begin())->meta()->findInt64(
|
||||
"ntp-time", (int64_t *)&ntpTime));
|
||||
|
||||
size_t totalSize = 0;
|
||||
for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
|
||||
it != mNALUnits.end(); ++it) {
|
||||
@@ -347,7 +343,7 @@ void AAVCAssembler::submitAccessUnit() {
|
||||
offset += nal->size();
|
||||
}
|
||||
|
||||
accessUnit->meta()->setInt64("ntp-time", ntpTime);
|
||||
CopyTimes(accessUnit, *mNALUnits.begin());
|
||||
|
||||
#if 0
|
||||
printf(mAccessUnitDamaged ? "X" : ".");
|
||||
|
||||
@@ -128,10 +128,6 @@ void AH263Assembler::submitAccessUnit() {
|
||||
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
|
||||
#endif
|
||||
|
||||
uint64_t ntpTime;
|
||||
CHECK((*mPackets.begin())->meta()->findInt64(
|
||||
"ntp-time", (int64_t *)&ntpTime));
|
||||
|
||||
size_t totalSize = 0;
|
||||
List<sp<ABuffer> >::iterator it = mPackets.begin();
|
||||
while (it != mPackets.end()) {
|
||||
@@ -155,7 +151,7 @@ void AH263Assembler::submitAccessUnit() {
|
||||
++it;
|
||||
}
|
||||
|
||||
accessUnit->meta()->setInt64("ntp-time", ntpTime);
|
||||
CopyTimes(accessUnit, *mPackets.begin());
|
||||
|
||||
#if 0
|
||||
printf(mAccessUnitDamaged ? "X" : ".");
|
||||
|
||||
@@ -103,10 +103,6 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
|
||||
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
|
||||
#endif
|
||||
|
||||
uint64_t ntpTime;
|
||||
CHECK((*mPackets.begin())->meta()->findInt64(
|
||||
"ntp-time", (int64_t *)&ntpTime));
|
||||
|
||||
size_t totalSize = 0;
|
||||
List<sp<ABuffer> >::iterator it = mPackets.begin();
|
||||
while (it != mPackets.end()) {
|
||||
@@ -142,7 +138,7 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
|
||||
++it;
|
||||
}
|
||||
|
||||
accessUnit->meta()->setInt64("ntp-time", ntpTime);
|
||||
CopyTimes(accessUnit, *mPackets.begin());
|
||||
|
||||
#if 0
|
||||
printf(mAccessUnitDamaged ? "X" : ".");
|
||||
|
||||
@@ -101,10 +101,6 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() {
|
||||
LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " nal units)";
|
||||
#endif
|
||||
|
||||
uint64_t ntpTime;
|
||||
CHECK((*mPackets.begin())->meta()->findInt64(
|
||||
"ntp-time", (int64_t *)&ntpTime));
|
||||
|
||||
size_t totalSize = 0;
|
||||
for (List<sp<ABuffer> >::iterator it = mPackets.begin();
|
||||
it != mPackets.end(); ++it) {
|
||||
@@ -120,7 +116,7 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() {
|
||||
offset += nal->size();
|
||||
}
|
||||
|
||||
accessUnit->meta()->setInt64("ntp-time", ntpTime);
|
||||
CopyTimes(accessUnit, *mPackets.begin());
|
||||
|
||||
#if 0
|
||||
printf(mAccessUnitDamaged ? "X" : ".");
|
||||
|
||||
@@ -402,16 +402,41 @@ sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
|
||||
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(
|
||||
const sp<ASessionDescription> &sessionDesc, size_t index)
|
||||
: mInitCheck(NO_INIT),
|
||||
mFormat(new MetaData),
|
||||
mEOSResult(OK) {
|
||||
mEOSResult(OK),
|
||||
mRTPTimeBase(0),
|
||||
mNormalPlayTimeBaseUs(0),
|
||||
mLastNormalPlayTimeUs(0) {
|
||||
unsigned long PT;
|
||||
AString desc;
|
||||
AString params;
|
||||
sessionDesc->getFormatType(index, &PT, &desc, ¶ms);
|
||||
|
||||
CHECK(GetClockRate(desc, &mClockRate));
|
||||
|
||||
int64_t durationUs;
|
||||
if (sessionDesc->getDurationUs(&durationUs)) {
|
||||
mFormat->setInt64(kKeyDuration, durationUs);
|
||||
@@ -571,6 +596,8 @@ status_t APacketSource::read(
|
||||
if (!mBuffers.empty()) {
|
||||
const sp<ABuffer> buffer = *mBuffers.begin();
|
||||
|
||||
updateNormalPlayTime_l(buffer);
|
||||
|
||||
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
|
||||
|
||||
int64_t timeUs;
|
||||
@@ -588,6 +615,16 @@ status_t APacketSource::read(
|
||||
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) {
|
||||
int32_t damaged;
|
||||
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
|
||||
@@ -613,4 +650,17 @@ void APacketSource::flushQueue() {
|
||||
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
|
||||
|
||||
@@ -45,6 +45,11 @@ struct APacketSource : public MediaSource {
|
||||
|
||||
void flushQueue();
|
||||
|
||||
int64_t getNormalPlayTimeUs();
|
||||
|
||||
void setNormalPlayTimeMapping(
|
||||
uint32_t rtpTime, int64_t normalPlayTimeUs);
|
||||
|
||||
protected:
|
||||
virtual ~APacketSource();
|
||||
|
||||
@@ -58,6 +63,15 @@ private:
|
||||
List<sp<ABuffer> > mBuffers;
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@@ -35,18 +35,6 @@ ARTPAssembler::ARTPAssembler()
|
||||
: 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) {
|
||||
AssemblyStatus status;
|
||||
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
|
||||
|
||||
@@ -40,12 +40,11 @@ struct ARTPAssembler : public RefBase {
|
||||
virtual void onByeReceived() = 0;
|
||||
|
||||
protected:
|
||||
static void PropagateTimes(
|
||||
const sp<ABuffer> &from, const sp<ABuffer> &to);
|
||||
|
||||
virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source) = 0;
|
||||
virtual void packetLost() = 0;
|
||||
|
||||
static void CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from);
|
||||
|
||||
private:
|
||||
int64_t mFirstFailureTimeUs;
|
||||
|
||||
|
||||
@@ -138,4 +138,9 @@ void ARTSPController::onMessageReceived(const sp<AMessage> &msg) {
|
||||
}
|
||||
}
|
||||
|
||||
int64_t ARTSPController::getNormalPlayTimeUs() {
|
||||
CHECK(mHandler != NULL);
|
||||
return mHandler->getNormalPlayTimeUs();
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "ARTSPConnection.h"
|
||||
#include "ASessionDescription.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <media/stagefright/foundation/ABuffer.h>
|
||||
#include <media/stagefright/foundation/ADebug.h>
|
||||
#include <media/stagefright/foundation/ALooper.h>
|
||||
@@ -33,6 +35,34 @@
|
||||
|
||||
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 {
|
||||
MyHandler(const char *url, const sp<ALooper> &looper)
|
||||
: mLooper(looper),
|
||||
@@ -43,7 +73,9 @@ struct MyHandler : public AHandler {
|
||||
mSetupTracksSuccessful(false),
|
||||
mSeekPending(false),
|
||||
mFirstAccessUnit(true),
|
||||
mFirstAccessUnitNTP(0) {
|
||||
mFirstAccessUnitNTP(0),
|
||||
mNumAccessUnitsReceived(0),
|
||||
mCheckPending(false) {
|
||||
|
||||
mNetLooper->start(false /* runOnCallingThread */,
|
||||
false /* canCallJava */,
|
||||
@@ -76,6 +108,20 @@ struct MyHandler : public AHandler {
|
||||
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) {
|
||||
switch (msg->what()) {
|
||||
case 'conn':
|
||||
@@ -269,6 +315,8 @@ struct MyHandler : public AHandler {
|
||||
|
||||
CHECK_EQ(response->mStatusCode, 200u);
|
||||
|
||||
parsePlayResponse(response);
|
||||
|
||||
mDoneMsg->setInt32("result", OK);
|
||||
mDoneMsg->post();
|
||||
mDoneMsg = NULL;
|
||||
@@ -332,16 +380,38 @@ struct MyHandler : public AHandler {
|
||||
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':
|
||||
{
|
||||
++mNumAccessUnitsReceived;
|
||||
|
||||
if (!mCheckPending) {
|
||||
mCheckPending = true;
|
||||
sp<AMessage> check = new AMessage('chek', id());
|
||||
check->post(500000);
|
||||
}
|
||||
|
||||
size_t trackIndex;
|
||||
CHECK(msg->findSize("track-index", &trackIndex));
|
||||
|
||||
TrackInfo *track = &mTracks.editItemAt(trackIndex);
|
||||
|
||||
int32_t eos;
|
||||
if (msg->findInt32("eos", &eos)) {
|
||||
LOG(INFO) << "received BYE on track index " << trackIndex;
|
||||
#if 0
|
||||
TrackInfo *track = &mTracks.editItemAt(trackIndex);
|
||||
track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
|
||||
#endif
|
||||
return;
|
||||
@@ -352,10 +422,32 @@ struct MyHandler : public AHandler {
|
||||
|
||||
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;
|
||||
CHECK(accessUnit->meta()->findInt64(
|
||||
"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) {
|
||||
mFirstAccessUnit = false;
|
||||
mFirstAccessUnitNTP = ntpTime;
|
||||
@@ -414,6 +506,11 @@ struct MyHandler : public AHandler {
|
||||
|
||||
case 'see1':
|
||||
{
|
||||
// Session is paused now.
|
||||
for (size_t i = 0; i < mTracks.size(); ++i) {
|
||||
mTracks.editItemAt(i).mPacketSource->flushQueue();
|
||||
}
|
||||
|
||||
int64_t timeUs;
|
||||
CHECK(msg->findInt64("time", &timeUs));
|
||||
|
||||
@@ -440,15 +537,13 @@ struct MyHandler : public AHandler {
|
||||
{
|
||||
CHECK(mSeekPending);
|
||||
|
||||
LOG(INFO) << "seek completed.";
|
||||
mSeekPending = false;
|
||||
|
||||
int32_t result;
|
||||
CHECK(msg->findInt32("result", &result));
|
||||
if (result != OK) {
|
||||
LOG(ERROR) << "seek FAILED";
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(INFO) << "PLAY completed with result "
|
||||
<< result << " (" << strerror(-result) << ")";
|
||||
|
||||
CHECK_EQ(result, (status_t)OK);
|
||||
|
||||
sp<RefBase> obj;
|
||||
CHECK(msg->findObject("response", &obj));
|
||||
@@ -457,9 +552,10 @@ struct MyHandler : public AHandler {
|
||||
|
||||
CHECK_EQ(response->mStatusCode, 200u);
|
||||
|
||||
for (size_t i = 0; i < mTracks.size(); ++i) {
|
||||
mTracks.editItemAt(i).mPacketSource->flushQueue();
|
||||
}
|
||||
parsePlayResponse(response);
|
||||
|
||||
LOG(INFO) << "seek completed.";
|
||||
mSeekPending = false;
|
||||
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) {
|
||||
CHECK_GE(index, 0u);
|
||||
CHECK_LT(index, mTracks.size());
|
||||
@@ -515,11 +695,16 @@ private:
|
||||
bool mSeekPending;
|
||||
bool mFirstAccessUnit;
|
||||
uint64_t mFirstAccessUnitNTP;
|
||||
int64_t mNumAccessUnitsReceived;
|
||||
bool mCheckPending;
|
||||
|
||||
struct TrackInfo {
|
||||
AString mURL;
|
||||
int mRTPSocket;
|
||||
int mRTCPSocket;
|
||||
bool mUsingInterleavedTCP;
|
||||
uint32_t mFirstSeqNumInSegment;
|
||||
bool mNewSegment;
|
||||
|
||||
sp<APacketSource> mPacketSource;
|
||||
};
|
||||
@@ -549,8 +734,13 @@ private:
|
||||
|
||||
mTracks.push(TrackInfo());
|
||||
TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
|
||||
info->mURL = trackURL;
|
||||
info->mPacketSource = source;
|
||||
info->mUsingInterleavedTCP = false;
|
||||
info->mFirstSeqNumInSegment = 0;
|
||||
info->mNewSegment = true;
|
||||
|
||||
LOG(VERBOSE) << "track #" << mTracks.size() << " URL=" << trackURL;
|
||||
|
||||
AString request = "SETUP ";
|
||||
request.append(trackURL);
|
||||
|
||||
Reference in New Issue
Block a user