Merge "Fix AudioMixer MONO muted track handling" into lmp-dev
diff --git a/camera/CameraUtils.cpp b/camera/CameraUtils.cpp
index 1ff63ab..04244ac 100644
--- a/camera/CameraUtils.cpp
+++ b/camera/CameraUtils.cpp
@@ -73,23 +73,23 @@
return INVALID_OPERATION;
}
} else {
- // Front camera needs to be horizontally flipped for
- // mirror-like behavior.
- // Note: Flips are applied before rotates.
+ // Front camera needs to be horizontally flipped for mirror-like behavior.
+ // Note: Flips are applied before rotates; using XOR here as some of these flags are
+ // composed in terms of other flip/rotation flags, and are not bitwise-ORable.
switch (orientation) {
case 0:
flags = NATIVE_WINDOW_TRANSFORM_FLIP_H;
break;
case 90:
- flags = NATIVE_WINDOW_TRANSFORM_FLIP_H |
+ flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
NATIVE_WINDOW_TRANSFORM_ROT_270;
break;
case 180:
- flags = NATIVE_WINDOW_TRANSFORM_FLIP_H |
+ flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
NATIVE_WINDOW_TRANSFORM_ROT_180;
break;
case 270:
- flags = NATIVE_WINDOW_TRANSFORM_FLIP_H |
+ flags = NATIVE_WINDOW_TRANSFORM_FLIP_H ^
NATIVE_WINDOW_TRANSFORM_ROT_90;
break;
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
index 3fb9e36..8000e84 100644
--- a/include/media/stagefright/DataSource.h
+++ b/include/media/stagefright/DataSource.h
@@ -48,7 +48,7 @@
const sp<IMediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers = NULL,
- AString *sniffedMIME = NULL);
+ String8 *contentType = NULL);
DataSource() {}
@@ -102,10 +102,6 @@
virtual ~DataSource() {}
private:
- enum {
- kDefaultMetaSize = 200000,
- };
-
static Mutex gSnifferMutex;
static List<SnifferFunc> gSniffers;
static bool gSniffersRegistered;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 2c48306..b5bd988 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1798,7 +1798,9 @@
//ALOGV("write(%p, %u)", buffer, size);
if (mTrack != 0) {
ssize_t ret = mTrack->write(buffer, size);
- mBytesWritten += ret;
+ if (ret >= 0) {
+ mBytesWritten += ret;
+ }
return ret;
}
return NO_INIT;
@@ -1945,7 +1947,7 @@
#define LOG_TAG "AudioCache"
MediaPlayerService::AudioCache::AudioCache(const sp<IMemoryHeap>& heap) :
mHeap(heap), mChannelCount(0), mFrameCount(1024), mSampleRate(0), mSize(0),
- mError(NO_ERROR), mCommandComplete(false)
+ mFrameSize(1), mError(NO_ERROR), mCommandComplete(false)
{
}
@@ -1962,14 +1964,14 @@
status_t MediaPlayerService::AudioCache::getPosition(uint32_t *position) const
{
if (position == 0) return BAD_VALUE;
- *position = mSize;
+ *position = mSize / mFrameSize;
return NO_ERROR;
}
status_t MediaPlayerService::AudioCache::getFramesWritten(uint32_t *written) const
{
if (written == 0) return BAD_VALUE;
- *written = mSize;
+ *written = mSize / mFrameSize;
return NO_ERROR;
}
@@ -2031,6 +2033,8 @@
if (actualSize > 0) {
sink->write(mBuffer, actualSize);
+ // Could return false on sink->write() error or short count.
+ // Not necessarily appropriate but would work for AudioCache behavior.
}
return true;
@@ -2053,6 +2057,9 @@
mChannelCount = (uint16_t)channelCount;
mFormat = format;
mMsecsPerFrame = 1.e3 / (float) sampleRate;
+ mFrameSize = audio_is_linear_pcm(mFormat)
+ ? mChannelCount * audio_bytes_per_sample(mFormat) : 1;
+ mFrameCount = mHeap->getSize() / mFrameSize;
if (cb != NULL) {
mCallbackThread = new CallbackThread(this, cb, cookie);
@@ -2082,12 +2089,26 @@
if (p == NULL) return NO_INIT;
p += mSize;
ALOGV("memcpy(%p, %p, %u)", p, buffer, size);
- if (mSize + size > mHeap->getSize()) {
+
+ bool overflow = mSize + size > mHeap->getSize();
+ if (overflow) {
ALOGE("Heap size overflow! req size: %d, max size: %d", (mSize + size), mHeap->getSize());
size = mHeap->getSize() - mSize;
}
+ size -= size % mFrameSize; // consume only integral amounts of frame size
memcpy(p, buffer, size);
mSize += size;
+
+ if (overflow) {
+ // Signal heap filled here (last frame may be truncated).
+ // After this point, no more data should be written as the
+ // heap is filled and the AudioCache should be effectively
+ // immutable with respect to future writes.
+ //
+ // It is thus safe for another thread to read the AudioCache.
+ mCommandComplete = true;
+ mSignal.signal();
+ }
return size;
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 406e3f6..4fe7075 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -194,7 +194,7 @@
virtual ssize_t bufferSize() const { return frameSize() * mFrameCount; }
virtual ssize_t frameCount() const { return mFrameCount; }
virtual ssize_t channelCount() const { return (ssize_t)mChannelCount; }
- virtual ssize_t frameSize() const { return ssize_t(mChannelCount * ((mFormat == AUDIO_FORMAT_PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
+ virtual ssize_t frameSize() const { return (ssize_t)mFrameSize; }
virtual uint32_t latency() const;
virtual float msecsPerFrame() const;
virtual status_t getPosition(uint32_t *position) const;
@@ -244,6 +244,7 @@
ssize_t mFrameCount;
uint32_t mSampleRate;
uint32_t mSize;
+ size_t mFrameSize;
int mError;
bool mCommandComplete;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 8774117..e2bcb1e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -1433,6 +1433,10 @@
format->setString("mime", MEDIA_MIMETYPE_VIDEO_AVC);
break;
+ case VIDEO_ENCODER_VP8:
+ format->setString("mime", MEDIA_MIMETYPE_VIDEO_VP8);
+ break;
+
default:
CHECK(!"Should not be here, unsupported video encoding.");
break;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 76e1d54..f0f4e45 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -32,6 +32,7 @@
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
+#include "../../libstagefright/include/NuCachedSource2.h"
#include "../../libstagefright/include/WVMExtractor.h"
namespace android {
@@ -47,7 +48,10 @@
mAudioIsVorbis(false),
mIsWidevine(false),
mUIDValid(uidValid),
- mUID(uid) {
+ mUID(uid),
+ mMetaDataSize(-1ll),
+ mBitrate(-1ll),
+ mPollBufferingGeneration(0) {
resetDataSource();
DataSource::RegisterDefaultSniffers();
}
@@ -92,18 +96,18 @@
return OK;
}
-status_t NuPlayer::GenericSource::initFromDataSource(
- const sp<DataSource> &dataSource,
- const char* mime) {
+status_t NuPlayer::GenericSource::initFromDataSource() {
sp<MediaExtractor> extractor;
+ CHECK(mDataSource != NULL);
+
if (mIsWidevine) {
String8 mimeType;
float confidence;
sp<AMessage> dummy;
bool success;
- success = SniffWVM(dataSource, &mimeType, &confidence, &dummy);
+ success = SniffWVM(mDataSource, &mimeType, &confidence, &dummy);
if (!success
|| strcasecmp(
mimeType.string(), MEDIA_MIMETYPE_CONTAINER_WVM)) {
@@ -111,14 +115,15 @@
return UNKNOWN_ERROR;
}
- sp<WVMExtractor> wvmExtractor = new WVMExtractor(dataSource);
- wvmExtractor->setAdaptiveStreamingMode(true);
+ mWVMExtractor = new WVMExtractor(mDataSource);
+ mWVMExtractor->setAdaptiveStreamingMode(true);
if (mUIDValid) {
- wvmExtractor->setUID(mUID);
+ mWVMExtractor->setUID(mUID);
}
- extractor = wvmExtractor;
+ extractor = mWVMExtractor;
} else {
- extractor = MediaExtractor::Create(dataSource, mime);
+ extractor = MediaExtractor::Create(mDataSource,
+ mSniffedMIME.empty() ? NULL: mSniffedMIME.c_str());
}
if (extractor == NULL) {
@@ -133,14 +138,20 @@
}
}
+ int32_t totalBitrate = 0;
+
for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ sp<MediaSource> track = extractor->getTrack(i);
+
sp<MetaData> meta = extractor->getTrackMetaData(i);
const char *mime;
CHECK(meta->findCString(kKeyMIMEType, &mime));
- sp<MediaSource> track = extractor->getTrack(i);
-
+ // Do the string compare immediately with "mime",
+ // we can't assume "mime" would stay valid after another
+ // extractor operation, some extractors might modify meta
+ // during getTrack() and make it invalid.
if (!strncasecmp(mime, "audio/", 6)) {
if (mAudioTrack.mSource == NULL) {
mAudioTrack.mIndex = i;
@@ -177,9 +188,18 @@
mDurationUs = durationUs;
}
}
+
+ int32_t bitrate;
+ if (totalBitrate >= 0 && meta->findInt32(kKeyBitRate, &bitrate)) {
+ totalBitrate += bitrate;
+ } else {
+ totalBitrate = -1;
+ }
}
}
+ mBitrate = totalBitrate;
+
return OK;
}
@@ -213,34 +233,53 @@
void NuPlayer::GenericSource::onPrepareAsync() {
// delayed data source creation
- AString sniffedMIME;
- sp<DataSource> dataSource;
+ if (mDataSource == NULL) {
+ if (!mUri.empty()) {
+ mIsWidevine = !strncasecmp(mUri.c_str(), "widevine://", 11);
- if (!mUri.empty()) {
- mIsWidevine = !strncasecmp(mUri.c_str(), "widevine://", 11);
+ mDataSource = DataSource::CreateFromURI(
+ mHTTPService, mUri.c_str(), &mUriHeaders, &mContentType);
+ } else {
+ // set to false first, if the extractor
+ // comes back as secure, set it to true then.
+ mIsWidevine = false;
- dataSource = DataSource::CreateFromURI(
- mHTTPService, mUri.c_str(), &mUriHeaders, &sniffedMIME);
- } else {
- // set to false first, if the extractor
- // comes back as secure, set it to true then.
- mIsWidevine = false;
+ mDataSource = new FileSource(mFd, mOffset, mLength);
+ }
- dataSource = new FileSource(mFd, mOffset, mLength);
+ if (mDataSource == NULL) {
+ ALOGE("Failed to create data source!");
+ notifyPreparedAndCleanup(UNKNOWN_ERROR);
+ return;
+ }
+
+ if (mDataSource->flags() & DataSource::kIsCachingDataSource) {
+ mCachedSource = static_cast<NuCachedSource2 *>(mDataSource.get());
+ }
+
+ if (mIsWidevine || mCachedSource != NULL) {
+ schedulePollBuffering();
+ }
}
- if (dataSource == NULL) {
- ALOGE("Failed to create data source!");
- notifyPrepared(UNKNOWN_ERROR);
+ // 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;
}
- status_t err = initFromDataSource(
- dataSource, sniffedMIME.empty() ? NULL : sniffedMIME.c_str());
+ // init extrator from data source
+ err = initFromDataSource();
if (err != OK) {
ALOGE("Failed to init from data source!");
- notifyPrepared(err);
+ notifyPreparedAndCleanup(err);
return;
}
@@ -258,6 +297,89 @@
notifyPrepared();
}
+void NuPlayer::GenericSource::notifyPreparedAndCleanup(status_t err) {
+ if (err != OK) {
+ mMetaDataSize = -1ll;
+ mContentType = "";
+ mSniffedMIME = "";
+ mDataSource.clear();
+ mCachedSource.clear();
+
+ cancelPollBuffering();
+ }
+ 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");
@@ -282,6 +404,65 @@
return OK;
}
+void NuPlayer::GenericSource::schedulePollBuffering() {
+ sp<AMessage> msg = new AMessage(kWhatPollBuffering, id());
+ msg->setInt32("generation", mPollBufferingGeneration);
+ msg->post(1000000ll);
+}
+
+void NuPlayer::GenericSource::cancelPollBuffering() {
+ ++mPollBufferingGeneration;
+}
+
+void NuPlayer::GenericSource::notifyBufferingUpdate(int percentage) {
+ sp<AMessage> msg = dupNotify();
+ msg->setInt32("what", kWhatBufferingUpdate);
+ msg->setInt32("percentage", percentage);
+ msg->post();
+}
+
+void NuPlayer::GenericSource::onPollBuffering() {
+ status_t finalStatus = UNKNOWN_ERROR;
+ int64_t cachedDurationUs = 0ll;
+
+ if (mCachedSource != NULL) {
+ size_t cachedDataRemaining =
+ mCachedSource->approxDataRemaining(&finalStatus);
+
+ if (finalStatus == OK) {
+ off64_t size;
+ int64_t bitrate = 0ll;
+ if (mDurationUs > 0 && mCachedSource->getSize(&size) == OK) {
+ bitrate = size * 8000000ll / mDurationUs;
+ } else if (mBitrate > 0) {
+ bitrate = mBitrate;
+ }
+ if (bitrate > 0) {
+ cachedDurationUs = cachedDataRemaining * 8000000ll / bitrate;
+ }
+ }
+ } else if (mWVMExtractor != NULL) {
+ cachedDurationUs
+ = mWVMExtractor->getCachedDurationUs(&finalStatus);
+ }
+
+ if (finalStatus == ERROR_END_OF_STREAM) {
+ notifyBufferingUpdate(100);
+ cancelPollBuffering();
+ return;
+ } else if (cachedDurationUs > 0ll && mDurationUs > 0ll) {
+ int percentage = 100.0 * cachedDurationUs / mDurationUs;
+ if (percentage > 100) {
+ percentage = 100;
+ }
+
+ notifyBufferingUpdate(percentage);
+ }
+
+ schedulePollBuffering();
+}
+
+
void NuPlayer::GenericSource::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatPrepareAsync:
@@ -364,7 +545,15 @@
break;
}
-
+ case kWhatPollBuffering:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+ if (generation == mPollBufferingGeneration) {
+ onPollBuffering();
+ }
+ break;
+ }
default:
Source::onMessageReceived(msg);
break;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index d3081de..663bfae 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -33,6 +33,8 @@
struct IMediaHTTPService;
struct MediaSource;
class MediaBuffer;
+struct NuCachedSource2;
+struct WVMExtractor;
struct NuPlayer::GenericSource : public NuPlayer::Source {
GenericSource(const sp<AMessage> ¬ify, bool uidValid, uid_t uid);
@@ -76,6 +78,7 @@
kWhatSendSubtitleData,
kWhatSendTimedTextData,
kWhatChangeAVSource,
+ kWhatPollBuffering,
};
Vector<sp<MediaSource> > mSources;
@@ -105,14 +108,24 @@
int64_t mOffset;
int64_t mLength;
- sp<ALooper> mLooper;
+ sp<DataSource> mDataSource;
+ sp<NuCachedSource2> mCachedSource;
+ sp<WVMExtractor> mWVMExtractor;
+ String8 mContentType;
+ AString mSniffedMIME;
+ off64_t mMetaDataSize;
+ int64_t mBitrate;
+ int32_t mPollBufferingGeneration;
+ sp<ALooper> mLooper;
void resetDataSource();
- status_t initFromDataSource(
- const sp<DataSource> &dataSource,
- const char *mime);
+ status_t initFromDataSource();
+
+ status_t prefillCacheIfNecessary();
+
+ void notifyPreparedAndCleanup(status_t err);
void onPrepareAsync();
@@ -133,6 +146,11 @@
media_track_type trackType,
int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL, bool formatChange = false);
+ void schedulePollBuffering();
+ void cancelPollBuffering();
+ void onPollBuffering();
+ void notifyBufferingUpdate(int percentage);
+
DISALLOW_EVIL_CONSTRUCTORS(GenericSource);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 5f2c20a..2b7457b 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -280,7 +280,7 @@
msg->setObject(
"native-window",
new NativeWindowWrapper(
- new Surface(bufferProducer)));
+ new Surface(bufferProducer, true /* controlledByApp */)));
}
msg->post();
@@ -756,7 +756,7 @@
ALOGV("initiating %s decoder shutdown",
audio ? "audio" : "video");
- (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown();
+ getDecoder(audio)->initiateShutdown();
if (audio) {
mFlushingAudio = SHUTTING_DOWN_DECODER;
@@ -803,6 +803,14 @@
err = UNKNOWN_ERROR;
}
mRenderer->queueEOS(audio, err);
+ if (audio && mFlushingAudio != NONE) {
+ mAudioDecoder.clear();
+ mFlushingAudio = SHUT_DOWN;
+ } else if (!audio && mFlushingVideo != NONE){
+ mVideoDecoder.clear();
+ mFlushingVideo = SHUT_DOWN;
+ }
+ finishFlushIfPossible();
} else if (what == Decoder::kWhatDrainThisBuffer) {
renderBuffer(audio, msg);
} else {
@@ -964,11 +972,13 @@
}
void NuPlayer::finishFlushIfPossible() {
- if (mFlushingAudio != FLUSHED && mFlushingAudio != SHUT_DOWN) {
+ if (mFlushingAudio != NONE && mFlushingAudio != FLUSHED
+ && mFlushingAudio != SHUT_DOWN) {
return;
}
- if (mFlushingVideo != FLUSHED && mFlushingVideo != SHUT_DOWN) {
+ if (mFlushingVideo != NONE && mFlushingVideo != FLUSHED
+ && mFlushingVideo != SHUT_DOWN) {
return;
}
@@ -979,11 +989,11 @@
mTimeDiscontinuityPending = false;
}
- if (mAudioDecoder != NULL) {
+ if (mAudioDecoder != NULL && mFlushingAudio == FLUSHED) {
mAudioDecoder->signalResume();
}
- if (mVideoDecoder != NULL) {
+ if (mVideoDecoder != NULL && mFlushingVideo == FLUSHED) {
mVideoDecoder->signalResume();
}
@@ -1221,8 +1231,8 @@
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
- if ((audio && IsFlushingState(mFlushingAudio))
- || (!audio && IsFlushingState(mFlushingVideo))) {
+ if ((audio && mFlushingAudio != NONE)
+ || (!audio && mFlushingVideo != NONE)) {
reply->setInt32("err", INFO_DISCONTINUITY);
reply->post();
return OK;
@@ -1282,7 +1292,24 @@
mTimeDiscontinuityPending =
mTimeDiscontinuityPending || timeChange;
- if (mFlushingAudio == NONE && mFlushingVideo == NONE) {
+ bool seamlessFormatChange = false;
+ sp<AMessage> newFormat = mSource->getFormat(audio);
+ if (formatChange) {
+ seamlessFormatChange =
+ getDecoder(audio)->supportsSeamlessFormatChange(newFormat);
+ // treat seamless format change separately
+ formatChange = !seamlessFormatChange;
+ }
+ bool shutdownOrFlush = formatChange || timeChange;
+
+ // We want to queue up scan-sources only once per discontinuity.
+ // We control this by doing it only if neither audio nor video are
+ // flushing or shutting down. (After handling 1st discontinuity, one
+ // of the flushing states will not be NONE.)
+ // No need to scan sources if this discontinuity does not result
+ // in a flush or shutdown, as the flushing state will stay NONE.
+ if (mFlushingAudio == NONE && mFlushingVideo == NONE &&
+ shutdownOrFlush) {
// And we'll resume scanning sources once we're done
// flushing.
mDeferredActions.push_front(
@@ -1290,27 +1317,19 @@
&NuPlayer::performScanSources));
}
- if (formatChange || timeChange) {
-
- sp<AMessage> newFormat = mSource->getFormat(audio);
- sp<Decoder> &decoder = audio ? mAudioDecoder : mVideoDecoder;
- if (formatChange && !decoder->supportsSeamlessFormatChange(newFormat)) {
- flushDecoder(audio, /* needShutdown = */ true);
- } else {
- flushDecoder(audio, /* needShutdown = */ false);
- err = OK;
- }
+ if (formatChange /* not seamless */) {
+ // must change decoder
+ flushDecoder(audio, /* needShutdown = */ true);
+ } else if (timeChange) {
+ // need to flush
+ flushDecoder(audio, /* needShutdown = */ false, newFormat);
+ err = OK;
+ } else if (seamlessFormatChange) {
+ // reuse existing decoder and don't flush
+ updateDecoderFormatWithoutFlush(audio, newFormat);
+ err = OK;
} else {
// This stream is unaffected by the discontinuity
-
- if (audio) {
- mFlushingAudio = FLUSHED;
- } else {
- mFlushingVideo = FLUSHED;
- }
-
- finishFlushIfPossible();
-
return -EWOULDBLOCK;
}
}
@@ -1361,7 +1380,8 @@
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
- if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) {
+ if ((audio && mFlushingAudio != NONE)
+ || (!audio && mFlushingVideo != NONE)) {
// We're currently attempting to flush the decoder, in order
// to complete this, the decoder wants all its buffers back,
// so we don't want any output buffers it sent us (from before
@@ -1486,50 +1506,53 @@
driver->notifyListener(msg, ext1, ext2, in);
}
-void NuPlayer::flushDecoder(bool audio, bool needShutdown) {
+void NuPlayer::flushDecoder(
+ bool audio, bool needShutdown, const sp<AMessage> &newFormat) {
ALOGV("[%s] flushDecoder needShutdown=%d",
audio ? "audio" : "video", needShutdown);
- if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) {
+ const sp<Decoder> &decoder = getDecoder(audio);
+ if (decoder == NULL) {
ALOGI("flushDecoder %s without decoder present",
audio ? "audio" : "video");
+ return;
}
// Make sure we don't continue to scan sources until we finish flushing.
++mScanSourcesGeneration;
mScanSourcesPending = false;
- (audio ? mAudioDecoder : mVideoDecoder)->signalFlush();
+ decoder->signalFlush(newFormat);
mRenderer->flush(audio);
FlushStatus newStatus =
needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER;
if (audio) {
- CHECK(mFlushingAudio == NONE
- || mFlushingAudio == AWAITING_DISCONTINUITY);
-
+ ALOGE_IF(mFlushingAudio != NONE,
+ "audio flushDecoder() is called in state %d", mFlushingAudio);
mFlushingAudio = newStatus;
-
- if (mFlushingVideo == NONE) {
- mFlushingVideo = (mVideoDecoder != NULL)
- ? AWAITING_DISCONTINUITY
- : FLUSHED;
- }
} else {
- CHECK(mFlushingVideo == NONE
- || mFlushingVideo == AWAITING_DISCONTINUITY);
-
+ ALOGE_IF(mFlushingVideo != NONE,
+ "video flushDecoder() is called in state %d", mFlushingVideo);
mFlushingVideo = newStatus;
-
- if (mFlushingAudio == NONE) {
- mFlushingAudio = (mAudioDecoder != NULL)
- ? AWAITING_DISCONTINUITY
- : FLUSHED;
- }
}
}
+void NuPlayer::updateDecoderFormatWithoutFlush(
+ bool audio, const sp<AMessage> &format) {
+ ALOGV("[%s] updateDecoderFormatWithoutFlush", audio ? "audio" : "video");
+
+ const sp<Decoder> &decoder = getDecoder(audio);
+ if (decoder == NULL) {
+ ALOGI("updateDecoderFormatWithoutFlush %s without decoder present",
+ audio ? "audio" : "video");
+ return;
+ }
+
+ decoder->signalUpdateFormat(format);
+}
+
void NuPlayer::queueDecoderShutdown(
bool audio, bool video, const sp<AMessage> &reply) {
ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video);
@@ -1616,18 +1639,6 @@
// an intermediate state, i.e. one more more decoders are currently
// flushing or shutting down.
- if (mRenderer != NULL) {
- // There's an edge case where the renderer owns all output
- // buffers and is paused, therefore the decoder will not read
- // more input data and will never encounter the matching
- // discontinuity. To avoid this, we resume the renderer.
-
- if (mFlushingAudio == AWAITING_DISCONTINUITY
- || mFlushingVideo == AWAITING_DISCONTINUITY) {
- mRenderer->resume();
- }
- }
-
if (mFlushingAudio != NONE || mFlushingVideo != NONE) {
// We're currently flushing, postpone the reset until that's
// completed.
@@ -1692,14 +1703,6 @@
mTimeDiscontinuityPending = true;
- if (mFlushingAudio == NONE && (!audio || mAudioDecoder == NULL)) {
- mFlushingAudio = FLUSHED;
- }
-
- if (mFlushingVideo == NONE && (!video || mVideoDecoder == NULL)) {
- mFlushingVideo = FLUSHED;
- }
-
if (audio && mAudioDecoder != NULL) {
flushDecoder(true /* audio */, true /* needShutdown */);
}
@@ -1764,6 +1767,13 @@
// XXX - ignore error from setVideoScalingMode for now
setVideoScalingMode(mVideoScalingMode);
+
+ if (mDriver != NULL) {
+ sp<NuPlayerDriver> driver = mDriver.promote();
+ if (driver != NULL) {
+ driver->notifySetSurfaceComplete();
+ }
+ }
}
void NuPlayer::onSourceNotify(const sp<AMessage> &msg) {
@@ -1829,6 +1839,15 @@
break;
}
+ case Source::kWhatBufferingUpdate:
+ {
+ int32_t percentage;
+ CHECK(msg->findInt32("percentage", &percentage));
+
+ notifyListener(MEDIA_BUFFERING_UPDATE, percentage, 0);
+ break;
+ }
+
case Source::kWhatBufferingStart:
{
notifyListener(MEDIA_INFO, MEDIA_INFO_BUFFERING_START, 0);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index bb76636..511d752 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -25,6 +25,7 @@
namespace android {
struct ABuffer;
+struct AMessage;
struct MetaData;
struct NuPlayerDriver;
@@ -145,7 +146,6 @@
enum FlushStatus {
NONE,
- AWAITING_DISCONTINUITY,
FLUSHING_DECODER,
FLUSHING_DECODER_SHUTDOWN,
SHUTTING_DOWN_DECODER,
@@ -170,6 +170,10 @@
bool mStarted;
+ inline const sp<Decoder> &getDecoder(bool audio) {
+ return audio ? mAudioDecoder : mVideoDecoder;
+ }
+
void openAudioSink(const sp<AMessage> &format, bool offloadOnly);
void closeAudioSink();
@@ -186,7 +190,9 @@
void finishFlushIfPossible();
- void flushDecoder(bool audio, bool needShutdown);
+ void flushDecoder(
+ bool audio, bool needShutdown, const sp<AMessage> &newFormat = NULL);
+ void updateDecoderFormatWithoutFlush(bool audio, const sp<AMessage> &format);
static bool IsFlushingState(FlushStatus state, bool *needShutdown = NULL);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 8fce2f4..d1aac50 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -44,11 +44,11 @@
// Every decoder has its own looper because MediaCodec operations
// are blocking, but NuPlayer needs asynchronous operations.
mDecoderLooper = new ALooper;
- mDecoderLooper->setName("NuPlayerDecoder");
+ mDecoderLooper->setName("NPDecoder");
mDecoderLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
mCodecLooper = new ALooper;
- mCodecLooper->setName("NuPlayerDecoder-MC");
+ mCodecLooper->setName("NPDecoder-CL");
mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
}
@@ -71,6 +71,19 @@
return err;
}
+void NuPlayer::Decoder::rememberCodecSpecificData(const sp<AMessage> &format) {
+ mCSDsForCurrentFormat.clear();
+ for (int32_t i = 0; ; ++i) {
+ AString tag = "csd-";
+ tag.append(i);
+ sp<ABuffer> buffer;
+ if (!format->findBuffer(tag.c_str(), &buffer)) {
+ break;
+ }
+ mCSDsForCurrentFormat.push(buffer);
+ }
+}
+
void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
CHECK(mCodec == NULL);
@@ -123,6 +136,8 @@
handleError(err);
return;
}
+ rememberCodecSpecificData(format);
+
// the following should work in configured state
CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
@@ -189,6 +204,12 @@
msg->post();
}
+void NuPlayer::Decoder::signalUpdateFormat(const sp<AMessage> &format) {
+ sp<AMessage> msg = new AMessage(kWhatUpdateFormat, id());
+ msg->setMessage("format", format);
+ msg->post();
+}
+
status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id());
msg->setPointer("buffers", buffers);
@@ -229,6 +250,15 @@
reply->setSize("buffer-ix", bufferIx);
reply->setInt32("generation", mBufferGeneration);
+ if (!mCSDsToSubmit.isEmpty()) {
+ sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
+ ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
+ reply->setBuffer("buffer", buffer);
+ mCSDsToSubmit.removeAt(0);
+ reply->post();
+ return true;
+ }
+
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFillThisBuffer);
notify->setBuffer("buffer", mInputBuffers[bufferIx]);
@@ -312,10 +342,12 @@
uint32_t flags = 0;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
- int32_t eos;
- // we do not expect CODECCONFIG or SYNCFRAME for decoder
+ int32_t eos, csd;
+ // we do not expect SYNCFRAME for decoder
if (buffer->meta()->findInt32("eos", &eos) && eos) {
flags |= MediaCodec::BUFFER_FLAG_EOS;
+ } else if (buffer->meta()->findInt32("csd", &csd) && csd) {
+ flags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
}
// copy into codec buffer
@@ -448,6 +480,7 @@
status_t err = OK;
if (mCodec != NULL) {
err = mCodec->flush();
+ mCSDsToSubmit = mCSDsForCurrentFormat; // copy operator
++mBufferGeneration;
}
@@ -478,10 +511,13 @@
if (mNativeWindow != NULL) {
// reconnect to surface as MediaCodec disconnected from it
- CHECK_EQ((int)NO_ERROR,
+ status_t error =
native_window_api_connect(
mNativeWindow->getNativeWindow().get(),
- NATIVE_WINDOW_API_MEDIA));
+ NATIVE_WINDOW_API_MEDIA);
+ ALOGW_IF(error != NO_ERROR,
+ "[%s] failed to connect to native window, error=%d",
+ mComponentName.c_str(), error);
}
mComponentName = "decoder";
}
@@ -512,6 +548,14 @@
break;
}
+ case kWhatUpdateFormat:
+ {
+ sp<AMessage> format;
+ CHECK(msg->findMessage("format", &format));
+ rememberCodecSpecificData(format);
+ break;
+ }
+
case kWhatGetInputBuffers:
{
uint32_t replyID;
@@ -563,6 +607,10 @@
case kWhatFlush:
{
+ sp<AMessage> format;
+ if (msg->findMessage("new-format", &format)) {
+ rememberCodecSpecificData(format);
+ }
onFlush();
break;
}
@@ -585,8 +633,12 @@
}
}
-void NuPlayer::Decoder::signalFlush() {
- (new AMessage(kWhatFlush, id()))->post();
+void NuPlayer::Decoder::signalFlush(const sp<AMessage> &format) {
+ sp<AMessage> msg = new AMessage(kWhatFlush, id());
+ if (format != NULL) {
+ msg->setMessage("new-format", format);
+ }
+ msg->post();
}
void NuPlayer::Decoder::signalResume() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index c6fc237..67bddb8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -36,7 +36,8 @@
virtual void init();
status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const;
- virtual void signalFlush();
+ virtual void signalFlush(const sp<AMessage> &format = NULL);
+ virtual void signalUpdateFormat(const sp<AMessage> &format);
virtual void signalResume();
virtual void initiateShutdown();
@@ -67,6 +68,7 @@
kWhatRenderBuffer = 'rndr',
kWhatFlush = 'flus',
kWhatShutdown = 'shuD',
+ kWhatUpdateFormat = 'uFmt',
};
sp<AMessage> mNotify;
@@ -80,6 +82,8 @@
Vector<sp<ABuffer> > mInputBuffers;
Vector<sp<ABuffer> > mOutputBuffers;
+ Vector<sp<ABuffer> > mCSDsForCurrentFormat;
+ Vector<sp<ABuffer> > mCSDsToSubmit;
Vector<bool> mInputBufferIsDequeued;
Vector<MediaBuffer *> mMediaBuffers;
@@ -103,6 +107,7 @@
AString mComponentName;
bool supportsSeamlessAudioFormatChange(const sp<AMessage> &targetFormat) const;
+ void rememberCodecSpecificData(const sp<AMessage> &format);
DISALLOW_EVIL_CONSTRUCTORS(Decoder);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 140e1ae..c4bbcdf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -34,8 +34,11 @@
: mState(STATE_IDLE),
mIsAsyncPrepare(false),
mAsyncResult(UNKNOWN_ERROR),
+ mSetSurfaceInProgress(false),
mDurationUs(-1),
mPositionUs(-1),
+ mNotifyTimeRealUs(-1),
+ mPauseStartedTimeUs(-1),
mNumFramesTotal(0),
mNumFramesDropped(0),
mLooper(new ALooper),
@@ -134,6 +137,10 @@
const sp<IGraphicBufferProducer> &bufferProducer) {
Mutex::Autolock autoLock(mLock);
+ if (mSetSurfaceInProgress) {
+ return INVALID_OPERATION;
+ }
+
switch (mState) {
case STATE_SET_DATASOURCE_PENDING:
case STATE_RESET_IN_PROGRESS:
@@ -143,8 +150,14 @@
break;
}
+ mSetSurfaceInProgress = true;
+
mPlayer->setVideoSurfaceTextureAsync(bufferProducer);
+ while (mSetSurfaceInProgress) {
+ mCondition.wait(mLock);
+ }
+
return OK;
}
@@ -227,7 +240,7 @@
if (mStartupSeekTimeUs >= 0) {
if (mStartupSeekTimeUs == 0) {
- notifySeekComplete();
+ notifySeekComplete_l();
} else {
mPlayer->seekToAsync(mStartupSeekTimeUs);
}
@@ -238,12 +251,20 @@
}
case STATE_RUNNING:
+ {
+ if (mAtEOS) {
+ mPlayer->seekToAsync(0);
+ mAtEOS = false;
+ mPositionUs = -1;
+ }
break;
+ }
case STATE_PAUSED:
case STATE_STOPPED_AND_PREPARED:
{
mPlayer->resume();
+ mPositionUs -= ALooper::GetNowUs() - mPauseStartedTimeUs;
break;
}
@@ -252,6 +273,7 @@
}
mState = STATE_RUNNING;
+ mPauseStartedTimeUs = -1;
return OK;
}
@@ -265,8 +287,9 @@
// fall through
case STATE_PAUSED:
- notifyListener(MEDIA_STOPPED);
- // fall through
+ mState = STATE_STOPPED;
+ notifyListener_l(MEDIA_STOPPED);
+ break;
case STATE_PREPARED:
case STATE_STOPPED:
@@ -278,6 +301,7 @@
default:
return INVALID_OPERATION;
}
+ setPauseStartedTimeIfNeeded();
return OK;
}
@@ -291,7 +315,9 @@
return OK;
case STATE_RUNNING:
- notifyListener(MEDIA_PAUSED);
+ setPauseStartedTimeIfNeeded();
+ mState = STATE_PAUSED;
+ notifyListener_l(MEDIA_PAUSED);
mPlayer->pause();
break;
@@ -299,8 +325,6 @@
return INVALID_OPERATION;
}
- mState = STATE_PAUSED;
-
return OK;
}
@@ -320,7 +344,7 @@
// pretend that the seek completed. It will actually happen when starting playback.
// TODO: actually perform the seek here, so the player is ready to go at the new
// location
- notifySeekComplete();
+ notifySeekComplete_l();
break;
}
@@ -329,7 +353,7 @@
{
mAtEOS = false;
// seeks can take a while, so we essentially paused
- notifyListener(MEDIA_PAUSED);
+ notifyListener_l(MEDIA_PAUSED);
mPlayer->seekToAsync(seekTimeUs);
break;
}
@@ -338,6 +362,8 @@
return INVALID_OPERATION;
}
+ mPositionUs = seekTimeUs;
+ mNotifyTimeRealUs = -1;
return OK;
}
@@ -346,8 +372,12 @@
if (mPositionUs < 0) {
*msec = 0;
+ } else if (mNotifyTimeRealUs == -1) {
+ *msec = mPositionUs / 1000;
} else {
- *msec = (mPositionUs + 500ll) / 1000;
+ int64_t nowUs =
+ (isPlaying() ? ALooper::GetNowUs() : mPauseStartedTimeUs);
+ *msec = (mPositionUs + nowUs - mNotifyTimeRealUs + 500ll) / 1000;
}
return OK;
@@ -380,7 +410,7 @@
{
CHECK(mIsAsyncPrepare);
- notifyListener(MEDIA_PREPARED);
+ notifyListener_l(MEDIA_PREPARED);
break;
}
@@ -389,7 +419,7 @@
}
if (mState != STATE_STOPPED) {
- notifyListener(MEDIA_STOPPED);
+ notifyListener_l(MEDIA_STOPPED);
}
mState = STATE_RESET_IN_PROGRESS;
@@ -514,6 +544,15 @@
mCondition.broadcast();
}
+void NuPlayerDriver::notifySetSurfaceComplete() {
+ Mutex::Autolock autoLock(mLock);
+
+ CHECK(mSetSurfaceInProgress);
+ mSetSurfaceInProgress = false;
+
+ mCondition.broadcast();
+}
+
void NuPlayerDriver::notifyDuration(int64_t durationUs) {
Mutex::Autolock autoLock(mLock);
mDurationUs = durationUs;
@@ -522,26 +561,29 @@
void NuPlayerDriver::notifyPosition(int64_t positionUs) {
Mutex::Autolock autoLock(mLock);
mPositionUs = positionUs;
+ mNotifyTimeRealUs = ALooper::GetNowUs();
}
void NuPlayerDriver::notifySeekComplete() {
+ Mutex::Autolock autoLock(mLock);
+ notifySeekComplete_l();
+}
+
+void NuPlayerDriver::notifySeekComplete_l() {
bool wasSeeking = true;
- {
- Mutex::Autolock autoLock(mLock);
- if (mState == STATE_STOPPED_AND_PREPARING) {
- wasSeeking = false;
- mState = STATE_STOPPED_AND_PREPARED;
- mCondition.broadcast();
- if (!mIsAsyncPrepare) {
- // if we are preparing synchronously, no need to notify listener
- return;
- }
- } else if (mState == STATE_STOPPED) {
- // no need to notify listener
+ if (mState == STATE_STOPPED_AND_PREPARING) {
+ wasSeeking = false;
+ mState = STATE_STOPPED_AND_PREPARED;
+ mCondition.broadcast();
+ if (!mIsAsyncPrepare) {
+ // if we are preparing synchronously, no need to notify listener
return;
}
+ } else if (mState == STATE_STOPPED) {
+ // no need to notify listener
+ return;
}
- notifyListener(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
+ notifyListener_l(wasSeeking ? MEDIA_SEEK_COMPLETE : MEDIA_PREPARED);
}
void NuPlayerDriver::notifyFrameStats(
@@ -573,11 +615,19 @@
void NuPlayerDriver::notifyListener(
int msg, int ext1, int ext2, const Parcel *in) {
+ Mutex::Autolock autoLock(mLock);
+ notifyListener_l(msg, ext1, ext2, in);
+}
+
+void NuPlayerDriver::notifyListener_l(
+ int msg, int ext1, int ext2, const Parcel *in) {
switch (msg) {
case MEDIA_PLAYBACK_COMPLETE:
{
if (mLooping) {
+ mLock.unlock();
mPlayer->seekToAsync(0);
+ mLock.lock();
break;
}
// fall through
@@ -586,6 +636,7 @@
case MEDIA_ERROR:
{
mAtEOS = true;
+ setPauseStartedTimeIfNeeded();
break;
}
@@ -593,7 +644,9 @@
break;
}
+ mLock.unlock();
sendEvent(msg, ext1, ext2, in);
+ mLock.lock();
}
void NuPlayerDriver::notifySetDataSourceCompleted(status_t err) {
@@ -622,15 +675,17 @@
mAsyncResult = err;
if (err == OK) {
- if (mIsAsyncPrepare) {
- notifyListener(MEDIA_PREPARED);
- }
+ // update state before notifying client, so that if client calls back into NuPlayerDriver
+ // in response, NuPlayerDriver has the right state
mState = STATE_PREPARED;
- } else {
if (mIsAsyncPrepare) {
- notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ notifyListener_l(MEDIA_PREPARED);
}
+ } else {
mState = STATE_UNPREPARED;
+ if (mIsAsyncPrepare) {
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+ }
}
mCondition.broadcast();
@@ -642,4 +697,10 @@
mPlayerFlags = flags;
}
+void NuPlayerDriver::setPauseStartedTimeIfNeeded() {
+ if (mPauseStartedTimeUs == -1) {
+ mPauseStartedTimeUs = ALooper::GetNowUs();
+ }
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
index f520395..e81d605 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.h
@@ -66,9 +66,11 @@
void notifySetDataSourceCompleted(status_t err);
void notifyPrepareCompleted(status_t err);
void notifyResetComplete();
+ void notifySetSurfaceComplete();
void notifyDuration(int64_t durationUs);
void notifyPosition(int64_t positionUs);
void notifySeekComplete();
+ void notifySeekComplete_l();
void notifyFrameStats(int64_t numFramesTotal, int64_t numFramesDropped);
void notifyListener(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
void notifyFlagsChanged(uint32_t flags);
@@ -101,8 +103,11 @@
// The following are protected through "mLock"
// >>>
+ bool mSetSurfaceInProgress;
int64_t mDurationUs;
int64_t mPositionUs;
+ int64_t mNotifyTimeRealUs;
+ int64_t mPauseStartedTimeUs;
int64_t mNumFramesTotal;
int64_t mNumFramesDropped;
// <<<
@@ -117,6 +122,8 @@
int64_t mStartupSeekTimeUs;
status_t prepare_l();
+ void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0, const Parcel *in = NULL);
+ void setPauseStartedTimeIfNeeded();
DISALLOW_EVIL_CONSTRUCTORS(NuPlayerDriver);
};
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 3777f64..a3c976d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -93,10 +93,14 @@
{
Mutex::Autolock autoLock(mFlushLock);
if (audio) {
- CHECK(!mFlushingAudio);
+ if (mFlushingAudio) {
+ return;
+ }
mFlushingAudio = true;
} else {
- CHECK(!mFlushingVideo);
+ if (mFlushingVideo) {
+ return;
+ }
mFlushingVideo = true;
}
}
@@ -115,6 +119,14 @@
mSyncQueues = false;
}
+void NuPlayer::Renderer::signalAudioSinkChanged() {
+ (new AMessage(kWhatAudioSinkChanged, id()))->post();
+}
+
+void NuPlayer::Renderer::signalDisableOffloadAudio() {
+ (new AMessage(kWhatDisableOffloadAudio, id()))->post();
+}
+
void NuPlayer::Renderer::pause() {
(new AMessage(kWhatPause, id()))->post();
}
@@ -251,14 +263,6 @@
msg->post(delayUs);
}
-void NuPlayer::Renderer::signalAudioSinkChanged() {
- (new AMessage(kWhatAudioSinkChanged, id()))->post();
-}
-
-void NuPlayer::Renderer::signalDisableOffloadAudio() {
- (new AMessage(kWhatDisableOffloadAudio, id()))->post();
-}
-
void NuPlayer::Renderer::prepareForMediaRenderingStart() {
mAudioRenderingStartGeneration = mAudioQueueGeneration;
mVideoRenderingStartGeneration = mVideoQueueGeneration;
@@ -444,11 +448,13 @@
copy = numBytesAvailableToWrite;
}
- CHECK_EQ(mAudioSink->write(
- entry->mBuffer->data() + entry->mOffset, copy),
- (ssize_t)copy);
+ ssize_t written = mAudioSink->write(entry->mBuffer->data() + entry->mOffset, copy);
+ if (written < 0) {
+ // An error in AudioSink write is fatal here.
+ LOG_ALWAYS_FATAL("AudioSink write error(%zd) when writing %zu bytes", written, copy);
+ }
- entry->mOffset += copy;
+ entry->mOffset += written;
if (entry->mOffset == entry->mBuffer->size()) {
entry->mNotifyConsumed->post();
mAudioQueue.erase(mAudioQueue.begin());
@@ -456,13 +462,33 @@
entry = NULL;
}
- numBytesAvailableToWrite -= copy;
- size_t copiedFrames = copy / mAudioSink->frameSize();
+ numBytesAvailableToWrite -= written;
+ size_t copiedFrames = written / mAudioSink->frameSize();
mNumFramesWritten += copiedFrames;
notifyIfMediaRenderingStarted();
- }
+ 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:
+ //
+ // 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.
+
+ // (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)
+ // 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);
+ break;
+ }
+ }
notifyPosition();
return !mAudioQueue.empty();
@@ -717,6 +743,15 @@
int32_t audio;
CHECK(msg->findInt32("audio", &audio));
+ {
+ Mutex::Autolock autoLock(mFlushLock);
+ if (audio) {
+ mFlushingAudio = false;
+ } else {
+ mFlushingVideo = 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
@@ -735,17 +770,18 @@
{
Mutex::Autolock autoLock(mLock);
flushQueue(&mAudioQueue);
+
+ ++mAudioQueueGeneration;
+ prepareForMediaRenderingStart();
+
+ if (offloadingAudio()) {
+ mFirstAudioTimeUs = -1;
+ }
}
- Mutex::Autolock autoLock(mFlushLock);
- mFlushingAudio = false;
-
mDrainAudioQueuePending = false;
- ++mAudioQueueGeneration;
- prepareForMediaRenderingStart();
if (offloadingAudio()) {
- mFirstAudioTimeUs = -1;
mAudioSink->pause();
mAudioSink->flush();
mAudioSink->start();
@@ -753,9 +789,6 @@
} else {
flushQueue(&mVideoQueue);
- Mutex::Autolock autoLock(mFlushLock);
- mFlushingVideo = false;
-
mDrainVideoQueuePending = false;
++mVideoQueueGeneration;
@@ -853,13 +886,15 @@
void NuPlayer::Renderer::onPause() {
CHECK(!mPaused);
+ {
+ Mutex::Autolock autoLock(mLock);
+ ++mAudioQueueGeneration;
+ ++mVideoQueueGeneration;
+ prepareForMediaRenderingStart();
+ }
+
mDrainAudioQueuePending = false;
- ++mAudioQueueGeneration;
-
mDrainVideoQueuePending = false;
- ++mVideoQueueGeneration;
-
- prepareForMediaRenderingStart();
if (mHasAudio) {
mAudioSink->pause();
@@ -896,7 +931,12 @@
uint32_t numFramesPlayed;
CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
- int64_t currentPositionUs = mFirstAudioTimeUs
+ int64_t firstAudioTimeUs;
+ {
+ Mutex::Autolock autoLock(mLock);
+ firstAudioTimeUs = mFirstAudioTimeUs;
+ }
+ int64_t currentPositionUs = firstAudioTimeUs
+ (numFramesPlayed * mAudioSink->msecsPerFrame()) * 1000ll;
mAudioSink->stop();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
index 74892b6..45657c2 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerSource.h
@@ -45,6 +45,7 @@
kWhatPrepared,
kWhatFlagsChanged,
kWhatVideoSizeChanged,
+ kWhatBufferingUpdate,
kWhatBufferingStart,
kWhatBufferingEnd,
kWhatSubtitleData,
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index b44d5cc..e4e463a 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -813,7 +813,10 @@
for (OMX_U32 i = cancelStart; i < cancelEnd; i++) {
BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
- cancelBufferToNativeWindow(info);
+ status_t error = cancelBufferToNativeWindow(info);
+ if (err == 0) {
+ err = error;
+ }
}
return err;
@@ -888,11 +891,12 @@
int err = mNativeWindow->cancelBuffer(
mNativeWindow.get(), info->mGraphicBuffer.get(), -1);
- CHECK_EQ(err, 0);
+ ALOGW_IF(err != 0, "[%s] can not return buffer %u to native window",
+ mComponentName.c_str(), info->mBufferID);
info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
- return OK;
+ return err;
}
ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
@@ -992,7 +996,7 @@
if (portIndex == kPortIndexOutput && mNativeWindow != NULL
&& info->mStatus == BufferInfo::OWNED_BY_US) {
- CHECK_EQ((status_t)OK, cancelBufferToNativeWindow(info));
+ cancelBufferToNativeWindow(info);
}
CHECK_EQ(mOMX->freeBuffer(
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
index 008da5a..9d6fd78 100644
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -186,9 +186,9 @@
const sp<IMediaHTTPService> &httpService,
const char *uri,
const KeyedVector<String8, String8> *headers,
- AString *sniffedMIME) {
- if (sniffedMIME != NULL) {
- *sniffedMIME = "";
+ String8 *contentType) {
+ if (contentType != NULL) {
+ *contentType = "";
}
bool isWidevine = !strncasecmp("widevine://", uri, 11);
@@ -226,77 +226,14 @@
}
if (!isWidevine) {
- String8 contentType = httpSource->getMIMEType();
+ if (contentType != NULL) {
+ *contentType = httpSource->getMIMEType();
+ }
- sp<NuCachedSource2> cachedSource = new NuCachedSource2(
+ source = new NuCachedSource2(
httpSource,
cacheConfig.isEmpty() ? NULL : cacheConfig.string(),
disconnectAtHighwatermark);
-
- if (strncasecmp(contentType.string(), "audio/", 6)) {
- // 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.
-
- // 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;
-
- off64_t metaDataSize = -1ll;
- for (;;) {
- status_t finalStatus;
- size_t cachedDataRemaining =
- cachedSource->approxDataRemaining(&finalStatus);
-
- if (finalStatus != OK || (metaDataSize >= 0
- && (off64_t)cachedDataRemaining >= metaDataSize)) {
- ALOGV("stop caching, status %d, "
- "metaDataSize %lld, cachedDataRemaining %zu",
- finalStatus, metaDataSize, cachedDataRemaining);
- break;
- }
-
- ALOGV("now cached %zu bytes of data", cachedDataRemaining);
-
- if (metaDataSize < 0
- && cachedDataRemaining >= kMinBytesForSniffing) {
- String8 tmp;
- float confidence;
- sp<AMessage> meta;
- if (!cachedSource->sniff(&tmp, &confidence, &meta)) {
- return NULL;
- }
-
- // 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()
- if (sniffedMIME != NULL) {
- *sniffedMIME = tmp.string();
- }
-
- if (meta == NULL
- || !meta->findInt64("meta-data-size",
- reinterpret_cast<int64_t*>(&metaDataSize))) {
- metaDataSize = kDefaultMetaSize;
- }
-
- if (metaDataSize < 0ll) {
- ALOGE("invalid metaDataSize = %lld bytes", metaDataSize);
- return NULL;
- }
- }
-
- usleep(200000);
- }
- }
-
- source = cachedSource;
} else {
// We do not want that prefetching, caching, datasource wrapper
// in the widevine:// case.
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7bb7ed9..76f730f 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -716,12 +716,13 @@
CHECK(msg->findInt32("err", &err));
CHECK(msg->findInt32("actionCode", &actionCode));
- ALOGE("Codec reported err %#x, actionCode %d", err, actionCode);
+ ALOGE("Codec reported err %#x, actionCode %d, while in state %d",
+ err, actionCode, mState);
if (err == DEAD_OBJECT) {
mFlags |= kFlagSawMediaServerDie;
}
- bool sendErrorReponse = true;
+ bool sendErrorResponse = true;
switch (mState) {
case INITIALIZING:
@@ -748,7 +749,7 @@
// Ignore the error, assuming we'll still get
// the shutdown complete notification.
- sendErrorReponse = false;
+ sendErrorResponse = false;
if (mFlags & kFlagSawMediaServerDie) {
// MediaServer died, there definitely won't
@@ -767,15 +768,19 @@
case FLUSHING:
{
- setState(
- (mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
+ if (actionCode == ACTION_CODE_FATAL) {
+ setState(UNINITIALIZED);
+ } else {
+ setState(
+ (mFlags & kFlagIsAsync) ? FLUSHED : STARTED);
+ }
break;
}
case FLUSHED:
case STARTED:
{
- sendErrorReponse = false;
+ sendErrorResponse = false;
setStickyError(err);
postActivityNotificationIfPossible();
@@ -800,7 +805,7 @@
default:
{
- sendErrorReponse = false;
+ sendErrorResponse = false;
setStickyError(err);
postActivityNotificationIfPossible();
@@ -826,7 +831,7 @@
}
}
- if (sendErrorReponse) {
+ if (sendErrorResponse) {
PostReplyWithError(mReplyID, err);
}
break;
@@ -1113,7 +1118,11 @@
case CodecBase::kWhatFlushCompleted:
{
- CHECK_EQ(mState, FLUSHING);
+ if (mState != FLUSHING) {
+ ALOGW("received FlushCompleted message in state %d",
+ mState);
+ break;
+ }
if (mFlags & kFlagIsAsync) {
setState(FLUSHED);
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 09c6e69..8b4dd6f 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -493,7 +493,8 @@
return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
}
-void SoftAAC2::onQueueFilled(OMX_U32 portIndex) {
+
+void SoftAAC2::onQueueFilled(OMX_U32 /* portIndex */) {
if (mSignalledError || mOutputPortSettingsChange != NONE) {
return;
}
@@ -505,59 +506,54 @@
List<BufferInfo *> &inQueue = getPortQueue(0);
List<BufferInfo *> &outQueue = getPortQueue(1);
- if (portIndex == 0 && mInputBufferCount == 0) {
- BufferInfo *inInfo = *inQueue.begin();
- OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
-
- inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
- inBufferLength[0] = inHeader->nFilledLen;
-
- AAC_DECODER_ERROR decoderErr =
- aacDecoder_ConfigRaw(mAACDecoder,
- inBuffer,
- inBufferLength);
-
- if (decoderErr != AAC_DEC_OK) {
- ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
-
- mInputBufferCount++;
- mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
-
- inInfo->mOwnedByUs = false;
- inQueue.erase(inQueue.begin());
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
-
- configureDownmix();
- // Only send out port settings changed event if both sample rate
- // and numChannels are valid.
- if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
- ALOGI("Initially configuring decoder: %d Hz, %d channels",
- mStreamInfo->sampleRate,
- mStreamInfo->numChannels);
-
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
- }
-
- return;
- }
-
while ((!inQueue.empty() || mEndOfInput) && !outQueue.empty()) {
if (!inQueue.empty()) {
INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
- if (inHeader->nFlags & OMX_BUFFERFLAG_EOS) {
- mEndOfInput = true;
- } else {
- mEndOfInput = false;
+ mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
+ if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
+ BufferInfo *inInfo = *inQueue.begin();
+ OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+
+ inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+ inBufferLength[0] = inHeader->nFilledLen;
+
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_ConfigRaw(mAACDecoder,
+ inBuffer,
+ inBufferLength);
+
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_ConfigRaw decoderErr = 0x%4.4x", decoderErr);
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+
+ mInputBufferCount++;
+ mOutputBufferCount++; // fake increase of outputBufferCount to keep the counters aligned
+
+ inInfo->mOwnedByUs = false;
+ inQueue.erase(inQueue.begin());
+ mLastInHeader = NULL;
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+
+ configureDownmix();
+ // Only send out port settings changed event if both sample rate
+ // and numChannels are valid.
+ if (mStreamInfo->sampleRate && mStreamInfo->numChannels) {
+ ALOGI("Initially configuring decoder: %d Hz, %d channels",
+ mStreamInfo->sampleRate,
+ mStreamInfo->numChannels);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ }
+ return;
}
if (inHeader->nFilledLen == 0) {
@@ -567,206 +563,193 @@
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
- } else {
- if (mIsADTS) {
- size_t adtsHeaderSize = 0;
- // skip 30 bits, aac_frame_length follows.
- // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
+ continue;
+ }
- const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+ if (mIsADTS) {
+ size_t adtsHeaderSize = 0;
+ // skip 30 bits, aac_frame_length follows.
+ // ssssssss ssssiiip ppffffPc ccohCCll llllllll lll?????
- bool signalError = false;
- if (inHeader->nFilledLen < 7) {
- ALOGE("Audio data too short to contain even the ADTS header. "
- "Got %d bytes.", inHeader->nFilledLen);
+ const uint8_t *adtsHeader = inHeader->pBuffer + inHeader->nOffset;
+
+ bool signalError = false;
+ if (inHeader->nFilledLen < 7) {
+ ALOGE("Audio data too short to contain even the ADTS header. "
+ "Got %d bytes.", inHeader->nFilledLen);
+ hexdump(adtsHeader, inHeader->nFilledLen);
+ signalError = true;
+ } else {
+ bool protectionAbsent = (adtsHeader[1] & 1);
+
+ unsigned aac_frame_length =
+ ((adtsHeader[3] & 3) << 11)
+ | (adtsHeader[4] << 3)
+ | (adtsHeader[5] >> 5);
+
+ if (inHeader->nFilledLen < aac_frame_length) {
+ ALOGE("Not enough audio data for the complete frame. "
+ "Got %d bytes, frame size according to the ADTS "
+ "header is %u bytes.",
+ inHeader->nFilledLen, aac_frame_length);
hexdump(adtsHeader, inHeader->nFilledLen);
signalError = true;
} else {
- bool protectionAbsent = (adtsHeader[1] & 1);
+ adtsHeaderSize = (protectionAbsent ? 7 : 9);
- unsigned aac_frame_length =
- ((adtsHeader[3] & 3) << 11)
- | (adtsHeader[4] << 3)
- | (adtsHeader[5] >> 5);
+ inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
+ inBufferLength[0] = aac_frame_length - adtsHeaderSize;
- if (inHeader->nFilledLen < aac_frame_length) {
- ALOGE("Not enough audio data for the complete frame. "
- "Got %d bytes, frame size according to the ADTS "
- "header is %u bytes.",
- inHeader->nFilledLen, aac_frame_length);
- hexdump(adtsHeader, inHeader->nFilledLen);
- signalError = true;
- } else {
- adtsHeaderSize = (protectionAbsent ? 7 : 9);
-
- inBuffer[0] = (UCHAR *)adtsHeader + adtsHeaderSize;
- inBufferLength[0] = aac_frame_length - adtsHeaderSize;
-
- inHeader->nOffset += adtsHeaderSize;
- inHeader->nFilledLen -= adtsHeaderSize;
- }
- }
-
- if (signalError) {
- mSignalledError = true;
-
- notify(OMX_EventError,
- OMX_ErrorStreamCorrupt,
- ERROR_MALFORMED,
- NULL);
-
- return;
- }
- } else {
- inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
- inBufferLength[0] = inHeader->nFilledLen;
- }
-
- // Fill and decode
- bytesValid[0] = inBufferLength[0];
-
- INT prevSampleRate = mStreamInfo->sampleRate;
- INT prevNumChannels = mStreamInfo->numChannels;
-
- if (inHeader != mLastInHeader) {
- mLastInHeader = inHeader;
- mCurrentInputTime = inHeader->nTimeStamp;
- } else {
- if (mStreamInfo->sampleRate) {
- mCurrentInputTime += mStreamInfo->aacSamplesPerFrame *
- 1000000ll / mStreamInfo->sampleRate;
- } else {
- ALOGW("no sample rate yet");
+ inHeader->nOffset += adtsHeaderSize;
+ inHeader->nFilledLen -= adtsHeaderSize;
}
}
- mAnchorTimes.add(mCurrentInputTime);
- aacDecoder_Fill(mAACDecoder,
- inBuffer,
- inBufferLength,
- bytesValid);
- // run DRC check
- mDrcWrap.submitStreamData(mStreamInfo);
- mDrcWrap.update();
-
- AAC_DECODER_ERROR decoderErr =
- aacDecoder_DecodeFrame(mAACDecoder,
- tmpOutBuffer,
- 2048 * MAX_CHANNEL_COUNT,
- 0 /* flags */);
-
- if (decoderErr != AAC_DEC_OK) {
- ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
- }
-
- if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
- ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+ if (signalError) {
mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ notify(OMX_EventError, OMX_ErrorStreamCorrupt, ERROR_MALFORMED, NULL);
return;
}
+ } else {
+ inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
+ inBufferLength[0] = inHeader->nFilledLen;
+ }
- if (bytesValid[0] != 0) {
- ALOGE("bytesValid[0] != 0 should never happen");
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- return;
- }
+ // Fill and decode
+ bytesValid[0] = inBufferLength[0];
- size_t numOutBytes =
- mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+ INT prevSampleRate = mStreamInfo->sampleRate;
+ INT prevNumChannels = mStreamInfo->numChannels;
- if (decoderErr == AAC_DEC_OK) {
- if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
- mStreamInfo->frameSize * mStreamInfo->numChannels)) {
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
- UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
- inHeader->nFilledLen -= inBufferUsedLength;
- inHeader->nOffset += inBufferUsedLength;
+ if (inHeader != mLastInHeader) {
+ mLastInHeader = inHeader;
+ mCurrentInputTime = inHeader->nTimeStamp;
+ } else {
+ if (mStreamInfo->sampleRate) {
+ mCurrentInputTime += mStreamInfo->aacSamplesPerFrame *
+ 1000000ll / mStreamInfo->sampleRate;
} else {
- ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
-
- memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
-
- if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
- mStreamInfo->frameSize * mStreamInfo->numChannels)) {
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
-
- // Discard input buffer.
- inHeader->nFilledLen = 0;
-
- aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-
- // fall through
+ ALOGW("no sample rate yet");
}
+ }
+ mAnchorTimes.add(mCurrentInputTime);
+ aacDecoder_Fill(mAACDecoder,
+ inBuffer,
+ inBufferLength,
+ bytesValid);
- /*
- * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
- * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
- * rate system and the sampling rate in the final output is actually
- * doubled compared with the core AAC decoder sampling rate.
- *
- * Explicit signalling is done by explicitly defining SBR audio object
- * type in the bitstream. Implicit signalling is done by embedding
- * SBR content in AAC extension payload specific to SBR, and hence
- * requires an AAC decoder to perform pre-checks on actual audio frames.
- *
- * Thus, we could not say for sure whether a stream is
- * AAC+/eAAC+ until the first data frame is decoded.
- */
- if (mOutputBufferCount > 1) {
- if (mStreamInfo->sampleRate != prevSampleRate ||
- mStreamInfo->numChannels != prevNumChannels) {
- ALOGE("can not reconfigure AAC output");
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
- }
- if (mInputBufferCount <= 2) { // TODO: <= 1
- if (mStreamInfo->sampleRate != prevSampleRate ||
- mStreamInfo->numChannels != prevNumChannels) {
- ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
- prevSampleRate, mStreamInfo->sampleRate,
- prevNumChannels, mStreamInfo->numChannels);
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ 0 /* flags */);
- if (inHeader->nFilledLen == 0) {
- inInfo->mOwnedByUs = false;
- mInputBufferCount++;
- inQueue.erase(inQueue.begin());
- mLastInHeader = NULL;
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
- }
- return;
- }
- } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
- ALOGW("Invalid AAC stream");
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+ ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ if (bytesValid[0] != 0) {
+ ALOGE("bytesValid[0] != 0 should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
+
+ size_t numOutBytes =
+ mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
+
+ if (decoderErr == AAC_DEC_OK) {
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
- if (inHeader->nFilledLen == 0) {
- inInfo->mOwnedByUs = false;
- mInputBufferCount++;
- inQueue.erase(inQueue.begin());
- mLastInHeader = NULL;
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
- } else {
- ALOGV("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+ UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+ inHeader->nFilledLen -= inBufferUsedLength;
+ inHeader->nOffset += inBufferUsedLength;
+ } else {
+ ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
+
+ memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
+
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
}
+
+ // Discard input buffer.
+ inHeader->nFilledLen = 0;
+
+ aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+
+ // fall through
+ }
+
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (mInputBufferCount <= 2 || mOutputBufferCount > 1) { // TODO: <= 1
+ if (mStreamInfo->sampleRate != prevSampleRate ||
+ mStreamInfo->numChannels != prevNumChannels) {
+ ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+ prevSampleRate, mStreamInfo->sampleRate,
+ prevNumChannels, mStreamInfo->numChannels);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ mLastInHeader = NULL;
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ return;
+ }
+ } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+ ALOGW("Invalid AAC stream");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ mLastInHeader = NULL;
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ } else {
+ ALOGV("inHeader->nFilledLen = %d", inHeader->nFilledLen);
}
}
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 8667a6b..7b18348 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -1019,11 +1019,19 @@
}
size_t LiveSession::getTrackCount() const {
- return mPlaylist->getTrackCount();
+ if (mPlaylist == NULL) {
+ return 0;
+ } else {
+ return mPlaylist->getTrackCount();
+ }
}
sp<AMessage> LiveSession::getTrackInfo(size_t trackIndex) const {
- return mPlaylist->getTrackInfo(trackIndex);
+ if (mPlaylist == NULL) {
+ return NULL;
+ } else {
+ return mPlaylist->getTrackInfo(trackIndex);
+ }
}
status_t LiveSession::selectTrack(size_t index, bool select) {
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 98b50dd..7eb6542 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -249,11 +249,15 @@
mPackets.push_back(buffer);
} else {
// hexdump(buffer->data(), buffer->size());
+ if (buffer->size() < 2) {
+ return MALFORMED_PACKET;
+ }
- CHECK_GE(buffer->size(), 2u);
unsigned AU_headers_length = U16_AT(buffer->data()); // in bits
- CHECK_GE(buffer->size(), 2 + (AU_headers_length + 7) / 8);
+ if (buffer->size() < 2 + (AU_headers_length + 7) / 8) {
+ return MALFORMED_PACKET;
+ }
List<AUHeader> headers;
@@ -342,7 +346,9 @@
it != headers.end(); ++it) {
const AUHeader &header = *it;
- CHECK_LE(offset + header.mSize, buffer->size());
+ if (buffer->size() < offset + header.mSize) {
+ return MALFORMED_PACKET;
+ }
sp<ABuffer> accessUnit = new ABuffer(header.mSize);
memcpy(accessUnit->data(), buffer->data() + offset, header.mSize);
@@ -353,7 +359,10 @@
mPackets.push_back(accessUnit);
}
- CHECK_EQ(offset, buffer->size());
+ if (offset != buffer->size()) {
+ ALOGW("potentially malformed packet (offset %d, size %d)",
+ offset, buffer->size());
+ }
}
queue->erase(queue->begin());
@@ -400,6 +409,7 @@
const sp<ARTPSource> &source) {
AssemblyStatus status = addPacket(source);
if (status == MALFORMED_PACKET) {
+ ALOGI("access unit is damaged");
mAccessUnitDamaged = true;
}
return status;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 7766b90..fd5a426 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -487,12 +487,12 @@
}
if (client == NULL) {
needsNewClient = true;
- ret = connectHelperLocked(/*cameraClient*/NULL, // Empty binder callbacks
+ ret = connectHelperLocked(/*out*/client,
+ /*cameraClient*/NULL, // Empty binder callbacks
cameraId,
internalPackageName,
uid,
- pid,
- client);
+ pid);
if (ret != OK) {
// Error already logged by callee
@@ -659,14 +659,17 @@
return true;
}
-status_t CameraService::connectHelperLocked(const sp<ICameraClient>& cameraClient,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- int callingPid,
- /*out*/
- sp<Client>& client,
- int halVersion) {
+status_t CameraService::connectHelperLocked(
+ /*out*/
+ sp<Client>& client,
+ /*in*/
+ const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ int callingPid,
+ int halVersion,
+ bool legacyMode) {
int facing = -1;
int deviceVersion = getDeviceVersion(cameraId, &facing);
@@ -678,7 +681,7 @@
case CAMERA_DEVICE_API_VERSION_1_0:
client = new CameraClient(this, cameraClient,
clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid());
+ facing, callingPid, clientUid, getpid(), legacyMode);
break;
case CAMERA_DEVICE_API_VERSION_2_0:
case CAMERA_DEVICE_API_VERSION_2_1:
@@ -687,7 +690,7 @@
case CAMERA_DEVICE_API_VERSION_3_2:
client = new Camera2Client(this, cameraClient,
clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid());
+ facing, callingPid, clientUid, getpid(), legacyMode);
break;
case -1:
ALOGE("Invalid camera id %d", cameraId);
@@ -704,7 +707,7 @@
// Only support higher HAL version device opened as HAL1.0 device.
client = new CameraClient(this, cameraClient,
clientPackageName, cameraId,
- facing, callingPid, clientUid, getpid());
+ facing, callingPid, clientUid, getpid(), legacyMode);
} else {
// Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
@@ -760,12 +763,12 @@
return OK;
}
- status = connectHelperLocked(cameraClient,
+ status = connectHelperLocked(/*out*/client,
+ cameraClient,
cameraId,
clientPackageName,
clientUid,
- callingPid,
- client);
+ callingPid);
if (status != OK) {
return status;
}
@@ -823,13 +826,14 @@
return OK;
}
- status = connectHelperLocked(cameraClient,
+ status = connectHelperLocked(/*out*/client,
+ cameraClient,
cameraId,
clientPackageName,
clientUid,
callingPid,
- client,
- halVersion);
+ halVersion,
+ /*legacyMode*/true);
if (status != OK) {
return status;
}
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index cb98c96..a7328cf 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -452,14 +452,17 @@
*
* Returns OK on success, or a negative error code.
*/
- status_t connectHelperLocked(const sp<ICameraClient>& cameraClient,
- int cameraId,
- const String16& clientPackageName,
- int clientUid,
- int callingPid,
- /*out*/
- sp<Client>& client,
- int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED);
+ status_t connectHelperLocked(
+ /*out*/
+ sp<Client>& client,
+ /*in*/
+ const sp<ICameraClient>& cameraClient,
+ int cameraId,
+ const String16& clientPackageName,
+ int clientUid,
+ int callingPid,
+ int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED,
+ bool legacyMode = false);
};
} // namespace android
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 5eb5181..bc40971 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -53,7 +53,8 @@
int cameraFacing,
int clientPid,
uid_t clientUid,
- int servicePid):
+ int servicePid,
+ bool legacyMode):
Camera2ClientBase(cameraService, cameraClient, clientPackageName,
cameraId, cameraFacing, clientPid, clientUid, servicePid),
mParameters(cameraId, cameraFacing)
@@ -62,6 +63,8 @@
SharedParameters::Lock l(mParameters);
l.mParameters.state = Parameters::DISCONNECTED;
+
+ mLegacyMode = legacyMode;
}
status_t Camera2Client::initialize(camera_module_t *module)
@@ -1449,6 +1452,13 @@
return OK;
}
+ // the camera2 api legacy mode can unconditionally disable the shutter sound
+ if (mLegacyMode) {
+ ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__);
+ l.mParameters.playShutterSound = false;
+ return OK;
+ }
+
// Disabling shutter sound may not be allowed. In that case only
// allow the mediaserver process to disable the sound.
char value[PROPERTY_VALUE_MAX];
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 5ce757a..f5c3a30 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -89,7 +89,8 @@
int cameraFacing,
int clientPid,
uid_t clientUid,
- int servicePid);
+ int servicePid,
+ bool legacyMode);
virtual ~Camera2Client();
@@ -203,6 +204,7 @@
bool mAfInMotion;
/** Utility members */
+ bool mLegacyMode;
// Wait until the camera device has received the latest control settings
status_t syncWithDevice();
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index fb6b678..abe1235 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -38,7 +38,7 @@
const String16& clientPackageName,
int cameraId, int cameraFacing,
int clientPid, int clientUid,
- int servicePid):
+ int servicePid, bool legacyMode):
Client(cameraService, cameraClient, clientPackageName,
cameraId, cameraFacing, clientPid, clientUid, servicePid)
{
@@ -54,6 +54,7 @@
// Callback is disabled by default
mPreviewCallbackFlag = CAMERA_FRAME_CALLBACK_FLAG_NOOP;
mOrientation = getOrientation(0, mCameraFacing == CAMERA_FACING_FRONT);
+ mLegacyMode = legacyMode;
mPlayShutterSound = true;
LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
}
@@ -576,6 +577,13 @@
return OK;
}
+ // the camera2 api legacy mode can unconditionally disable the shutter sound
+ if (mLegacyMode) {
+ ALOGV("%s: Disable shutter sound in legacy mode", __FUNCTION__);
+ mPlayShutterSound = false;
+ return OK;
+ }
+
// Disabling shutter sound may not be allowed. In that case only
// allow the mediaserver process to disable the sound.
char value[PROPERTY_VALUE_MAX];
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 4b89564..6779f5e 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -64,7 +64,8 @@
int cameraFacing,
int clientPid,
int clientUid,
- int servicePid);
+ int servicePid,
+ bool legacyMode = false);
~CameraClient();
status_t initialize(camera_module_t *module);
@@ -129,6 +130,7 @@
int mPreviewCallbackFlag;
int mOrientation; // Current display orientation
bool mPlayShutterSound;
+ bool mLegacyMode; // camera2 api legacy mode?
// Ensures atomicity among the public methods
mutable Mutex mLock;
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 37de610..b388079 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -51,7 +51,8 @@
mZslStreamId(NO_STREAM),
mFrameListHead(0),
mZslQueueHead(0),
- mZslQueueTail(0) {
+ mZslQueueTail(0),
+ mHasFocuser(false) {
// Initialize buffer queue and frame list based on pipeline max depth.
size_t pipelineMaxDepth = kDefaultMaxPipelineDepth;
if (client != 0) {
@@ -67,6 +68,11 @@
" use default pipeline max depth %zu", __FUNCTION__,
kDefaultMaxPipelineDepth);
}
+
+ entry = device->info().find(ANDROID_LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+ if (entry.count > 0 && entry.data.f[0] != 0.) {
+ mHasFocuser = true;
+ }
}
}
@@ -489,20 +495,23 @@
continue;
}
- // Make sure the candidate frame has good focus.
- entry = frame.find(ANDROID_CONTROL_AF_STATE);
- if (entry.count == 0) {
- ALOGW("%s: ZSL queue frame has no AF state field!",
- __FUNCTION__);
- continue;
- }
- uint8_t afState = entry.data.u8[0];
- if (afState != ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED &&
- afState != ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED &&
- afState != ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
- ALOGW("%s: ZSL queue frame AF state is %d is not good for capture, skip it",
- __FUNCTION__, afState);
- continue;
+ // Check AF state if device has focuser
+ if (mHasFocuser) {
+ // Make sure the candidate frame has good focus.
+ entry = frame.find(ANDROID_CONTROL_AF_STATE);
+ if (entry.count == 0) {
+ ALOGW("%s: ZSL queue frame has no AF state field!",
+ __FUNCTION__);
+ continue;
+ }
+ uint8_t afState = entry.data.u8[0];
+ if (afState != ANDROID_CONTROL_AF_STATE_PASSIVE_FOCUSED &&
+ afState != ANDROID_CONTROL_AF_STATE_FOCUSED_LOCKED &&
+ afState != ANDROID_CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
+ ALOGW("%s: ZSL queue frame AF state is %d is not good for capture, skip it",
+ __FUNCTION__, afState);
+ continue;
+ }
}
minTimestamp = frameTimestamp;
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index dfb1457..daa352b 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -121,6 +121,8 @@
CameraMetadata mLatestCapturedRequest;
+ bool mHasFocuser;
+
virtual bool threadLoop();
status_t clearZslQueueLocked();
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 86f82a3..80c797a 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -254,9 +254,17 @@
}
status_t CameraDeviceClient::endConfigure() {
- // TODO: Implement this.
- ALOGE("%s: Not implemented yet.", __FUNCTION__);
- return OK;
+ ALOGV("%s: ending configure (%zu streams)",
+ __FUNCTION__, mStreamMap.size());
+
+ status_t res;
+ if ( (res = checkPid(__FUNCTION__) ) != OK) return res;
+
+ Mutex::Autolock icl(mBinderSerializationLock);
+
+ if (!mDevice.get()) return DEAD_OBJECT;
+
+ return mDevice->configureStreams();
}
status_t CameraDeviceClient::deleteStream(int streamId) {
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 9e124b0..d26e20c 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -141,6 +141,18 @@
virtual status_t deleteReprocessStream(int id) = 0;
/**
+ * Take the currently-defined set of streams and configure the HAL to use
+ * them. This is a long-running operation (may be several hundered ms).
+ *
+ * The device must be idle (see waitUntilDrained) before calling this.
+ *
+ * Returns OK on success; otherwise on error:
+ * - BAD_VALUE if the set of streams was invalid (e.g. fmts or sizes)
+ * - INVALID_OPERATION if the device was in the wrong state
+ */
+ virtual status_t configureStreams() = 0;
+
+ /**
* Create a metadata buffer with fields that the HAL device believes are
* best for the given use case
*/
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index d473a76..8caadd6 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -415,6 +415,19 @@
return OK;
}
+status_t Camera2Device::configureStreams() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+
+ /**
+ * HAL2 devices do not need to configure streams;
+ * streams are created on the fly.
+ */
+ ALOGW("%s: No-op for HAL2 devices", __FUNCTION__);
+
+ return OK;
+}
+
status_t Camera2Device::createDefaultRequest(int templateId,
CameraMetadata *request) {
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index d0ca46e..2a3f1d9 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -64,6 +64,8 @@
virtual status_t setStreamTransform(int id, int transform);
virtual status_t deleteStream(int id);
virtual status_t deleteReprocessStream(int id);
+ // No-op on HAL2 devices
+ virtual status_t configureStreams();
virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
virtual status_t waitUntilDrained();
virtual status_t setNotifyCallback(NotificationListener *listener);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index ed350c1..0d33406 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1000,6 +1000,15 @@
return INVALID_OPERATION;
}
+status_t Camera3Device::configureStreams() {
+ ATRACE_CALL();
+ ALOGV("%s: E", __FUNCTION__);
+
+ Mutex::Autolock il(mInterfaceLock);
+ Mutex::Autolock l(mLock);
+
+ return configureStreamsLocked();
+}
status_t Camera3Device::createDefaultRequest(int templateId,
CameraMetadata *request) {
@@ -2129,6 +2138,8 @@
mPaused(true),
mFrameNumber(0),
mLatestRequestId(NAME_NOT_FOUND),
+ mCurrentAfTriggerId(0),
+ mCurrentPreCaptureTriggerId(0),
mRepeatingLastFrameNumber(NO_IN_FLIGHT_REPEATING_FRAMES) {
mStatusId = statusTracker->addComponent();
}
@@ -2611,6 +2622,8 @@
if (nextRequest != NULL) {
nextRequest->mResultExtras.frameNumber = mFrameNumber++;
+ nextRequest->mResultExtras.afTriggerId = mCurrentAfTriggerId;
+ nextRequest->mResultExtras.precaptureTriggerId = mCurrentPreCaptureTriggerId;
}
return nextRequest;
}
@@ -2690,8 +2703,13 @@
if (tag == ANDROID_CONTROL_AF_TRIGGER_ID || tag == ANDROID_CONTROL_AE_PRECAPTURE_ID) {
bool isAeTrigger = (trigger.metadataTag == ANDROID_CONTROL_AE_PRECAPTURE_ID);
uint32_t triggerId = static_cast<uint32_t>(trigger.entryValue);
- isAeTrigger ? request->mResultExtras.precaptureTriggerId = triggerId :
- request->mResultExtras.afTriggerId = triggerId;
+ if (isAeTrigger) {
+ request->mResultExtras.precaptureTriggerId = triggerId;
+ mCurrentPreCaptureTriggerId = triggerId;
+ } else {
+ request->mResultExtras.afTriggerId = triggerId;
+ mCurrentAfTriggerId = triggerId;
+ }
if (parent->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
continue; // Trigger ID tag is deprecated since device HAL 3.2
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 7656237..915c024 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -114,6 +114,8 @@
virtual status_t deleteStream(int id);
virtual status_t deleteReprocessStream(int id);
+ virtual status_t configureStreams();
+
virtual status_t createDefaultRequest(int templateId, CameraMetadata *request);
// Transitions to the idle state on success
@@ -484,6 +486,8 @@
TriggerMap mTriggerMap;
TriggerMap mTriggerRemovedMap;
TriggerMap mTriggerReplacedMap;
+ uint32_t mCurrentAfTriggerId;
+ uint32_t mCurrentPreCaptureTriggerId;
int64_t mRepeatingLastFrameNumber;
};
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index 50a2c10..cc66459 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -184,15 +184,6 @@
return INVALID_OPERATION;
}
- // Only limit dequeue amount when fully configured
- if (mState == STATE_CONFIGURED &&
- mHandoutTotalBufferCount == camera3_stream::max_buffers) {
- ALOGE("%s: Stream %d: Already dequeued maximum number of simultaneous"
- " buffers (%d)", __FUNCTION__, mId,
- camera3_stream::max_buffers);
- return INVALID_OPERATION;
- }
-
return OK;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index d7b1871..3f6254f 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -381,18 +381,7 @@
if (hal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_2) {
ALOGV("%s: register_stream_buffers unused as of HAL3.2", __FUNCTION__);
- /**
- * Skip the NULL check if camera.dev.register_stream is 1.
- *
- * For development-validation purposes only.
- *
- * TODO: Remove the property check before shipping L (b/13914251).
- */
- char value[PROPERTY_VALUE_MAX] = { '\0', };
- property_get("camera.dev.register_stream", value, "0");
- int propInt = atoi(value);
-
- if (propInt == 0 && hal3Device->ops->register_stream_buffers != NULL) {
+ if (hal3Device->ops->register_stream_buffers != NULL) {
ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; "
"must be set to NULL in camera3_device::ops", __FUNCTION__);
return INVALID_OPERATION;