summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice/nuplayer
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmediaplayerservice/nuplayer')
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.cpp209
-rw-r--r--media/libmediaplayerservice/nuplayer/GenericSource.h9
-rw-r--r--media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp32
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp73
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp66
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h1
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp131
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h6
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp18
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h2
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp28
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp5
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDriver.h1
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp554
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h48
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp12
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h4
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.cpp10
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.h3
-rw-r--r--media/libmediaplayerservice/nuplayer/StreamingSource.cpp4
22 files changed, 617 insertions, 605 deletions
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 1b2fc5e..5a31b74 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -65,12 +65,12 @@ NuPlayer::GenericSource::GenericSource(
mUID(uid),
mFd(-1),
mDrmManagerClient(NULL),
- mMetaDataSize(-1ll),
mBitrate(-1ll),
mPollBufferingGeneration(0),
mPendingReadBufferTypes(0),
mBuffering(false),
- mPrepareBuffering(false) {
+ mPrepareBuffering(false),
+ mPrevBufferPercentage(-1) {
resetDataSource();
DataSource::RegisterDefaultSniffers();
}
@@ -130,23 +130,34 @@ sp<MetaData> NuPlayer::GenericSource::getFileFormatMeta() const {
status_t NuPlayer::GenericSource::initFromDataSource() {
sp<MediaExtractor> extractor;
+ String8 mimeType;
+ float confidence;
+ sp<AMessage> dummy;
+ bool isWidevineStreaming = false;
CHECK(mDataSource != NULL);
if (mIsWidevine) {
- String8 mimeType;
- float confidence;
- sp<AMessage> dummy;
- bool success;
-
- success = SniffWVM(mDataSource, &mimeType, &confidence, &dummy);
- if (!success
- || strcasecmp(
+ isWidevineStreaming = SniffWVM(
+ mDataSource, &mimeType, &confidence, &dummy);
+ if (!isWidevineStreaming ||
+ strcasecmp(
mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
ALOGE("unsupported widevine mime: %s", mimeType.string());
return UNKNOWN_ERROR;
}
+ } else if (mIsStreaming) {
+ if (!mDataSource->sniff(&mimeType, &confidence, &dummy)) {
+ return UNKNOWN_ERROR;
+ }
+ isWidevineStreaming = !strcasecmp(
+ mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM);
+ }
+ if (isWidevineStreaming) {
+ // we don't want cached source for widevine streaming.
+ mCachedSource.clear();
+ mDataSource = mHttpSource;
mWVMExtractor = new WVMExtractor(mDataSource);
mWVMExtractor->setAdaptiveStreamingMode(true);
if (mUIDValid) {
@@ -155,7 +166,7 @@ status_t NuPlayer::GenericSource::initFromDataSource() {
extractor = mWVMExtractor;
} else {
extractor = MediaExtractor::Create(mDataSource,
- mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str());
+ mimeType.isEmpty() ? NULL : mimeType.string());
}
if (extractor == NULL) {
@@ -181,14 +192,6 @@ status_t NuPlayer::GenericSource::initFromDataSource() {
if (mFileMeta->findCString(kKeyMIMEType, &fileMime)
&& !strncasecmp(fileMime, "video/wvm", 9)) {
mIsWidevine = true;
- if (!mUri.empty()) {
- // streaming, but the app forgot to specify widevine:// url
- mWVMExtractor = static_cast<WVMExtractor *>(extractor.get());
- mWVMExtractor->setAdaptiveStreamingMode(true);
- if (mUIDValid) {
- mWVMExtractor->setUID(mUID);
- }
- }
}
}
}
@@ -332,7 +335,7 @@ void NuPlayer::GenericSource::prepareAsync() {
mLooper->registerHandler(this);
}
- sp<AMessage> msg = new AMessage(kWhatPrepareAsync, id());
+ sp<AMessage> msg = new AMessage(kWhatPrepareAsync, this);
msg->post();
}
@@ -345,6 +348,7 @@ void NuPlayer::GenericSource::onPrepareAsync() {
if (!mUri.empty()) {
const char* uri = mUri.c_str();
+ String8 contentType;
mIsWidevine = !strncasecmp(uri, "widevine://", 11);
if (!strncasecmp("http://", uri, 7)
@@ -359,7 +363,7 @@ void NuPlayer::GenericSource::onPrepareAsync() {
}
mDataSource = DataSource::CreateFromURI(
- mHTTPService, uri, &mUriHeaders, &mContentType,
+ mHTTPService, uri, &mUriHeaders, &contentType,
static_cast<HTTPBase *>(mHttpSource.get()));
} else {
mIsWidevine = false;
@@ -387,20 +391,8 @@ void NuPlayer::GenericSource::onPrepareAsync() {
mIsStreaming = (mIsWidevine || mCachedSource != NULL);
}
- // check initial caching status
- status_t err = prefillCacheIfNecessary();
- if (err != OK) {
- if (err == -EAGAIN) {
- (new AMessage(kWhatPrepareAsync, id()))->post(200000);
- } else {
- ALOGE("Failed to prefill data cache!");
- notifyPreparedAndCleanup(UNKNOWN_ERROR);
- }
- return;
- }
-
- // init extrator from data source
- err = initFromDataSource();
+ // init extractor from data source
+ status_t err = initFromDataSource();
if (err != OK) {
ALOGE("Failed to init from data source!");
@@ -429,7 +421,7 @@ void NuPlayer::GenericSource::onPrepareAsync() {
if (mIsSecure) {
// secure decoders must be instantiated before starting widevine source
- sp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, id());
+ sp<AMessage> reply = new AMessage(kWhatSecureDecodersInstantiated, this);
notifyInstantiateSecureDecoders(reply);
} else {
finishPrepareAsync();
@@ -465,9 +457,6 @@ void NuPlayer::GenericSource::finishPrepareAsync() {
void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
if (err != OK) {
- mMetaDataSize = -1ll;
- mContentType = "";
- mSniffedMIME = "";
mDataSource.clear();
mCachedSource.clear();
mHttpSource.clear();
@@ -478,76 +467,6 @@ void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
notifyPrepared(err);
}
-status_t NuPlayer::GenericSource::prefillCacheIfNecessary() {
- CHECK(mDataSource != NULL);
-
- if (mCachedSource == NULL) {
- // no prefill if the data source is not cached
- return OK;
- }
-
- // We're not doing this for streams that appear to be audio-only
- // streams to ensure that even low bandwidth streams start
- // playing back fairly instantly.
- if (!strncasecmp(mContentType.string(), "audio/", 6)) {
- return OK;
- }
-
- // We're going to prefill the cache before trying to instantiate
- // the extractor below, as the latter is an operation that otherwise
- // could block on the datasource for a significant amount of time.
- // During that time we'd be unable to abort the preparation phase
- // without this prefill.
-
- // Initially make sure we have at least 192 KB for the sniff
- // to complete without blocking.
- static const size_t kMinBytesForSniffing = 192 * 1024;
- static const size_t kDefaultMetaSize = 200000;
-
- status_t finalStatus;
-
- size_t cachedDataRemaining =
- mCachedSource->approxDataRemaining(&finalStatus);
-
- if (finalStatus != OK || (mMetaDataSize >= 0
- && (off64_t)cachedDataRemaining >= mMetaDataSize)) {
- ALOGV("stop caching, status %d, "
- "metaDataSize %lld, cachedDataRemaining %zu",
- finalStatus, mMetaDataSize, cachedDataRemaining);
- return OK;
- }
-
- ALOGV("now cached %zu bytes of data", cachedDataRemaining);
-
- if (mMetaDataSize < 0
- && cachedDataRemaining >= kMinBytesForSniffing) {
- String8 tmp;
- float confidence;
- sp<AMessage> meta;
- if (!mCachedSource->sniff(&tmp, &confidence, &meta)) {
- return UNKNOWN_ERROR;
- }
-
- // We successfully identified the file's extractor to
- // be, remember this mime type so we don't have to
- // sniff it again when we call MediaExtractor::Create()
- mSniffedMIME = tmp.string();
-
- if (meta == NULL
- || !meta->findInt64("meta-data-size",
- reinterpret_cast<int64_t*>(&mMetaDataSize))) {
- mMetaDataSize = kDefaultMetaSize;
- }
-
- if (mMetaDataSize < 0ll) {
- ALOGE("invalid metaDataSize = %lld bytes", mMetaDataSize);
- return UNKNOWN_ERROR;
- }
- }
-
- return -EAGAIN;
-}
-
void NuPlayer::GenericSource::start() {
ALOGI("start");
@@ -563,7 +482,7 @@ void NuPlayer::GenericSource::start() {
setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
mStarted = true;
- (new AMessage(kWhatStart, id()))->post();
+ (new AMessage(kWhatStart, this))->post();
}
void NuPlayer::GenericSource::stop() {
@@ -572,7 +491,7 @@ void NuPlayer::GenericSource::stop() {
mStarted = false;
if (mIsWidevine || mIsSecure) {
// For widevine or secure sources we need to prevent any further reads.
- sp<AMessage> msg = new AMessage(kWhatStopWidevine, id());
+ sp<AMessage> msg = new AMessage(kWhatStopWidevine, this);
sp<AMessage> response;
(void) msg->postAndAwaitResponse(&response);
}
@@ -589,7 +508,7 @@ void NuPlayer::GenericSource::resume() {
setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
mStarted = true;
- (new AMessage(kWhatResume, id()))->post();
+ (new AMessage(kWhatResume, this))->post();
}
void NuPlayer::GenericSource::disconnect() {
@@ -616,7 +535,7 @@ status_t NuPlayer::GenericSource::feedMoreTSData() {
}
void NuPlayer::GenericSource::schedulePollBuffering() {
- sp<AMessage> msg = new AMessage(kWhatPollBuffering, id());
+ sp<AMessage> msg = new AMessage(kWhatPollBuffering, this);
msg->setInt32("generation", mPollBufferingGeneration);
msg->post(1000000ll);
}
@@ -624,6 +543,7 @@ void NuPlayer::GenericSource::schedulePollBuffering() {
void NuPlayer::GenericSource::cancelPollBuffering() {
mBuffering = false;
++mPollBufferingGeneration;
+ mPrevBufferPercentage = -1;
}
void NuPlayer::GenericSource::restartPollBuffering() {
@@ -633,7 +553,19 @@ void NuPlayer::GenericSource::restartPollBuffering() {
}
}
-void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) {
+void NuPlayer::GenericSource::notifyBufferingUpdate(int32_t percentage) {
+ // Buffering percent could go backward as it's estimated from remaining
+ // data and last access time. This could cause the buffering position
+ // drawn on media control to jitter slightly. Remember previously reported
+ // percentage and don't allow it to go backward.
+ if (percentage < mPrevBufferPercentage) {
+ percentage = mPrevBufferPercentage;
+ } else if (percentage > 100) {
+ percentage = 100;
+ }
+
+ mPrevBufferPercentage = percentage;
+
ALOGV("notifyBufferingUpdate: buffering %d%%", percentage);
sp<AMessage> msg = dupNotify();
@@ -687,10 +619,10 @@ void NuPlayer::GenericSource::sendCacheStats() {
int32_t kbps = 0;
status_t err = UNKNOWN_ERROR;
- if (mCachedSource != NULL) {
- err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
- } else if (mWVMExtractor != NULL) {
+ if (mWVMExtractor != NULL) {
err = mWVMExtractor->getEstimatedBandwidthKbps(&kbps);
+ } else if (mCachedSource != NULL) {
+ err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
}
if (err == OK) {
@@ -712,7 +644,13 @@ void NuPlayer::GenericSource::onPollBuffering() {
int64_t cachedDurationUs = -1ll;
ssize_t cachedDataRemaining = -1;
- if (mCachedSource != NULL) {
+ ALOGW_IF(mWVMExtractor != NULL && mCachedSource != NULL,
+ "WVMExtractor and NuCachedSource both present");
+
+ if (mWVMExtractor != NULL) {
+ cachedDurationUs =
+ mWVMExtractor->getCachedDurationUs(&finalStatus);
+ } else if (mCachedSource != NULL) {
cachedDataRemaining =
mCachedSource->approxDataRemaining(&finalStatus);
@@ -728,9 +666,6 @@ void NuPlayer::GenericSource::onPollBuffering() {
cachedDurationUs = cachedDataRemaining * 8000000ll / bitrate;
}
}
- } else if (mWVMExtractor != NULL) {
- cachedDurationUs
- = mWVMExtractor->getCachedDurationUs(&finalStatus);
}
if (finalStatus != OK) {
@@ -918,7 +853,7 @@ void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
mVideoTrack.mPackets->clear();
}
sp<AMessage> response = new AMessage;
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
break;
@@ -958,7 +893,7 @@ void NuPlayer::GenericSource::fetchTextData(
const int64_t oneSecUs = 1000000ll;
delayUs -= oneSecUs;
}
- sp<AMessage> msg2 = new AMessage(sendWhat, id());
+ sp<AMessage> msg2 = new AMessage(sendWhat, this);
msg2->setInt32("generation", msgGeneration);
msg2->post(delayUs < 0 ? 0 : delayUs);
}
@@ -998,7 +933,7 @@ void NuPlayer::GenericSource::sendTextData(
}
sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) {
- sp<AMessage> msg = new AMessage(kWhatGetFormat, id());
+ sp<AMessage> msg = new AMessage(kWhatGetFormat, this);
msg->setInt32("audio", audio);
sp<AMessage> response;
@@ -1020,7 +955,7 @@ void NuPlayer::GenericSource::onGetFormatMeta(sp<AMessage> msg) const {
sp<MetaData> format = doGetFormatMeta(audio);
response->setPointer("format", format.get());
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
}
@@ -1087,7 +1022,7 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit(
if (mSubtitleTrack.mSource != NULL
&& !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) {
- sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
+ sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
msg->setInt64("timeUs", timeUs);
msg->setInt32("generation", mFetchSubtitleDataGeneration);
msg->post();
@@ -1095,7 +1030,7 @@ status_t NuPlayer::GenericSource::dequeueAccessUnit(
if (mTimedTextTrack.mSource != NULL
&& !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) {
- sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id());
+ sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this);
msg->setInt64("timeUs", timeUs);
msg->setInt32("generation", mFetchTimedTextDataGeneration);
msg->post();
@@ -1160,7 +1095,7 @@ sp<AMessage> NuPlayer::GenericSource::getTrackInfo(size_t trackIndex) const {
}
ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const {
- sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id());
+ sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
msg->setInt32("type", type);
sp<AMessage> response;
@@ -1183,7 +1118,7 @@ void NuPlayer::GenericSource::onGetSelectedTrack(sp<AMessage> msg) const {
ssize_t index = doGetSelectedTrack(type);
response->setInt32("index", index);
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
}
@@ -1216,7 +1151,7 @@ ssize_t NuPlayer::GenericSource::doGetSelectedTrack(media_track_type type) const
status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select, int64_t timeUs) {
ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex);
- sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
+ sp<AMessage> msg = new AMessage(kWhatSelectTrack, this);
msg->setInt32("trackIndex", trackIndex);
msg->setInt32("select", select);
msg->setInt64("timeUs", timeUs);
@@ -1241,7 +1176,7 @@ void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) {
status_t err = doSelectTrack(trackIndex, select, timeUs);
response->setInt32("err", err);
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
}
@@ -1302,7 +1237,7 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select,
status_t eosResult; // ignored
if (mSubtitleTrack.mSource != NULL
&& !mSubtitleTrack.mPackets->hasBufferAvailable(&eosResult)) {
- sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
+ sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
msg->setInt64("timeUs", timeUs);
msg->setInt32("generation", mFetchSubtitleDataGeneration);
msg->post();
@@ -1310,7 +1245,7 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select,
if (mTimedTextTrack.mSource != NULL
&& !mTimedTextTrack.mPackets->hasBufferAvailable(&eosResult)) {
- sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, id());
+ sp<AMessage> msg = new AMessage(kWhatFetchTimedTextData, this);
msg->setInt64("timeUs", timeUs);
msg->setInt32("generation", mFetchTimedTextDataGeneration);
msg->post();
@@ -1324,7 +1259,7 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select,
return OK;
}
- sp<AMessage> msg = new AMessage(kWhatChangeAVSource, id());
+ sp<AMessage> msg = new AMessage(kWhatChangeAVSource, this);
msg->setInt32("trackIndex", trackIndex);
msg->post();
return OK;
@@ -1334,7 +1269,7 @@ status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select,
}
status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) {
- sp<AMessage> msg = new AMessage(kWhatSeek, id());
+ sp<AMessage> msg = new AMessage(kWhatSeek, this);
msg->setInt64("seekTimeUs", seekTimeUs);
sp<AMessage> response;
@@ -1354,7 +1289,7 @@ void NuPlayer::GenericSource::onSeek(sp<AMessage> msg) {
status_t err = doSeek(seekTimeUs);
response->setInt32("err", err);
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
}
@@ -1474,7 +1409,7 @@ void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
if ((mPendingReadBufferTypes & (1 << trackType)) == 0) {
mPendingReadBufferTypes |= (1 << trackType);
- sp<AMessage> msg = new AMessage(kWhatReadBuffer, id());
+ sp<AMessage> msg = new AMessage(kWhatReadBuffer, this);
msg->setInt32("trackType", trackType);
msg->post();
}
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 2d73ea9..862ee5f 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -140,14 +140,13 @@ private:
sp<DecryptHandle> mDecryptHandle;
bool mStarted;
bool mStopRead;
- String8 mContentType;
- AString mSniffedMIME;
- off64_t mMetaDataSize;
int64_t mBitrate;
int32_t mPollBufferingGeneration;
uint32_t mPendingReadBufferTypes;
bool mBuffering;
bool mPrepareBuffering;
+ int32_t mPrevBufferPercentage;
+
mutable Mutex mReadBufferLock;
sp<ALooper> mLooper;
@@ -159,8 +158,6 @@ private:
int64_t getLastReadPosition();
void setDrmPlaybackStatusIfNeeded(int playbackStatus, int64_t position);
- status_t prefillCacheIfNecessary();
-
void notifyPreparedAndCleanup(status_t err);
void onSecureDecodersInstantiated(status_t err);
void finishPrepareAsync();
@@ -204,7 +201,7 @@ private:
void cancelPollBuffering();
void restartPollBuffering();
void onPollBuffering();
- void notifyBufferingUpdate(int percentage);
+ void notifyBufferingUpdate(int32_t percentage);
void startBufferingIfNecessary();
void stopBufferingIfNecessary();
void sendCacheStats();
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index a26ef9e..0476c9b 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -81,7 +81,7 @@ void NuPlayer::HTTPLiveSource::prepareAsync() {
mLiveLooper->registerHandler(this);
}
- sp<AMessage> notify = new AMessage(kWhatSessionNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatSessionNotify, this);
mLiveSession = new LiveSession(
notify,
@@ -153,7 +153,7 @@ status_t NuPlayer::HTTPLiveSource::selectTrack(size_t trackIndex, bool select, i
if (err == OK) {
mFetchSubtitleDataGeneration++;
if (select) {
- sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, id());
+ sp<AMessage> msg = new AMessage(kWhatFetchSubtitleData, this);
msg->setInt32("generation", mFetchSubtitleDataGeneration);
msg->post();
}
@@ -281,6 +281,34 @@ void NuPlayer::HTTPLiveSource::onSessionNotify(const sp<AMessage> &msg) {
break;
}
+ case LiveSession::kWhatBufferingStart:
+ {
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatPauseOnBufferingStart);
+ notify->post();
+ break;
+ }
+
+ case LiveSession::kWhatBufferingEnd:
+ {
+ sp<AMessage> notify = dupNotify();
+ notify->setInt32("what", kWhatResumeOnBufferingEnd);
+ notify->post();
+ break;
+ }
+
+
+ case LiveSession::kWhatBufferingUpdate:
+ {
+ sp<AMessage> notify = dupNotify();
+ int32_t percentage;
+ CHECK(msg->findInt32("percentage", &percentage));
+ notify->setInt32("what", kWhatBufferingUpdate);
+ notify->setInt32("percentage", percentage);
+ notify->post();
+ break;
+ }
+
case LiveSession::kWhatError:
{
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index aeea204..02d9f32 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -180,6 +180,7 @@ NuPlayer::NuPlayer()
mFlushingVideo(NONE),
mResumePending(false),
mVideoScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW),
+ mPlaybackRate(1.0),
mStarted(false),
mPaused(false),
mPausedByClient(false) {
@@ -199,9 +200,9 @@ void NuPlayer::setDriver(const wp<NuPlayerDriver> &driver) {
}
void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) {
- sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+ sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
- sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
msg->setObject("source", new StreamingSource(notify, source));
msg->post();
@@ -229,10 +230,10 @@ void NuPlayer::setDataSourceAsync(
const char *url,
const KeyedVector<String8, String8> *headers) {
- sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+ sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
size_t len = strlen(url);
- sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
sp<Source> source;
if (IsHTTPLiveURL(url)) {
@@ -266,9 +267,9 @@ void NuPlayer::setDataSourceAsync(
}
void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
- sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+ sp<AMessage> msg = new AMessage(kWhatSetDataSource, this);
- sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatSourceNotify, this);
sp<GenericSource> source =
new GenericSource(notify, mUIDValid, mUID);
@@ -285,12 +286,12 @@ void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) {
}
void NuPlayer::prepareAsync() {
- (new AMessage(kWhatPrepare, id()))->post();
+ (new AMessage(kWhatPrepare, this))->post();
}
void NuPlayer::setVideoSurfaceTextureAsync(
const sp<IGraphicBufferProducer> &bufferProducer) {
- sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, id());
+ sp<AMessage> msg = new AMessage(kWhatSetVideoNativeWindow, this);
if (bufferProducer == NULL) {
msg->setObject("native-window", NULL);
@@ -305,17 +306,23 @@ void NuPlayer::setVideoSurfaceTextureAsync(
}
void NuPlayer::setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink) {
- sp<AMessage> msg = new AMessage(kWhatSetAudioSink, id());
+ sp<AMessage> msg = new AMessage(kWhatSetAudioSink, this);
msg->setObject("sink", sink);
msg->post();
}
void NuPlayer::start() {
- (new AMessage(kWhatStart, id()))->post();
+ (new AMessage(kWhatStart, this))->post();
+}
+
+void NuPlayer::setPlaybackRate(float rate) {
+ sp<AMessage> msg = new AMessage(kWhatSetRate, this);
+ msg->setFloat("rate", rate);
+ msg->post();
}
void NuPlayer::pause() {
- (new AMessage(kWhatPause, id()))->post();
+ (new AMessage(kWhatPause, this))->post();
}
void NuPlayer::resetAsync() {
@@ -329,11 +336,11 @@ void NuPlayer::resetAsync() {
mSource->disconnect();
}
- (new AMessage(kWhatReset, id()))->post();
+ (new AMessage(kWhatReset, this))->post();
}
void NuPlayer::seekToAsync(int64_t seekTimeUs, bool needNotify) {
- sp<AMessage> msg = new AMessage(kWhatSeek, id());
+ sp<AMessage> msg = new AMessage(kWhatSeek, this);
msg->setInt64("seekTimeUs", seekTimeUs);
msg->setInt32("needNotify", needNotify);
msg->post();
@@ -401,7 +408,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatGetTrackInfo:
{
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
Parcel* reply;
@@ -454,7 +461,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
sp<AMessage> response = new AMessage;
response->setInt32("err", err);
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
break;
@@ -462,7 +469,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatSelectTrack:
{
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
size_t trackIndex;
@@ -604,6 +611,16 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case kWhatSetRate:
+ {
+ ALOGV("kWhatSetRate");
+ CHECK(msg->findFloat("rate", &mPlaybackRate));
+ if (mRenderer != NULL) {
+ mRenderer->setPlaybackRate(mPlaybackRate);
+ }
+ break;
+ }
+
case kWhatScanSources:
{
int32_t generation;
@@ -1062,15 +1079,17 @@ void NuPlayer::onStart() {
flags |= Renderer::FLAG_OFFLOAD_AUDIO;
}
- sp<AMessage> notify = new AMessage(kWhatRendererNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatRendererNotify, this);
++mRendererGeneration;
notify->setInt32("generation", mRendererGeneration);
mRenderer = new Renderer(mAudioSink, notify, flags);
-
mRendererLooper = new ALooper;
mRendererLooper->setName("NuPlayerRenderer");
mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
mRendererLooper->registerHandler(mRenderer);
+ if (mPlaybackRate != 1.0) {
+ mRenderer->setPlaybackRate(mPlaybackRate);
+ }
sp<MetaData> meta = getFileMeta();
int32_t rate;
@@ -1176,7 +1195,7 @@ void NuPlayer::postScanSources() {
return;
}
- sp<AMessage> msg = new AMessage(kWhatScanSources, id());
+ sp<AMessage> msg = new AMessage(kWhatScanSources, this);
msg->setInt32("generation", mScanSourcesGeneration);
msg->post();
@@ -1218,7 +1237,7 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) {
AString mime;
CHECK(format->findString("mime", &mime));
- sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id());
+ sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, this);
if (mCCDecoder == NULL) {
mCCDecoder = new CCDecoder(ccNotify);
}
@@ -1233,17 +1252,19 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<DecoderBase> *decoder) {
}
if (audio) {
- sp<AMessage> notify = new AMessage(kWhatAudioNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatAudioNotify, this);
++mAudioDecoderGeneration;
notify->setInt32("generation", mAudioDecoderGeneration);
if (mOffloadAudio) {
+ const bool hasVideo = (mSource->getFormat(false /*audio */) != NULL);
+ format->setInt32("has-video", hasVideo);
*decoder = new DecoderPassThrough(notify, mSource, mRenderer);
} else {
*decoder = new Decoder(notify, mSource, mRenderer);
}
} else {
- sp<AMessage> notify = new AMessage(kWhatVideoNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatVideoNotify, this);
++mVideoDecoderGeneration;
notify->setInt32("generation", mVideoDecoderGeneration);
@@ -1434,7 +1455,7 @@ status_t NuPlayer::setVideoScalingMode(int32_t mode) {
}
status_t NuPlayer::getTrackInfo(Parcel* reply) const {
- sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, id());
+ sp<AMessage> msg = new AMessage(kWhatGetTrackInfo, this);
msg->setPointer("reply", reply);
sp<AMessage> response;
@@ -1443,7 +1464,7 @@ status_t NuPlayer::getTrackInfo(Parcel* reply) const {
}
status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const {
- sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id());
+ sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, this);
msg->setPointer("reply", reply);
msg->setInt32("type", type);
@@ -1456,7 +1477,7 @@ status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const {
}
status_t NuPlayer::selectTrack(size_t trackIndex, bool select, int64_t timeUs) {
- sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
+ sp<AMessage> msg = new AMessage(kWhatSelectTrack, this);
msg->setSize("trackIndex", trackIndex);
msg->setInt32("select", select);
msg->setInt64("timeUs", timeUs);
@@ -1499,7 +1520,7 @@ sp<MetaData> NuPlayer::getFileMeta() {
}
void NuPlayer::schedulePollDuration() {
- sp<AMessage> msg = new AMessage(kWhatPollDuration, id());
+ sp<AMessage> msg = new AMessage(kWhatPollDuration, this);
msg->setInt32("generation", mPollDurationGeneration);
msg->post();
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 30ede1a..2bc20d7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -51,6 +51,7 @@ struct NuPlayer : public AHandler {
const sp<IGraphicBufferProducer> &bufferProducer);
void setAudioSink(const sp<MediaPlayerBase::AudioSink> &sink);
+ void setPlaybackRate(float rate);
void start();
void pause();
@@ -104,6 +105,7 @@ private:
kWhatSetVideoNativeWindow = '=NaW',
kWhatSetAudioSink = '=AuS',
kWhatMoreDataQueued = 'more',
+ kWhatSetRate = 'setR',
kWhatStart = 'strt',
kWhatScanSources = 'scan',
kWhatVideoNotify = 'vidN',
@@ -175,6 +177,7 @@ private:
int32_t mVideoScalingMode;
+ float mPlaybackRate;
bool mStarted;
// Actual pause state, either as requested by client or due to buffering.
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
index 9229704..cf3e8ad 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <inttypes.h>
+#include "avc_utils.h"
#include "NuPlayerCCDecoder.h"
#include <media/stagefright/foundation/ABitReader.h>
@@ -185,17 +186,38 @@ int32_t NuPlayer::CCDecoder::getTrackIndex(size_t channel) const {
// returns true if a new CC track is found
bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
- int64_t timeUs;
- CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
-
sp<ABuffer> sei;
if (!accessUnit->meta()->findBuffer("sei", &sei) || sei == NULL) {
return false;
}
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
bool trackAdded = false;
- NALBitReader br(sei->data() + 1, sei->size() - 1);
+ const NALPosition *nal = (NALPosition *) sei->data();
+
+ for (size_t i = 0; i < sei->size() / sizeof(NALPosition); ++i, ++nal) {
+ trackAdded |= parseSEINalUnit(
+ timeUs, accessUnit->data() + nal->nalOffset, nal->nalSize);
+ }
+
+ return trackAdded;
+}
+
+// returns true if a new CC track is found
+bool NuPlayer::CCDecoder::parseSEINalUnit(
+ int64_t timeUs, const uint8_t *nalStart, size_t nalSize) {
+ unsigned nalType = nalStart[0] & 0x1f;
+
+ // the buffer should only have SEI in it
+ if (nalType != 6) {
+ return false;
+ }
+
+ bool trackAdded = false;
+ NALBitReader br(nalStart + 1, nalSize - 1);
// sei_message()
while (br.atLeastNumBitsLeft(16)) { // at least 16-bit for sei_message()
uint32_t payload_type = 0;
@@ -214,20 +236,25 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
// sei_payload()
if (payload_type == 4) {
- // user_data_registered_itu_t_t35()
-
- // ATSC A/72: 6.4.2
- uint8_t itu_t_t35_country_code = br.getBits(8);
- uint16_t itu_t_t35_provider_code = br.getBits(16);
- uint32_t user_identifier = br.getBits(32);
- uint8_t user_data_type_code = br.getBits(8);
-
- payload_size -= 1 + 2 + 4 + 1;
+ bool isCC = false;
+ if (payload_size > 1 + 2 + 4 + 1) {
+ // user_data_registered_itu_t_t35()
+
+ // ATSC A/72: 6.4.2
+ uint8_t itu_t_t35_country_code = br.getBits(8);
+ uint16_t itu_t_t35_provider_code = br.getBits(16);
+ uint32_t user_identifier = br.getBits(32);
+ uint8_t user_data_type_code = br.getBits(8);
+
+ payload_size -= 1 + 2 + 4 + 1;
+
+ isCC = itu_t_t35_country_code == 0xB5
+ && itu_t_t35_provider_code == 0x0031
+ && user_identifier == 'GA94'
+ && user_data_type_code == 0x3;
+ }
- if (itu_t_t35_country_code == 0xB5
- && itu_t_t35_provider_code == 0x0031
- && user_identifier == 'GA94'
- && user_data_type_code == 0x3) {
+ if (isCC && payload_size > 2) {
// MPEG_cc_data()
// ATSC A/53 Part 4: 6.2.3.1
br.skipBits(1); //process_em_data_flag
@@ -243,7 +270,7 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
sp<ABuffer> ccBuf = new ABuffer(cc_count * sizeof(CCData));
ccBuf->setRange(0, 0);
- for (size_t i = 0; i < cc_count; i++) {
+ for (size_t i = 0; i < cc_count && payload_size >= 3; i++) {
uint8_t marker = br.getBits(5);
CHECK_EQ(marker, 0x1f);
@@ -253,6 +280,8 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
uint8_t cc_data_1 = br.getBits(8) & 0x7f;
uint8_t cc_data_2 = br.getBits(8) & 0x7f;
+ payload_size -= 3;
+
if (cc_valid
&& (cc_type == 0 || cc_type == 1)) {
CCData cc(cc_type, cc_data_1, cc_data_2);
@@ -269,7 +298,6 @@ bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
}
}
}
- payload_size -= cc_count * 3;
mCCMap.add(timeUs, ccBuf);
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h
index 5e06f4e..77fb0fe 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerCCDecoder.h
@@ -49,6 +49,7 @@ private:
bool isTrackValid(size_t index) const;
int32_t getTrackIndex(size_t channel) const;
bool extractFromSEI(const sp<ABuffer> &accessUnit);
+ bool parseSEINalUnit(int64_t timeUs, const uint8_t *nalStart, size_t nalSize);
sp<ABuffer> filterCCBuf(const sp<ABuffer> &ccBuf, size_t index);
DISALLOW_EVIL_CONSTRUCTORS(CCDecoder);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 5d98d98..04ac699 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -56,6 +56,7 @@ NuPlayer::Decoder::Decoder(
mIsVideoAVC(false),
mIsSecure(false),
mFormatChangePending(false),
+ mTimeChangePending(false),
mPaused(true),
mResumePending(false),
mComponentName("decoder") {
@@ -121,6 +122,7 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK(mCodec == NULL);
mFormatChangePending = false;
+ mTimeChangePending = false;
++mBufferGeneration;
@@ -235,7 +237,7 @@ void NuPlayer::Decoder::onResume(bool notifyComplete) {
}
}
-void NuPlayer::Decoder::onFlush(bool notifyComplete) {
+void NuPlayer::Decoder::doFlush(bool notifyComplete) {
if (mCCDecoder != NULL) {
mCCDecoder->flush();
}
@@ -259,13 +261,22 @@ void NuPlayer::Decoder::onFlush(bool notifyComplete) {
// we attempt to release the buffers even if flush fails.
}
releaseAndResetMediaBuffers();
+}
- if (notifyComplete) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatFlushCompleted);
- notify->post();
- mPaused = true;
+void NuPlayer::Decoder::onFlush() {
+ doFlush(true);
+
+ if (isDiscontinuityPending()) {
+ // This could happen if the client starts seeking/shutdown
+ // after we queued an EOS for discontinuities.
+ // We can consider discontinuity handled.
+ finishHandleDiscontinuity(false /* flushOnTimeChange */);
}
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushCompleted);
+ notify->post();
+ mPaused = true;
}
void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -309,16 +320,17 @@ void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
}
void NuPlayer::Decoder::doRequestBuffers() {
- if (mFormatChangePending) {
+ if (isDiscontinuityPending()) {
return;
}
status_t err = OK;
- while (!mDequeuedInputBuffers.empty()) {
+ while (err == OK && !mDequeuedInputBuffers.empty()) {
size_t bufferIx = *mDequeuedInputBuffers.begin();
sp<AMessage> msg = new AMessage();
msg->setSize("buffer-ix", bufferIx);
err = fetchInputData(msg);
- if (err != OK) {
+ if (err != OK && err != ERROR_END_OF_STREAM) {
+ // if EOS, need to queue EOS buffer
break;
}
mDequeuedInputBuffers.erase(mDequeuedInputBuffers.begin());
@@ -336,7 +348,7 @@ void NuPlayer::Decoder::doRequestBuffers() {
}
bool NuPlayer::Decoder::handleAnInputBuffer() {
- if (mFormatChangePending) {
+ if (isDiscontinuityPending()) {
return false;
}
size_t bufferIx = -1;
@@ -391,9 +403,6 @@ bool NuPlayer::Decoder::handleAnInputBuffer() {
}
bool NuPlayer::Decoder::handleAnOutputBuffer() {
- if (mFormatChangePending) {
- return false;
- }
size_t bufferIx = -1;
size_t offset;
size_t size;
@@ -474,17 +483,20 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
buffer->setRange(offset, size);
buffer->meta()->clear();
buffer->meta()->setInt64("timeUs", timeUs);
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
- buffer->meta()->setInt32("eos", true);
- notifyResumeCompleteIfNecessary();
- }
+
+ bool eos = flags & MediaCodec::BUFFER_FLAG_EOS;
// we do not expect CODECCONFIG or SYNCFRAME for decoder
- sp<AMessage> reply = new AMessage(kWhatRenderBuffer, id());
+ sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
reply->setSize("buffer-ix", bufferIx);
reply->setInt32("generation", mBufferGeneration);
- if (mSkipRenderingUntilMediaTimeUs >= 0) {
+ if (eos) {
+ ALOGI("[%s] saw output EOS", mIsAudio ? "audio" : "video");
+
+ buffer->meta()->setInt32("eos", true);
+ reply->setInt32("eos", true);
+ } else if (mSkipRenderingUntilMediaTimeUs >= 0) {
if (timeUs < mSkipRenderingUntilMediaTimeUs) {
ALOGV("[%s] dropping buffer at time %lld as requested.",
mComponentName.c_str(), (long long)timeUs);
@@ -502,7 +514,7 @@ bool NuPlayer::Decoder::handleAnOutputBuffer() {
if (mRenderer != NULL) {
// send the buffer to renderer.
mRenderer->queueBuffer(mIsAudio, buffer, reply);
- if (flags & MediaCodec::BUFFER_FLAG_EOS) {
+ if (eos && !isDiscontinuityPending()) {
mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
}
}
@@ -533,11 +545,8 @@ void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
}
void NuPlayer::Decoder::requestCodecNotification() {
- if (mFormatChangePending) {
- return;
- }
if (mCodec != NULL) {
- sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
+ sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
reply->setInt32("generation", mBufferGeneration);
mCodec->requestActivityNotification(reply);
}
@@ -582,39 +591,31 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
formatChange = !seamlessFormatChange;
}
- if (formatChange || timeChange) {
- sp<AMessage> msg = mNotify->dup();
- msg->setInt32("what", kWhatInputDiscontinuity);
- msg->setInt32("formatChange", formatChange);
- msg->post();
- }
-
+ // For format or time change, return EOS to queue EOS input,
+ // then wait for EOS on output.
if (formatChange /* not seamless */) {
- // must change decoder
- // return EOS and wait to be killed
mFormatChangePending = true;
- return ERROR_END_OF_STREAM;
+ err = ERROR_END_OF_STREAM;
} else if (timeChange) {
- // need to flush
- // TODO: Ideally we shouldn't need a flush upon time
- // discontinuity, flushing will cause loss of frames.
- // We probably should queue a time change marker to the
- // output queue, and handles it in renderer instead.
rememberCodecSpecificData(newFormat);
- onFlush(false /* notifyComplete */);
- err = OK;
+ mTimeChangePending = true;
+ err = ERROR_END_OF_STREAM;
} else if (seamlessFormatChange) {
// reuse existing decoder and don't flush
rememberCodecSpecificData(newFormat);
- err = OK;
+ continue;
} else {
// This stream is unaffected by the discontinuity
return -EWOULDBLOCK;
}
}
+ // reply should only be returned without a buffer set
+ // when there is an error (including EOS)
+ CHECK(err != OK);
+
reply->setInt32("err", err);
- return OK;
+ return ERROR_END_OF_STREAM;
}
if (!mIsAudio) {
@@ -636,7 +637,7 @@ status_t NuPlayer::Decoder::fetchInputData(sp<AMessage> &reply) {
#if 0
int64_t mediaTimeUs;
CHECK(accessUnit->meta()->findInt64("timeUs", &mediaTimeUs));
- ALOGV("feeding %s input buffer at media time %.2f secs",
+ ALOGV("[%s] feeding input buffer at media time %" PRId64,
mIsAudio ? "audio" : "video",
mediaTimeUs / 1E6);
#endif
@@ -696,10 +697,7 @@ bool NuPlayer::Decoder::onInputBufferFetched(const sp<AMessage> &msg) {
int32_t streamErr = ERROR_END_OF_STREAM;
CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
- if (streamErr == OK) {
- /* buffers are returned to hold on to */
- return true;
- }
+ CHECK(streamErr != OK);
// attempt to queue EOS
status_t err = mCodec->queueInputBuffer(
@@ -781,6 +779,7 @@ void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
status_t err;
int32_t render;
size_t bufferIx;
+ int32_t eos;
CHECK(msg->findSize("buffer-ix", &bufferIx));
if (!mIsAudio) {
@@ -805,6 +804,42 @@ void NuPlayer::Decoder::onRenderBuffer(const sp<AMessage> &msg) {
mComponentName.c_str(), err);
handleError(err);
}
+ if (msg->findInt32("eos", &eos) && eos
+ && isDiscontinuityPending()) {
+ finishHandleDiscontinuity(true /* flushOnTimeChange */);
+ }
+}
+
+bool NuPlayer::Decoder::isDiscontinuityPending() const {
+ return mFormatChangePending || mTimeChangePending;
+}
+
+void NuPlayer::Decoder::finishHandleDiscontinuity(bool flushOnTimeChange) {
+ ALOGV("finishHandleDiscontinuity: format %d, time %d, flush %d",
+ mFormatChangePending, mTimeChangePending, flushOnTimeChange);
+
+ // If we have format change, pause and wait to be killed;
+ // If we have time change only, flush and restart fetching.
+
+ if (mFormatChangePending) {
+ mPaused = true;
+ } else if (mTimeChangePending) {
+ if (flushOnTimeChange) {
+ doFlush(false /*notifyComplete*/);
+ }
+
+ // restart fetching input
+ scheduleRequestBuffers();
+ }
+
+ // Notify NuPlayer to either shutdown decoder, or rescan sources
+ sp<AMessage> msg = mNotify->dup();
+ msg->setInt32("what", kWhatInputDiscontinuity);
+ msg->setInt32("formatChange", mFormatChangePending);
+ msg->post();
+
+ mFormatChangePending = false;
+ mTimeChangePending = false;
}
bool NuPlayer::Decoder::supportsSeamlessAudioFormatChange(
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 1bfa94f..4aab2c6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -43,7 +43,7 @@ protected:
virtual void onSetRenderer(const sp<Renderer> &renderer);
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
- virtual void onFlush(bool notifyComplete);
+ virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
virtual void doRequestBuffers();
@@ -81,6 +81,7 @@ private:
bool mIsVideoAVC;
bool mIsSecure;
bool mFormatChangePending;
+ bool mTimeChangePending;
bool mPaused;
bool mResumePending;
@@ -93,6 +94,7 @@ private:
void requestCodecNotification();
bool isStaleReply(const sp<AMessage> &msg);
+ void doFlush(bool notifyComplete);
status_t fetchInputData(sp<AMessage> &reply);
bool onInputBufferFetched(const sp<AMessage> &msg);
void onRenderBuffer(const sp<AMessage> &msg);
@@ -100,6 +102,8 @@ private:
bool supportsSeamlessFormatChange(const sp<AMessage> &to) const;
bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
void rememberCodecSpecificData(const sp<AMessage> &format);
+ bool isDiscontinuityPending() const;
+ void finishHandleDiscontinuity(bool flushOnTimeChange);
void notifyResumeCompleteIfNecessary();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index d56fc4d..4636f0a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -61,7 +61,7 @@ status_t PostAndAwaitResponse(
}
void NuPlayer::DecoderBase::configure(const sp<AMessage> &format) {
- sp<AMessage> msg = new AMessage(kWhatConfigure, id());
+ sp<AMessage> msg = new AMessage(kWhatConfigure, this);
msg->setMessage("format", format);
msg->post();
}
@@ -71,13 +71,13 @@ void NuPlayer::DecoderBase::init() {
}
void NuPlayer::DecoderBase::setRenderer(const sp<Renderer> &renderer) {
- sp<AMessage> msg = new AMessage(kWhatSetRenderer, id());
+ sp<AMessage> msg = new AMessage(kWhatSetRenderer, this);
msg->setObject("renderer", renderer);
msg->post();
}
status_t NuPlayer::DecoderBase::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
- sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id());
+ sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, this);
msg->setPointer("buffers", buffers);
sp<AMessage> response;
@@ -85,17 +85,17 @@ status_t NuPlayer::DecoderBase::getInputBuffers(Vector<sp<ABuffer> > *buffers) c
}
void NuPlayer::DecoderBase::signalFlush() {
- (new AMessage(kWhatFlush, id()))->post();
+ (new AMessage(kWhatFlush, this))->post();
}
void NuPlayer::DecoderBase::signalResume(bool notifyComplete) {
- sp<AMessage> msg = new AMessage(kWhatResume, id());
+ sp<AMessage> msg = new AMessage(kWhatResume, this);
msg->setInt32("notifyComplete", notifyComplete);
msg->post();
}
void NuPlayer::DecoderBase::initiateShutdown() {
- (new AMessage(kWhatShutdown, id()))->post();
+ (new AMessage(kWhatShutdown, this))->post();
}
void NuPlayer::DecoderBase::onRequestInputBuffers() {
@@ -111,7 +111,7 @@ void NuPlayer::DecoderBase::scheduleRequestBuffers() {
return;
}
mRequestInputBuffersPending = true;
- sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, id());
+ sp<AMessage> msg = new AMessage(kWhatRequestInputBuffers, this);
msg->post(10 * 1000ll);
}
@@ -136,7 +136,7 @@ void NuPlayer::DecoderBase::onMessageReceived(const sp<AMessage> &msg) {
case kWhatGetInputBuffers:
{
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
Vector<sp<ABuffer> > *dstBuffers;
@@ -157,7 +157,7 @@ void NuPlayer::DecoderBase::onMessageReceived(const sp<AMessage> &msg) {
case kWhatFlush:
{
- onFlush(true);
+ onFlush();
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index 6732ff4..97e9269 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -65,7 +65,7 @@ protected:
virtual void onSetRenderer(const sp<Renderer> &renderer) = 0;
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers) = 0;
virtual void onResume(bool notifyComplete) = 0;
- virtual void onFlush(bool notifyComplete) = 0;
+ virtual void onFlush() = 0;
virtual void onShutdown(bool notifyComplete) = 0;
void onRequestInputBuffers();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index 9f7f09a..563de5e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -74,11 +74,14 @@ void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) {
onRequestInputBuffers();
+ int32_t hasVideo = 0;
+ format->findInt32("has-video", &hasVideo);
+
// The audio sink is already opened before the PassThrough decoder is created.
// Opening again might be relevant if decoder is instantiated after shutdown and
// format is different.
status_t err = mRenderer->openAudioSink(
- format, true /* offloadOnly */, false /* hasVideo */,
+ format, true /* offloadOnly */, hasVideo,
AUDIO_OUTPUT_FLAG_NONE /* flags */, NULL /* isOffloaded */);
if (err != OK) {
handleError(err);
@@ -247,7 +250,7 @@ status_t NuPlayer::DecoderPassThrough::fetchInputData(sp<AMessage> &reply) {
}
if (timeChange) {
- onFlush(false /* notifyComplete */);
+ doFlush(false /* notifyComplete */);
err = OK;
} else if (formatChange) {
// do seamless format change
@@ -333,7 +336,7 @@ void NuPlayer::DecoderPassThrough::onInputBufferFetched(
return;
}
- sp<AMessage> reply = new AMessage(kWhatBufferConsumed, id());
+ sp<AMessage> reply = new AMessage(kWhatBufferConsumed, this);
reply->setInt32("generation", mBufferGeneration);
reply->setInt32("size", bufferSize);
@@ -364,7 +367,7 @@ void NuPlayer::DecoderPassThrough::onResume(bool notifyComplete) {
}
}
-void NuPlayer::DecoderPassThrough::onFlush(bool notifyComplete) {
+void NuPlayer::DecoderPassThrough::doFlush(bool notifyComplete) {
++mBufferGeneration;
mSkipRenderingUntilMediaTimeUs = -1;
mPendingAudioAccessUnit.clear();
@@ -376,18 +379,21 @@ void NuPlayer::DecoderPassThrough::onFlush(bool notifyComplete) {
mRenderer->signalTimeDiscontinuity();
}
- if (notifyComplete) {
- mPaused = true;
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatFlushCompleted);
- notify->post();
- }
-
mPendingBuffersToDrain = 0;
mCachedBytes = 0;
mReachedEOS = false;
}
+void NuPlayer::DecoderPassThrough::onFlush() {
+ doFlush(true /* notifyComplete */);
+
+ mPaused = true;
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatFlushCompleted);
+ notify->post();
+
+}
+
void NuPlayer::DecoderPassThrough::onShutdown(bool notifyComplete) {
++mBufferGeneration;
mSkipRenderingUntilMediaTimeUs = -1;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index a6e1faf..173cfbd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -43,7 +43,7 @@ protected:
virtual void onSetRenderer(const sp<Renderer> &renderer);
virtual void onGetInputBuffers(Vector<sp<ABuffer> > *dstBuffers);
virtual void onResume(bool notifyComplete);
- virtual void onFlush(bool notifyComplete);
+ virtual void onFlush();
virtual void onShutdown(bool notifyComplete);
virtual void doRequestBuffers();
@@ -77,6 +77,7 @@ private:
status_t dequeueAccessUnit(sp<ABuffer> *accessUnit);
sp<ABuffer> aggregateBuffer(const sp<ABuffer> &accessUnit);
status_t fetchInputData(sp<AMessage> &reply);
+ void doFlush(bool notifyComplete);
void onInputBufferFetched(const sp<AMessage> &msg);
void onBufferConsumed(int32_t size);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index bc79fdb..1fa9cef 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -341,6 +341,11 @@ bool NuPlayerDriver::isPlaying() {
return mState == STATE_RUNNING && !mAtEOS;
}
+status_t NuPlayerDriver::setPlaybackRate(float rate) {
+ mPlayer->setPlaybackRate(rate);
+ return OK;
+}
+
status_t NuPlayerDriver::seekTo(int msec) {
ALOGD("seekTo(%p) %d ms", this, msec);
Mutex::Autolock autoLock(mLock);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index 5cba7d9..e53abcd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -47,6 +47,7 @@ struct NuPlayerDriver : public MediaPlayerInterface {
virtual status_t stop();
virtual status_t pause();
virtual bool isPlaying();
+ virtual status_t setPlaybackRate(float rate);
virtual status_t seekTo(int msec);
virtual status_t getCurrentPosition(int *msec);
virtual status_t getDuration(int *msec);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 25225a8..a2ec51c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -25,6 +25,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AUtils.h>
#include <media/stagefright/foundation/AWakeLock.h>
+#include <media/stagefright/MediaClock.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
@@ -63,22 +64,19 @@ NuPlayer::Renderer::Renderer(
mDrainVideoQueuePending(false),
mAudioQueueGeneration(0),
mVideoQueueGeneration(0),
+ mAudioDrainGeneration(0),
+ mVideoDrainGeneration(0),
+ mPlaybackRate(1.0),
mAudioFirstAnchorTimeMediaUs(-1),
mAnchorTimeMediaUs(-1),
- mAnchorTimeRealUs(-1),
mAnchorNumFramesWritten(-1),
- mAnchorMaxMediaUs(-1),
mVideoLateByUs(0ll),
mHasAudio(false),
mHasVideo(false),
- mPauseStartedTimeRealUs(-1),
- mFlushingAudio(false),
- mFlushingVideo(false),
mNotifyCompleteAudio(false),
mNotifyCompleteVideo(false),
mSyncQueues(false),
mPaused(false),
- mPausePositionMediaTimeUs(-1),
mVideoSampleReceived(false),
mVideoRenderingStarted(false),
mVideoRenderingStartGeneration(0),
@@ -90,7 +88,7 @@ NuPlayer::Renderer::Renderer(
mTotalBuffersQueued(0),
mLastAudioBufferDrained(0),
mWakeLock(new AWakeLock()) {
-
+ mMediaClock = new MediaClock;
}
NuPlayer::Renderer::~Renderer() {
@@ -105,7 +103,8 @@ void NuPlayer::Renderer::queueBuffer(
bool audio,
const sp<ABuffer> &buffer,
const sp<AMessage> &notifyConsumed) {
- sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
+ sp<AMessage> msg = new AMessage(kWhatQueueBuffer, this);
+ msg->setInt32("queueGeneration", getQueueGeneration(audio));
msg->setInt32("audio", static_cast<int32_t>(audio));
msg->setBuffer("buffer", buffer);
msg->setMessage("notifyConsumed", notifyConsumed);
@@ -115,199 +114,108 @@ void NuPlayer::Renderer::queueBuffer(
void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
CHECK_NE(finalResult, (status_t)OK);
- sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
+ sp<AMessage> msg = new AMessage(kWhatQueueEOS, this);
+ msg->setInt32("queueGeneration", getQueueGeneration(audio));
msg->setInt32("audio", static_cast<int32_t>(audio));
msg->setInt32("finalResult", finalResult);
msg->post();
}
+void NuPlayer::Renderer::setPlaybackRate(float rate) {
+ sp<AMessage> msg = new AMessage(kWhatSetRate, this);
+ msg->setFloat("rate", rate);
+ msg->post();
+}
+
void NuPlayer::Renderer::flush(bool audio, bool notifyComplete) {
{
- Mutex::Autolock autoLock(mFlushLock);
+ Mutex::Autolock autoLock(mLock);
if (audio) {
mNotifyCompleteAudio |= notifyComplete;
- if (mFlushingAudio) {
- return;
- }
- mFlushingAudio = true;
+ ++mAudioQueueGeneration;
+ ++mAudioDrainGeneration;
} else {
mNotifyCompleteVideo |= notifyComplete;
- if (mFlushingVideo) {
- return;
- }
- mFlushingVideo = true;
+ ++mVideoQueueGeneration;
+ ++mVideoDrainGeneration;
}
+
+ clearAnchorTime_l();
+ clearAudioFirstAnchorTime_l();
+ mVideoLateByUs = 0;
+ mSyncQueues = false;
}
- sp<AMessage> msg = new AMessage(kWhatFlush, id());
+ sp<AMessage> msg = new AMessage(kWhatFlush, this);
msg->setInt32("audio", static_cast<int32_t>(audio));
msg->post();
}
void NuPlayer::Renderer::signalTimeDiscontinuity() {
- Mutex::Autolock autoLock(mLock);
- // CHECK(mAudioQueue.empty());
- // CHECK(mVideoQueue.empty());
- setAudioFirstAnchorTime(-1);
- setAnchorTime(-1, -1);
- setVideoLateByUs(0);
- mSyncQueues = false;
-}
-
-void NuPlayer::Renderer::signalAudioSinkChanged() {
- (new AMessage(kWhatAudioSinkChanged, id()))->post();
}
void NuPlayer::Renderer::signalDisableOffloadAudio() {
- (new AMessage(kWhatDisableOffloadAudio, id()))->post();
+ (new AMessage(kWhatDisableOffloadAudio, this))->post();
}
void NuPlayer::Renderer::signalEnableOffloadAudio() {
- (new AMessage(kWhatEnableOffloadAudio, id()))->post();
+ (new AMessage(kWhatEnableOffloadAudio, this))->post();
}
void NuPlayer::Renderer::pause() {
- (new AMessage(kWhatPause, id()))->post();
+ (new AMessage(kWhatPause, this))->post();
}
void NuPlayer::Renderer::resume() {
- (new AMessage(kWhatResume, id()))->post();
+ (new AMessage(kWhatResume, this))->post();
}
void NuPlayer::Renderer::setVideoFrameRate(float fps) {
- sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, id());
+ sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, this);
msg->setFloat("frame-rate", fps);
msg->post();
}
-// Called on any threads, except renderer's thread.
-status_t NuPlayer::Renderer::getCurrentPosition(int64_t *mediaUs) {
- {
- Mutex::Autolock autoLock(mLock);
- int64_t currentPositionUs;
- if (getCurrentPositionIfPaused_l(&currentPositionUs)) {
- *mediaUs = currentPositionUs;
- return OK;
- }
- }
- return getCurrentPositionFromAnchor(mediaUs, ALooper::GetNowUs());
-}
-
-// Called on only renderer's thread.
-status_t NuPlayer::Renderer::getCurrentPositionOnLooper(int64_t *mediaUs) {
- return getCurrentPositionOnLooper(mediaUs, ALooper::GetNowUs());
-}
-
-// Called on only renderer's thread.
-// Since mPaused and mPausePositionMediaTimeUs are changed only on renderer's
-// thread, no need to acquire mLock.
-status_t NuPlayer::Renderer::getCurrentPositionOnLooper(
- int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo) {
- int64_t currentPositionUs;
- if (getCurrentPositionIfPaused_l(&currentPositionUs)) {
- *mediaUs = currentPositionUs;
- return OK;
- }
- return getCurrentPositionFromAnchor(mediaUs, nowUs, allowPastQueuedVideo);
-}
-
-// Called either with mLock acquired or on renderer's thread.
-bool NuPlayer::Renderer::getCurrentPositionIfPaused_l(int64_t *mediaUs) {
- if (!mPaused || mPausePositionMediaTimeUs < 0ll) {
- return false;
- }
- *mediaUs = mPausePositionMediaTimeUs;
- return true;
-}
-
// Called on any threads.
-status_t NuPlayer::Renderer::getCurrentPositionFromAnchor(
- int64_t *mediaUs, int64_t nowUs, bool allowPastQueuedVideo) {
- Mutex::Autolock autoLock(mTimeLock);
- if (!mHasAudio && !mHasVideo) {
- return NO_INIT;
- }
-
- if (mAnchorTimeMediaUs < 0) {
- return NO_INIT;
- }
-
- int64_t positionUs = (nowUs - mAnchorTimeRealUs) + mAnchorTimeMediaUs;
-
- if (mPauseStartedTimeRealUs != -1) {
- positionUs -= (nowUs - mPauseStartedTimeRealUs);
- }
-
- // limit position to the last queued media time (for video only stream
- // position will be discrete as we don't know how long each frame lasts)
- if (mAnchorMaxMediaUs >= 0 && !allowPastQueuedVideo) {
- if (positionUs > mAnchorMaxMediaUs) {
- positionUs = mAnchorMaxMediaUs;
- }
- }
-
- if (positionUs < mAudioFirstAnchorTimeMediaUs) {
- positionUs = mAudioFirstAnchorTimeMediaUs;
- }
-
- *mediaUs = (positionUs <= 0) ? 0 : positionUs;
- return OK;
-}
-
-void NuPlayer::Renderer::setHasMedia(bool audio) {
- Mutex::Autolock autoLock(mTimeLock);
- if (audio) {
- mHasAudio = true;
- } else {
- mHasVideo = true;
- }
+status_t NuPlayer::Renderer::getCurrentPosition(int64_t *mediaUs) {
+ return mMediaClock->getMediaTime(ALooper::GetNowUs(), mediaUs);
}
-void NuPlayer::Renderer::setAudioFirstAnchorTime(int64_t mediaUs) {
- Mutex::Autolock autoLock(mTimeLock);
- mAudioFirstAnchorTimeMediaUs = mediaUs;
+void NuPlayer::Renderer::clearAudioFirstAnchorTime_l() {
+ mAudioFirstAnchorTimeMediaUs = -1;
+ mMediaClock->setStartingTimeMedia(-1);
}
-void NuPlayer::Renderer::setAudioFirstAnchorTimeIfNeeded(int64_t mediaUs) {
- Mutex::Autolock autoLock(mTimeLock);
+void NuPlayer::Renderer::setAudioFirstAnchorTimeIfNeeded_l(int64_t mediaUs) {
if (mAudioFirstAnchorTimeMediaUs == -1) {
mAudioFirstAnchorTimeMediaUs = mediaUs;
+ mMediaClock->setStartingTimeMedia(mediaUs);
}
}
-void NuPlayer::Renderer::setAnchorTime(
- int64_t mediaUs, int64_t realUs, int64_t numFramesWritten, bool resume) {
- Mutex::Autolock autoLock(mTimeLock);
- mAnchorTimeMediaUs = mediaUs;
- mAnchorTimeRealUs = realUs;
- mAnchorNumFramesWritten = numFramesWritten;
- if (resume) {
- mPauseStartedTimeRealUs = -1;
- }
+void NuPlayer::Renderer::clearAnchorTime_l() {
+ mMediaClock->clearAnchor();
+ mAnchorTimeMediaUs = -1;
+ mAnchorNumFramesWritten = -1;
}
void NuPlayer::Renderer::setVideoLateByUs(int64_t lateUs) {
- Mutex::Autolock autoLock(mTimeLock);
+ Mutex::Autolock autoLock(mLock);
mVideoLateByUs = lateUs;
}
int64_t NuPlayer::Renderer::getVideoLateByUs() {
- Mutex::Autolock autoLock(mTimeLock);
+ Mutex::Autolock autoLock(mLock);
return mVideoLateByUs;
}
-void NuPlayer::Renderer::setPauseStartedTimeRealUs(int64_t realUs) {
- Mutex::Autolock autoLock(mTimeLock);
- mPauseStartedTimeRealUs = realUs;
-}
-
status_t NuPlayer::Renderer::openAudioSink(
const sp<AMessage> &format,
bool offloadOnly,
bool hasVideo,
uint32_t flags,
bool *isOffloaded) {
- sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, id());
+ sp<AMessage> msg = new AMessage(kWhatOpenAudioSink, this);
msg->setMessage("format", format);
msg->setInt32("offload-only", offloadOnly);
msg->setInt32("has-video", hasVideo);
@@ -328,7 +236,7 @@ status_t NuPlayer::Renderer::openAudioSink(
}
void NuPlayer::Renderer::closeAudioSink() {
- sp<AMessage> msg = new AMessage(kWhatCloseAudioSink, id());
+ sp<AMessage> msg = new AMessage(kWhatCloseAudioSink, this);
sp<AMessage> response;
msg->postAndAwaitResponse(&response);
@@ -356,7 +264,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
response->setInt32("err", err);
response->setInt32("offload", offloadingAudio());
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
response->postReply(replyID);
@@ -365,7 +273,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatCloseAudioSink:
{
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
onCloseAudioSink();
@@ -384,8 +292,8 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatDrainAudioQueue:
{
int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mAudioQueueGeneration) {
+ CHECK(msg->findInt32("drainGeneration", &generation));
+ if (generation != getDrainGeneration(true /* audio */)) {
break;
}
@@ -407,9 +315,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
// Let's give it more data after about half that time
// has elapsed.
- // kWhatDrainAudioQueue is used for non-offloading mode,
- // and mLock is used only for offloading mode. Therefore,
- // no need to acquire mLock here.
+ Mutex::Autolock autoLock(mLock);
postDrainAudioQueue_l(delayUs / 2);
}
break;
@@ -418,8 +324,8 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatDrainVideoQueue:
{
int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mVideoQueueGeneration) {
+ CHECK(msg->findInt32("drainGeneration", &generation));
+ if (generation != getDrainGeneration(false /* audio */)) {
break;
}
@@ -427,22 +333,20 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
onDrainVideoQueue();
- Mutex::Autolock autoLock(mLock);
- postDrainVideoQueue_l();
+ postDrainVideoQueue();
break;
}
case kWhatPostDrainVideoQueue:
{
int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
- if (generation != mVideoQueueGeneration) {
+ CHECK(msg->findInt32("drainGeneration", &generation));
+ if (generation != getDrainGeneration(false /* audio */)) {
break;
}
mDrainVideoQueuePending = false;
- Mutex::Autolock autoLock(mLock);
- postDrainVideoQueue_l();
+ postDrainVideoQueue();
break;
}
@@ -458,15 +362,19 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- case kWhatFlush:
+ case kWhatSetRate:
{
- onFlush(msg);
+ CHECK(msg->findFloat("rate", &mPlaybackRate));
+ int32_t ratePermille = (int32_t)(0.5f + 1000 * mPlaybackRate);
+ mPlaybackRate = ratePermille / 1000.0f;
+ mMediaClock->setPlaybackRate(mPlaybackRate);
+ mAudioSink->setPlaybackRatePermille(ratePermille);
break;
}
- case kWhatAudioSinkChanged:
+ case kWhatFlush:
{
- onAudioSinkChanged();
+ onFlush(msg);
break;
}
@@ -511,7 +419,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
case kWhatAudioOffloadPauseTimeout:
{
int32_t generation;
- CHECK(msg->findInt32("generation", &generation));
+ CHECK(msg->findInt32("drainGeneration", &generation));
if (generation != mAudioOffloadPauseTimeoutGeneration) {
break;
}
@@ -538,19 +446,19 @@ void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) {
}
mDrainAudioQueuePending = true;
- sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
- msg->setInt32("generation", mAudioQueueGeneration);
+ sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, this);
+ msg->setInt32("drainGeneration", mAudioDrainGeneration);
msg->post(delayUs);
}
-void NuPlayer::Renderer::prepareForMediaRenderingStart() {
- mAudioRenderingStartGeneration = mAudioQueueGeneration;
- mVideoRenderingStartGeneration = mVideoQueueGeneration;
+void NuPlayer::Renderer::prepareForMediaRenderingStart_l() {
+ mAudioRenderingStartGeneration = mAudioDrainGeneration;
+ mVideoRenderingStartGeneration = mVideoDrainGeneration;
}
-void NuPlayer::Renderer::notifyIfMediaRenderingStarted() {
- if (mVideoRenderingStartGeneration == mVideoQueueGeneration &&
- mAudioRenderingStartGeneration == mAudioQueueGeneration) {
+void NuPlayer::Renderer::notifyIfMediaRenderingStarted_l() {
+ if (mVideoRenderingStartGeneration == mVideoDrainGeneration &&
+ mAudioRenderingStartGeneration == mAudioDrainGeneration) {
mVideoRenderingStartGeneration = -1;
mAudioRenderingStartGeneration = -1;
@@ -618,7 +526,7 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
- setAudioFirstAnchorTimeIfNeeded(mediaTimeUs);
+ setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs);
}
size_t copy = entry->mBuffer->size() - entry->mOffset;
@@ -638,34 +546,45 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
entry = NULL;
}
sizeCopied += copy;
- notifyIfMediaRenderingStarted();
+
+ notifyIfMediaRenderingStarted_l();
}
if (mAudioFirstAnchorTimeMediaUs >= 0) {
int64_t nowUs = ALooper::GetNowUs();
- setAnchorTime(mAudioFirstAnchorTimeMediaUs, nowUs - getPlayedOutAudioDurationUs(nowUs));
+ int64_t nowMediaUs =
+ mAudioFirstAnchorTimeMediaUs + getPlayedOutAudioDurationUs(nowUs);
+ // we don't know how much data we are queueing for offloaded tracks.
+ mMediaClock->updateAnchor(nowMediaUs, nowUs, INT64_MAX);
}
- // we don't know how much data we are queueing for offloaded tracks
- mAnchorMaxMediaUs = -1;
-
if (hasEOS) {
- (new AMessage(kWhatStopAudioSink, id()))->post();
+ (new AMessage(kWhatStopAudioSink, this))->post();
}
return sizeCopied;
}
bool NuPlayer::Renderer::onDrainAudioQueue() {
+ // TODO: This call to getPosition checks if AudioTrack has been created
+ // in AudioSink before draining audio. If AudioTrack doesn't exist, then
+ // CHECKs on getPosition will fail.
+ // We still need to figure out why AudioTrack is not created when
+ // this function is called. One possible reason could be leftover
+ // audio. Another possible place is to check whether decoder
+ // has received INFO_FORMAT_CHANGED as the first buffer since
+ // AudioSink is opened there, and possible interactions with flush
+ // immediately after start. Investigate error message
+ // "vorbis_dsp_synthesis returned -135", along with RTSP.
uint32_t numFramesPlayed;
if (mAudioSink->getPosition(&numFramesPlayed) != OK) {
return false;
}
+#if 0
ssize_t numFramesAvailableToWrite =
mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
-#if 0
if (numFramesAvailableToWrite == mAudioSink->frameCount()) {
ALOGI("audio sink underrun");
} else {
@@ -674,10 +593,7 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
}
#endif
- size_t numBytesAvailableToWrite =
- numFramesAvailableToWrite * mAudioSink->frameSize();
-
- while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) {
+ while (!mAudioQueue.empty()) {
QueueEntry *entry = &*mAudioQueue.begin();
mLastAudioBufferDrained = entry->mBufferOrdinal;
@@ -710,14 +626,16 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
}
size_t copy = entry->mBuffer->size() - entry->mOffset;
- if (copy > numBytesAvailableToWrite) {
- copy = numBytesAvailableToWrite;
- }
- ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset, copy);
+ ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset,
+ copy, false /* blocking */);
if (written < 0) {
// An error in AudioSink write. Perhaps the AudioSink was not properly opened.
- ALOGE("AudioSink write error(%zd) when writing %zu bytes", written, copy);
+ if (written == WOULD_BLOCK) {
+ ALOGW("AudioSink write would block when writing %zu bytes", copy);
+ } else {
+ ALOGE("AudioSink write error(%zd) when writing %zu bytes", written, copy);
+ }
break;
}
@@ -729,73 +647,92 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
entry = NULL;
}
- numBytesAvailableToWrite -= written;
size_t copiedFrames = written / mAudioSink->frameSize();
mNumFramesWritten += copiedFrames;
- notifyIfMediaRenderingStarted();
+ {
+ Mutex::Autolock autoLock(mLock);
+ notifyIfMediaRenderingStarted_l();
+ }
if (written != (ssize_t)copy) {
// A short count was received from AudioSink::write()
//
- // AudioSink write should block until exactly the number of bytes are delivered.
- // But it may return with a short count (without an error) when:
+ // AudioSink write is called in non-blocking mode.
+ // It may return with a short count when:
//
// 1) Size to be copied is not a multiple of the frame size. We consider this fatal.
- // 2) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded.
+ // 2) The data to be copied exceeds the available buffer in AudioSink.
+ // 3) An error occurs and data has been partially copied to the buffer in AudioSink.
+ // 4) AudioSink is an AudioCache for data retrieval, and the AudioCache is exceeded.
// (Case 1)
// Must be a multiple of the frame size. If it is not a multiple of a frame size, it
// needs to fail, as we should not carry over fractional frames between calls.
CHECK_EQ(copy % mAudioSink->frameSize(), 0);
- // (Case 2)
+ // (Case 2, 3, 4)
// Return early to the caller.
// Beware of calling immediately again as this may busy-loop if you are not careful.
- ALOGW("AudioSink write short frame count %zd < %zu", written, copy);
+ ALOGV("AudioSink write short frame count %zd < %zu", written, copy);
break;
}
}
- mAnchorMaxMediaUs =
- mAnchorTimeMediaUs +
- (int64_t)(max((long long)mNumFramesWritten - mAnchorNumFramesWritten, 0LL)
- * 1000LL * mAudioSink->msecsPerFrame());
+ int64_t maxTimeMedia;
+ {
+ Mutex::Autolock autoLock(mLock);
+ maxTimeMedia =
+ mAnchorTimeMediaUs +
+ (int64_t)(max((long long)mNumFramesWritten - mAnchorNumFramesWritten, 0LL)
+ * 1000LL * mAudioSink->msecsPerFrame());
+ }
+ mMediaClock->updateMaxTimeMedia(maxTimeMedia);
return !mAudioQueue.empty();
}
+int64_t NuPlayer::Renderer::getDurationUsIfPlayedAtSampleRate(uint32_t numFrames) {
+ int32_t sampleRate = offloadingAudio() ?
+ mCurrentOffloadInfo.sample_rate : mCurrentPcmInfo.mSampleRate;
+ // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
+ return (int64_t)((int32_t)numFrames * 1000000LL / sampleRate);
+}
+
+// Calculate duration of pending samples if played at normal rate (i.e., 1.0).
int64_t NuPlayer::Renderer::getPendingAudioPlayoutDurationUs(int64_t nowUs) {
- int64_t writtenAudioDurationUs =
- mNumFramesWritten * 1000LL * mAudioSink->msecsPerFrame();
+ int64_t writtenAudioDurationUs = getDurationUsIfPlayedAtSampleRate(mNumFramesWritten);
return writtenAudioDurationUs - getPlayedOutAudioDurationUs(nowUs);
}
int64_t NuPlayer::Renderer::getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs) {
- int64_t currentPositionUs;
- if (mPaused || getCurrentPositionOnLooper(
- &currentPositionUs, nowUs, true /* allowPastQueuedVideo */) != OK) {
- // If failed to get current position, e.g. due to audio clock is not ready, then just
- // play out video immediately without delay.
+ int64_t realUs;
+ if (mMediaClock->getRealTimeFor(mediaTimeUs, &realUs) != OK) {
+ // If failed to get current position, e.g. due to audio clock is
+ // not ready, then just play out video immediately without delay.
return nowUs;
}
- return (mediaTimeUs - currentPositionUs) + nowUs;
+ return realUs;
}
void NuPlayer::Renderer::onNewAudioMediaTime(int64_t mediaTimeUs) {
+ Mutex::Autolock autoLock(mLock);
// TRICKY: vorbis decoder generates multiple frames with the same
// timestamp, so only update on the first frame with a given timestamp
if (mediaTimeUs == mAnchorTimeMediaUs) {
return;
}
- setAudioFirstAnchorTimeIfNeeded(mediaTimeUs);
+ setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs);
int64_t nowUs = ALooper::GetNowUs();
- setAnchorTime(
- mediaTimeUs, nowUs + getPendingAudioPlayoutDurationUs(nowUs), mNumFramesWritten);
+ int64_t nowMediaUs = mediaTimeUs - getPendingAudioPlayoutDurationUs(nowUs);
+ mMediaClock->updateAnchor(nowMediaUs, nowUs, mediaTimeUs);
+ mAnchorNumFramesWritten = mNumFramesWritten;
+ mAnchorTimeMediaUs = mediaTimeUs;
}
-void NuPlayer::Renderer::postDrainVideoQueue_l() {
+// Called without mLock acquired.
+void NuPlayer::Renderer::postDrainVideoQueue() {
if (mDrainVideoQueuePending
- || mSyncQueues
+ || getSyncQueues()
|| (mPaused && mVideoSampleReceived)) {
return;
}
@@ -806,8 +743,8 @@ void NuPlayer::Renderer::postDrainVideoQueue_l() {
QueueEntry &entry = *mVideoQueue.begin();
- sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
- msg->setInt32("generation", mVideoQueueGeneration);
+ sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, this);
+ msg->setInt32("drainGeneration", getDrainGeneration(false /* audio */));
if (entry.mBuffer == NULL) {
// EOS doesn't carry a timestamp.
@@ -827,16 +764,19 @@ void NuPlayer::Renderer::postDrainVideoQueue_l() {
int64_t mediaTimeUs;
CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- if (mAnchorTimeMediaUs < 0) {
- setAnchorTime(mediaTimeUs, nowUs);
- mPausePositionMediaTimeUs = mediaTimeUs;
- mAnchorMaxMediaUs = mediaTimeUs;
- realTimeUs = nowUs;
- } else {
- realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
+ {
+ Mutex::Autolock autoLock(mLock);
+ if (mAnchorTimeMediaUs < 0) {
+ mMediaClock->updateAnchor(mediaTimeUs, nowUs, mediaTimeUs);
+ mAnchorTimeMediaUs = mediaTimeUs;
+ realTimeUs = nowUs;
+ } else {
+ realTimeUs = getRealTimeUs(mediaTimeUs, nowUs);
+ }
}
if (!mHasAudio) {
- mAnchorMaxMediaUs = mediaTimeUs + 100000; // smooth out videos >= 10fps
+ // smooth out videos >= 10fps
+ mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
}
// Heuristics to handle situation when media time changed without a
@@ -915,16 +855,19 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
ALOGV("video late by %lld us (%.2f secs)",
mVideoLateByUs, mVideoLateByUs / 1E6);
} else {
+ int64_t mediaUs = 0;
+ mMediaClock->getMediaTime(realTimeUs, &mediaUs);
ALOGV("rendering video at media time %.2f secs",
(mFlags & FLAG_REAL_TIME ? realTimeUs :
- (realTimeUs + mAnchorTimeMediaUs - mAnchorTimeRealUs)) / 1E6);
+ mediaUs) / 1E6);
}
} else {
setVideoLateByUs(0);
if (!mVideoSampleReceived && !mHasAudio) {
// This will ensure that the first frame after a flush won't be used as anchor
// when renderer is in paused state, because resume can happen any time after seek.
- setAnchorTime(-1, -1);
+ Mutex::Autolock autoLock(mLock);
+ clearAnchorTime_l();
}
}
@@ -941,7 +884,8 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
mVideoRenderingStarted = true;
notifyVideoRenderingStart();
}
- notifyIfMediaRenderingStarted();
+ Mutex::Autolock autoLock(mLock);
+ notifyIfMediaRenderingStarted_l();
}
}
@@ -960,14 +904,22 @@ void NuPlayer::Renderer::notifyEOS(bool audio, status_t finalResult, int64_t del
}
void NuPlayer::Renderer::notifyAudioOffloadTearDown() {
- (new AMessage(kWhatAudioOffloadTearDown, id()))->post();
+ (new AMessage(kWhatAudioOffloadTearDown, this))->post();
}
void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
int32_t audio;
CHECK(msg->findInt32("audio", &audio));
- setHasMedia(audio);
+ if (dropBufferIfStale(audio, msg)) {
+ return;
+ }
+
+ if (audio) {
+ mHasAudio = true;
+ } else {
+ mHasVideo = true;
+ }
if (mHasVideo) {
if (mVideoScheduler == NULL) {
@@ -976,10 +928,6 @@ void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
}
}
- if (dropBufferWhileFlushing(audio, msg)) {
- return;
- }
-
sp<ABuffer> buffer;
CHECK(msg->findBuffer("buffer", &buffer));
@@ -993,15 +941,16 @@ void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
entry.mFinalResult = OK;
entry.mBufferOrdinal = ++mTotalBuffersQueued;
- Mutex::Autolock autoLock(mLock);
if (audio) {
+ Mutex::Autolock autoLock(mLock);
mAudioQueue.push_back(entry);
postDrainAudioQueue_l();
} else {
mVideoQueue.push_back(entry);
- postDrainVideoQueue_l();
+ postDrainVideoQueue();
}
+ Mutex::Autolock autoLock(mLock);
if (!mSyncQueues || mAudioQueue.empty() || mVideoQueue.empty()) {
return;
}
@@ -1050,7 +999,9 @@ void NuPlayer::Renderer::syncQueuesDone_l() {
}
if (!mVideoQueue.empty()) {
- postDrainVideoQueue_l();
+ mLock.unlock();
+ postDrainVideoQueue();
+ mLock.lock();
}
}
@@ -1058,7 +1009,7 @@ void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
int32_t audio;
CHECK(msg->findInt32("audio", &audio));
- if (dropBufferWhileFlushing(audio, msg)) {
+ if (dropBufferIfStale(audio, msg)) {
return;
}
@@ -1069,19 +1020,20 @@ void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
entry.mOffset = 0;
entry.mFinalResult = finalResult;
- Mutex::Autolock autoLock(mLock);
if (audio) {
+ Mutex::Autolock autoLock(mLock);
if (mAudioQueue.empty() && mSyncQueues) {
syncQueuesDone_l();
}
mAudioQueue.push_back(entry);
postDrainAudioQueue_l();
} else {
- if (mVideoQueue.empty() && mSyncQueues) {
+ if (mVideoQueue.empty() && getSyncQueues()) {
+ Mutex::Autolock autoLock(mLock);
syncQueuesDone_l();
}
mVideoQueue.push_back(entry);
- postDrainVideoQueue_l();
+ postDrainVideoQueue();
}
}
@@ -1090,31 +1042,25 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
CHECK(msg->findInt32("audio", &audio));
{
- Mutex::Autolock autoLock(mFlushLock);
+ Mutex::Autolock autoLock(mLock);
if (audio) {
- mFlushingAudio = false;
notifyComplete = mNotifyCompleteAudio;
mNotifyCompleteAudio = false;
} else {
- mFlushingVideo = false;
notifyComplete = mNotifyCompleteVideo;
mNotifyCompleteVideo = false;
}
- }
- // If we're currently syncing the queues, i.e. dropping audio while
- // aligning the first audio/video buffer times and only one of the
- // two queues has data, we may starve that queue by not requesting
- // more buffers from the decoder. If the other source then encounters
- // a discontinuity that leads to flushing, we'll never find the
- // corresponding discontinuity on the other queue.
- // Therefore we'll stop syncing the queues if at least one of them
- // is flushed.
- {
- Mutex::Autolock autoLock(mLock);
- syncQueuesDone_l();
- setPauseStartedTimeRealUs(-1);
- setAnchorTime(-1, -1);
+ // If we're currently syncing the queues, i.e. dropping audio while
+ // aligning the first audio/video buffer times and only one of the
+ // two queues has data, we may starve that queue by not requesting
+ // more buffers from the decoder. If the other source then encounters
+ // a discontinuity that leads to flushing, we'll never find the
+ // corresponding discontinuity on the other queue.
+ // Therefore we'll stop syncing the queues if at least one of them
+ // is flushed.
+ syncQueuesDone_l();
+ clearAnchorTime_l();
}
ALOGV("flushing %s", audio ? "audio" : "video");
@@ -1123,11 +1069,11 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
Mutex::Autolock autoLock(mLock);
flushQueue(&mAudioQueue);
- ++mAudioQueueGeneration;
- prepareForMediaRenderingStart();
+ ++mAudioDrainGeneration;
+ prepareForMediaRenderingStart_l();
if (offloadingAudio()) {
- setAudioFirstAnchorTime(-1);
+ clearAudioFirstAnchorTime_l();
}
}
@@ -1142,13 +1088,14 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
flushQueue(&mVideoQueue);
mDrainVideoQueuePending = false;
- ++mVideoQueueGeneration;
if (mVideoScheduler != NULL) {
mVideoScheduler->restart();
}
- prepareForMediaRenderingStart();
+ Mutex::Autolock autoLock(mLock);
+ ++mVideoDrainGeneration;
+ prepareForMediaRenderingStart_l();
}
mVideoSampleReceived = false;
@@ -1178,20 +1125,12 @@ void NuPlayer::Renderer::notifyFlushComplete(bool audio) {
notify->post();
}
-bool NuPlayer::Renderer::dropBufferWhileFlushing(
+bool NuPlayer::Renderer::dropBufferIfStale(
bool audio, const sp<AMessage> &msg) {
- bool flushing = false;
-
- {
- Mutex::Autolock autoLock(mFlushLock);
- if (audio) {
- flushing = mFlushingAudio;
- } else {
- flushing = mFlushingVideo;
- }
- }
+ int32_t queueGeneration;
+ CHECK(msg->findInt32("queueGeneration", &queueGeneration));
- if (!flushing) {
+ if (queueGeneration == getQueueGeneration(audio)) {
return false;
}
@@ -1209,7 +1148,10 @@ void NuPlayer::Renderer::onAudioSinkChanged() {
}
CHECK(!mDrainAudioQueuePending);
mNumFramesWritten = 0;
- mAnchorNumFramesWritten = -1;
+ {
+ Mutex::Autolock autoLock(mLock);
+ mAnchorNumFramesWritten = -1;
+ }
uint32_t written;
if (mAudioSink->getFramesWritten(&written) == OK) {
mNumFramesWritten = written;
@@ -1219,13 +1161,13 @@ void NuPlayer::Renderer::onAudioSinkChanged() {
void NuPlayer::Renderer::onDisableOffloadAudio() {
Mutex::Autolock autoLock(mLock);
mFlags &= ~FLAG_OFFLOAD_AUDIO;
- ++mAudioQueueGeneration;
+ ++mAudioDrainGeneration;
}
void NuPlayer::Renderer::onEnableOffloadAudio() {
Mutex::Autolock autoLock(mLock);
mFlags |= FLAG_OFFLOAD_AUDIO;
- ++mAudioQueueGeneration;
+ ++mAudioDrainGeneration;
}
void NuPlayer::Renderer::onPause() {
@@ -1234,25 +1176,13 @@ void NuPlayer::Renderer::onPause() {
return;
}
int64_t currentPositionUs;
- int64_t pausePositionMediaTimeUs;
- if (getCurrentPositionFromAnchor(
- &currentPositionUs, ALooper::GetNowUs()) == OK) {
- pausePositionMediaTimeUs = currentPositionUs;
- } else {
- // Set paused position to -1 (unavailabe) if we don't have anchor time
- // This could happen if client does a seekTo() immediately followed by
- // pause(). Renderer will be flushed with anchor time cleared. We don't
- // want to leave stale value in mPausePositionMediaTimeUs.
- pausePositionMediaTimeUs = -1;
- }
{
Mutex::Autolock autoLock(mLock);
- mPausePositionMediaTimeUs = pausePositionMediaTimeUs;
- ++mAudioQueueGeneration;
- ++mVideoQueueGeneration;
- prepareForMediaRenderingStart();
+ ++mAudioDrainGeneration;
+ ++mVideoDrainGeneration;
+ prepareForMediaRenderingStart_l();
mPaused = true;
- setPauseStartedTimeRealUs(ALooper::GetNowUs());
+ mMediaClock->setPlaybackRate(0.0);
}
mDrainAudioQueuePending = false;
@@ -1277,21 +1207,18 @@ void NuPlayer::Renderer::onResume() {
mAudioSink->start();
}
- Mutex::Autolock autoLock(mLock);
- mPaused = false;
- if (mPauseStartedTimeRealUs != -1) {
- int64_t newAnchorRealUs =
- mAnchorTimeRealUs + ALooper::GetNowUs() - mPauseStartedTimeRealUs;
- setAnchorTime(
- mAnchorTimeMediaUs, newAnchorRealUs, mAnchorNumFramesWritten, true /* resume */);
- }
+ {
+ Mutex::Autolock autoLock(mLock);
+ mPaused = false;
+ mMediaClock->setPlaybackRate(mPlaybackRate);
- if (!mAudioQueue.empty()) {
- postDrainAudioQueue_l();
+ if (!mAudioQueue.empty()) {
+ postDrainAudioQueue_l();
+ }
}
if (!mVideoQueue.empty()) {
- postDrainVideoQueue_l();
+ postDrainVideoQueue();
}
}
@@ -1302,6 +1229,21 @@ void NuPlayer::Renderer::onSetVideoFrameRate(float fps) {
mVideoScheduler->init(fps);
}
+int32_t NuPlayer::Renderer::getQueueGeneration(bool audio) {
+ Mutex::Autolock autoLock(mLock);
+ return (audio ? mAudioQueueGeneration : mVideoQueueGeneration);
+}
+
+int32_t NuPlayer::Renderer::getDrainGeneration(bool audio) {
+ Mutex::Autolock autoLock(mLock);
+ return (audio ? mAudioDrainGeneration : mVideoDrainGeneration);
+}
+
+bool NuPlayer::Renderer::getSyncQueues() {
+ Mutex::Autolock autoLock(mLock);
+ return mSyncQueues;
+}
+
// TODO: Remove unnecessary calls to getPlayedOutAudioDurationUs()
// as it acquires locks and may query the audio driver.
//
@@ -1309,6 +1251,7 @@ void NuPlayer::Renderer::onSetVideoFrameRate(float fps) {
// accessing getTimestamp() or getPosition() every time a data buffer with
// a media time is received.
//
+// Calculate duration of played samples if played at normal rate (i.e., 1.0).
int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) {
uint32_t numFramesPlayed;
int64_t numFramesPlayedAt;
@@ -1346,9 +1289,8 @@ int64_t NuPlayer::Renderer::getPlayedOutAudioDurationUs(int64_t nowUs) {
//ALOGD("getPosition: %d %lld", numFramesPlayed, numFramesPlayedAt);
}
- // TODO: remove the (int32_t) casting below as it may overflow at 12.4 hours.
//CHECK_EQ(numFramesPlayed & (1 << 31), 0); // can't be negative until 12.4 hrs, test
- int64_t durationUs = (int64_t)((int32_t)numFramesPlayed * 1000LL * mAudioSink->msecsPerFrame())
+ int64_t durationUs = getDurationUsIfPlayedAtSampleRate(numFramesPlayed)
+ nowUs - numFramesPlayedAt;
if (durationUs < 0) {
// Occurs when numFramesPlayed position is very small and the following:
@@ -1373,7 +1315,7 @@ void NuPlayer::Renderer::onAudioOffloadTearDown(AudioOffloadTearDownReason reaso
mAudioOffloadTornDown = true;
int64_t currentPositionUs;
- if (getCurrentPositionOnLooper(&currentPositionUs) != OK) {
+ if (getCurrentPosition(&currentPositionUs) != OK) {
currentPositionUs = 0;
}
@@ -1390,8 +1332,8 @@ void NuPlayer::Renderer::onAudioOffloadTearDown(AudioOffloadTearDownReason reaso
void NuPlayer::Renderer::startAudioOffloadPauseTimeout() {
if (offloadingAudio()) {
mWakeLock->acquire();
- sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, id());
- msg->setInt32("generation", mAudioOffloadPauseTimeoutGeneration);
+ sp<AMessage> msg = new AMessage(kWhatAudioOffloadPauseTimeout, this);
+ msg->setInt32("drainGeneration", mAudioOffloadPauseTimeoutGeneration);
msg->post(kOffloadPauseMaxUs);
}
}
@@ -1487,6 +1429,10 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
&offloadInfo);
if (err == OK) {
+ if (mPlaybackRate != 1.0) {
+ mAudioSink->setPlaybackRatePermille(
+ (int32_t)(mPlaybackRate * 1000 + 0.5f));
+ }
// If the playback is offloaded to h/w, we pass
// the HAL some metadata information.
// We don't want to do this for PCM because it
@@ -1542,6 +1488,10 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
return err;
}
mCurrentPcmInfo = info;
+ if (mPlaybackRate != 1.0) {
+ mAudioSink->setPlaybackRatePermille(
+ (int32_t)(mPlaybackRate * 1000 + 0.5f));
+ }
mAudioSink->start();
}
if (audioSinkChanged) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 003d1d0..38843d5 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -24,6 +24,7 @@ namespace android {
struct ABuffer;
class AWakeLock;
+struct MediaClock;
struct VideoFrameScheduler;
struct NuPlayer::Renderer : public AHandler {
@@ -47,6 +48,8 @@ struct NuPlayer::Renderer : public AHandler {
void queueEOS(bool audio, status_t finalResult);
+ void setPlaybackRate(float rate);
+
void flush(bool audio, bool notifyComplete);
void signalTimeDiscontinuity();
@@ -61,16 +64,8 @@ struct NuPlayer::Renderer : public AHandler {
void setVideoFrameRate(float fps);
- // Following setters and getters are protected by mTimeLock.
status_t getCurrentPosition(int64_t *mediaUs);
- void setHasMedia(bool audio);
- void setAudioFirstAnchorTime(int64_t mediaUs);
- void setAudioFirstAnchorTimeIfNeeded(int64_t mediaUs);
- void setAnchorTime(
- int64_t mediaUs, int64_t realUs, int64_t numFramesWritten = -1, bool resume = false);
- void setVideoLateByUs(int64_t lateUs);
int64_t getVideoLateByUs();
- void setPauseStartedTimeRealUs(int64_t realUs);
status_t openAudioSink(
const sp<AMessage> &format,
@@ -107,8 +102,8 @@ private:
kWhatPostDrainVideoQueue = 'pDVQ',
kWhatQueueBuffer = 'queB',
kWhatQueueEOS = 'qEOS',
+ kWhatSetRate = 'setR',
kWhatFlush = 'flus',
- kWhatAudioSinkChanged = 'auSC',
kWhatPause = 'paus',
kWhatResume = 'resm',
kWhatOpenAudioSink = 'opnA',
@@ -142,26 +137,18 @@ private:
bool mDrainVideoQueuePending;
int32_t mAudioQueueGeneration;
int32_t mVideoQueueGeneration;
+ int32_t mAudioDrainGeneration;
+ int32_t mVideoDrainGeneration;
- Mutex mTimeLock;
- // |mTimeLock| protects the following 7 member vars that are related to time.
- // Note: those members are only written on Renderer thread, so reading on Renderer thread
- // doesn't need to be protected. Otherwise accessing those members must be protected by
- // |mTimeLock|.
- // TODO: move those members to a seperated media clock class.
+ sp<MediaClock> mMediaClock;
+ float mPlaybackRate;
int64_t mAudioFirstAnchorTimeMediaUs;
int64_t mAnchorTimeMediaUs;
- int64_t mAnchorTimeRealUs;
int64_t mAnchorNumFramesWritten;
- int64_t mAnchorMaxMediaUs;
int64_t mVideoLateByUs;
bool mHasAudio;
bool mHasVideo;
- int64_t mPauseStartedTimeRealUs;
- Mutex mFlushLock; // protects the following 2 member vars.
- bool mFlushingAudio;
- bool mFlushingVideo;
bool mNotifyCompleteAudio;
bool mNotifyCompleteVideo;
@@ -169,7 +156,6 @@ private:
// modified on only renderer's thread.
bool mPaused;
- int64_t mPausePositionMediaTimeUs;
bool mVideoSampleReceived;
bool mVideoRenderingStarted;
@@ -211,14 +197,19 @@ private:
int64_t getPlayedOutAudioDurationUs(int64_t nowUs);
void postDrainAudioQueue_l(int64_t delayUs = 0);
+ void clearAnchorTime_l();
+ void clearAudioFirstAnchorTime_l();
+ void setAudioFirstAnchorTimeIfNeeded_l(int64_t mediaUs);
+ void setVideoLateByUs(int64_t lateUs);
+
void onNewAudioMediaTime(int64_t mediaTimeUs);
int64_t getRealTimeUs(int64_t mediaTimeUs, int64_t nowUs);
void onDrainVideoQueue();
- void postDrainVideoQueue_l();
+ void postDrainVideoQueue();
- void prepareForMediaRenderingStart();
- void notifyIfMediaRenderingStarted();
+ void prepareForMediaRenderingStart_l();
+ void notifyIfMediaRenderingStarted_l();
void onQueueBuffer(const sp<AMessage> &msg);
void onQueueEOS(const sp<AMessage> &msg);
@@ -229,6 +220,9 @@ private:
void onPause();
void onResume();
void onSetVideoFrameRate(float fps);
+ int32_t getQueueGeneration(bool audio);
+ int32_t getDrainGeneration(bool audio);
+ bool getSyncQueues();
void onAudioOffloadTearDown(AudioOffloadTearDownReason reason);
status_t onOpenAudioSink(
const sp<AMessage> &format,
@@ -245,7 +239,7 @@ private:
void notifyAudioOffloadTearDown();
void flushQueue(List<QueueEntry> *queue);
- bool dropBufferWhileFlushing(bool audio, const sp<AMessage> &msg);
+ bool dropBufferIfStale(bool audio, const sp<AMessage> &msg);
void syncQueuesDone_l();
bool offloadingAudio() const { return (mFlags & FLAG_OFFLOAD_AUDIO) != 0; }
@@ -253,6 +247,8 @@ private:
void startAudioOffloadPauseTimeout();
void cancelAudioOffloadPauseTimeout();
+ int64_t getDurationUsIfPlayedAtSampleRate(uint32_t numFrames);
+
DISALLOW_EVIL_CONSTRUCTORS(Renderer);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index 885ebe4..f53afbd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -29,9 +29,9 @@ namespace android {
NuPlayer::NuPlayerStreamListener::NuPlayerStreamListener(
const sp<IStreamSource> &source,
- ALooper::handler_id id)
+ const sp<AHandler> &targetHandler)
: mSource(source),
- mTargetID(id),
+ mTargetHandler(targetHandler),
mEOS(false),
mSendDataNotification(true) {
mSource->setListener(this);
@@ -65,8 +65,8 @@ void NuPlayer::NuPlayerStreamListener::queueBuffer(size_t index, size_t size) {
if (mSendDataNotification) {
mSendDataNotification = false;
- if (mTargetID != 0) {
- (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+ if (mTargetHandler != NULL) {
+ (new AMessage(kWhatMoreDataQueued, mTargetHandler))->post();
}
}
}
@@ -86,8 +86,8 @@ void NuPlayer::NuPlayerStreamListener::issueCommand(
if (mSendDataNotification) {
mSendDataNotification = false;
- if (mTargetID != 0) {
- (new AMessage(kWhatMoreDataQueued, mTargetID))->post();
+ if (mTargetHandler != NULL) {
+ (new AMessage(kWhatMoreDataQueued, mTargetHandler))->post();
}
}
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index 1874d80..2de829b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -29,7 +29,7 @@ struct MemoryDealer;
struct NuPlayer::NuPlayerStreamListener : public BnStreamListener {
NuPlayerStreamListener(
const sp<IStreamSource> &source,
- ALooper::handler_id targetID);
+ const sp<AHandler> &targetHandler);
virtual void queueBuffer(size_t index, size_t size);
@@ -59,7 +59,7 @@ private:
Mutex mLock;
sp<IStreamSource> mSource;
- ALooper::handler_id mTargetID;
+ sp<AHandler> mTargetHandler;
sp<MemoryDealer> mMemoryDealer;
Vector<sp<IMemory> > mBuffers;
List<QueueEntry> mQueue;
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 0282a9f..5210fc8 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -87,7 +87,7 @@ void NuPlayer::RTSPSource::prepareAsync() {
CHECK(mHandler == NULL);
CHECK(mSDPLoader == NULL);
- sp<AMessage> notify = new AMessage(kWhatNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatNotify, this);
CHECK_EQ(mState, (int)DISCONNECTED);
mState = CONNECTING;
@@ -116,7 +116,7 @@ void NuPlayer::RTSPSource::stop() {
if (mLooper == NULL) {
return;
}
- sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
+ sp<AMessage> msg = new AMessage(kWhatDisconnect, this);
sp<AMessage> dummy;
msg->postAndAwaitResponse(&dummy);
@@ -292,7 +292,7 @@ status_t NuPlayer::RTSPSource::getDuration(int64_t *durationUs) {
}
status_t NuPlayer::RTSPSource::seekTo(int64_t seekTimeUs) {
- sp<AMessage> msg = new AMessage(kWhatPerformSeek, id());
+ sp<AMessage> msg = new AMessage(kWhatPerformSeek, this);
msg->setInt32("generation", ++mSeekGeneration);
msg->setInt64("timeUs", seekTimeUs);
msg->post(200000ll);
@@ -311,7 +311,7 @@ void NuPlayer::RTSPSource::performSeek(int64_t seekTimeUs) {
void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
if (msg->what() == kWhatDisconnect) {
- uint32_t replyID;
+ sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
mDisconnectReplyID = replyID;
@@ -600,7 +600,7 @@ void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) {
ALOGE("Unable to find url in SDP");
err = UNKNOWN_ERROR;
} else {
- sp<AMessage> notify = new AMessage(kWhatNotify, id());
+ sp<AMessage> notify = new AMessage(kWhatNotify, this);
mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID);
mLooper->registerHandler(mHandler);
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.h b/media/libmediaplayerservice/nuplayer/RTSPSource.h
index ac3299a..5f2cf33 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.h
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.h
@@ -25,6 +25,7 @@
namespace android {
struct ALooper;
+struct AReplyToken;
struct AnotherPacketSource;
struct MyHandler;
struct SDPLoader;
@@ -96,7 +97,7 @@ private:
bool mIsSDP;
State mState;
status_t mFinalResult;
- uint32_t mDisconnectReplyID;
+ sp<AReplyToken> mDisconnectReplyID;
Mutex mBufferingLock;
bool mBuffering;
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index b3f224d..0246b59 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -63,7 +63,7 @@ void NuPlayer::StreamingSource::prepareAsync() {
}
void NuPlayer::StreamingSource::start() {
- mStreamListener = new NuPlayerStreamListener(mSource, 0);
+ mStreamListener = new NuPlayerStreamListener(mSource, NULL);
uint32_t sourceFlags = mSource->flags();
@@ -163,7 +163,7 @@ status_t NuPlayer::StreamingSource::postReadBuffer() {
mBuffering = true;
}
- (new AMessage(kWhatReadBuffer, id()))->post();
+ (new AMessage(kWhatReadBuffer, this))->post();
return OK;
}