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:
committed by
Android (Google) Code Review
commit
577615c98e
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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" : ".");
|
||||||
|
|||||||
@@ -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" : ".");
|
||||||
|
|||||||
@@ -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" : ".");
|
||||||
|
|||||||
@@ -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" : ".");
|
||||||
|
|||||||
@@ -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, ¶ms);
|
sessionDesc->getFormatType(index, &PT, &desc, ¶ms);
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user