commit
f4edf442741886cdbe071e2d15f6e6247269f7c5
Author: Andreas Huber <andih@google.com>
Date: Tue Mar 12 09:09:18 2013 -0700
Pass additional flags to the sink, use TCP by default in wolfiecast mode.
Change-Id: I41e11a2375d4199656e45c4f149d8441d0016092
commit
6302602ed280a38287f507159abfb40a1da38c5a
Author: Andreas Huber <andih@google.com>
Date: Tue Mar 12 08:51:58 2013 -0700
tweaks
Change-Id: Ie29e422d7258be522f4bb1f6c5afcf74c937e547
commit
a38a860e4979ba563cadbaafa21b084439449d26
Author: Andreas Huber <andih@google.com>
Date: Mon Mar 11 16:57:43 2013 -0700
Report average lateness all the way from NuPlayerRenderer...
Change-Id: I2e7700703ae656515e44b9c25610d26c75778111
commit
a7d49b11675ea88be4029dd8451d1649db94571d
Author: Andreas Huber <andih@google.com>
Date: Mon Mar 11 14:54:19 2013 -0700
Make TimeSyncer smarter, enable TunnelRenderer
Change-Id: I27377a60cd8feb01589da456967fddd34532c20e
commit
0f214c8ef68179f7b61512c37040939554013151
Author: Andreas Huber <andih@google.com>
Date: Thu Mar 7 15:57:56 2013 -0800
convert source timestamps to sink timestamps, report lateness.
Change-Id: I051a60fbbceca2f7b508ae3dac6e01e402bae39e
commit
04a4f8e16bad09157b5615a5fa45310438955832
Author: Andreas Huber <andih@google.com>
Date: Thu Mar 7 09:00:28 2013 -0800
Sync time between sink and source.
Change-Id: Ie8b4d75c957aa48310e7c81d1279761b9f821efe
commit
aebe20e6184e3636a99082f8ece08e708015cb8d
Author: Andreas Huber <andih@google.com>
Date: Wed Mar 6 09:03:12 2013 -0800
play with back pressure
Change-Id: I51eb69257e6a79e76f5f9c75ff99d8adbd083947
Change-Id: Ifdf57228667fed7fc71c5090a2c3f7cea1037c5c
enum {
// Video PES packets contain exactly one (aligned) access unit.
kFlagAlignedVideoData = 1,
+
+ // Timestamps are in ALooper::GetNowUs() units.
+ kFlagIsRealTimeData = 2,
};
virtual uint32_t flags() const { return 0; }
};
mSource->start();
+ uint32_t flags = 0;
+
+ if (mSource->isRealTime()) {
+ flags |= Renderer::FLAG_REAL_TIME;
+ }
+
mRenderer = new Renderer(
mAudioSink,
- new AMessage(kWhatRendererNotify, id()));
+ new AMessage(kWhatRendererNotify, id()),
+ flags);
looper()->registerHandler(mRenderer);
int mode = request.readInt32();
return mPlayer->setVideoScalingMode(mode);
}
+
default:
{
return INVALID_OPERATION;
NuPlayer::Renderer::Renderer(
const sp<MediaPlayerBase::AudioSink> &sink,
- const sp<AMessage> ¬ify)
+ const sp<AMessage> ¬ify,
+ uint32_t flags)
: mAudioSink(sink),
mNotify(notify),
+ mFlags(flags),
mNumFramesWritten(0),
mDrainAudioQueuePending(false),
mDrainVideoQueuePending(false),
if (entry.mBuffer == NULL) {
// EOS doesn't carry a timestamp.
delayUs = 0;
+ } else if (mFlags & FLAG_REAL_TIME) {
+ int64_t mediaTimeUs;
+ CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ delayUs = mediaTimeUs - ALooper::GetNowUs();
} else {
int64_t mediaTimeUs;
CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
return;
}
- int64_t mediaTimeUs;
- CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+ int64_t realTimeUs;
+ if (mFlags & FLAG_REAL_TIME) {
+ CHECK(entry->mBuffer->meta()->findInt64("timeUs", &realTimeUs));
+ } else {
+ int64_t mediaTimeUs;
+ CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+ realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs;
+ }
- int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs;
mVideoLateByUs = ALooper::GetNowUs() - realTimeUs;
-
bool tooLate = (mVideoLateByUs > 40000);
if (tooLate) {
struct ABuffer;
struct NuPlayer::Renderer : public AHandler {
+ enum Flags {
+ FLAG_REAL_TIME = 1,
+ };
Renderer(const sp<MediaPlayerBase::AudioSink> &sink,
- const sp<AMessage> ¬ify);
+ const sp<AMessage> ¬ify,
+ uint32_t flags = 0);
void queueBuffer(
bool audio,
sp<MediaPlayerBase::AudioSink> mAudioSink;
sp<AMessage> mNotify;
+ uint32_t mFlags;
List<QueueEntry> mAudioQueue;
List<QueueEntry> mVideoQueue;
uint32_t mNumFramesWritten;
return INVALID_OPERATION;
}
+ virtual bool isRealTime() const {
+ return false;
+ }
+
protected:
virtual ~Source() {}
return err;
}
+bool NuPlayer::StreamingSource::isRealTime() const {
+ return mSource->flags() & IStreamSource::kFlagIsRealTimeData;
+}
+
} // namespace android
virtual status_t dequeueAccessUnit(bool audio, sp<ABuffer> *accessUnit);
+ virtual bool isRealTime() const;
+
protected:
virtual ~StreamingSource();
timeUs += mParser->mAbsoluteTimeAnchorUs;
}
+ if (mParser->mTimeOffsetValid) {
+ timeUs += mParser->mTimeOffsetUs;
+ }
+
return timeUs;
}
ATSParser::ATSParser(uint32_t flags)
: mFlags(flags),
mAbsoluteTimeAnchorUs(-1ll),
+ mTimeOffsetValid(false),
+ mTimeOffsetUs(0ll),
mNumTSPacketsParsed(0),
mNumPCRs(0) {
mPSISections.add(0 /* PID */, new PSISection);
CHECK(mPrograms.empty());
mAbsoluteTimeAnchorUs = timeUs;
return;
+ } else if (type == DISCONTINUITY_TIME_OFFSET) {
+ int64_t offset;
+ CHECK(extra->findInt64("offset", &offset));
+
+ mTimeOffsetValid = true;
+ mTimeOffsetUs = offset;
+ return;
}
for (size_t i = 0; i < mPrograms.size(); ++i) {
DISCONTINUITY_AUDIO_FORMAT = 2,
DISCONTINUITY_VIDEO_FORMAT = 4,
DISCONTINUITY_ABSOLUTE_TIME = 8,
+ DISCONTINUITY_TIME_OFFSET = 16,
DISCONTINUITY_SEEK = DISCONTINUITY_TIME,
int64_t mAbsoluteTimeAnchorUs;
+ bool mTimeOffsetValid;
+ int64_t mTimeOffsetUs;
+
size_t mNumTSPacketsParsed;
void parseProgramAssociationTable(ABitReader *br);
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
+#include <sys/ioctl.h>
#include <sys/socket.h>
#include <media/stagefright/foundation/ABuffer.h>
mSawSendFailure = true;
}
+#if 0
+ int numBytesQueued;
+ int res = ioctl(mSocket, SIOCOUTQ, &numBytesQueued);
+ if (res == 0 && numBytesQueued > 102400) {
+ ALOGI("numBytesQueued = %d", numBytesQueued);
+ }
+#endif
+
return err;
}
sink/TunnelRenderer.cpp \
sink/WifiDisplaySink.cpp \
SNTPClient.cpp \
+ TimeSyncer.cpp \
source/Converter.cpp \
source/MediaPuller.cpp \
source/PlaybackSession.cpp \
case 202: // SDES
case 203:
+ break;
+
case 204: // APP
break;
#include <gui/SurfaceComposerClient.h>
#include <media/IMediaPlayerService.h>
#include <media/IStreamSource.h>
+#include <media/mediaplayer.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
void doSomeWork();
+ void setTimeOffset(int64_t offset);
+
protected:
virtual ~StreamSource();
size_t mNumDeqeued;
+ int64_t mTimeOffsetUs;
+ bool mTimeOffsetChanged;
+
DISALLOW_EVIL_CONSTRUCTORS(StreamSource);
};
TunnelRenderer::StreamSource::StreamSource(TunnelRenderer *owner)
: mOwner(owner),
- mNumDeqeued(0) {
+ mNumDeqeued(0),
+ mTimeOffsetUs(0ll),
+ mTimeOffsetChanged(false) {
}
TunnelRenderer::StreamSource::~StreamSource() {
}
uint32_t TunnelRenderer::StreamSource::flags() const {
- return kFlagAlignedVideoData;
+ return kFlagAlignedVideoData | kFlagIsRealTimeData;
}
void TunnelRenderer::StreamSource::doSomeWork() {
++mNumDeqeued;
- if (mNumDeqeued == 1) {
- ALOGI("fixing real time now.");
-
+ if (mTimeOffsetChanged) {
sp<AMessage> extra = new AMessage;
extra->setInt32(
IStreamListener::kKeyDiscontinuityMask,
- ATSParser::DISCONTINUITY_ABSOLUTE_TIME);
+ ATSParser::DISCONTINUITY_TIME_OFFSET);
- extra->setInt64("timeUs", ALooper::GetNowUs());
+ extra->setInt64("offset", mTimeOffsetUs);
mListener->issueCommand(
IStreamListener::DISCONTINUITY,
false /* synchronous */,
extra);
+
+ mTimeOffsetChanged = false;
}
ALOGV("dequeue TS packet of size %d", srcBuffer->size());
}
}
+void TunnelRenderer::StreamSource::setTimeOffset(int64_t offset) {
+ Mutex::Autolock autoLock(mLock);
+
+ if (offset != mTimeOffsetUs) {
+ mTimeOffsetUs = offset;
+ mTimeOffsetChanged = true;
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////
TunnelRenderer::TunnelRenderer(
const sp<IGraphicBufferProducer> &bufferProducer)
: mSurfaceTex(bufferProducer),
mStartup(true) {
+ mStreamSource = new StreamSource(this);
}
TunnelRenderer::~TunnelRenderer() {
destroyPlayer();
}
+void TunnelRenderer::setTimeOffset(int64_t offset) {
+ mStreamSource->setTimeOffset(offset);
+}
+
void TunnelRenderer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
default:
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
CHECK(service.get() != NULL);
- mStreamSource = new StreamSource(this);
-
mPlayerClient = new PlayerClient;
mPlayer = service->create(mPlayerClient, 0);
void TunnelRenderer::destroyPlayer() {
mStreamSource.clear();
+ mPlayer->setVideoSurfaceTexture(NULL);
+
mPlayer->stop();
mPlayer.clear();
void queueBuffer(const sp<ABuffer> &buffer);
sp<ABuffer> dequeueBuffer();
+ void setTimeOffset(int64_t offset);
+
+ int64_t getAvgLatenessUs() {
+ return 0ll;
+ }
+
protected:
virtual void onMessageReceived(const sp<AMessage> &msg);
virtual ~TunnelRenderer();
#include "Parameters.h"
#include "ParsedMessage.h"
#include "rtp/RTPSender.h"
+#include "TimeSyncer.h"
#include <binder/IServiceManager.h>
#include <gui/IGraphicBufferProducer.h>
}
if (err == OK) {
+ sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+ mTimeSyncer = new TimeSyncer(mNetSession, notify);
+ looper()->registerHandler(mTimeSyncer);
+
+ mTimeSyncer->startServer(8123);
+
mState = AWAITING_CLIENT_CONNECTION;
}
break;
}
+ case kWhatTimeSyncerNotify:
+ {
+ break;
+ }
+
default:
TRESPASS();
}
struct IHDCP;
struct IRemoteDisplayClient;
struct ParsedMessage;
+struct TimeSyncer;
// Represents the RTSP server acting as a wifi display source.
// Manages incoming connections, sets up Playback sessions as necessary.
kWhatHDCPNotify,
kWhatFinishStop2,
kWhatTeardownTriggerTimedOut,
+ kWhatTimeSyncerNotify,
};
struct ResponseID {
VideoFormats mSupportedSourceVideoFormats;
sp<ANetworkSession> mNetSession;
sp<IRemoteDisplayClient> mClient;
+ sp<TimeSyncer> mTimeSyncer;
struct in_addr mInterfaceAddr;
int32_t mSessionID;
sp<ALooper> looper = new ALooper;
sp<WifiDisplaySink> sink = new WifiDisplaySink(
- session, surface->getIGraphicBufferProducer());
+ 0 /* flags */,
+ session,
+ surface->getIGraphicBufferProducer());
+
looper->registerHandler(sink);
if (connectToPort >= 0) {