Merge "MediaCodec: Fix deallocated pointer reference when calling reset()" into lmp-dev
diff --git a/include/media/AudioEffect.h b/include/media/AudioEffect.h
index f98002d..4932d40 100644
--- a/include/media/AudioEffect.h
+++ b/include/media/AudioEffect.h
@@ -449,6 +449,7 @@
sp<EffectClient> mIEffectClient; // IEffectClient implementation
sp<IMemory> mCblkMemory; // shared memory for deferred parameter setting
effect_param_cblk_t* mCblk; // control block for deferred parameter setting
+ pid_t mClientPid;
};
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index 3d79596..c8a50e8 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -18,6 +18,8 @@
#define MEDIA_BUFFER_H_
+#include <media/stagefright/foundation/MediaBufferBase.h>
+
#include <pthread.h>
#include <utils/Errors.h>
@@ -43,7 +45,7 @@
MediaBufferObserver &operator=(const MediaBufferObserver &);
};
-class MediaBuffer {
+class MediaBuffer : public MediaBufferBase {
public:
// The underlying data remains the responsibility of the caller!
MediaBuffer(void *data, size_t size);
@@ -56,10 +58,10 @@
// Decrements the reference count and returns the buffer to its
// associated MediaBufferGroup if the reference count drops to 0.
- void release();
+ virtual void release();
// Increments the reference count.
- void add_ref();
+ virtual void add_ref();
void *data() const;
size_t size() const;
diff --git a/include/media/stagefright/foundation/ABuffer.h b/include/media/stagefright/foundation/ABuffer.h
index 602f7ab..6294ee7 100644
--- a/include/media/stagefright/foundation/ABuffer.h
+++ b/include/media/stagefright/foundation/ABuffer.h
@@ -27,6 +27,7 @@
namespace android {
struct AMessage;
+class MediaBufferBase;
struct ABuffer : public RefBase {
ABuffer(size_t capacity);
@@ -50,6 +51,9 @@
sp<AMessage> meta();
+ MediaBufferBase *getMediaBufferBase();
+ void setMediaBufferBase(MediaBufferBase *mediaBuffer);
+
protected:
virtual ~ABuffer();
@@ -57,6 +61,8 @@
sp<AMessage> mFarewell;
sp<AMessage> mMeta;
+ MediaBufferBase *mMediaBufferBase;
+
void *mData;
size_t mCapacity;
size_t mRangeOffset;
diff --git a/include/media/stagefright/foundation/MediaBufferBase.h b/include/media/stagefright/foundation/MediaBufferBase.h
new file mode 100644
index 0000000..99418fb
--- /dev/null
+++ b/include/media/stagefright/foundation/MediaBufferBase.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MEDIA_BUFFER_BASE_H_
+
+#define MEDIA_BUFFER_BASE_H_
+
+namespace android {
+
+class MediaBufferBase {
+public:
+ MediaBufferBase() {}
+
+ virtual void release() = 0;
+ virtual void add_ref() = 0;
+
+protected:
+ virtual ~MediaBufferBase() {}
+
+private:
+ MediaBufferBase(const MediaBufferBase &);
+ MediaBufferBase &operator=(const MediaBufferBase &);
+};
+
+} // namespace android
+
+#endif // MEDIA_BUFFER_BASE_H_
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index 35f6557..0d5d7e4 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -145,15 +145,19 @@
return mStatus;
}
- mIEffect = iEffect;
mCblkMemory = cblk;
mCblk = static_cast<effect_param_cblk_t*>(cblk->pointer());
int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int);
mCblk->buffer = (uint8_t *)mCblk + bufOffset;
iEffect->asBinder()->linkToDeath(mIEffectClient);
- ALOGV("set() %p OK effect: %s id: %d status %d enabled %d", this, mDescriptor.name, mId,
- mStatus, mEnabled);
+ mClientPid = IPCThreadState::self()->getCallingPid();
+ ALOGV("set() %p OK effect: %s id: %d status %d enabled %d pid %d", this, mDescriptor.name, mId,
+ mStatus, mEnabled, mClientPid);
+
+ if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
+ AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
+ }
return mStatus;
}
@@ -164,6 +168,9 @@
ALOGV("Destructor %p", this);
if (mStatus == NO_ERROR || mStatus == ALREADY_EXISTS) {
+ if (mSessionId > AUDIO_SESSION_OUTPUT_MIX) {
+ AudioSystem::releaseAudioSessionId(mSessionId, mClientPid);
+ }
if (mIEffect != NULL) {
mIEffect->disconnect();
mIEffect->asBinder()->unlinkToDeath(mIEffectClient);
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 762dca5..ea7b279 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1935,7 +1935,7 @@
// To avoid a race, read the presented frames first. This ensures that presented <= consumed.
status_t status = mAudioTrack->getTimestamp(timestamp);
if (status != NO_ERROR) {
- ALOGW_IF(status != WOULD_BLOCK, "getTimestamp error:%#x", status);
+ ALOGV_IF(status != WOULD_BLOCK, "getTimestamp error:%#x", status);
return status;
}
if (isOffloadedOrDirect_l()) {
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index adc066d..2cf5710 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -22,6 +22,7 @@
StagefrightPlayer.cpp \
StagefrightRecorder.cpp \
TestPlayerStub.cpp \
+ VideoFrameScheduler.cpp \
LOCAL_SHARED_LIBRARIES := \
libbinder \
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.cpp b/media/libmediaplayerservice/VideoFrameScheduler.cpp
new file mode 100644
index 0000000..4251c4e
--- /dev/null
+++ b/media/libmediaplayerservice/VideoFrameScheduler.cpp
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "VideoFrameScheduler"
+#include <utils/Log.h>
+#define ATRACE_TAG ATRACE_TAG_VIDEO
+#include <utils/Trace.h>
+
+#include <sys/time.h>
+
+#include <binder/IServiceManager.h>
+#include <gui/ISurfaceComposer.h>
+#include <ui/DisplayStatInfo.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "VideoFrameScheduler.h"
+
+namespace android {
+
+static const nsecs_t kNanosIn1s = 1000000000;
+
+template<class T>
+inline static const T divRound(const T &nom, const T &den) {
+ if ((nom >= 0) ^ (den >= 0)) {
+ return (nom - den / 2) / den;
+ } else {
+ return (nom + den / 2) / den;
+ }
+}
+
+template<class T>
+inline static T abs(const T &a) {
+ return a < 0 ? -a : a;
+}
+
+template<class T>
+inline static const T &min(const T &a, const T &b) {
+ return a < b ? a : b;
+}
+
+template<class T>
+inline static const T &max(const T &a, const T &b) {
+ return a > b ? a : b;
+}
+
+template<class T>
+inline static T periodicError(const T &val, const T &period) {
+ T err = abs(val) % period;
+ return (err < (period / 2)) ? err : (period - err);
+}
+
+template<class T>
+static int compare(const T *lhs, const T *rhs) {
+ if (*lhs < *rhs) {
+ return -1;
+ } else if (*lhs > *rhs) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* ======================================================================= */
+/* PLL */
+/* ======================================================================= */
+
+static const size_t kMinSamplesToStartPrime = 3;
+static const size_t kMinSamplesToStopPrime = VideoFrameScheduler::kHistorySize;
+static const size_t kMinSamplesToEstimatePeriod = 3;
+static const size_t kMaxSamplesToEstimatePeriod = VideoFrameScheduler::kHistorySize;
+
+static const size_t kPrecision = 12;
+static const size_t kErrorThreshold = (1 << (kPrecision * 2)) / 10;
+static const int64_t kMultiplesThresholdDiv = 4; // 25%
+static const int64_t kReFitThresholdDiv = 100; // 1%
+static const nsecs_t kMaxAllowedFrameSkip = kNanosIn1s; // 1 sec
+static const nsecs_t kMinPeriod = kNanosIn1s / 120; // 120Hz
+static const nsecs_t kRefitRefreshPeriod = 10 * kNanosIn1s; // 10 sec
+
+VideoFrameScheduler::PLL::PLL()
+ : mPeriod(-1),
+ mPhase(0),
+ mPrimed(false),
+ mSamplesUsedForPriming(0),
+ mLastTime(-1),
+ mNumSamples(0) {
+}
+
+void VideoFrameScheduler::PLL::reset(float fps) {
+ //test();
+
+ mSamplesUsedForPriming = 0;
+ mLastTime = -1;
+
+ // set up or reset video PLL
+ if (fps <= 0.f) {
+ mPeriod = -1;
+ mPrimed = false;
+ } else {
+ ALOGV("reset at %.1f fps", fps);
+ mPeriod = (nsecs_t)(1e9 / fps + 0.5);
+ mPrimed = true;
+ }
+
+ restart();
+}
+
+// reset PLL but keep previous period estimate
+void VideoFrameScheduler::PLL::restart() {
+ mNumSamples = 0;
+ mPhase = -1;
+}
+
+#if 0
+
+void VideoFrameScheduler::PLL::test() {
+ nsecs_t period = kNanosIn1s / 60;
+ mTimes[0] = 0;
+ mTimes[1] = period;
+ mTimes[2] = period * 3;
+ mTimes[3] = period * 4;
+ mTimes[4] = period * 7;
+ mTimes[5] = period * 8;
+ mTimes[6] = period * 10;
+ mTimes[7] = period * 12;
+ mNumSamples = 8;
+ int64_t a, b, err;
+ fit(0, period * 12 / 7, 8, &a, &b, &err);
+ // a = 0.8(5)+
+ // b = -0.14097(2)+
+ // err = 0.2750578(703)+
+ ALOGD("a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)",
+ (long long)a, (a / (float)(1 << kPrecision)),
+ (long long)b, (b / (float)(1 << kPrecision)),
+ (long long)err, (err / (float)(1 << (kPrecision * 2))));
+}
+
+#endif
+
+void VideoFrameScheduler::PLL::fit(
+ nsecs_t phase, nsecs_t period, size_t numSamplesToUse,
+ int64_t *a, int64_t *b, int64_t *err) {
+ if (numSamplesToUse > mNumSamples) {
+ numSamplesToUse = mNumSamples;
+ }
+
+ int64_t sumX = 0;
+ int64_t sumXX = 0;
+ int64_t sumXY = 0;
+ int64_t sumYY = 0;
+ int64_t sumY = 0;
+
+ int64_t x = 0; // x usually is in [0..numSamplesToUse)
+ nsecs_t lastTime;
+ for (size_t i = 0; i < numSamplesToUse; i++) {
+ size_t ix = (mNumSamples - numSamplesToUse + i) % kHistorySize;
+ nsecs_t time = mTimes[ix];
+ if (i > 0) {
+ x += divRound(time - lastTime, period);
+ }
+ // y is usually in [-numSamplesToUse..numSamplesToUse+kRefitRefreshPeriod/kMinPeriod) << kPrecision
+ // ideally in [0..numSamplesToUse), but shifted by -numSamplesToUse during
+ // priming, and possibly shifted by up to kRefitRefreshPeriod/kMinPeriod
+ // while we are not refitting.
+ int64_t y = divRound(time - phase, period >> kPrecision);
+ sumX += x;
+ sumY += y;
+ sumXX += x * x;
+ sumXY += x * y;
+ sumYY += y * y;
+ lastTime = time;
+ }
+
+ int64_t div = numSamplesToUse * sumXX - sumX * sumX;
+ int64_t a_nom = numSamplesToUse * sumXY - sumX * sumY;
+ int64_t b_nom = sumXX * sumY - sumX * sumXY;
+ *a = divRound(a_nom, div);
+ *b = divRound(b_nom, div);
+ // don't use a and b directly as the rounding error is significant
+ *err = sumYY - divRound(a_nom * sumXY + b_nom * sumY, div);
+ ALOGV("fitting[%zu] a=%lld (%.6f), b=%lld (%.6f), err=%lld (%.6f)",
+ numSamplesToUse,
+ (long long)*a, (*a / (float)(1 << kPrecision)),
+ (long long)*b, (*b / (float)(1 << kPrecision)),
+ (long long)*err, (*err / (float)(1 << (kPrecision * 2))));
+}
+
+void VideoFrameScheduler::PLL::prime(size_t numSamplesToUse) {
+ if (numSamplesToUse > mNumSamples) {
+ numSamplesToUse = mNumSamples;
+ }
+ CHECK(numSamplesToUse >= 3); // must have at least 3 samples
+
+ // estimate video framerate from deltas between timestamps, and
+ // 2nd order deltas
+ Vector<nsecs_t> deltas;
+ nsecs_t lastTime, firstTime;
+ for (size_t i = 0; i < numSamplesToUse; ++i) {
+ size_t index = (mNumSamples - numSamplesToUse + i) % kHistorySize;
+ nsecs_t time = mTimes[index];
+ if (i > 0) {
+ if (time - lastTime > kMinPeriod) {
+ //ALOGV("delta: %lld", (long long)(time - lastTime));
+ deltas.push(time - lastTime);
+ }
+ } else {
+ firstTime = time;
+ }
+ lastTime = time;
+ }
+ deltas.sort(compare<nsecs_t>);
+ size_t numDeltas = deltas.size();
+ if (numDeltas > 1) {
+ nsecs_t deltaMinLimit = min(deltas[0] / kMultiplesThresholdDiv, kMinPeriod);
+ nsecs_t deltaMaxLimit = deltas[numDeltas / 2] * kMultiplesThresholdDiv;
+ for (size_t i = numDeltas / 2 + 1; i < numDeltas; ++i) {
+ if (deltas[i] > deltaMaxLimit) {
+ deltas.resize(i);
+ numDeltas = i;
+ break;
+ }
+ }
+ for (size_t i = 1; i < numDeltas; ++i) {
+ nsecs_t delta2nd = deltas[i] - deltas[i - 1];
+ if (delta2nd >= deltaMinLimit) {
+ //ALOGV("delta2: %lld", (long long)(delta2nd));
+ deltas.push(delta2nd);
+ }
+ }
+ }
+
+ // use the one that yields the best match
+ int64_t bestScore;
+ for (size_t i = 0; i < deltas.size(); ++i) {
+ nsecs_t delta = deltas[i];
+ int64_t score = 0;
+#if 1
+ // simplest score: number of deltas that are near multiples
+ size_t matches = 0;
+ for (size_t j = 0; j < deltas.size(); ++j) {
+ nsecs_t err = periodicError(deltas[j], delta);
+ if (err < delta / kMultiplesThresholdDiv) {
+ ++matches;
+ }
+ }
+ score = matches;
+#if 0
+ // could be weighed by the (1 - normalized error)
+ if (numSamplesToUse >= kMinSamplesToEstimatePeriod) {
+ int64_t a, b, err;
+ fit(firstTime, delta, numSamplesToUse, &a, &b, &err);
+ err = (1 << (2 * kPrecision)) - err;
+ score *= max(0, err);
+ }
+#endif
+#else
+ // or use the error as a negative score
+ if (numSamplesToUse >= kMinSamplesToEstimatePeriod) {
+ int64_t a, b, err;
+ fit(firstTime, delta, numSamplesToUse, &a, &b, &err);
+ score = -delta * err;
+ }
+#endif
+ if (i == 0 || score > bestScore) {
+ bestScore = score;
+ mPeriod = delta;
+ mPhase = firstTime;
+ }
+ }
+ ALOGV("priming[%zu] phase:%lld period:%lld", numSamplesToUse, mPhase, mPeriod);
+}
+
+nsecs_t VideoFrameScheduler::PLL::addSample(nsecs_t time) {
+ if (mLastTime >= 0
+ // if time goes backward, or we skipped rendering
+ && (time > mLastTime + kMaxAllowedFrameSkip || time < mLastTime)) {
+ restart();
+ }
+
+ mLastTime = time;
+ mTimes[mNumSamples % kHistorySize] = time;
+ ++mNumSamples;
+
+ bool doFit = time > mRefitAt;
+ if ((mPeriod <= 0 || !mPrimed) && mNumSamples >= kMinSamplesToStartPrime) {
+ prime(kMinSamplesToStopPrime);
+ ++mSamplesUsedForPriming;
+ doFit = true;
+ }
+ if (mPeriod > 0 && mNumSamples >= kMinSamplesToEstimatePeriod) {
+ if (mPhase < 0) {
+ // initialize phase to the current render time
+ mPhase = time;
+ doFit = true;
+ } else if (!doFit) {
+ int64_t err = periodicError(time - mPhase, mPeriod);
+ doFit = err > mPeriod / kReFitThresholdDiv;
+ }
+
+ if (doFit) {
+ int64_t a, b, err;
+ mRefitAt = time + kRefitRefreshPeriod;
+ fit(mPhase, mPeriod, kMaxSamplesToEstimatePeriod, &a, &b, &err);
+ mPhase += (mPeriod * b) >> kPrecision;
+ mPeriod = (mPeriod * a) >> kPrecision;
+ ALOGV("new phase:%lld period:%lld", (long long)mPhase, (long long)mPeriod);
+
+ if (err < kErrorThreshold) {
+ if (!mPrimed && mSamplesUsedForPriming >= kMinSamplesToStopPrime) {
+ mPrimed = true;
+ }
+ } else {
+ mPrimed = false;
+ mSamplesUsedForPriming = 0;
+ }
+ }
+ }
+ return mPeriod;
+}
+
+/* ======================================================================= */
+/* Frame Scheduler */
+/* ======================================================================= */
+
+static const nsecs_t kDefaultVsyncPeriod = kNanosIn1s / 60; // 60Hz
+static const nsecs_t kVsyncRefreshPeriod = kNanosIn1s; // 1 sec
+
+VideoFrameScheduler::VideoFrameScheduler()
+ : mVsyncTime(0),
+ mVsyncPeriod(0),
+ mVsyncRefreshAt(0),
+ mLastVsyncTime(-1),
+ mTimeCorrection(0) {
+}
+
+void VideoFrameScheduler::updateVsync() {
+ mVsyncRefreshAt = systemTime(SYSTEM_TIME_MONOTONIC) + kVsyncRefreshPeriod;
+ mVsyncPeriod = 0;
+ mVsyncTime = 0;
+
+ // TODO: schedule frames for the destination surface
+ // For now, surface flinger only schedules frames on the primary display
+ if (mComposer == NULL) {
+ String16 name("SurfaceFlinger");
+ sp<IServiceManager> sm = defaultServiceManager();
+ mComposer = interface_cast<ISurfaceComposer>(sm->checkService(name));
+ }
+ if (mComposer != NULL) {
+ DisplayStatInfo stats;
+ status_t res = mComposer->getDisplayStats(NULL /* display */, &stats);
+ if (res == OK) {
+ ALOGV("vsync time:%lld period:%lld",
+ (long long)stats.vsyncTime, (long long)stats.vsyncPeriod);
+ mVsyncTime = stats.vsyncTime;
+ mVsyncPeriod = stats.vsyncPeriod;
+ } else {
+ ALOGW("getDisplayStats returned %d", res);
+ }
+ } else {
+ ALOGW("could not get surface mComposer service");
+ }
+}
+
+void VideoFrameScheduler::init(float videoFps) {
+ updateVsync();
+
+ mLastVsyncTime = -1;
+ mTimeCorrection = 0;
+
+ mPll.reset(videoFps);
+}
+
+void VideoFrameScheduler::restart() {
+ mLastVsyncTime = -1;
+ mTimeCorrection = 0;
+
+ mPll.restart();
+}
+
+nsecs_t VideoFrameScheduler::getVsyncPeriod() {
+ if (mVsyncPeriod > 0) {
+ return mVsyncPeriod;
+ }
+ return kDefaultVsyncPeriod;
+}
+
+nsecs_t VideoFrameScheduler::schedule(nsecs_t renderTime) {
+ nsecs_t origRenderTime = renderTime;
+
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ if (now >= mVsyncRefreshAt) {
+ updateVsync();
+ }
+
+ // without VSYNC info, there is nothing to do
+ if (mVsyncPeriod == 0) {
+ ALOGV("no vsync: render=%lld", (long long)renderTime);
+ return renderTime;
+ }
+
+ // ensure vsync time is well before (corrected) render time
+ if (mVsyncTime > renderTime - 4 * mVsyncPeriod) {
+ mVsyncTime -=
+ ((mVsyncTime - renderTime) / mVsyncPeriod + 5) * mVsyncPeriod;
+ }
+
+ // Video presentation takes place at the VSYNC _after_ renderTime. Adjust renderTime
+ // so this effectively becomes a rounding operation (to the _closest_ VSYNC.)
+ renderTime -= mVsyncPeriod / 2;
+
+ const nsecs_t videoPeriod = mPll.addSample(origRenderTime);
+ if (videoPeriod > 0) {
+ // Smooth out rendering
+ size_t N = 12;
+ nsecs_t fiveSixthDev =
+ abs(((videoPeriod * 5 + mVsyncPeriod) % (mVsyncPeriod * 6)) - mVsyncPeriod)
+ / (mVsyncPeriod / 100);
+ // use 20 samples if we are doing 5:6 ratio +- 1% (e.g. playing 50Hz on 60Hz)
+ if (fiveSixthDev < 12) { /* 12% / 6 = 2% */
+ N = 20;
+ }
+
+ nsecs_t offset = 0;
+ nsecs_t edgeRemainder = 0;
+ for (size_t i = 1; i <= N; i++) {
+ offset +=
+ (renderTime + mTimeCorrection + videoPeriod * i - mVsyncTime) % mVsyncPeriod;
+ edgeRemainder += (videoPeriod * i) % mVsyncPeriod;
+ }
+ mTimeCorrection += mVsyncPeriod / 2 - offset / N;
+ renderTime += mTimeCorrection;
+ nsecs_t correctionLimit = mVsyncPeriod * 3 / 5;
+ edgeRemainder = abs(edgeRemainder / N - mVsyncPeriod / 2);
+ if (edgeRemainder <= mVsyncPeriod / 3) {
+ correctionLimit /= 2;
+ }
+
+ // estimate how many VSYNCs a frame will spend on the display
+ nsecs_t nextVsyncTime =
+ renderTime + mVsyncPeriod - ((renderTime - mVsyncTime) % mVsyncPeriod);
+ if (mLastVsyncTime >= 0) {
+ size_t minVsyncsPerFrame = videoPeriod / mVsyncPeriod;
+ size_t vsyncsForLastFrame = divRound(nextVsyncTime - mLastVsyncTime, mVsyncPeriod);
+ bool vsyncsPerFrameAreNearlyConstant =
+ periodicError(videoPeriod, mVsyncPeriod) / (mVsyncPeriod / 20) == 0;
+
+ if (mTimeCorrection > correctionLimit &&
+ (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame > minVsyncsPerFrame)) {
+ // remove a VSYNC
+ mTimeCorrection -= mVsyncPeriod / 2;
+ renderTime -= mVsyncPeriod / 2;
+ nextVsyncTime -= mVsyncPeriod;
+ --vsyncsForLastFrame;
+ } else if (mTimeCorrection < -correctionLimit &&
+ (vsyncsPerFrameAreNearlyConstant || vsyncsForLastFrame == minVsyncsPerFrame)) {
+ // add a VSYNC
+ mTimeCorrection += mVsyncPeriod / 2;
+ renderTime += mVsyncPeriod / 2;
+ nextVsyncTime += mVsyncPeriod;
+ ++vsyncsForLastFrame;
+ }
+ ATRACE_INT("FRAME_VSYNCS", vsyncsForLastFrame);
+ }
+ mLastVsyncTime = nextVsyncTime;
+ }
+
+ // align rendertime to the center between VSYNC edges
+ renderTime -= (renderTime - mVsyncTime) % mVsyncPeriod;
+ renderTime += mVsyncPeriod / 2;
+ ALOGV("adjusting render: %lld => %lld", (long long)origRenderTime, (long long)renderTime);
+ ATRACE_INT("FRAME_FLIP_IN(ms)", (renderTime - now) / 1000000);
+ return renderTime;
+}
+
+void VideoFrameScheduler::release() {
+ mComposer.clear();
+}
+
+VideoFrameScheduler::~VideoFrameScheduler() {
+ release();
+}
+
+} // namespace android
+
diff --git a/media/libmediaplayerservice/VideoFrameScheduler.h b/media/libmediaplayerservice/VideoFrameScheduler.h
new file mode 100644
index 0000000..19f0787
--- /dev/null
+++ b/media/libmediaplayerservice/VideoFrameScheduler.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VIDEO_FRAME_SCHEDULER_H_
+#define VIDEO_FRAME_SCHEDULER_H_
+
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+struct ISurfaceComposer;
+
+struct VideoFrameScheduler : public RefBase {
+ VideoFrameScheduler();
+
+ // (re)initialize scheduler
+ void init(float videoFps = -1);
+ // use in case of video render-time discontinuity, e.g. seek
+ void restart();
+ // get adjusted nanotime for a video frame render at renderTime
+ nsecs_t schedule(nsecs_t renderTime);
+
+ // returns the vsync period for the main display
+ nsecs_t getVsyncPeriod();
+
+ void release();
+
+ static const size_t kHistorySize = 8;
+
+protected:
+ virtual ~VideoFrameScheduler();
+
+private:
+ struct PLL {
+ PLL();
+
+ // reset PLL to new PLL
+ void reset(float fps = -1);
+ // keep current estimate, but restart phase
+ void restart();
+ // returns period
+ nsecs_t addSample(nsecs_t time);
+
+ private:
+ nsecs_t mPeriod;
+ nsecs_t mPhase;
+
+ bool mPrimed; // have an estimate for the period
+ size_t mSamplesUsedForPriming;
+
+ nsecs_t mLastTime; // last input time
+ nsecs_t mRefitAt; // next input time to fit at
+
+ size_t mNumSamples; // can go past kHistorySize
+ nsecs_t mTimes[kHistorySize];
+
+ void test();
+ void fit(nsecs_t phase, nsecs_t period, size_t numSamples,
+ int64_t *a, int64_t *b, int64_t *err);
+ void prime(size_t numSamples);
+ };
+
+ void updateVsync();
+
+ nsecs_t mVsyncTime; // vsync timing from display
+ nsecs_t mVsyncPeriod;
+ nsecs_t mVsyncRefreshAt; // next time to refresh timing info
+
+ nsecs_t mLastVsyncTime; // estimated vsync time for last frame
+ nsecs_t mTimeCorrection; // running adjustment
+
+ PLL mPll; // PLL for video frame rate based on render time
+
+ sp<ISurfaceComposer> mComposer;
+
+ DISALLOW_EVIL_CONSTRUCTORS(VideoFrameScheduler);
+};
+
+} // namespace android
+
+#endif // VIDEO_FRAME_SCHEDULER_H_
+
diff --git a/media/libmediaplayerservice/nuplayer/Android.mk b/media/libmediaplayerservice/nuplayer/Android.mk
index 0dd2b61..676c0a6 100644
--- a/media/libmediaplayerservice/nuplayer/Android.mk
+++ b/media/libmediaplayerservice/nuplayer/Android.mk
@@ -19,6 +19,7 @@
$(TOP)/frameworks/av/media/libstagefright/mpeg2ts \
$(TOP)/frameworks/av/media/libstagefright/rtsp \
$(TOP)/frameworks/av/media/libstagefright/timedtext \
+ $(TOP)/frameworks/av/media/libmediaplayerservice \
$(TOP)/frameworks/native/include/media/openmax
LOCAL_MODULE:= libstagefright_nuplayer
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 142107d..bd75034 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -158,7 +158,12 @@
int32_t totalBitrate = 0;
- for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ size_t numtracks = extractor->countTracks();
+ if (numtracks == 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ for (size_t i = 0; i < numtracks; ++i) {
sp<MediaSource> track = extractor->getTrack(i);
sp<MetaData> meta = extractor->getTrackMetaData(i);
@@ -1110,8 +1115,8 @@
if (mIsWidevine && !audio) {
// data is already provided in the buffer
ab = new ABuffer(NULL, mb->range_length());
- ab->meta()->setPointer("mediaBuffer", mb);
mb->add_ref();
+ ab->setMediaBufferBase(mb);
} else {
ab = new ABuffer(outLength);
memcpy(ab->data(),
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 5321deb..dad480d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -643,6 +643,13 @@
mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
mRendererLooper->registerHandler(mRenderer);
+ sp<MetaData> meta = getFileMeta();
+ int32_t rate;
+ if (meta != NULL
+ && meta->findInt32(kKeyFrameRate, &rate) && rate > 0) {
+ mRenderer->setVideoFrameRate(rate);
+ }
+
postScanSources();
break;
}
@@ -1270,8 +1277,8 @@
// Aggregate smaller buffers into a larger buffer.
// The goal is to reduce power consumption.
- // Unfortunately this does not work with the software AAC decoder.
- bool doBufferAggregation = (audio && mOffloadAudio);;
+ // Note this will not work if the decoder requires one frame per buffer.
+ bool doBufferAggregation = (audio && mOffloadAudio);
bool needMoreData = false;
bool dropAccessUnit;
@@ -1291,7 +1298,7 @@
return err;
} else if (err != OK) {
if (err == INFO_DISCONTINUITY) {
- if (mAggregateBuffer != NULL) {
+ if (doBufferAggregation && (mAggregateBuffer != NULL)) {
// We already have some data so save this for later.
mPendingAudioErr = err;
mPendingAudioAccessUnit = accessUnit;
@@ -1414,7 +1421,7 @@
mAggregateBuffer->setRange(0, 0); // start empty
}
- if (mAggregateBuffer != NULL) {
+ if (doBufferAggregation && (mAggregateBuffer != NULL)) {
int64_t timeUs;
int64_t dummy;
bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs);
@@ -1463,7 +1470,7 @@
mCCDecoder->decode(accessUnit);
}
- if (mAggregateBuffer != NULL) {
+ if (doBufferAggregation && (mAggregateBuffer != NULL)) {
ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu",
mAggregateBuffer->size());
reply->setBuffer("buffer", mAggregateBuffer);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 87f85e7..cdb860c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -283,14 +283,9 @@
// handle widevine classic source - that fills an arbitrary input buffer
MediaBuffer *mediaBuffer = NULL;
- if (hasBuffer && buffer->meta()->findPointer(
- "mediaBuffer", (void **)&mediaBuffer)) {
- if (mediaBuffer == NULL) {
- // received no actual buffer
- ALOGW("[%s] received null MediaBuffer %s",
- mComponentName.c_str(), msg->debugString().c_str());
- buffer = NULL;
- } else {
+ if (hasBuffer) {
+ mediaBuffer = (MediaBuffer *)(buffer->getMediaBufferBase());
+ if (mediaBuffer != NULL) {
// likely filled another buffer than we requested: adjust buffer index
size_t ix;
for (ix = 0; ix < mInputBuffers.size(); ix++) {
@@ -470,7 +465,9 @@
size_t bufferIx;
CHECK(msg->findSize("buffer-ix", &bufferIx));
if (msg->findInt32("render", &render) && render) {
- err = mCodec->renderOutputBufferAndRelease(bufferIx);
+ int64_t timestampNs;
+ CHECK(msg->findInt64("timestampNs", ×tampNs));
+ err = mCodec->renderOutputBufferAndRelease(bufferIx, timestampNs);
} else {
err = mCodec->releaseOutputBuffer(bufferIx);
}
@@ -598,16 +595,6 @@
{
if (!isStaleReply(msg)) {
onInputBufferFilled(msg);
- } else {
- /* release any MediaBuffer passed in the stale buffer */
- sp<ABuffer> buffer;
- MediaBuffer *mediaBuffer = NULL;
- if (msg->findBuffer("buffer", &buffer) &&
- buffer->meta()->findPointer(
- "mediaBuffer", (void **)&mediaBuffer) &&
- mediaBuffer != NULL) {
- mediaBuffer->release();
- }
}
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 7674616..a8c8818 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -26,6 +26,8 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <VideoFrameScheduler.h>
+
#include <inttypes.h>
namespace android {
@@ -137,6 +139,12 @@
(new AMessage(kWhatResume, id()))->post();
}
+void NuPlayer::Renderer::setVideoFrameRate(float fps) {
+ sp<AMessage> msg = new AMessage(kWhatSetVideoFrameRate, id());
+ msg->setFloat("frame-rate", fps);
+ msg->post();
+}
+
void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatStopAudioSink:
@@ -237,6 +245,14 @@
break;
}
+ case kWhatSetVideoFrameRate:
+ {
+ float fps;
+ CHECK(msg->findFloat("frame-rate", &fps));
+ onSetVideoFrameRate(fps);
+ break;
+ }
+
case kWhatAudioOffloadTearDown:
{
onAudioOffloadTearDown();
@@ -502,16 +518,20 @@
sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
msg->setInt32("generation", mVideoQueueGeneration);
- int64_t delayUs;
-
if (entry.mBuffer == NULL) {
// EOS doesn't carry a timestamp.
- delayUs = 0;
- } else if (mFlags & FLAG_REAL_TIME) {
+ msg->post();
+ mDrainVideoQueuePending = true;
+ return;
+ }
+
+ int64_t delayUs;
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t realTimeUs;
+ if (mFlags & FLAG_REAL_TIME) {
int64_t mediaTimeUs;
CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
-
- delayUs = mediaTimeUs - ALooper::GetNowUs();
+ realTimeUs = mediaTimeUs;
} else {
int64_t mediaTimeUs;
CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
@@ -520,23 +540,26 @@
mFirstAnchorTimeMediaUs = mediaTimeUs;
}
if (mAnchorTimeMediaUs < 0) {
- delayUs = 0;
-
if (!mHasAudio) {
mAnchorTimeMediaUs = mediaTimeUs;
- mAnchorTimeRealUs = ALooper::GetNowUs();
+ mAnchorTimeRealUs = nowUs;
notifyPosition();
}
+ realTimeUs = nowUs;
} else {
- int64_t realTimeUs =
+ realTimeUs =
(mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
-
- delayUs = realTimeUs - ALooper::GetNowUs();
}
}
+ realTimeUs = mVideoScheduler->schedule(realTimeUs * 1000) / 1000;
+ int64_t twoVsyncsUs = 2 * (mVideoScheduler->getVsyncPeriod() / 1000);
+
+ delayUs = realTimeUs - nowUs;
+
ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
- msg->post(delayUs);
+ // post 2 display refreshes before rendering is due
+ msg->post(delayUs > twoVsyncsUs ? delayUs - twoVsyncsUs : 0);
mDrainVideoQueuePending = true;
}
@@ -588,6 +611,7 @@
mVideoLateByUs = 0ll;
}
+ entry->mNotifyConsumed->setInt64("timestampNs", realTimeUs * 1000ll);
entry->mNotifyConsumed->setInt32("render", !tooLate);
entry->mNotifyConsumed->post();
mVideoQueue.erase(mVideoQueue.begin());
@@ -630,6 +654,10 @@
mHasAudio = true;
} else {
mHasVideo = true;
+ if (mVideoScheduler == NULL) {
+ mVideoScheduler = new VideoFrameScheduler();
+ mVideoScheduler->init();
+ }
}
if (dropBufferWhileFlushing(audio, msg)) {
@@ -795,6 +823,10 @@
mDrainVideoQueuePending = false;
++mVideoQueueGeneration;
+ if (mVideoScheduler != NULL) {
+ mVideoScheduler->restart();
+ }
+
prepareForMediaRenderingStart();
}
@@ -946,6 +978,13 @@
}
}
+void NuPlayer::Renderer::onSetVideoFrameRate(float fps) {
+ if (mVideoScheduler == NULL) {
+ mVideoScheduler = new VideoFrameScheduler();
+ }
+ mVideoScheduler->init(fps);
+}
+
// TODO: Remove unnecessary calls to getPlayedOutAudioDurationUs()
// as it acquires locks and may query the audio driver.
//
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index 97fdae7..e28071f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -23,6 +23,7 @@
namespace android {
struct ABuffer;
+struct VideoFrameScheduler;
struct NuPlayer::Renderer : public AHandler {
enum Flags {
@@ -56,6 +57,8 @@
void pause();
void resume();
+ void setVideoFrameRate(float fps);
+
enum {
kWhatEOS = 'eos ',
kWhatFlushComplete = 'fluC',
@@ -82,6 +85,7 @@
kWhatResume = 'resm',
kWhatStopAudioSink = 'stpA',
kWhatDisableOffloadAudio = 'noOA',
+ kWhatSetVideoFrameRate = 'sVFR',
};
struct QueueEntry {
@@ -100,6 +104,7 @@
List<QueueEntry> mAudioQueue;
List<QueueEntry> mVideoQueue;
uint32_t mNumFramesWritten;
+ sp<VideoFrameScheduler> mVideoScheduler;
bool mDrainAudioQueuePending;
bool mDrainVideoQueuePending;
@@ -147,6 +152,7 @@
void onDisableOffloadAudio();
void onPause();
void onResume();
+ void onSetVideoFrameRate(float fps);
void onAudioOffloadTearDown();
void notifyEOS(bool audio, status_t finalResult, int64_t delayUs = 0);
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 9b03b71..3c04859 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -3782,23 +3782,12 @@
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_COMPONENT);
info->mStatus = BufferInfo::OWNED_BY_US;
- const sp<AMessage> &bufferMeta = info->mData->meta();
- void *mediaBuffer;
- if (bufferMeta->findPointer("mediaBuffer", &mediaBuffer)
- && mediaBuffer != NULL) {
- // We're in "store-metadata-in-buffers" mode, the underlying
- // OMX component had access to data that's implicitly refcounted
- // by this "mediaBuffer" object. Now that the OMX component has
- // told us that it's done with the input buffer, we can decrement
- // the mediaBuffer's reference count.
-
- ALOGV("releasing mbuf %p", mediaBuffer);
-
- ((MediaBuffer *)mediaBuffer)->release();
- mediaBuffer = NULL;
-
- bufferMeta->setPointer("mediaBuffer", NULL);
- }
+ // We're in "store-metadata-in-buffers" mode, the underlying
+ // OMX component had access to data that's implicitly refcounted
+ // by this "MediaBuffer" object. Now that the OMX component has
+ // told us that it's done with the input buffer, we can decrement
+ // the mediaBuffer's reference count.
+ info->mData->setMediaBufferBase(NULL);
PortMode mode = getPortMode(kPortIndexInput);
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 1a80dcc..27cd231 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -37,19 +37,6 @@
namespace android {
-static void ReleaseMediaBufferReference(const sp<ABuffer> &accessUnit) {
- void *mbuf;
- if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
- && mbuf != NULL) {
- ALOGV("releasing mbuf %p", mbuf);
-
- accessUnit->meta()->setPointer("mediaBuffer", NULL);
-
- static_cast<MediaBuffer *>(mbuf)->release();
- mbuf = NULL;
- }
-}
-
struct MediaCodecSource::Puller : public AHandler {
Puller(const sp<MediaSource> &source);
@@ -477,7 +464,7 @@
for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
- ReleaseMediaBufferReference(accessUnit);
+ accessUnit->setMediaBufferBase(NULL);
}
mEncoderInputBuffers.clear();
@@ -608,8 +595,8 @@
if (mIsVideo) {
// video encoder will release MediaBuffer when done
// with underlying data.
- mEncoderInputBuffers.itemAt(bufferIndex)->meta()
- ->setPointer("mediaBuffer", mbuf);
+ mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase(
+ mbuf);
} else {
mbuf->release();
}
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 456be89..fb27dca 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -137,6 +137,7 @@
mOutputDelayRingBuffer = new short[mOutputDelayRingBufferSize];
mOutputDelayRingBufferWritePos = 0;
mOutputDelayRingBufferReadPos = 0;
+ mOutputDelayRingBufferFilled = 0;
if (mAACDecoder == NULL) {
ALOGE("AAC decoder is null. TODO: Can not call aacDecoder_SetParam in the following code");
@@ -408,6 +409,13 @@
}
bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
+ if (numSamples == 0) {
+ return true;
+ }
+ if (outputDelayRingBufferSpaceLeft() < numSamples) {
+ ALOGE("RING BUFFER WOULD OVERFLOW");
+ return false;
+ }
if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
&& (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
|| mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
@@ -419,10 +427,6 @@
if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
}
- if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
- ALOGE("RING BUFFER OVERFLOW");
- return false;
- }
} else {
ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");
@@ -432,16 +436,19 @@
if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
}
- if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
- ALOGE("RING BUFFER OVERFLOW");
- return false;
- }
}
}
+ mOutputDelayRingBufferFilled += numSamples;
return true;
}
int32_t SoftAAC2::outputDelayRingBufferGetSamples(INT_PCM *samples, int32_t numSamples) {
+
+ if (numSamples > mOutputDelayRingBufferFilled) {
+ ALOGE("RING BUFFER WOULD UNDERRUN");
+ return -1;
+ }
+
if (mOutputDelayRingBufferReadPos + numSamples <= mOutputDelayRingBufferSize
&& (mOutputDelayRingBufferWritePos < mOutputDelayRingBufferReadPos
|| mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferReadPos + numSamples)) {
@@ -460,10 +467,6 @@
ALOGV("slow SoftAAC2::outputDelayRingBufferGetSamples()");
for (int32_t i = 0; i < numSamples; i++) {
- if (mOutputDelayRingBufferWritePos == mOutputDelayRingBufferReadPos) {
- ALOGE("RING BUFFER UNDERRUN");
- return -1;
- }
if (samples != 0) {
samples[i] = mOutputDelayRingBuffer[mOutputDelayRingBufferReadPos];
}
@@ -473,22 +476,15 @@
}
}
}
+ mOutputDelayRingBufferFilled -= numSamples;
return numSamples;
}
int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
- int32_t available = mOutputDelayRingBufferWritePos - mOutputDelayRingBufferReadPos;
- if (available < 0) {
- available += mOutputDelayRingBufferSize;
- }
- if (available < 0) {
- ALOGE("FATAL RING BUFFER ERROR");
- return 0;
- }
- return available;
+ return mOutputDelayRingBufferFilled;
}
-int32_t SoftAAC2::outputDelayRingBufferSamplesLeft() {
+int32_t SoftAAC2::outputDelayRingBufferSpaceLeft() {
return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
}
@@ -512,6 +508,11 @@
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
mEndOfInput = (inHeader->nFlags & OMX_BUFFERFLAG_EOS) != 0;
+
+ if (mInputBufferCount == 0 && !(inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG)) {
+ ALOGE("first buffer should have OMX_BUFFERFLAG_CODECCONFIG set");
+ inHeader->nFlags |= OMX_BUFFERFLAG_CODECCONFIG;
+ }
if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) != 0) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
@@ -650,7 +651,7 @@
AAC_DECODER_ERROR decoderErr;
do {
- if (outputDelayRingBufferSamplesLeft() <
+ if (outputDelayRingBufferSpaceLeft() <
(mStreamInfo->frameSize * mStreamInfo->numChannels)) {
ALOGV("skipping decode: not enough space left in ringbuffer");
break;
@@ -1024,6 +1025,7 @@
mOutputDelayCompensated = 0;
mOutputDelayRingBufferWritePos = 0;
mOutputDelayRingBufferReadPos = 0;
+ mOutputDelayRingBufferFilled = 0;
mEndOfInput = false;
mEndOfOutput = false;
mBufferTimestamps.clear();
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index 9fcb598..c3e4459 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -85,10 +85,11 @@
short *mOutputDelayRingBuffer;
int32_t mOutputDelayRingBufferWritePos;
int32_t mOutputDelayRingBufferReadPos;
+ int32_t mOutputDelayRingBufferFilled;
bool outputDelayRingBufferPutSamples(INT_PCM *samples, int numSamples);
int32_t outputDelayRingBufferGetSamples(INT_PCM *samples, int numSamples);
int32_t outputDelayRingBufferSamplesAvailable();
- int32_t outputDelayRingBufferSamplesLeft();
+ int32_t outputDelayRingBufferSpaceLeft();
DISALLOW_EVIL_CONSTRUCTORS(SoftAAC2);
};
diff --git a/media/libstagefright/foundation/ABuffer.cpp b/media/libstagefright/foundation/ABuffer.cpp
index c93c7e8..b214870 100644
--- a/media/libstagefright/foundation/ABuffer.cpp
+++ b/media/libstagefright/foundation/ABuffer.cpp
@@ -19,11 +19,13 @@
#include "ADebug.h"
#include "ALooper.h"
#include "AMessage.h"
+#include "MediaBufferBase.h"
namespace android {
ABuffer::ABuffer(size_t capacity)
- : mData(malloc(capacity)),
+ : mMediaBufferBase(NULL),
+ mData(malloc(capacity)),
mCapacity(capacity),
mRangeOffset(0),
mRangeLength(capacity),
@@ -32,7 +34,8 @@
}
ABuffer::ABuffer(void *data, size_t capacity)
- : mData(data),
+ : mMediaBufferBase(NULL),
+ mData(data),
mCapacity(capacity),
mRangeOffset(0),
mRangeLength(capacity),
@@ -59,6 +62,8 @@
if (mFarewell != NULL) {
mFarewell->post();
}
+
+ setMediaBufferBase(NULL);
}
void ABuffer::setRange(size_t offset, size_t size) {
@@ -80,5 +85,19 @@
return mMeta;
}
+MediaBufferBase *ABuffer::getMediaBufferBase() {
+ if (mMediaBufferBase != NULL) {
+ mMediaBufferBase->add_ref();
+ }
+ return mMediaBufferBase;
+}
+
+void ABuffer::setMediaBufferBase(MediaBufferBase *mediaBuffer) {
+ if (mMediaBufferBase != NULL) {
+ mMediaBufferBase->release();
+ }
+ mMediaBufferBase = mediaBuffer;
+}
+
} // namespace android
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index b465566..a289637 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -63,6 +63,7 @@
mSwapMask(0),
mCheckBandwidthGeneration(0),
mSwitchGeneration(0),
+ mSubtitleGeneration(0),
mLastDequeuedTimeUs(0ll),
mRealTimeBaseUs(0ll),
mReconfigurationInProgress(false),
@@ -289,6 +290,11 @@
mLastDequeuedTimeUs = timeUs;
mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
} else if (stream == STREAMTYPE_SUBTITLES) {
+ int32_t subtitleGeneration;
+ if ((*accessUnit)->meta()->findInt32("subtitleGeneration", &subtitleGeneration)
+ && subtitleGeneration != mSubtitleGeneration) {
+ return -EAGAIN;
+ };
(*accessUnit)->meta()->setInt32(
"trackIndex", mPlaylist->getSelectedIndex());
(*accessUnit)->meta()->setInt64("baseUs", mRealTimeBaseUs);
@@ -759,7 +765,7 @@
notify->setInt32("switchGeneration", mSwitchGeneration);
FetcherInfo info;
- info.mFetcher = new PlaylistFetcher(notify, this, uri);
+ info.mFetcher = new PlaylistFetcher(notify, this, uri, mSubtitleGeneration);
info.mDurationUs = -1ll;
info.mIsPrepared = false;
info.mToBeRemoved = false;
@@ -1065,6 +1071,24 @@
return index;
}
+int64_t LiveSession::latestMediaSegmentStartTimeUs() {
+ sp<AMessage> audioMeta = mPacketSources.valueFor(STREAMTYPE_AUDIO)->getLatestDequeuedMeta();
+ int64_t minSegmentStartTimeUs = -1, videoSegmentStartTimeUs = -1;
+ if (audioMeta != NULL) {
+ audioMeta->findInt64("segmentStartTimeUs", &minSegmentStartTimeUs);
+ }
+
+ sp<AMessage> videoMeta = mPacketSources.valueFor(STREAMTYPE_VIDEO)->getLatestDequeuedMeta();
+ if (videoMeta != NULL
+ && videoMeta->findInt64("segmentStartTimeUs", &videoSegmentStartTimeUs)) {
+ if (minSegmentStartTimeUs < 0 || videoSegmentStartTimeUs < minSegmentStartTimeUs) {
+ minSegmentStartTimeUs = videoSegmentStartTimeUs;
+ }
+
+ }
+ return minSegmentStartTimeUs;
+}
+
status_t LiveSession::onSeek(const sp<AMessage> &msg) {
int64_t timeUs;
CHECK(msg->findInt64("timeUs", &timeUs));
@@ -1117,6 +1141,11 @@
}
status_t LiveSession::selectTrack(size_t index, bool select) {
+ if (mPlaylist == NULL) {
+ return INVALID_OPERATION;
+ }
+
+ ++mSubtitleGeneration;
status_t err = mPlaylist->selectTrack(index, select);
if (err == OK) {
sp<AMessage> msg = new AMessage(kWhatChangeConfiguration, id());
@@ -1399,6 +1428,10 @@
int32_t discontinuitySeq = -1;
sp<AnotherPacketSource> sources[kMaxStreams];
+ if (i == kSubtitleIndex) {
+ segmentStartTimeUs = latestMediaSegmentStartTimeUs();
+ }
+
// TRICKY: looping from i as earlier streams are already removed from streamMask
for (size_t j = i; j < kMaxStreams; ++j) {
const AString &streamUri = switching ? mStreams[j].mNewUri : mStreams[j].mUri;
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 6be86cf..7aacca6 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -189,6 +189,7 @@
int32_t mCheckBandwidthGeneration;
int32_t mSwitchGeneration;
+ int32_t mSubtitleGeneration;
size_t mContinuationCounter;
sp<AMessage> mContinuation;
@@ -240,6 +241,7 @@
const char *url, uint8_t *curPlaylistHash, bool *unchanged);
size_t getBandwidthIndex();
+ int64_t latestMediaSegmentStartTimeUs();
static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
static StreamType indexToType(int idx);
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index f78f8b4..30fa868 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -55,7 +55,8 @@
PlaylistFetcher::PlaylistFetcher(
const sp<AMessage> ¬ify,
const sp<LiveSession> &session,
- const char *uri)
+ const char *uri,
+ int32_t subtitleGeneration)
: mNotify(notify),
mStartTimeUsNotify(notify->dup()),
mSession(session),
@@ -73,6 +74,7 @@
mPrepared(false),
mNextPTSTimeUs(-1ll),
mMonitorQueueGeneration(0),
+ mSubtitleGeneration(subtitleGeneration),
mRefreshState(INITIAL_MINIMUM_RELOAD_DELAY),
mFirstPTSValid(false),
mAbsoluteTimeAnchorUs(0ll),
@@ -1407,6 +1409,7 @@
buffer->meta()->setInt64("durationUs", durationUs);
buffer->meta()->setInt64("segmentStartTimeUs", getSegmentStartTimeUs(mSeqNumber));
buffer->meta()->setInt32("discontinuitySeq", mDiscontinuitySeq);
+ buffer->meta()->setInt32("subtitleGeneration", mSubtitleGeneration);
packetSource->queueAccessUnit(buffer);
return OK;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index 4b09df1..78c358f 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -49,7 +49,8 @@
PlaylistFetcher(
const sp<AMessage> ¬ify,
const sp<LiveSession> &session,
- const char *uri);
+ const char *uri,
+ int32_t subtitleGeneration);
sp<DataSource> getDataSource();
@@ -133,6 +134,7 @@
int64_t mNextPTSTimeUs;
int32_t mMonitorQueueGeneration;
+ const int32_t mSubtitleGeneration;
enum RefreshState {
INITIAL_MINIMUM_RELOAD_DELAY,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index c74c3e7..a03f6f9 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -254,11 +254,6 @@
int32_t oldDiscontinuityType;
if (!oldBuffer->meta()->findInt32(
"discontinuity", &oldDiscontinuityType)) {
- MediaBuffer *mbuf = NULL;
- oldBuffer->meta()->findPointer("mediaBuffer", (void**)&mbuf);
- if (mbuf != NULL) {
- mbuf->release();
- }
it = mBuffers.erase(it);
continue;
}
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 753b3ec..2834a66 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -74,19 +74,6 @@
}
}
-static void ReleaseMediaBufferReference(const sp<ABuffer> &accessUnit) {
- void *mbuf;
- if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
- && mbuf != NULL) {
- ALOGV("releasing mbuf %p", mbuf);
-
- accessUnit->meta()->setPointer("mediaBuffer", NULL);
-
- static_cast<MediaBuffer *>(mbuf)->release();
- mbuf = NULL;
- }
-}
-
void Converter::releaseEncoder() {
if (mEncoder == NULL) {
return;
@@ -95,18 +82,7 @@
mEncoder->release();
mEncoder.clear();
- while (!mInputBufferQueue.empty()) {
- sp<ABuffer> accessUnit = *mInputBufferQueue.begin();
- mInputBufferQueue.erase(mInputBufferQueue.begin());
-
- ReleaseMediaBufferReference(accessUnit);
- }
-
- for (size_t i = 0; i < mEncoderInputBuffers.size(); ++i) {
- sp<ABuffer> accessUnit = mEncoderInputBuffers.itemAt(i);
- ReleaseMediaBufferReference(accessUnit);
- }
-
+ mInputBufferQueue.clear();
mEncoderInputBuffers.clear();
mEncoderOutputBuffers.clear();
}
@@ -328,7 +304,7 @@
sp<ABuffer> accessUnit;
CHECK(msg->findBuffer("accessUnit", &accessUnit));
- ReleaseMediaBufferReference(accessUnit);
+ accessUnit->setMediaBufferBase(NULL);
}
break;
}
@@ -351,15 +327,16 @@
ALOGI("dropping frame.");
}
- ReleaseMediaBufferReference(accessUnit);
+ accessUnit->setMediaBufferBase(NULL);
break;
}
#if 0
- void *mbuf;
- if (accessUnit->meta()->findPointer("mediaBuffer", &mbuf)
- && mbuf != NULL) {
+ MediaBuffer *mbuf =
+ (MediaBuffer *)(accessUnit->getMediaBufferBase());
+ if (mbuf != NULL) {
ALOGI("queueing mbuf %p", mbuf);
+ mbuf->release();
}
#endif
@@ -647,13 +624,13 @@
buffer->data(),
buffer->size());
- void *mediaBuffer;
- if (buffer->meta()->findPointer("mediaBuffer", &mediaBuffer)
- && mediaBuffer != NULL) {
- mEncoderInputBuffers.itemAt(bufferIndex)->meta()
- ->setPointer("mediaBuffer", mediaBuffer);
+ MediaBuffer *mediaBuffer =
+ (MediaBuffer *)(buffer->getMediaBufferBase());
+ if (mediaBuffer != NULL) {
+ mEncoderInputBuffers.itemAt(bufferIndex)->setMediaBufferBase(
+ mediaBuffer);
- buffer->meta()->setPointer("mediaBuffer", NULL);
+ buffer->setMediaBufferBase(NULL);
}
} else {
flags = MediaCodec::BUFFER_FLAG_EOS;
diff --git a/media/libstagefright/wifi-display/source/MediaPuller.cpp b/media/libstagefright/wifi-display/source/MediaPuller.cpp
index 7e8891d..86b918f 100644
--- a/media/libstagefright/wifi-display/source/MediaPuller.cpp
+++ b/media/libstagefright/wifi-display/source/MediaPuller.cpp
@@ -179,7 +179,7 @@
} else {
// video encoder will release MediaBuffer when done
// with underlying data.
- accessUnit->meta()->setPointer("mediaBuffer", mbuf);
+ accessUnit->setMediaBufferBase(mbuf);
}
sp<AMessage> notify = mNotify->dup();
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index de31e23..f110b66 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -573,6 +573,11 @@
continue;
}
uint8_t afMode = entry.data.u8[0];
+ if (afMode == ANDROID_CONTROL_AF_MODE_OFF) {
+ // Skip all the ZSL buffer for manual AF mode, as we don't really
+ // know the af state.
+ continue;
+ }
// Check AF state if device has focuser and focus mode isn't fixed
if (mHasFocuser && !isFixedFocusMode(afMode)) {
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 80c797a..e3301aa 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -512,12 +512,24 @@
status_t CameraDeviceClient::dump(int fd, const Vector<String16>& args) {
String8 result;
- result.appendFormat("CameraDeviceClient[%d] (%p) PID: %d, dump:\n",
+ result.appendFormat("CameraDeviceClient[%d] (%p) dump:\n",
mCameraId,
- getRemoteCallback()->asBinder().get(),
- mClientPid);
- result.append(" State: ");
+ getRemoteCallback()->asBinder().get());
+ result.appendFormat(" Current client: %s (PID %d, UID %u)\n",
+ String8(mClientPackageName).string(),
+ mClientPid, mClientUid);
+ result.append(" State:\n");
+ result.appendFormat(" Request ID counter: %d\n", mRequestIdCounter);
+ if (!mStreamMap.isEmpty()) {
+ result.append(" Current stream IDs:\n");
+ for (size_t i = 0; i < mStreamMap.size(); i++) {
+ result.appendFormat(" Stream %d\n", mStreamMap.valueAt(i));
+ }
+ } else {
+ result.append(" No streams configured.\n");
+ }
+ write(fd, result.string(), result.size());
// TODO: print dynamic/request section from most recent requests
mFrameProcessor->dump(fd, args);
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
index f8823a3..2ea460f 100644
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
@@ -336,11 +336,11 @@
mCameraId,
getRemoteCallback()->asBinder().get(),
mClientPid);
- result.append(" State: ");
+ result.append(" State:\n");
+ write(fd, result.string(), result.size());
// TODO: print dynamic/request section from most recent requests
mFrameProcessor->dump(fd, args);
-
return dumpDevice(fd, args);
}
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 24d173c..d6db151 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -57,8 +57,10 @@
mSharedCameraCallbacks(remoteCallback),
mDeviceVersion(cameraService->getDeviceVersion(cameraId))
{
- ALOGI("Camera %d: Opened", cameraId);
+ ALOGI("Camera %d: Opened. Client: %s (PID %d, UID %d)", cameraId,
+ String8(clientPackageName).string(), clientPid, clientUid);
+ mInitialClientPid = clientPid;
mDevice = CameraDeviceFactory::createDevice(cameraId);
LOG_ALWAYS_FATAL_IF(mDevice == 0, "Device should never be NULL here.");
}
@@ -114,7 +116,10 @@
disconnect();
- ALOGI("Closed Camera %d", TClientBase::mCameraId);
+ ALOGI("Closed Camera %d. Client was: %s (PID %d, UID %u)",
+ TClientBase::mCameraId,
+ String8(TClientBase::mClientPackageName).string(),
+ mInitialClientPid, TClientBase::mClientUid);
}
template <typename TClientBase>
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index f57d204..d198e4e 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -107,6 +107,9 @@
protected:
+ // The PID provided in the constructor call
+ pid_t mInitialClientPid;
+
virtual sp<IBinder> asBinderWrapper() {
return IInterface::asBinder();
}